From 6b3541dceb2dd2718398e559077ef2c1b4688c76 Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Thu, 8 Oct 2026 09:42:46 +0000 Subject: [PATCH 001/129] feat(cli)!: remove deprecated commands and the supabase-go binary (#6609) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the last of the Go CLI: the deprecated commands and flags the TypeScript CLI still forwarded to a bundled `supabase-go` binary, and the Go source tree, toolchain and packaging behind them. Targets `next`, so it ships in the next major. The user-facing changes are in the `BREAKING CHANGE` footer at the end, which is what the release notes pick up. ## Notes for reviewers - The removed commands and flags stay registered but hidden, so using one exits 1 with its replacement rather than an unknown-command error. Each records a `RemovedSurfaceError` telemetry fingerprint (`removed_command` / `removed_flag`), so remaining usage can be tracked before the stubs are deleted outright. - `GoProxy` and its wiring in `cli/root.ts` and `shared/cli/run.ts` are deleted. - `apps/cli-go/` is deleted (its `LICENSE` moves to the repo root), along with the Go CI workflows, toolchain entries and Go-specific lint, format, knip, CODEOWNERS and dependabot config. Every platform package ships one `supabase` binary, and the install script removes a leftover `supabase-go`. - The `pkg/v*` tags of `github.com/supabase/cli/pkg` are frozen and protected by a ruleset. [ADR 0030](docs/adr/0030-go-cli-removal.md) records the removal, supersedes ADR 0016, and lists the known external importers of that module. Part of CLI-2432 BREAKING CHANGE: Removed the deprecated commands and flags that still ran through the bundled Go binary, and the `supabase-go` binary itself. If your scripts or CI use any of these, switch to the replacement. Each removed command or flag now exits with code 1 and prints its replacement. - `supabase db remote changes` → `supabase db diff --linked` - `supabase gen keys` → `supabase projects api-keys --project-ref ` - `supabase db diff --use-pg-schema` → omit the flag to use the default engine, or pass `--use-pg-delta` - `supabase functions download --legacy-bundle` → `supabase functions download --use-api`. If that also fails and the Function was deployed with a CLI older than 1.120.0, redeploy it with the current CLI first. - `supabase db branch create`, `delete`, `list` and `switch` → removed with no local replacement. Hosted preview branches are managed with `supabase branches`. - `--create-ticket` (global flag) → removed. Report CLI problems with `supabase issue bug`. - `supabase-go` → no longer installed. Call `supabase` instead. --------- Co-authored-by: Claude Sonnet 5 --- .gitattributes | 17 +- .github/CODEOWNERS | 7 - .github/ISSUE_TEMPLATE/bug-report.yml | 2 +- .github/actions/setup/action.yml | 25 - .github/dependabot.yml | 22 - .github/workflows/build-cli-artifacts.yml | 38 +- .github/workflows/cli-go-api-sync.yml | 87 - .github/workflows/cli-go-ci.yml | 137 - .github/workflows/cli-go-mirror.yml | 64 - .github/workflows/cli-go-tag-pkg.yml | 43 - .../{cli-go-codeql.yml => codeql.yml} | 30 +- .github/workflows/lint-pull-request.yml | 1 - ...i-go-mirror-image.yml => mirror-image.yml} | 0 .github/workflows/mirror-template-images.yml | 11 +- ...go-publish-migra.yml => publish-migra.yml} | 5 +- ...i-go-pg-prove.yml => publish-pg-prove.yml} | 5 +- .github/workflows/slim-release-published.yml | 7 +- .github/workflows/test.yml | 23 - .github/workflows/verify-install-channels.yml | 60 +- .oxfmtrc.json | 1 - .oxlintrc.json | 1 - CONTRIBUTING.md | 41 +- apps/cli-go/LICENSE => LICENSE | 0 README.md | 1 - apps/cli-e2e/.env.example | 6 - apps/cli-e2e/AGENTS.md | 29 - apps/cli-e2e/src/tests/functions.e2e.test.ts | 4 +- .../src/tests/go-binary-surface.e2e.test.ts | 210 - apps/cli-go/.gitignore | 33 - apps/cli-go/.golangci.yml | 49 - apps/cli-go/CONTRIBUTING.md | 50 - apps/cli-go/api/README.md | 42 - apps/cli-go/api/overlay.yaml | 79 - apps/cli-go/api/v1-openapi.yaml | 16788 ----------- apps/cli-go/cmd/db.go | 136 - apps/cli-go/cmd/functions.go | 51 - apps/cli-go/cmd/gen.go | 54 - apps/cli-go/cmd/root.go | 379 - apps/cli-go/cmd/root_analytics.go | 180 - apps/cli-go/cmd/root_test.go | 137 - apps/cli-go/fsevents/fsevents.go | 117 - apps/cli-go/fsevents/go.mod | 3 - apps/cli-go/fsevents/wrap.go | 40 - apps/cli-go/go.mod | 379 - apps/cli-go/go.sum | 1232 - apps/cli-go/internal/db/diff/diff.go | 254 - apps/cli-go/internal/db/diff/diff_test.go | 516 - apps/cli-go/internal/db/diff/migra.go | 162 - apps/cli-go/internal/db/diff/pgschema.go | 58 - .../cli-go/internal/db/diff/save_diff_test.go | 50 - apps/cli-go/internal/db/diff/shadow.go | 74 - .../internal/db/diff/templates/migra.sh | 30 - .../internal/db/diff/templates/migra.ts | 150 - apps/cli-go/internal/db/reset/reset.go | 313 - apps/cli-go/internal/db/reset/reset_test.go | 568 - apps/cli-go/internal/db/start/start.go | 425 - apps/cli-go/internal/db/start/start_test.go | 434 - .../internal/db/start/templates/_supabase.sql | 13 - .../internal/db/start/templates/restore.sh | 44 - .../internal/db/start/templates/schema.sql | 17 - .../internal/db/start/templates/webhook.sql | 232 - apps/cli-go/internal/debug/http.go | 24 - apps/cli-go/internal/debug/postgres.go | 262 - apps/cli-go/internal/debug/postgres_test.go | 65 - .../internal/functions/download/download.go | 650 - .../functions/download/download_test.go | 914 - .../gen/types/templates/prod-ca-2021.crt | 23 - .../gen/types/templates/prod-ca-2025.crt | 23 - .../gen/types/templates/staging-ca-2021.crt | 23 - apps/cli-go/internal/gen/types/types.go | 253 - apps/cli-go/internal/gen/types/types_test.go | 192 - apps/cli-go/internal/migration/apply/apply.go | 68 - .../internal/migration/apply/apply_test.go | 80 - apps/cli-go/internal/migration/down/down.go | 64 - .../internal/migration/down/down_test.go | 139 - apps/cli-go/internal/migration/list/list.go | 87 - .../internal/migration/list/list_test.go | 193 - apps/cli-go/internal/migration/new/new.go | 46 - .../cli-go/internal/migration/new/new_test.go | 72 - .../internal/migration/repair/repair.go | 108 - .../internal/migration/repair/repair_test.go | 160 - apps/cli-go/internal/seed/buckets/buckets.go | 84 - .../internal/seed/buckets/buckets_test.go | 297 - apps/cli-go/internal/status/status.go | 392 - apps/cli-go/internal/status/status_test.go | 214 - apps/cli-go/internal/storage/client/api.go | 46 - apps/cli-go/internal/storage/client/scheme.go | 38 - .../internal/storage/client/scheme_test.go | 83 - apps/cli-go/internal/telemetry/client.go | 149 - apps/cli-go/internal/telemetry/client_test.go | 169 - apps/cli-go/internal/telemetry/events.go | 184 - apps/cli-go/internal/telemetry/project.go | 109 - .../cli-go/internal/telemetry/project_test.go | 126 - apps/cli-go/internal/telemetry/service.go | 294 - .../cli-go/internal/telemetry/service_test.go | 570 - apps/cli-go/internal/telemetry/state.go | 192 - apps/cli-go/internal/telemetry/state_test.go | 289 - .../cli-go/internal/testing/apitest/docker.go | 156 - .../internal/testing/apitest/platform.go | 60 - apps/cli-go/internal/testing/fstest/create.go | 20 - apps/cli-go/internal/testing/fstest/open.go | 28 - apps/cli-go/internal/testing/fstest/stat.go | 20 - apps/cli-go/internal/testing/fstest/stdin.go | 28 - apps/cli-go/internal/testing/fstest/stdout.go | 26 - .../cli-go/internal/testing/helper/history.go | 28 - .../internal/testing/helper/privileges.go | 19 - apps/cli-go/internal/utils/access_token.go | 138 - .../internal/utils/access_token_test.go | 243 - apps/cli-go/internal/utils/agent.go | 24 - apps/cli-go/internal/utils/agent/agent.go | 61 - .../cli-go/internal/utils/agent/agent_test.go | 102 - apps/cli-go/internal/utils/api.go | 165 - apps/cli-go/internal/utils/api_test.go | 299 - apps/cli-go/internal/utils/cloudflare/api.go | 29 - apps/cli-go/internal/utils/cloudflare/dns.go | 154 - .../internal/utils/cloudflare/dns_test.go | 128 - apps/cli-go/internal/utils/colors.go | 28 - apps/cli-go/internal/utils/config.go | 268 - apps/cli-go/internal/utils/config_test.go | 197 - apps/cli-go/internal/utils/connect.go | 386 - apps/cli-go/internal/utils/connect_test.go | 440 - apps/cli-go/internal/utils/console.go | 107 - apps/cli-go/internal/utils/console_test.go | 55 - .../cli-go/internal/utils/container_output.go | 53 - .../internal/utils/container_output_test.go | 82 - .../internal/utils/credentials/input.go | 30 - .../internal/utils/credentials/input_test.go | 35 - .../utils/credentials/keyring_test.go | 28 - .../internal/utils/credentials/store.go | 88 - .../internal/utils/credentials/store_test.go | 99 - apps/cli-go/internal/utils/deno.go | 272 - apps/cli-go/internal/utils/deno_darwin.go | 29 - apps/cli-go/internal/utils/deno_generic.go | 22 - apps/cli-go/internal/utils/deno_test.go | 160 - apps/cli-go/internal/utils/denos/extract.ts | 71 - apps/cli-go/internal/utils/docker.go | 686 - apps/cli-go/internal/utils/docker_darwin.go | 11 - apps/cli-go/internal/utils/docker_linux.go | 12 - apps/cli-go/internal/utils/docker_test.go | 386 - .../cli-go/internal/utils/docker_wait_test.go | 66 - apps/cli-go/internal/utils/docker_windows.go | 15 - apps/cli-go/internal/utils/edgeruntime.go | 164 - .../cli-go/internal/utils/edgeruntime_test.go | 168 - apps/cli-go/internal/utils/enum.go | 35 - .../internal/utils/flags/config_path.go | 26 - apps/cli-go/internal/utils/flags/db_url.go | 293 - .../internal/utils/flags/db_url_test.go | 180 - .../internal/utils/flags/project_ref.go | 76 - .../internal/utils/flags/project_ref_test.go | 116 - apps/cli-go/internal/utils/git.go | 27 - .../internal/utils/identity_transport.go | 21 - .../internal/utils/identity_transport_test.go | 101 - apps/cli-go/internal/utils/logger.go | 15 - apps/cli-go/internal/utils/misc.go | 318 - apps/cli-go/internal/utils/misc_test.go | 217 - apps/cli-go/internal/utils/output.go | 123 - apps/cli-go/internal/utils/output_test.go | 146 - apps/cli-go/internal/utils/plan_gate.go | 97 - apps/cli-go/internal/utils/plan_gate_test.go | 251 - apps/cli-go/internal/utils/profile.go | 160 - apps/cli-go/internal/utils/profile_test.go | 98 - apps/cli-go/internal/utils/prompt.go | 144 - apps/cli-go/internal/utils/release.go | 48 - apps/cli-go/internal/utils/release_test.go | 61 - apps/cli-go/internal/utils/render.go | 60 - apps/cli-go/internal/utils/retry.go | 35 - apps/cli-go/internal/utils/slice.go | 12 - apps/cli-go/internal/utils/slice_test.go | 30 - apps/cli-go/internal/utils/supabase_home.go | 24 - apps/cli-go/internal/utils/tea.go | 201 - .../internal/utils/templates/globals.sql | 146 - .../utils/templates/initial_schemas/13.sql | 3094 -- .../utils/templates/initial_schemas/14.sql | 3690 --- .../utils/templates/initial_schemas/README.md | 33 - apps/cli-go/internal/utils/tenant/client.go | 97 - .../internal/utils/tenant/client_test.go | 151 - apps/cli-go/internal/utils/tenant/database.go | 24 - .../internal/utils/tenant/database_test.go | 69 - apps/cli-go/internal/utils/tenant/gotrue.go | 32 - .../internal/utils/tenant/gotrue_test.go | 59 - .../cli-go/internal/utils/tenant/postgrest.go | 39 - .../internal/utils/tenant/postgrest_test.go | 68 - apps/cli-go/internal/utils/tenant/storage.go | 27 - .../internal/utils/tenant/storage_test.go | 54 - .../internal/utils/testdata/invalid.json | 6 - .../internal/utils/testdata/malformed.json | 4 - .../internal/utils/testdata/profile.json | 6 - apps/cli-go/legacy/branch/create/create.go | 103 - .../legacy/branch/create/create_test.go | 106 - .../legacy/branch/create/templates/clone.sh | 5 - apps/cli-go/legacy/branch/delete/delete.go | 90 - .../legacy/branch/delete/delete_test.go | 79 - apps/cli-go/legacy/branch/list/list.go | 36 - apps/cli-go/legacy/branch/list/list_test.go | 66 - apps/cli-go/legacy/branch/switch_/switch_.go | 88 - .../legacy/branch/switch_/switch__test.go | 290 - apps/cli-go/legacy/keys/keys.go | 75 - apps/cli-go/main.go | 16 - apps/cli-go/package.json | 11 - apps/cli-go/pkg/api/client.cfg.yaml | 7 - apps/cli-go/pkg/api/client.gen.go | 23229 ---------------- apps/cli-go/pkg/api/types.cfg.yaml | 8 - apps/cli-go/pkg/api/types.gen.go | 11659 -------- apps/cli-go/pkg/cast/cast.go | 62 - apps/cli-go/pkg/config/api.go | 106 - apps/cli-go/pkg/config/api_test.go | 197 - apps/cli-go/pkg/config/apikeys.go | 211 - apps/cli-go/pkg/config/auth.go | 1727 -- apps/cli-go/pkg/config/auth_test.go | 1497 - apps/cli-go/pkg/config/config.go | 1909 -- apps/cli-go/pkg/config/config_test.go | 1265 - apps/cli-go/pkg/config/constants.go | 77 - apps/cli-go/pkg/config/constants_test.go | 19 - apps/cli-go/pkg/config/db.go | 269 - apps/cli-go/pkg/config/db_test.go | 281 - apps/cli-go/pkg/config/decode_hooks.go | 50 - apps/cli-go/pkg/config/secret.go | 109 - apps/cli-go/pkg/config/secret_test.go | 52 - apps/cli-go/pkg/config/storage.go | 170 - apps/cli-go/pkg/config/storage_test.go | 36 - apps/cli-go/pkg/config/templates/Dockerfile | 47 - .../pkg/config/templates/certs/kong.local.crt | 30 - .../pkg/config/templates/certs/kong.local.key | 52 - apps/cli-go/pkg/config/templates/config.toml | 416 - .../TestApiDiff/detects_differences.diff | 14 - .../handles_api_disabled_on_local_side.diff | 9 - .../handles_api_disabled_on_remote_side.diff | 9 - .../local_enabled_and_disabled.diff | 30 - .../local_disabled_remote_enabled.diff | 12 - .../local_enabled_remote_disabled.diff | 16 - .../detects_differences.diff | 10 - .../handles_api_disabled_on_local_side.diff | 7 - .../handles_api_disabled_on_remote_side.diff | 7 - .../local_disabled_remote_enabled.diff | 57 - .../local_enabled_remote_disabled.diff | 99 - .../local_enabled_and_disabled.diff | 34 - .../local_disabled_remote_enabled.diff | 32 - .../local_enabled_remote_disabled.diff | 39 - .../local_enabled_and_disabled.diff | 26 - .../local_and_remote_rate_limits_differ.diff | 20 - .../enable_sign_up_without_provider.diff | 12 - .../local_disabled_remote_enabled.diff | 30 - .../local_enabled_remote_disabled.diff | 42 - apps/cli-go/pkg/config/testdata/config.toml | 378 - apps/cli-go/pkg/config/updater.go | 318 - apps/cli-go/pkg/config/updater_test.go | 397 - apps/cli-go/pkg/config/utils.go | 116 - apps/cli-go/pkg/diff/diff.go | 258 - apps/cli-go/pkg/fetcher/gateway.go | 34 - apps/cli-go/pkg/fetcher/http.go | 152 - apps/cli-go/pkg/fetcher/http_test.go | 37 - apps/cli-go/pkg/function/api.go | 53 - apps/cli-go/pkg/function/batch.go | 170 - apps/cli-go/pkg/function/batch_test.go | 229 - apps/cli-go/pkg/function/bundle.go | 153 - apps/cli-go/pkg/function/bundle_test.go | 93 - apps/cli-go/pkg/function/deno.go | 181 - apps/cli-go/pkg/function/deno_test.go | 216 - apps/cli-go/pkg/function/deploy.go | 304 - apps/cli-go/pkg/function/deploy_test.go | 448 - .../testdata/geometries/Geometries.js | 0 .../function/testdata/modules/import_types.ts | 22 - .../pkg/function/testdata/modules/imports.ts | 91 - .../pkg/function/testdata/nested/deno.json | 5 - .../pkg/function/testdata/nested/index.ts | 1 - .../pkg/function/testdata/shared/whatever.ts | 3 - .../pkg/function/testdata/types/database.ts | 11 - .../function/testdata/writes_import_map.form | 25 - apps/cli-go/pkg/go.mod | 55 - apps/cli-go/pkg/go.sum | 298 - apps/cli-go/pkg/migration/apply.go | 77 - apps/cli-go/pkg/migration/apply_test.go | 200 - apps/cli-go/pkg/migration/drop.go | 50 - apps/cli-go/pkg/migration/drop_test.go | 36 - apps/cli-go/pkg/migration/dump.go | 221 - apps/cli-go/pkg/migration/file.go | 289 - apps/cli-go/pkg/migration/file_test.go | 322 - apps/cli-go/pkg/migration/history.go | 73 - apps/cli-go/pkg/migration/list.go | 77 - apps/cli-go/pkg/migration/list_test.go | 92 - apps/cli-go/pkg/migration/queries/drop.sql | 136 - apps/cli-go/pkg/migration/queries/list.sql | 10 - .../cli-go/pkg/migration/scripts/dump_data.sh | 39 - .../cli-go/pkg/migration/scripts/dump_role.sh | 37 - .../pkg/migration/scripts/dump_schema.sh | 57 - apps/cli-go/pkg/migration/seed.go | 100 - apps/cli-go/pkg/migration/seed_test.go | 176 - .../pkg/migration/testdata/0_schema.sql | 1 - .../pkg/migration/testdata/1_globals.sql | 1 - apps/cli-go/pkg/migration/testdata/seed.sql | 1 - apps/cli-go/pkg/parser/state.go | 296 - apps/cli-go/pkg/parser/state_test.go | 293 - apps/cli-go/pkg/parser/testdata/all.sql | 140 - apps/cli-go/pkg/parser/testdata/split_00.sql | 1 - apps/cli-go/pkg/parser/testdata/split_01.sql | 11 - apps/cli-go/pkg/parser/testdata/split_02.sql | 4 - apps/cli-go/pkg/parser/testdata/split_03.sql | 4 - apps/cli-go/pkg/parser/testdata/split_04.sql | 21 - apps/cli-go/pkg/parser/testdata/split_05.sql | 4 - apps/cli-go/pkg/parser/testdata/split_06.sql | 4 - apps/cli-go/pkg/parser/testdata/split_07.sql | 4 - apps/cli-go/pkg/parser/testdata/split_08.sql | 7 - apps/cli-go/pkg/parser/testdata/split_09.sql | 12 - apps/cli-go/pkg/parser/testdata/split_10.sql | 4 - apps/cli-go/pkg/parser/testdata/split_11.sql | 7 - apps/cli-go/pkg/parser/testdata/split_12.sql | 4 - apps/cli-go/pkg/parser/testdata/split_13.sql | 19 - apps/cli-go/pkg/parser/testdata/split_14.sql | 30 - apps/cli-go/pkg/parser/testdata/split_15.sql | 7 - apps/cli-go/pkg/parser/testdata/split_16.sql | 7 - apps/cli-go/pkg/parser/testdata/split_17.sql | 7 - apps/cli-go/pkg/parser/token.go | 119 - apps/cli-go/pkg/parser/token_test.go | 52 - apps/cli-go/pkg/pgtest/mock.go | 331 - apps/cli-go/pkg/pgtest/step.go | 141 - apps/cli-go/pkg/pgxv5/connect.go | 42 - apps/cli-go/pkg/pgxv5/rows.go | 132 - apps/cli-go/pkg/queue/queue.go | 57 - apps/cli-go/pkg/storage/analytics.go | 68 - apps/cli-go/pkg/storage/api.go | 11 - apps/cli-go/pkg/storage/batch.go | 131 - apps/cli-go/pkg/storage/buckets.go | 78 - apps/cli-go/pkg/storage/objects.go | 219 - apps/cli-go/pkg/storage/objects_test.go | 178 - apps/cli-go/pkg/storage/vector.go | 81 - apps/cli-go/pkg/vault/batch.go | 60 - apps/cli-go/tools/dump_initial_schema.sh | 23 - apps/cli-go/tools/listdep/main.go | 25 - apps/cli/AGENTS.md | 9 - apps/cli/README.md | 33 +- apps/cli/docs/analytics.md | 11 +- apps/cli/docs/binary-distribution.md | 121 +- apps/cli/docs/go-cli-divergences.md | 246 - apps/cli/docs/go-cli-porting-status.md | 59 - apps/cli/docs/go-cli-reference.md | 2128 -- apps/cli/docs/release-process.md | 24 +- apps/cli/package.json | 3 +- apps/cli/scripts/build.ts | 77 +- apps/cli/scripts/macos-signing.ts | 14 +- apps/cli/scripts/render-service-dockerfile.ts | 25 +- .../render-service-dockerfile.unit.test.ts | 13 +- apps/cli/scripts/update-homebrew.ts | 8 +- apps/cli/src/SIDE_EFFECTS_TEMPLATE.md | 6 - apps/cli/src/cli/root.ts | 47 +- .../command-internal/db-pull-run.errors.ts | 3 +- apps/cli/src/command-internal/db-pull-run.ts | 3 +- apps/cli/src/command-internal/diff-engine.ts | 10 +- .../command-internal/diff-engine.unit.test.ts | 5 - apps/cli/src/command-internal/global-flags.ts | 5 +- .../command-internal/go-child-exit.error.ts | 27 - .../command-internal/go-proxy-invocation.ts | 21 - .../src/command-internal/go-proxy.layer.ts | 305 - .../go-proxy.layer.unit.test.ts | 571 - .../src/command-internal/go-proxy.service.ts | 44 - .../src/command-internal/removed-command.ts | 68 + .../removed-command.unit.test.ts | 101 + apps/cli/src/command-internal/schema-flags.ts | 17 +- .../upgrade-notice.integration.test.ts | 1 - .../src/command-internal/upgrade-notice.ts | 67 +- .../upgrade-notice.unit.test.ts | 13 +- .../src/commands/db/branch/SIDE_EFFECTS.md | 69 + .../src/commands/db/branch/branch.command.ts | 52 +- .../src/commands/db/branch/branch.e2e.test.ts | 27 + .../commands/db/branch/create/SIDE_EFFECTS.md | 53 - .../db/branch/create/create.command.ts | 17 - .../db/branch/create/create.handler.ts | 9 - .../commands/db/branch/delete/SIDE_EFFECTS.md | 52 - .../db/branch/delete/delete.command.ts | 17 - .../db/branch/delete/delete.handler.ts | 9 - .../commands/db/branch/list/SIDE_EFFECTS.md | 51 - .../commands/db/branch/list/list.command.ts | 13 - .../commands/db/branch/list/list.handler.ts | 8 - .../commands/db/branch/switch/SIDE_EFFECTS.md | 52 - .../db/branch/switch/switch.command.ts | 17 - .../db/branch/switch/switch.handler.ts | 9 - apps/cli/src/commands/db/diff/SIDE_EFFECTS.md | 70 +- apps/cli/src/commands/db/diff/diff.command.ts | 11 +- apps/cli/src/commands/db/diff/diff.errors.ts | 5 +- apps/cli/src/commands/db/diff/diff.handler.ts | 102 +- .../commands/db/diff/diff.integration.test.ts | 180 +- apps/cli/src/commands/db/pull/SIDE_EFFECTS.md | 8 +- .../commands/db/pull/pull.integration.test.ts | 36 +- .../db/remote/changes/SIDE_EFFECTS.md | 48 +- .../db/remote/changes/changes.command.ts | 22 +- .../db/remote/changes/changes.handler.ts | 17 - .../declarative/declarative.former-default.ts | 2 +- .../declarative/generate/SIDE_EFFECTS.md | 5 +- .../generate/generate.integration.test.ts | 8 - .../cli/src/commands/db/start/SIDE_EFFECTS.md | 6 +- .../commands/db/test/test.integration.test.ts | 8 +- .../functions/download/SIDE_EFFECTS.md | 84 +- .../functions/download/download.command.ts | 35 +- .../functions/download/download.e2e.test.ts | 6 - .../functions/download/download.handler.ts | 24 +- .../download/download.integration.test.ts | 285 +- .../download.removed-flag.integration.test.ts | 136 + apps/cli/src/commands/gen/gen.command.ts | 2 +- .../cli/src/commands/gen/keys/SIDE_EFFECTS.md | 50 +- .../cli/src/commands/gen/keys/keys.command.ts | 21 +- .../cli/src/commands/gen/keys/keys.handler.ts | 13 - apps/cli/src/commands/issue/SIDE_EFFECTS.md | 2 + apps/cli/src/commands/issue/issue.command.ts | 2 +- apps/cli/src/commands/pull/pull.steps.ts | 11 +- apps/cli/src/commands/start/SIDE_EFFECTS.md | 6 +- apps/cli/src/docs/docs-spec.tables.ts | 1 - .../src/shared/cli/hidden-flag.unit.test.ts | 137 +- apps/cli/src/shared/cli/run.ts | 23 +- apps/cli/src/shared/cli/run.unit.test.ts | 40 +- apps/cli/src/shared/functions/deploy.ts | 9 +- apps/cli/src/shared/functions/download.ts | 134 +- .../src/shared/functions/functions.shared.ts | 11 +- .../src/shared/output/json-error-handling.ts | 2 +- .../output/json-error-handling.unit.test.ts | 19 +- apps/cli/src/shared/output/normalize-error.ts | 2 +- .../telemetry/__fixtures__/error-tags.txt | 2 +- .../shared/telemetry/error-actionability.ts | 9 + apps/cli/src/telemetry/command-telemetry.ts | 28 +- .../telemetry/command-telemetry.unit.test.ts | 67 + apps/cli/tests/helpers/macos-signature.ts | 3 +- apps/cli/tests/smoke-test-macos.ts | 4 - commitlint.config.js | 1 - ...1-cli-release-and-distribution-strategy.md | 10 +- ...t-completion-and-go-cli-authority-scope.md | 10 +- docs/adr/0030-go-cli-removal.md | 132 + docs/adr/README.md | 3 +- docs/openapi-sync.md | 2 + install | 25 +- knip.jsonc | 3 - mise.lock | 120 - mise.toml | 4 - package.json | 2 +- packages/cli-darwin-arm64/package.json | 3 +- packages/cli-darwin-x64/package.json | 3 +- packages/cli-linux-arm64-musl/package.json | 3 +- packages/cli-linux-arm64/package.json | 3 +- packages/cli-linux-x64-musl/package.json | 3 +- packages/cli-linux-x64/package.json | 3 +- packages/cli-test-helpers/src/harness.ts | 4 +- packages/cli-windows-arm64/package.json | 3 +- packages/cli-windows-x64/package.json | 3 +- .../src/project-config/api-attributes.ts | 4 + pnpm-lock.yaml | 2 - tools/release/local-release.ts | 57 +- tools/release/release-notes-prompt.md | 16 +- turbo.json | 8 +- 445 files changed, 1436 insertions(+), 105707 deletions(-) delete mode 100644 .github/workflows/cli-go-api-sync.yml delete mode 100644 .github/workflows/cli-go-ci.yml delete mode 100644 .github/workflows/cli-go-mirror.yml delete mode 100644 .github/workflows/cli-go-tag-pkg.yml rename .github/workflows/{cli-go-codeql.yml => codeql.yml} (55%) rename .github/workflows/{cli-go-mirror-image.yml => mirror-image.yml} (100%) rename .github/workflows/{cli-go-publish-migra.yml => publish-migra.yml} (96%) rename .github/workflows/{cli-go-pg-prove.yml => publish-pg-prove.yml} (96%) rename apps/cli-go/LICENSE => LICENSE (100%) delete mode 100644 apps/cli-e2e/src/tests/go-binary-surface.e2e.test.ts delete mode 100644 apps/cli-go/.gitignore delete mode 100644 apps/cli-go/.golangci.yml delete mode 100644 apps/cli-go/CONTRIBUTING.md delete mode 100644 apps/cli-go/api/README.md delete mode 100644 apps/cli-go/api/overlay.yaml delete mode 100644 apps/cli-go/api/v1-openapi.yaml delete mode 100644 apps/cli-go/cmd/db.go delete mode 100644 apps/cli-go/cmd/functions.go delete mode 100644 apps/cli-go/cmd/gen.go delete mode 100644 apps/cli-go/cmd/root.go delete mode 100644 apps/cli-go/cmd/root_analytics.go delete mode 100644 apps/cli-go/cmd/root_test.go delete mode 100644 apps/cli-go/fsevents/fsevents.go delete mode 100644 apps/cli-go/fsevents/go.mod delete mode 100644 apps/cli-go/fsevents/wrap.go delete mode 100644 apps/cli-go/go.mod delete mode 100644 apps/cli-go/go.sum delete mode 100644 apps/cli-go/internal/db/diff/diff.go delete mode 100644 apps/cli-go/internal/db/diff/diff_test.go delete mode 100644 apps/cli-go/internal/db/diff/migra.go delete mode 100644 apps/cli-go/internal/db/diff/pgschema.go delete mode 100644 apps/cli-go/internal/db/diff/save_diff_test.go delete mode 100644 apps/cli-go/internal/db/diff/shadow.go delete mode 100755 apps/cli-go/internal/db/diff/templates/migra.sh delete mode 100644 apps/cli-go/internal/db/diff/templates/migra.ts delete mode 100644 apps/cli-go/internal/db/reset/reset.go delete mode 100644 apps/cli-go/internal/db/reset/reset_test.go delete mode 100644 apps/cli-go/internal/db/start/start.go delete mode 100644 apps/cli-go/internal/db/start/start_test.go delete mode 100644 apps/cli-go/internal/db/start/templates/_supabase.sql delete mode 100755 apps/cli-go/internal/db/start/templates/restore.sh delete mode 100644 apps/cli-go/internal/db/start/templates/schema.sql delete mode 100644 apps/cli-go/internal/db/start/templates/webhook.sql delete mode 100644 apps/cli-go/internal/debug/http.go delete mode 100644 apps/cli-go/internal/debug/postgres.go delete mode 100644 apps/cli-go/internal/debug/postgres_test.go delete mode 100644 apps/cli-go/internal/functions/download/download.go delete mode 100644 apps/cli-go/internal/functions/download/download_test.go delete mode 100644 apps/cli-go/internal/gen/types/templates/prod-ca-2021.crt delete mode 100644 apps/cli-go/internal/gen/types/templates/prod-ca-2025.crt delete mode 100644 apps/cli-go/internal/gen/types/templates/staging-ca-2021.crt delete mode 100644 apps/cli-go/internal/gen/types/types.go delete mode 100644 apps/cli-go/internal/gen/types/types_test.go delete mode 100644 apps/cli-go/internal/migration/apply/apply.go delete mode 100644 apps/cli-go/internal/migration/apply/apply_test.go delete mode 100644 apps/cli-go/internal/migration/down/down.go delete mode 100644 apps/cli-go/internal/migration/down/down_test.go delete mode 100644 apps/cli-go/internal/migration/list/list.go delete mode 100644 apps/cli-go/internal/migration/list/list_test.go delete mode 100644 apps/cli-go/internal/migration/new/new.go delete mode 100644 apps/cli-go/internal/migration/new/new_test.go delete mode 100644 apps/cli-go/internal/migration/repair/repair.go delete mode 100644 apps/cli-go/internal/migration/repair/repair_test.go delete mode 100644 apps/cli-go/internal/seed/buckets/buckets.go delete mode 100644 apps/cli-go/internal/seed/buckets/buckets_test.go delete mode 100644 apps/cli-go/internal/status/status.go delete mode 100644 apps/cli-go/internal/status/status_test.go delete mode 100644 apps/cli-go/internal/storage/client/api.go delete mode 100644 apps/cli-go/internal/storage/client/scheme.go delete mode 100644 apps/cli-go/internal/storage/client/scheme_test.go delete mode 100644 apps/cli-go/internal/telemetry/client.go delete mode 100644 apps/cli-go/internal/telemetry/client_test.go delete mode 100644 apps/cli-go/internal/telemetry/events.go delete mode 100644 apps/cli-go/internal/telemetry/project.go delete mode 100644 apps/cli-go/internal/telemetry/project_test.go delete mode 100644 apps/cli-go/internal/telemetry/service.go delete mode 100644 apps/cli-go/internal/telemetry/service_test.go delete mode 100644 apps/cli-go/internal/telemetry/state.go delete mode 100644 apps/cli-go/internal/telemetry/state_test.go delete mode 100644 apps/cli-go/internal/testing/apitest/docker.go delete mode 100644 apps/cli-go/internal/testing/apitest/platform.go delete mode 100644 apps/cli-go/internal/testing/fstest/create.go delete mode 100644 apps/cli-go/internal/testing/fstest/open.go delete mode 100644 apps/cli-go/internal/testing/fstest/stat.go delete mode 100644 apps/cli-go/internal/testing/fstest/stdin.go delete mode 100644 apps/cli-go/internal/testing/fstest/stdout.go delete mode 100644 apps/cli-go/internal/testing/helper/history.go delete mode 100644 apps/cli-go/internal/testing/helper/privileges.go delete mode 100644 apps/cli-go/internal/utils/access_token.go delete mode 100644 apps/cli-go/internal/utils/access_token_test.go delete mode 100644 apps/cli-go/internal/utils/agent.go delete mode 100644 apps/cli-go/internal/utils/agent/agent.go delete mode 100644 apps/cli-go/internal/utils/agent/agent_test.go delete mode 100644 apps/cli-go/internal/utils/api.go delete mode 100644 apps/cli-go/internal/utils/api_test.go delete mode 100644 apps/cli-go/internal/utils/cloudflare/api.go delete mode 100644 apps/cli-go/internal/utils/cloudflare/dns.go delete mode 100644 apps/cli-go/internal/utils/cloudflare/dns_test.go delete mode 100644 apps/cli-go/internal/utils/colors.go delete mode 100644 apps/cli-go/internal/utils/config.go delete mode 100644 apps/cli-go/internal/utils/config_test.go delete mode 100644 apps/cli-go/internal/utils/connect.go delete mode 100644 apps/cli-go/internal/utils/connect_test.go delete mode 100644 apps/cli-go/internal/utils/console.go delete mode 100644 apps/cli-go/internal/utils/console_test.go delete mode 100644 apps/cli-go/internal/utils/container_output.go delete mode 100644 apps/cli-go/internal/utils/container_output_test.go delete mode 100644 apps/cli-go/internal/utils/credentials/input.go delete mode 100644 apps/cli-go/internal/utils/credentials/input_test.go delete mode 100644 apps/cli-go/internal/utils/credentials/keyring_test.go delete mode 100644 apps/cli-go/internal/utils/credentials/store.go delete mode 100644 apps/cli-go/internal/utils/credentials/store_test.go delete mode 100644 apps/cli-go/internal/utils/deno.go delete mode 100644 apps/cli-go/internal/utils/deno_darwin.go delete mode 100644 apps/cli-go/internal/utils/deno_generic.go delete mode 100644 apps/cli-go/internal/utils/deno_test.go delete mode 100644 apps/cli-go/internal/utils/denos/extract.ts delete mode 100644 apps/cli-go/internal/utils/docker.go delete mode 100644 apps/cli-go/internal/utils/docker_darwin.go delete mode 100644 apps/cli-go/internal/utils/docker_linux.go delete mode 100644 apps/cli-go/internal/utils/docker_test.go delete mode 100644 apps/cli-go/internal/utils/docker_wait_test.go delete mode 100644 apps/cli-go/internal/utils/docker_windows.go delete mode 100644 apps/cli-go/internal/utils/edgeruntime.go delete mode 100644 apps/cli-go/internal/utils/edgeruntime_test.go delete mode 100644 apps/cli-go/internal/utils/enum.go delete mode 100644 apps/cli-go/internal/utils/flags/config_path.go delete mode 100644 apps/cli-go/internal/utils/flags/db_url.go delete mode 100644 apps/cli-go/internal/utils/flags/db_url_test.go delete mode 100644 apps/cli-go/internal/utils/flags/project_ref.go delete mode 100644 apps/cli-go/internal/utils/flags/project_ref_test.go delete mode 100644 apps/cli-go/internal/utils/git.go delete mode 100644 apps/cli-go/internal/utils/identity_transport.go delete mode 100644 apps/cli-go/internal/utils/identity_transport_test.go delete mode 100644 apps/cli-go/internal/utils/logger.go delete mode 100644 apps/cli-go/internal/utils/misc.go delete mode 100644 apps/cli-go/internal/utils/misc_test.go delete mode 100644 apps/cli-go/internal/utils/output.go delete mode 100644 apps/cli-go/internal/utils/output_test.go delete mode 100644 apps/cli-go/internal/utils/plan_gate.go delete mode 100644 apps/cli-go/internal/utils/plan_gate_test.go delete mode 100644 apps/cli-go/internal/utils/profile.go delete mode 100644 apps/cli-go/internal/utils/profile_test.go delete mode 100644 apps/cli-go/internal/utils/prompt.go delete mode 100644 apps/cli-go/internal/utils/release.go delete mode 100644 apps/cli-go/internal/utils/release_test.go delete mode 100644 apps/cli-go/internal/utils/render.go delete mode 100644 apps/cli-go/internal/utils/retry.go delete mode 100644 apps/cli-go/internal/utils/slice.go delete mode 100644 apps/cli-go/internal/utils/slice_test.go delete mode 100644 apps/cli-go/internal/utils/supabase_home.go delete mode 100644 apps/cli-go/internal/utils/tea.go delete mode 100644 apps/cli-go/internal/utils/templates/globals.sql delete mode 100644 apps/cli-go/internal/utils/templates/initial_schemas/13.sql delete mode 100644 apps/cli-go/internal/utils/templates/initial_schemas/14.sql delete mode 100644 apps/cli-go/internal/utils/templates/initial_schemas/README.md delete mode 100644 apps/cli-go/internal/utils/tenant/client.go delete mode 100644 apps/cli-go/internal/utils/tenant/client_test.go delete mode 100644 apps/cli-go/internal/utils/tenant/database.go delete mode 100644 apps/cli-go/internal/utils/tenant/database_test.go delete mode 100644 apps/cli-go/internal/utils/tenant/gotrue.go delete mode 100644 apps/cli-go/internal/utils/tenant/gotrue_test.go delete mode 100644 apps/cli-go/internal/utils/tenant/postgrest.go delete mode 100644 apps/cli-go/internal/utils/tenant/postgrest_test.go delete mode 100644 apps/cli-go/internal/utils/tenant/storage.go delete mode 100644 apps/cli-go/internal/utils/tenant/storage_test.go delete mode 100644 apps/cli-go/internal/utils/testdata/invalid.json delete mode 100644 apps/cli-go/internal/utils/testdata/malformed.json delete mode 100644 apps/cli-go/internal/utils/testdata/profile.json delete mode 100644 apps/cli-go/legacy/branch/create/create.go delete mode 100644 apps/cli-go/legacy/branch/create/create_test.go delete mode 100755 apps/cli-go/legacy/branch/create/templates/clone.sh delete mode 100644 apps/cli-go/legacy/branch/delete/delete.go delete mode 100644 apps/cli-go/legacy/branch/delete/delete_test.go delete mode 100644 apps/cli-go/legacy/branch/list/list.go delete mode 100644 apps/cli-go/legacy/branch/list/list_test.go delete mode 100644 apps/cli-go/legacy/branch/switch_/switch_.go delete mode 100644 apps/cli-go/legacy/branch/switch_/switch__test.go delete mode 100644 apps/cli-go/legacy/keys/keys.go delete mode 100644 apps/cli-go/main.go delete mode 100644 apps/cli-go/package.json delete mode 100755 apps/cli-go/pkg/api/client.cfg.yaml delete mode 100644 apps/cli-go/pkg/api/client.gen.go delete mode 100755 apps/cli-go/pkg/api/types.cfg.yaml delete mode 100644 apps/cli-go/pkg/api/types.gen.go delete mode 100644 apps/cli-go/pkg/cast/cast.go delete mode 100644 apps/cli-go/pkg/config/api.go delete mode 100644 apps/cli-go/pkg/config/api_test.go delete mode 100644 apps/cli-go/pkg/config/apikeys.go delete mode 100644 apps/cli-go/pkg/config/auth.go delete mode 100644 apps/cli-go/pkg/config/auth_test.go delete mode 100644 apps/cli-go/pkg/config/config.go delete mode 100644 apps/cli-go/pkg/config/config_test.go delete mode 100644 apps/cli-go/pkg/config/constants.go delete mode 100644 apps/cli-go/pkg/config/constants_test.go delete mode 100644 apps/cli-go/pkg/config/db.go delete mode 100644 apps/cli-go/pkg/config/db_test.go delete mode 100644 apps/cli-go/pkg/config/decode_hooks.go delete mode 100644 apps/cli-go/pkg/config/secret.go delete mode 100644 apps/cli-go/pkg/config/secret_test.go delete mode 100644 apps/cli-go/pkg/config/storage.go delete mode 100644 apps/cli-go/pkg/config/storage_test.go delete mode 100644 apps/cli-go/pkg/config/templates/Dockerfile delete mode 100644 apps/cli-go/pkg/config/templates/certs/kong.local.crt delete mode 100644 apps/cli-go/pkg/config/templates/certs/kong.local.key delete mode 100644 apps/cli-go/pkg/config/templates/config.toml delete mode 100644 apps/cli-go/pkg/config/testdata/TestApiDiff/detects_differences.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestApiDiff/handles_api_disabled_on_local_side.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestApiDiff/handles_api_disabled_on_remote_side.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestAuthDiff/local_enabled_and_disabled.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestCaptchaDiff/local_disabled_remote_enabled.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestCaptchaDiff/local_enabled_remote_disabled.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestDbSettingsDiff/detects_differences.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestDbSettingsDiff/handles_api_disabled_on_local_side.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestDbSettingsDiff/handles_api_disabled_on_remote_side.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestEmailDiff/local_disabled_remote_enabled.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestEmailDiff/local_enabled_remote_disabled.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestExternalDiff/local_enabled_and_disabled.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestHookDiff/local_disabled_remote_enabled.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestHookDiff/local_enabled_remote_disabled.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestMfaDiff/local_enabled_and_disabled.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestRateLimitsDiff/local_and_remote_rate_limits_differ.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestSmsDiff/enable_sign_up_without_provider.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestSmsDiff/local_disabled_remote_enabled.diff delete mode 100644 apps/cli-go/pkg/config/testdata/TestSmsDiff/local_enabled_remote_disabled.diff delete mode 100644 apps/cli-go/pkg/config/testdata/config.toml delete mode 100644 apps/cli-go/pkg/config/updater.go delete mode 100644 apps/cli-go/pkg/config/updater_test.go delete mode 100644 apps/cli-go/pkg/config/utils.go delete mode 100644 apps/cli-go/pkg/diff/diff.go delete mode 100644 apps/cli-go/pkg/fetcher/gateway.go delete mode 100644 apps/cli-go/pkg/fetcher/http.go delete mode 100644 apps/cli-go/pkg/fetcher/http_test.go delete mode 100644 apps/cli-go/pkg/function/api.go delete mode 100644 apps/cli-go/pkg/function/batch.go delete mode 100644 apps/cli-go/pkg/function/batch_test.go delete mode 100644 apps/cli-go/pkg/function/bundle.go delete mode 100644 apps/cli-go/pkg/function/bundle_test.go delete mode 100644 apps/cli-go/pkg/function/deno.go delete mode 100644 apps/cli-go/pkg/function/deno_test.go delete mode 100644 apps/cli-go/pkg/function/deploy.go delete mode 100644 apps/cli-go/pkg/function/deploy_test.go delete mode 100644 apps/cli-go/pkg/function/testdata/geometries/Geometries.js delete mode 100644 apps/cli-go/pkg/function/testdata/modules/import_types.ts delete mode 100644 apps/cli-go/pkg/function/testdata/modules/imports.ts delete mode 100644 apps/cli-go/pkg/function/testdata/nested/deno.json delete mode 100644 apps/cli-go/pkg/function/testdata/nested/index.ts delete mode 100644 apps/cli-go/pkg/function/testdata/shared/whatever.ts delete mode 100644 apps/cli-go/pkg/function/testdata/types/database.ts delete mode 100644 apps/cli-go/pkg/function/testdata/writes_import_map.form delete mode 100644 apps/cli-go/pkg/go.mod delete mode 100644 apps/cli-go/pkg/go.sum delete mode 100644 apps/cli-go/pkg/migration/apply.go delete mode 100644 apps/cli-go/pkg/migration/apply_test.go delete mode 100644 apps/cli-go/pkg/migration/drop.go delete mode 100644 apps/cli-go/pkg/migration/drop_test.go delete mode 100644 apps/cli-go/pkg/migration/dump.go delete mode 100644 apps/cli-go/pkg/migration/file.go delete mode 100644 apps/cli-go/pkg/migration/file_test.go delete mode 100644 apps/cli-go/pkg/migration/history.go delete mode 100644 apps/cli-go/pkg/migration/list.go delete mode 100644 apps/cli-go/pkg/migration/list_test.go delete mode 100644 apps/cli-go/pkg/migration/queries/drop.sql delete mode 100644 apps/cli-go/pkg/migration/queries/list.sql delete mode 100755 apps/cli-go/pkg/migration/scripts/dump_data.sh delete mode 100755 apps/cli-go/pkg/migration/scripts/dump_role.sh delete mode 100755 apps/cli-go/pkg/migration/scripts/dump_schema.sh delete mode 100644 apps/cli-go/pkg/migration/seed.go delete mode 100644 apps/cli-go/pkg/migration/seed_test.go delete mode 100644 apps/cli-go/pkg/migration/testdata/0_schema.sql delete mode 100644 apps/cli-go/pkg/migration/testdata/1_globals.sql delete mode 100644 apps/cli-go/pkg/migration/testdata/seed.sql delete mode 100644 apps/cli-go/pkg/parser/state.go delete mode 100644 apps/cli-go/pkg/parser/state_test.go delete mode 100644 apps/cli-go/pkg/parser/testdata/all.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_00.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_01.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_02.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_03.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_04.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_05.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_06.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_07.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_08.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_09.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_10.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_11.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_12.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_13.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_14.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_15.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_16.sql delete mode 100644 apps/cli-go/pkg/parser/testdata/split_17.sql delete mode 100644 apps/cli-go/pkg/parser/token.go delete mode 100644 apps/cli-go/pkg/parser/token_test.go delete mode 100644 apps/cli-go/pkg/pgtest/mock.go delete mode 100644 apps/cli-go/pkg/pgtest/step.go delete mode 100644 apps/cli-go/pkg/pgxv5/connect.go delete mode 100644 apps/cli-go/pkg/pgxv5/rows.go delete mode 100644 apps/cli-go/pkg/queue/queue.go delete mode 100644 apps/cli-go/pkg/storage/analytics.go delete mode 100644 apps/cli-go/pkg/storage/api.go delete mode 100644 apps/cli-go/pkg/storage/batch.go delete mode 100644 apps/cli-go/pkg/storage/buckets.go delete mode 100644 apps/cli-go/pkg/storage/objects.go delete mode 100644 apps/cli-go/pkg/storage/objects_test.go delete mode 100644 apps/cli-go/pkg/storage/vector.go delete mode 100644 apps/cli-go/pkg/vault/batch.go delete mode 100755 apps/cli-go/tools/dump_initial_schema.sh delete mode 100644 apps/cli-go/tools/listdep/main.go delete mode 100644 apps/cli/docs/go-cli-divergences.md delete mode 100644 apps/cli/docs/go-cli-porting-status.md delete mode 100644 apps/cli/docs/go-cli-reference.md delete mode 100644 apps/cli/src/command-internal/go-child-exit.error.ts delete mode 100644 apps/cli/src/command-internal/go-proxy-invocation.ts delete mode 100644 apps/cli/src/command-internal/go-proxy.layer.ts delete mode 100644 apps/cli/src/command-internal/go-proxy.layer.unit.test.ts delete mode 100644 apps/cli/src/command-internal/go-proxy.service.ts create mode 100644 apps/cli/src/command-internal/removed-command.ts create mode 100644 apps/cli/src/command-internal/removed-command.unit.test.ts create mode 100644 apps/cli/src/commands/db/branch/SIDE_EFFECTS.md create mode 100644 apps/cli/src/commands/db/branch/branch.e2e.test.ts delete mode 100644 apps/cli/src/commands/db/branch/create/SIDE_EFFECTS.md delete mode 100644 apps/cli/src/commands/db/branch/create/create.command.ts delete mode 100644 apps/cli/src/commands/db/branch/create/create.handler.ts delete mode 100644 apps/cli/src/commands/db/branch/delete/SIDE_EFFECTS.md delete mode 100644 apps/cli/src/commands/db/branch/delete/delete.command.ts delete mode 100644 apps/cli/src/commands/db/branch/delete/delete.handler.ts delete mode 100644 apps/cli/src/commands/db/branch/list/SIDE_EFFECTS.md delete mode 100644 apps/cli/src/commands/db/branch/list/list.command.ts delete mode 100644 apps/cli/src/commands/db/branch/list/list.handler.ts delete mode 100644 apps/cli/src/commands/db/branch/switch/SIDE_EFFECTS.md delete mode 100644 apps/cli/src/commands/db/branch/switch/switch.command.ts delete mode 100644 apps/cli/src/commands/db/branch/switch/switch.handler.ts delete mode 100644 apps/cli/src/commands/db/remote/changes/changes.handler.ts create mode 100644 apps/cli/src/commands/functions/download/download.removed-flag.integration.test.ts delete mode 100644 apps/cli/src/commands/gen/keys/keys.handler.ts create mode 100644 docs/adr/0030-go-cli-removal.md diff --git a/.gitattributes b/.gitattributes index 7f30866cca..1cdece8670 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14,16 +14,12 @@ apps/cli/src/shared/telemetry/__fixtures__/error-tags.txt text eol=lf # statistics and collapses them in pull request diffs -- they are build output to # be regenerated, not code to review, and together they outweigh the hand-written # source. The attribute is GitHub-only: git, CI, and local diffs are unaffected. -# Collapsed is not hidden. v1-openapi.yaml and the lockfiles do carry changes -# worth reading -- an upstream API change, a dependency substitution -- and both -# stay one click away in review. -# Regeneration is owned by `go generate` in apps/cli-go and by the `generate` -# turbo task, whose `outputs` in turbo.json are the source of truth for this list. -# Only the generated files are listed, not their hand-written inputs -# (pkg/api/*.cfg.yaml, api/overlay.yaml). apps/docs/content/docs/commands/ is -# generated too but gitignored, so it needs no entry. -apps/cli-go/api/v1-openapi.yaml linguist-generated=true -apps/cli-go/pkg/api/*.gen.go linguist-generated=true +# Collapsed is not hidden. The lockfile does carry changes worth reading -- +# a dependency substitution -- and stays one click away in review. +# Regeneration is owned by the `generate` turbo task, whose `outputs` in +# turbo.json are the source of truth for this list. Only the generated files +# are listed, not their hand-written inputs. apps/docs/content/docs/commands/ +# is generated too but gitignored, so it needs no entry. packages/api/src/generated/* linguist-generated=true packages/api/scripts/openapi-source.json linguist-generated=true packages/stack/src/functions/generated/* text eol=lf linguist-generated=true @@ -34,7 +30,6 @@ apps/docs/public/cli/project-config.schema.json linguist-generated=true # Lockfiles. Collapsed rather than hidden -- a dependency change is still one # click away in review, which is the same treatment GitHub gives package-lock.json. pnpm-lock.yaml linguist-generated=true -apps/cli-go/go.sum linguist-generated=true # Not a language marker -- this is the service image manifest, not a container build. apps/cli/src/shared/services/Dockerfile linguist-detectable=false diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index adbacd4653..b5b107741f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -10,15 +10,8 @@ /tools/*/package.json @supabase/cli @supabase-oss /pnpm-lock.yaml @supabase/cli @supabase-oss /pnpm-workspace.yaml @supabase/cli @supabase-oss -/apps/cli-go/go.mod @supabase/cli @supabase-oss -/apps/cli-go/go.sum @supabase/cli @supabase-oss -/apps/cli-go/pkg/go.mod @supabase/cli @supabase-oss -/apps/cli-go/pkg/go.sum @supabase/cli @supabase-oss /apps/cli/src/shared/services/Dockerfile @supabase/cli @supabase-oss -/apps/cli-go/pkg/config/templates/Dockerfile @supabase/cli @supabase-oss /packages/stack/src/Artifacts.ts @supabase/cli @supabase-oss -/apps/cli-go/api/v1-openapi.yaml @supabase/cli @supabase-oss -/apps/cli-go/pkg/api/*.gen.go @supabase/cli @supabase-oss /packages/api/src/generated/ @supabase/cli @supabase-oss /packages/api/scripts/openapi-source.json @supabase/cli @supabase-oss diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 9304447079..29edac9a27 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -107,7 +107,7 @@ body: id: ticket-id attributes: label: Crash report ID - description: If the CLI printed one after rerunning with `--create-ticket`, paste the crash report ID. + description: If you have a crash report or support ticket ID, paste it here. placeholder: ab1ac733e31e4f928a4d7c8402543712 validations: required: false diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index a05bc49616..6e8e4fed34 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -14,12 +14,6 @@ inputs: fails with a path validation error. required: false default: "true" - go-cache: - description: >- - Whether to restore the Go module and build caches. Disable this in jobs - that never run Go. - required: false - default: "true" runs: using: "composite" @@ -75,25 +69,6 @@ runs: path: ${{ steps.pnpm-cache.outputs.path }}/lockfile-verified.jsonl key: pnpm-verify-${{ runner.os }}-${{ inputs.dependency-firewall-token != '' && 'firewall' || 'public' }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml') }} - - name: Resolve Go cache paths - if: inputs.dependency-cache == 'true' && inputs.go-cache == 'true' - id: go-cache - shell: bash - run: | - echo "mod=$(go env GOMODCACHE)" >> "$GITHUB_OUTPUT" - echo "build=$(go env GOCACHE)" >> "$GITHUB_OUTPUT" - - - name: Configure Go dependency cache - if: inputs.dependency-cache == 'true' && inputs.go-cache == 'true' - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: | - ${{ steps.go-cache.outputs.mod }} - ${{ steps.go-cache.outputs.build }} - key: go-cache-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('apps/cli-go/go.mod', 'apps/cli-go/go.sum') }} - restore-keys: | - go-cache-${{ runner.os }}-${{ runner.arch }}- - - name: Install dependencies shell: bash env: diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b66df12186..c24da2c9cc 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -37,28 +37,6 @@ updates: # — never bundle it into the actions-major group. cooldown: default-days: 7 - - package-ecosystem: "gomod" - directories: - - "/apps/cli-go" - - "/apps/cli-go/pkg" - schedule: - interval: "cron" - cronjob: "0 0 * * *" - commit-message: - prefix: "chore(cli-go): " - groups: - go-minor: - update-types: - - minor - - patch - exclude-patterns: - - github.com/compose-spec/compose-go/v2 - ignore: - - dependency-name: "github.com/compose-spec/compose-go/v2" - - dependency-name: "github.com/docker/cli" - - dependency-name: "github.com/docker/docker" - cooldown: - default-days: 7 - package-ecosystem: "npm" directory: "/" schedule: diff --git a/.github/workflows/build-cli-artifacts.yml b/.github/workflows/build-cli-artifacts.yml index 2e8c2a32db..43003d369b 100644 --- a/.github/workflows/build-cli-artifacts.yml +++ b/.github/workflows/build-cli-artifacts.yml @@ -71,10 +71,6 @@ jobs: with: dependency-firewall-token: ${{ secrets.DF_FIREWALL_TOKEN }} - - name: Pre-download Go modules - working-directory: apps/cli-go - run: go mod download -x - - name: Install nfpm env: NFPM_VERSION: "2.47.0" @@ -112,6 +108,7 @@ jobs: - name: Verify build artifacts run: | + set -euo pipefail for pkg in cli-darwin-arm64 cli-darwin-x64 cli-linux-arm64 cli-linux-arm64-musl cli-linux-x64 cli-linux-x64-musl cli-windows-arm64 cli-windows-x64; do echo "Checking packages/$pkg/bin/..." ls -la "packages/$pkg/bin/" @@ -119,6 +116,39 @@ jobs: echo "Checking dist/..." ls -la dist/ + # Every channel ships a single `supabase` binary; no `supabase-go` companion. + shopt -s nullglob + for f in packages/*/bin/supabase-go*; do + echo "::error::${f} is a supabase-go companion binary" + exit 1 + done + + check_listing() { + local artifact="$1" + shift + local listing + if ! listing="$("$@")"; then + echo "::error::failed to list the contents of ${artifact}" + exit 1 + fi + if grep -q 'supabase-go' <<<"$listing"; then + echo "::error::${artifact} contains supabase-go" + exit 1 + fi + } + + for a in dist/*.tar.gz dist/*.apk; do check_listing "$a" tar -tzf "$a"; done + for a in dist/*.zip; do check_listing "$a" unzip -Z1 "$a"; done + for a in dist/*.deb; do check_listing "$a" dpkg-deb -c "$a"; done + rpms=(dist/*.rpm) + if [[ ${#rpms[@]} -gt 0 ]]; then + if command -v rpm >/dev/null; then + for a in "${rpms[@]}"; do check_listing "$a" rpm -qlp "$a"; done + else + echo "::notice::rpm is not installed on this runner; rpm contents were not checked for supabase-go" + fi + fi + - name: Check existing build artifacts cache id: build-artifacts-cache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 diff --git a/.github/workflows/cli-go-api-sync.yml b/.github/workflows/cli-go-api-sync.yml deleted file mode 100644 index 0ca181493c..0000000000 --- a/.github/workflows/cli-go-api-sync.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: API Sync - -on: - repository_dispatch: - types: - - api-sync - # Fallback for a lost dispatch, per docs/openapi-sync.md. This matters more - # now that the per-pull-request Codegen check reads the committed snapshot: - # this workflow is the only thing that looks at the live specification, so a - # dropped dispatch would otherwise leave drift undetected indefinitely. - schedule: - - cron: "17 6 * * *" - workflow_dispatch: # allow manual triggering - -permissions: - contents: read - -jobs: - sync: - name: Sync API Types - runs-on: blacksmith-2vcpu-ubuntu-2404 - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 - with: - go-version-file: apps/cli-go/go.mod - cache: true - - # The only place that reads the live spec. Every other consumer, including - # the per-PR Codegen check, regenerates from the committed snapshot, so - # upstream drift turns into this one pull request instead of a failure on - # every open pull request that touches apps/cli-go. - - name: Refresh the OpenAPI snapshot - run: curl -fsSL "$API_SPEC_URL" -o api/v1-openapi.yaml - env: - API_SPEC_URL: https://api.supabase.green/api/v1-yaml - - - name: Run codegen - run: go generate - - # Covers the snapshot as well as the generated client: an upstream edit - # that codegen ignores (a description, an example) would otherwise be - # refetched and discarded on every run, leaving the snapshot stale. The - # snapshot is compared byte-for-byte for the same reason -- under - # --ignore-space-at-eol a trailing-whitespace-only upstream edit would be - # refetched and discarded forever, which is the loop this check prevents. - # pkg keeps the flag: it is oapi-codegen output, not upstream bytes. - - name: Check for changes - id: check - run: | - if git diff --exit-code --quiet api/v1-openapi.yaml && - git diff --ignore-space-at-eol --exit-code --quiet pkg; then - echo "No changes detected" - echo "has_changes=false" >> $GITHUB_OUTPUT - else - echo "Changes detected" - echo "has_changes=true" >> $GITHUB_OUTPUT - fi - - - name: Generate token - id: app-token - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 - with: - client-id: ${{ vars.GH_APP_CLIENT_ID }} - private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - permission-pull-requests: write - permission-contents: write - - - name: Create Pull Request - if: steps.check.outputs.has_changes == 'true' - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 - with: - token: ${{ steps.app-token.outputs.token }} - commit-message: "chore: sync API types from infrastructure" - title: "chore: sync API types from infrastructure" - body: | - This PR was automatically created to sync API types from the infrastructure repository. - - Changes were detected after refreshing `apps/cli-go/api/v1-openapi.yaml` from the latest spec from infrastructure, and `apps/cli-go/pkg/api` is regenerated from it. The snapshot diff is the upstream API change and is worth reading: it is marked `linguist-generated`, so GitHub collapses it behind a "Load diff" click rather than hiding it. - branch: sync/api-types - base: develop -defaults: - run: - working-directory: apps/cli-go diff --git a/.github/workflows/cli-go-ci.yml b/.github/workflows/cli-go-ci.yml deleted file mode 100644 index a4e60bfab4..0000000000 --- a/.github/workflows/cli-go-ci.yml +++ /dev/null @@ -1,137 +0,0 @@ -name: CI - -on: - pull_request: - paths: - - apps/cli-go/** - merge_group: - push: - branches: - - develop - paths: - - apps/cli-go/** - -permissions: - contents: read - -jobs: - test: - name: Test - runs-on: blacksmith-8vcpu-ubuntu-2404 - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 - with: - go-version-file: apps/cli-go/go.mod - cache: true - - # Required by: internal/utils/credentials/keyring_test.go - - uses: t1m0thyj/unlock-keyring@cbcf205c879ebd86add70bab3a6abfcce59a5cae # v1.2.0 - - run: | - pkgs=$(go list ./pkg/... | grep -Ev 'pkg/api' | paste -sd ',' -) - go tool gotestsum -- -race -v -count=1 ./... \ - -coverpkg="./cmd/...,./internal/...,${pkgs}" -coverprofile=coverage.out - - # internal/start (docker-compose/v2, buildx, moby/buildkit, k8s client-go, - # aws-sdk-go-v2, notary, secret-detector) was deleted outright as - # unreachable (CLI-1966) -- this guards against silently reintroducing - # that dependency tree via some other package, since nothing else checks - # the shipped binary's size on every change. - - name: Verify the heavy orchestration dependency tree stays out of the module - run: | - set -euo pipefail - go list -deps . > /tmp/deps.txt - ! grep -Eq \ - 'docker/compose|docker/buildx|moby/buildkit|k8s\.io/client-go|aws-sdk-go-v2' \ - /tmp/deps.txt - - - name: Check existing coverage cache - id: coverage-cache - uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: apps/cli-go/coverage.out - key: cli-go-coverage-${{ github.run_id }}-v1 - lookup-only: true - - - name: Save coverage cache - if: steps.coverage-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: apps/cli-go/coverage.out - key: cli-go-coverage-${{ github.run_id }}-v1 - - coverage: - name: Coverage - needs: - - test - runs-on: blacksmith-8vcpu-ubuntu-2404 - steps: - - name: Restore coverage cache - uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: apps/cli-go/coverage.out - key: cli-go-coverage-${{ github.run_id }}-v1 - fail-on-cache-miss: true - - name: Move coverage report - run: cp apps/cli-go/coverage.out coverage.out - working-directory: . - - uses: coverallsapp/github-action@8d6379e14d29928660c4ba802d8e85393440b329 # v2.3.8 - with: - file: coverage.out - format: golang - - lint: - name: Lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 - with: - version: 2026.9.0 - install: true - install_args: >- - go - golangci-lint - - - uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0 - with: - args: --timeout 5m --verbose - install-mode: none - only-new-issues: true - working-directory: apps/cli-go - - codegen: - name: Codegen - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 - with: - go-version-file: apps/cli-go/go.mod - cache: true - - # Regenerates from the committed api/v1-openapi.yaml snapshot, so this - # check is hermetic: it fails only when a pull request leaves pkg/api out - # of sync with the snapshot it was generated from. It deliberately does - # not compare against the live upstream spec -- doing so made unrelated - # pull requests fail together whenever staging drifted ahead of the - # committed snapshot. That drift is the API Sync workflow's job. - - run: go generate - - run: | - if ! git diff --ignore-space-at-eol --exit-code --quiet pkg; then - echo "Detected uncommitted changes after codegen. See status below:" - git diff - exit 1 - fi -defaults: - run: - working-directory: apps/cli-go diff --git a/.github/workflows/cli-go-mirror.yml b/.github/workflows/cli-go-mirror.yml deleted file mode 100644 index bba031acf3..0000000000 --- a/.github/workflows/cli-go-mirror.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Mirror Dependencies -# We mirror upstream container images like Migra, imgproxy, etc. because these -# are usually only available on certain image registry and not others (e.g. only -# on Docker Hub and not on ghcr.io or AWS ECR). -# -# For container images that we control, we usually publish to Docker Hub, -# ghcr.io, and AWS ECR. - -on: - # This workflow is the manual/bulk entry point for re-mirroring everything. - # Template image bumps are mirrored automatically by mirror-template-images.yml - # on push to develop, which backfills any unmirrored tag when the templates - # Dockerfile changes — so anyone pinning SUPABASE_INTERNAL_IMAGE_REGISTRY=ghcr.io - # doesn't inherit a `manifest unknown` failure. (No CI job pins that registry - # anymore -- see mirror-template-images.yml's own comment for why, CLI-1966.) - workflow_dispatch: -permissions: - contents: read - -jobs: - setup: - runs-on: blacksmith-2vcpu-ubuntu-2404 - outputs: - tags: ${{ steps.list.outputs.tags }} - curr: ${{ steps.curr.outputs.tags }} - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 - with: - go-version-file: apps/cli-go/go.mod - cache: true - - id: list - run: | - echo "tags=$(go run tools/listdep/main.go)" >> $GITHUB_OUTPUT - - id: curr - name: List main branch dependencies - if: github.ref != 'refs/heads/main' - run: | - git fetch origin main - git checkout main - echo "tags=$(go run tools/listdep/main.go)" >> $GITHUB_OUTPUT - - publish: - needs: - - setup - permissions: - contents: read - packages: write - id-token: write - if: ${{ needs.setup.outputs.tags != needs.setup.outputs.curr }} - strategy: - matrix: - src: ${{ fromJson(needs.setup.outputs.tags) }} - # Call workflow explicitly because events from actions cannot trigger more actions - uses: ./.github/workflows/cli-go-mirror-image.yml - with: - image: ${{ matrix.src }} - secrets: - PROD_AWS_ROLE: ${{ secrets.PROD_AWS_ROLE }} -defaults: - run: - working-directory: apps/cli-go diff --git a/.github/workflows/cli-go-tag-pkg.yml b/.github/workflows/cli-go-tag-pkg.yml deleted file mode 100644 index 9185ad5771..0000000000 --- a/.github/workflows/cli-go-tag-pkg.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Tag pkg - -on: - workflow_dispatch: - inputs: - version: - description: "pkg version to tag (e.g. v1.2.2)" - required: true - type: string - -permissions: - contents: write - -jobs: - tag: - name: Create pkg tag - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: develop - fetch-depth: 0 - persist-credentials: false - - - name: Create and push pkg tag - run: | - VERSION="${INPUTS_VERSION}" - if ! [[ "$VERSION" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then - echo "Error: version '$VERSION' does not match semver format (e.g. v1.2.2)" - exit 1 - fi - TAG="pkg/$VERSION" - if git rev-parse "$TAG" >/dev/null 2>&1; then - echo "Error: tag '$TAG' already exists" - exit 1 - fi - git tag "$TAG" - git push origin "$TAG" - env: - INPUTS_VERSION: ${{ inputs.version }} -defaults: - run: - working-directory: apps/cli-go diff --git a/.github/workflows/cli-go-codeql.yml b/.github/workflows/codeql.yml similarity index 55% rename from .github/workflows/cli-go-codeql.yml rename to .github/workflows/codeql.yml index 3f19507312..a25cab48b7 100644 --- a/.github/workflows/cli-go-codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,21 +2,19 @@ name: "CodeQL" on: pull_request: - paths: - - apps/cli-go/** - merge_group: push: branches: - develop - paths: - - apps/cli-go/** + +concurrency: + group: codeql-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: analyze: name: Analyze (${{ matrix.language }}) - # Swift requires a macOS runner; every other language in the matrix uses blacksmith. - runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'blacksmith-8vcpu-ubuntu-2404' }} - timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + runs-on: blacksmith-8vcpu-ubuntu-2404 + timeout-minutes: 360 permissions: # required for all workflows security-events: write @@ -32,8 +30,6 @@ jobs: fail-fast: false matrix: include: - - language: go - build-mode: autobuild - language: javascript-typescript build-mode: none steps: @@ -48,21 +44,7 @@ jobs: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - # Placeholder manual-build step; only runs if a language's build-mode above is set to "manual". - - if: matrix.build-mode == 'manual' - shell: bash - run: | - echo 'If you are using a "manual" build mode for one or more of the' \ - 'languages you are analyzing, replace this with the commands to build' \ - 'your code, for example:' - echo ' make bootstrap' - echo ' make release' - exit 1 - - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 with: category: "/language:${{matrix.language}}" -defaults: - run: - working-directory: apps/cli-go diff --git a/.github/workflows/lint-pull-request.yml b/.github/workflows/lint-pull-request.yml index e69d6bd814..e93acdf712 100644 --- a/.github/workflows/lint-pull-request.yml +++ b/.github/workflows/lint-pull-request.yml @@ -52,7 +52,6 @@ jobs: api cli cli-e2e - cli-go cli-test-helpers config docs diff --git a/.github/workflows/cli-go-mirror-image.yml b/.github/workflows/mirror-image.yml similarity index 100% rename from .github/workflows/cli-go-mirror-image.yml rename to .github/workflows/mirror-image.yml diff --git a/.github/workflows/mirror-template-images.yml b/.github/workflows/mirror-template-images.yml index dabc95430e..11745561ad 100644 --- a/.github/workflows/mirror-template-images.yml +++ b/.github/workflows/mirror-template-images.yml @@ -6,7 +6,7 @@ name: Mirror template images # Dockerfile changes on develop — most often via a merged catalog bump or # hotfix PR from slim-release-published.yml, or a Dependabot `docker` bump for # an upstream-only image — this workflow detects any tag that is not yet -# mirrored and backfills it the same way `cli-go-mirror-image.yml` does. +# mirrored and backfills it the same way `mirror-image.yml` does. # # It runs on `push` to develop (not on the PR) on purpose: mirroring needs the # AWS role + packages:write, which a dependabot-triggered `pull_request` run @@ -14,10 +14,9 @@ name: Mirror template images # backfill runs as soon as the bump lands on develop, repopulating ghcr.io/ECR # so anyone pinning SUPABASE_INTERNAL_IMAGE_REGISTRY=ghcr.io doesn't inherit a # `manifest unknown` failure. (No CI job pins that registry anymore -- the -# `cli-go-ci.yml` `Start` job that used to made it CI-observable was removed in -# CLI-1966 along with `internal/start`; this mirror now only serves users who -# opt into the mirror directly, and a mirroring failure no longer turns any -# check red.) +# `Start` job that used to make it CI-observable was removed in CLI-1966 along +# with `internal/start`; this mirror now only serves users who opt into the +# mirror directly, and a mirroring failure no longer turns any check red.) on: push: @@ -81,7 +80,7 @@ jobs: matrix: image: ${{ fromJson(needs.detect.outputs.missing) }} # Reuse the existing mirror logic (docker.io -> public.ecr.aws + ghcr.io). - uses: ./.github/workflows/cli-go-mirror-image.yml + uses: ./.github/workflows/mirror-image.yml with: image: ${{ matrix.image }} secrets: diff --git a/.github/workflows/cli-go-publish-migra.yml b/.github/workflows/publish-migra.yml similarity index 96% rename from .github/workflows/cli-go-publish-migra.yml rename to .github/workflows/publish-migra.yml index 4a63bb7085..224b63b426 100644 --- a/.github/workflows/cli-go-publish-migra.yml +++ b/.github/workflows/publish-migra.yml @@ -84,11 +84,8 @@ jobs: - settings - merge_manifest # Call workflow explicitly because events from actions cannot trigger more actions - uses: ./.github/workflows/cli-go-mirror-image.yml + uses: ./.github/workflows/mirror-image.yml with: image: ${{ needs.settings.outputs.image_tag }} secrets: PROD_AWS_ROLE: ${{ secrets.PROD_AWS_ROLE }} -defaults: - run: - working-directory: apps/cli-go diff --git a/.github/workflows/cli-go-pg-prove.yml b/.github/workflows/publish-pg-prove.yml similarity index 96% rename from .github/workflows/cli-go-pg-prove.yml rename to .github/workflows/publish-pg-prove.yml index 549b43530b..d6d47bd935 100644 --- a/.github/workflows/cli-go-pg-prove.yml +++ b/.github/workflows/publish-pg-prove.yml @@ -84,11 +84,8 @@ jobs: - settings - merge_manifest # Call workflow explicitly because events from actions cannot trigger more actions - uses: ./.github/workflows/cli-go-mirror-image.yml + uses: ./.github/workflows/mirror-image.yml with: image: ${{ needs.settings.outputs.image_tag }} secrets: PROD_AWS_ROLE: ${{ secrets.PROD_AWS_ROLE }} -defaults: - run: - working-directory: apps/cli-go diff --git a/.github/workflows/slim-release-published.yml b/.github/workflows/slim-release-published.yml index f8520274f1..c90eb95943 100644 --- a/.github/workflows/slim-release-published.yml +++ b/.github/workflows/slim-release-published.yml @@ -150,10 +150,9 @@ jobs: generated_files=( packages/stack/src/Artifacts.ts apps/cli/src/shared/services/Dockerfile - apps/cli-go/pkg/config/templates/Dockerfile ) if git diff --quiet -- "${generated_files[@]}"; then - echo "${branch}: catalog and Dockerfiles already match ${release}; skipping." + echo "${branch}: catalog and Dockerfile already match ${release}; skipping." continue fi @@ -163,10 +162,10 @@ jobs: if ! push_output="$(git push --force "$push_url" "HEAD:refs/heads/${branch}" 2>&1)"; then echo "$push_output" if ! grep -q "queued for merging cannot be updated" <<<"$push_output"; then - echo "::error ::Failed to push ${branch}. Run manually: ${manual_prefix} ${release}, regenerate the Dockerfiles with \`bun apps/cli/scripts/render-service-dockerfile.ts\`, then open a pull request by hand." + echo "::error ::Failed to push ${branch}. Run manually: ${manual_prefix} ${release}, regenerate the Dockerfile with \`bun apps/cli/scripts/render-service-dockerfile.ts\`, then open a pull request by hand." exit 1 fi - manual_steps="run manually: ${manual_prefix} ${release}, regenerate the Dockerfiles with \`bun apps/cli/scripts/render-service-dockerfile.ts\`, then open a pull request by hand." + manual_steps="run manually: ${manual_prefix} ${release}, regenerate the Dockerfile with \`bun apps/cli/scripts/render-service-dockerfile.ts\`, then open a pull request by hand." queued_steps="Once ${branch} leaves the merge queue, ${manual_steps}" # The app token minted just before this step expires after one hour; capping the wait at # 50 minutes into the step leaves room for the lookups and the dispatch that follow it. diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 981705f990..d32af4514c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -65,8 +65,6 @@ jobs: uses: ./.github/actions/setup with: dependency-firewall-token: ${{ secrets.DF_FIREWALL_TOKEN }} - - name: Unlock keyring (for cli-go keyring tests) - uses: t1m0thyj/unlock-keyring@cbcf205c879ebd86add70bab3a6abfcce59a5cae # v1.2.0 - name: Check code quality run: pnpm run check:all @@ -100,7 +98,6 @@ jobs: uses: ./.github/actions/setup with: dependency-firewall-token: ${{ secrets.DF_FIREWALL_TOKEN }} - go-cache: "false" - name: Run unit tests run: pnpm run test:unit @@ -123,7 +120,6 @@ jobs: uses: ./.github/actions/setup with: dependency-firewall-token: ${{ secrets.DF_FIREWALL_TOKEN }} - go-cache: "false" - name: Run integration tests run: pnpm run test:integration @@ -162,7 +158,6 @@ jobs: uses: ./.github/actions/setup with: dependency-firewall-token: ${{ secrets.DF_FIREWALL_TOKEN }} - go-cache: "false" - name: Run stack integration tests working-directory: packages/stack @@ -281,19 +276,6 @@ jobs: with: dependency-firewall-token: ${{ secrets.DF_FIREWALL_TOKEN }} - - name: Cache Go CLI binary - id: cache-go-binary - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: apps/cli-go/supabase-go - key: go-cli-${{ runner.os }}-${{ hashFiles('apps/cli-go/**/*.go', - 'apps/cli-go/go.mod', 'apps/cli-go/go.sum') }} - - - name: Build Go CLI - if: steps.cache-go-binary.outputs.cache-hit != 'true' - run: go build -o supabase-go . - working-directory: apps/cli-go - # The e2e harness invokes `node apps/cli/dist/supabase.js` with # `SUPABASE_CLI_BINARY_OVERRIDE` pointing at the compiled binary in # `apps/cli/dist/`. Build the CLI explicitly before invoking every @@ -304,14 +286,10 @@ jobs: - name: Run end-to-end tests if: matrix.suite == 'cli' run: pnpm exec turbo run test:e2e:run --only --filter=supabase -- --shard=${{ matrix.shard }}/${{ matrix.shards }} - env: - SUPABASE_GO_BINARY: ${{ github.workspace }}/apps/cli-go/supabase-go - name: Run cli-e2e end-to-end tests if: matrix.suite == 'cli-e2e' run: pnpm exec turbo run test:e2e:run --only --filter=@supabase/cli-e2e - env: - SUPABASE_GO_BINARY: ${{ github.workspace }}/apps/cli-go/supabase-go # Vitest writes its results cache under a sha1-named directory. The refresh script # (apps/cli/scripts/refresh-e2e-timings.ts) merges these into tests/e2e-timings.json. @@ -347,7 +325,6 @@ jobs: uses: ./.github/actions/setup with: dependency-firewall-token: ${{ secrets.DF_FIREWALL_TOKEN }} - go-cache: "false" - name: Setup Podman if: matrix.runtime == 'podman' diff --git a/.github/workflows/verify-install-channels.yml b/.github/workflows/verify-install-channels.yml index 26bfa0d90b..f2e4156ab6 100644 --- a/.github/workflows/verify-install-channels.yml +++ b/.github/workflows/verify-install-channels.yml @@ -6,9 +6,7 @@ name: Verify Install Channels # an already-published version. # # Performs a real install so a manifest/tarball checksum mismatch (as in CLI v2.107.0) reproduces -# here instead of being trusted from the publish step. Each leg also directly checks that the -# `supabase-go` sidecar binary is present and executable in the install directory, checking the -# file rather than a CLI command, since a command could later be natively ported off the sidecar. +# here instead of being trusted from the publish step. on: workflow_call: @@ -137,23 +135,8 @@ jobs: echo "Version mismatch: expected ${expected}, got ${actual}" >&2 exit 1 fi - - name: Verify Go sidecar - run: | - set -euo pipefail - # Homebrew's `bin.install` symlinks both `supabase` and `supabase-go` - # into the same prefix bin/ directory, so the sidecar must sit right - # next to whichever `supabase` resolved from PATH. - bin_dir="$(dirname "$(command -v supabase)")" - sidecar="${bin_dir}/supabase-go" - if [ ! -e "${sidecar}" ]; then - echo "Go sidecar probe failed: ${sidecar} does not exist" >&2 - exit 1 - fi - if [ ! -x "${sidecar}" ]; then - echo "Go sidecar probe failed: ${sidecar} exists but is not executable" >&2 - exit 1 - fi - echo "Go sidecar probe OK: ${sidecar}" + - name: Exercise supabase --help + run: supabase --help scoop: name: Scoop (${{ inputs.scoop_name }}) @@ -191,23 +174,9 @@ jobs: echo "Version mismatch: expected ${expected}, got ${actual}" >&2 exit 1 fi - - name: Verify Go sidecar + - name: Exercise supabase --help shell: bash - run: | - set -euo pipefail - # Scoop's manifest only declares `supabase.exe` in `bin` (see - # apps/cli/scripts/update-scoop.ts), so only `supabase` gets a shim in - # ~/scoop/shims — `dirname "$(command -v supabase)"` would resolve to - # the shim directory, not the real install directory supabase-go.exe - # actually lives in. Go straight to the app's current version - # directory instead, which Scoop always maintains regardless of shims. - app_dir="${HOME}/scoop/apps/${SCOOP_NAME}/current" - sidecar="${app_dir}/supabase-go.exe" - if [ ! -e "${sidecar}" ]; then - echo "Go sidecar probe failed: ${sidecar} does not exist" >&2 - exit 1 - fi - echo "Go sidecar probe OK: ${sidecar}" + run: supabase --help install-script: name: install script (${{ matrix.runner }}) @@ -248,21 +217,6 @@ jobs: echo "Version mismatch: expected ${expected}, got ${actual}" >&2 exit 1 fi - - name: Verify Go sidecar + - name: Exercise supabase --help shell: bash - run: | - set -euo pipefail - # The install script places `supabase-go` right next to `supabase`, - # so the sidecar must sit in the same directory `supabase` resolved - # from on PATH. - bin_dir="$(dirname "$(command -v supabase)")" - sidecar="${bin_dir}/supabase-go" - if [ ! -e "${sidecar}" ]; then - echo "Go sidecar probe failed: ${sidecar} does not exist" >&2 - exit 1 - fi - if [ ! -x "${sidecar}" ]; then - echo "Go sidecar probe failed: ${sidecar} exists but is not executable" >&2 - exit 1 - fi - echo "Go sidecar probe OK: ${sidecar}" + run: supabase --help diff --git a/.oxfmtrc.json b/.oxfmtrc.json index e364381a7e..33527f22c6 100644 --- a/.oxfmtrc.json +++ b/.oxfmtrc.json @@ -3,7 +3,6 @@ ".repos/", "apps/cli/src/shared/feedback/database.types.ts", "packages/stack/src/functions/generated/", - "apps/cli-go/", "apps/cli-e2e/fixtures/", "apps/docs/content/docs/commands/", "apps/docs/public/", diff --git a/.oxlintrc.json b/.oxlintrc.json index 9f4d34677e..b30bca88a4 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -6,7 +6,6 @@ }, "ignorePatterns": [ ".repos", - "apps/cli-go", // Generated by `supabase gen types` (see `gen:feedback-types` in apps/cli). "apps/cli/src/shared/feedback/database.types.ts", "apps/cli-e2e/fixtures", diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 05ae329b46..f6e3e10074 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,7 +20,7 @@ Pull requests from external contributors that do not follow this workflow are co ### Tool versions -This repo pins the versions of Node, Bun, Go, pnpm, and golangci-lint that contributors are expected to build against, and uses [`mise`](https://mise.jdx.dev/) — a polyglot version manager — to install and activate them automatically. If you don't already have these tools installed, `mise` is a great way to get up and running quickly. +This repo pins the versions of Node, Bun, and pnpm that contributors are expected to build against, and uses [`mise`](https://mise.jdx.dev/) — a polyglot version manager — to install and activate them automatically. If you don't already have these tools installed, `mise` is a great way to get up and running quickly. #### Installing mise @@ -52,21 +52,17 @@ mise install `mise install` resolves the versions this repo expects from a handful of files, rather than hardcoding them all in one place: -| Tool | Version source | -| ------------- | --------------------------------------------------- | -| Bun | `.bun-version` | -| Node.js | `.node-version` | -| pnpm | `devEngines.packageManager` field in `package.json` | -| Go | `mise.toml` | -| golangci-lint | `mise.toml` | - -The Go and golangci-lint entries in `mise.toml` are intentionally temporary while the Go CLI remains in the repo. The canonical Go module metadata still lives in `apps/cli-go/go.mod`; keep the `mise.toml` entries aligned only until the Go code is removed. +| Tool | Version source | +| ------- | --------------------------------------------------- | +| Bun | `.bun-version` | +| Node.js | `.node-version` | +| pnpm | `devEngines.packageManager` field in `package.json` | Once installed, `mise` activates these versions automatically whenever your shell is inside this repo — no manual `nvm use`, `gvm use`, or similar switching required. #### Without mise -`mise` is not required. If you already have Bun, Node, pnpm, and Go installed and managed some other way, just make sure your versions match the ones pinned in `.bun-version`, `.node-version`, `mise.toml`, `package.json`, and `apps/cli-go/go.mod`. +`mise` is not required. If you already have Bun, Node, and pnpm installed and managed some other way, just make sure your versions match the ones pinned in `.bun-version`, `.node-version`, and `package.json`. ### Install dependencies @@ -178,16 +174,14 @@ pnpm run test:unit && pnpm run test:integration ``` The root unit and integration scripts use Turbo to fan out the package-local -`test:*:run` tasks across the standard TypeScript/Vitest workspaces. The Go -workspace remains package-local because its tests run directly through Go: -`pnpm --dir apps/cli-go run test:unit`. Go tests are covered by the dedicated -Go CI workflow. Unit and integration tasks are uncached for now; e2e tasks are -also uncached and run one package at a time. Forward a Vitest shard to every -e2e package with `pnpm run test:e2e --shard=1/3`. +`test:*:run` tasks across the standard TypeScript/Vitest workspaces. Unit and +integration tasks are uncached for now; e2e tasks are also uncached and run +one package at a time. Forward a Vitest shard to every e2e package with +`pnpm run test:e2e --shard=1/3`. ## E2E Compatibility Test Suite -`apps/cli-e2e` implements the replay-and-record compatibility harness for the TypeScript CLI. Live tests are owned by `apps/cli` and run from the command they cover. The CLI still shells out to the bundled Go binary for the handful of commands the TS port proxies (`db diff --use-pg-schema`, `db branch *`, `db remote changes`, `gen keys`, `functions download`), so `apps/cli-go/` is built alongside the TS CLI for these suites, but there is no Go-vs-TypeScript parity runner. +`apps/cli-e2e` implements the replay-and-record compatibility harness for the TypeScript CLI. Live tests are owned by `apps/cli` and run from the command they cover. ### Architecture @@ -284,7 +278,6 @@ Test a real end-to-end publish and install of the CLI against a local npm regist ### Prerequisites - **Bun** — for compiling the CLI binary and running the scripts -- **Go** — required to build the `supabase-go` sidecar (commands proxied to the Go binary) - **pnpm** — already required by this repo - **Node.js** — required by `npx` / `npm install -g` to test the published package @@ -331,8 +324,6 @@ supabase --version | Problem | Fix | | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Error: Something is already running on port 4873` | Kill the leftover Verdaccio process (`lsof -ti:4873 \| xargs kill`) and retry | -| `go not found in PATH` | Install Go from https://go.dev/dl/ | -| `Error: Go CLI source not found` | Run `pnpm repos:install` to clone `apps/cli-go` | | `npm` / `pnpm` tries to fetch from `localhost:4873` when no registry is running | Stale global registry override left behind by an older version of `local-registry.ts` (the current script never modifies global config). Run `npm config delete registry` and `pnpm config delete registry`. Note that pnpm stores the override in its own global config (`~/Library/Preferences/pnpm/auth.ini` on macOS, `~/.config/pnpm/` on Linux), not `~/.npmrc` — check there if the delete command fails | | `npx` resolves from npm instead of local | Pass `--registry http://localhost:4873` explicitly to `npx` / `npm install` | @@ -361,15 +352,15 @@ pnpm run build pnpm run generate ``` -**Build only the CLI and its Go sidecar:** +**Build only the CLI:** ```sh pnpm exec turbo run supabase#build ``` -The CLI build names its config and Go build prerequisites explicitly. The -remaining CLI workspace dependencies intentionally have no build script, so -strict Turbo task selection does not synthesize no-op `^build` tasks for them. +The CLI build names its config build prerequisite explicitly. The remaining +CLI workspace dependencies intentionally have no build script, so strict +Turbo task selection does not synthesize no-op `^build` tasks for them. **Run the live suite:** diff --git a/apps/cli-go/LICENSE b/LICENSE similarity index 100% rename from apps/cli-go/LICENSE rename to LICENSE diff --git a/README.md b/README.md index cebc9eaab6..c5ea9ae1bd 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,6 @@ Useful source entry points: | Path | Purpose | | ----------------- | ------------------------------------ | | `apps/cli` | TypeScript/Bun CLI package | -| `apps/cli-go` | Go CLI source used by the CLI | | `packages/stack` | Local Supabase stack runtime | | `packages/config` | Config schema and generated types | | `packages/api` | Typed Supabase Management API client | diff --git a/apps/cli-e2e/.env.example b/apps/cli-e2e/.env.example index d437835f33..fb502b3f75 100644 --- a/apps/cli-e2e/.env.example +++ b/apps/cli-e2e/.env.example @@ -7,12 +7,6 @@ CLI_E2E_MODE=record # Staging Management API token. Required in record mode. SUPABASE_ACCESS_TOKEN=sbp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -# The CLI shells out to the bundled Go binary for the proxied commands -# (db diff --use-pg-schema, db branch *, db remote changes, gen keys, -# functions download --legacy-bundle). Point at a freshly built binary: -# cd apps/cli-go && go build -o /tmp/supabase-test-binary . -SUPABASE_GO_BINARY=/tmp/supabase-test-binary - # --- Optional record overrides (sensible defaults in src/tests/env.ts) --- # Management API base (also accepted as SUPABASE_STAGING_URL). # CLI_E2E_API_URL=https://api.supabase.green diff --git a/apps/cli-e2e/AGENTS.md b/apps/cli-e2e/AGENTS.md index 0e6c6ad342..5182804955 100644 --- a/apps/cli-e2e/AGENTS.md +++ b/apps/cli-e2e/AGENTS.md @@ -180,32 +180,3 @@ per-process, so each shard still has deterministic intra-shard ordering. is a single-job operation; parallel shards would race on the shared `fixtures/recorded/` directory. The `record` script does not accept `--shard`. - -## Go binary version requirement - -The CLI proxies a fixed, small set of commands to a Go binary (`SUPABASE_GO_BINARY` → bundled package binary → system `supabase`) — as of CLI-1970, `apps/cli-go/` contains only that residual proxied subset, nothing else, and it is slated for cleanup and removal (the surface only shrinks; never add tests that grow it). If your system `supabase` binary predates a flag or subcommand change on one of these, `testBehaviour` tests for it will fail with "unknown command" or "unknown flag". - -Build the Go CLI from source and point `SUPABASE_GO_BINARY` at it: - -```sh -(cd apps/cli-go && go build -o /tmp/supabase-test-binary .) - -# Replay -SUPABASE_GO_BINARY=/tmp/supabase-test-binary \ - pnpm exec turbo run @supabase/cli-e2e#test:e2e:run - -# Record -SUPABASE_GO_BINARY=/tmp/supabase-test-binary \ - SUPABASE_ACCESS_TOKEN=sbp_... SUPABASE_STAGING_URL=https://api.supabase.green \ - pnpm run record -``` - -`SUPABASE_GO_BINARY` is inherited by the CLI subprocess via `exec()` in the harness, so you only need to set it once in the shell. - -Commands currently requiring this — the full proxied surface, nothing else needs a Go binary at all: - -- `db diff` (for `--use-pg-schema`) -- `db branch create`, `db branch delete`, `db branch list`, `db branch switch` -- `db remote changes` -- `gen keys` -- `functions download` (for the hidden `--legacy-bundle` flag) diff --git a/apps/cli-e2e/src/tests/functions.e2e.test.ts b/apps/cli-e2e/src/tests/functions.e2e.test.ts index f39a423806..bfba4a5e38 100644 --- a/apps/cli-e2e/src/tests/functions.e2e.test.ts +++ b/apps/cli-e2e/src/tests/functions.e2e.test.ts @@ -78,8 +78,8 @@ describe("functions", () => { }); describe("functions:deploy", () => { - // Deploy requires the Go binary to bundle function files locally before any API call, - // so error injection tests pre-create the function with `functions new` first. + // Deploy bundles function files locally before any API call, so error injection tests + // pre-create the function with `functions new` first. testBehaviour("exits non-zero on 401", async ({ run, apiUrl }) => { await run(["functions", "new", FUNCTION_NAME]); diff --git a/apps/cli-e2e/src/tests/go-binary-surface.e2e.test.ts b/apps/cli-e2e/src/tests/go-binary-surface.e2e.test.ts deleted file mode 100644 index fc0b9acfe0..0000000000 --- a/apps/cli-e2e/src/tests/go-binary-surface.e2e.test.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; -import { afterAll, beforeAll, describe, expect, test } from "vitest"; - -// The bundled `supabase-go` binary retains only the commands the TypeScript -// CLI's `GoProxy` can spawn; every other Go command was deleted. TS -// integration tests stub that subprocess boundary, so nothing else notices if -// a still-reachable Go command gets trimmed away by mistake. -// -// This suite enumerates every argv shape the TS side can hand to `GoProxy` -// and asserts the built `supabase-go` binary still resolves it. If this -// fails, either the TS spawn surface grew (add the command to the retained -// set in `apps/cli-go`) or the trim cut too deep (restore the command). -// -// `SUPABASE_GO_BINARY` is only set to a freshly built binary in CI (see -// `.github/workflows/test.yml`); locally this whole suite no-ops. -const GO_BINARY = process.env["SUPABASE_GO_BINARY"]; - -describe.skipIf(GO_BINARY === undefined)("go binary spawn surface (CLI-1970)", () => { - const binary = GO_BINARY as string; - - let workspaceDir: string; - let bogusProfilePath: string; - - beforeAll(() => { - workspaceDir = mkdtempSync(join(tmpdir(), "cli-e2e-go-binary-surface-")); - - // The upgrade check hits the real GitHub releases API on every invocation - // that returns a nil error (e.g. every `--help` call below), unless a - // `supabase/.temp/cli-latest` cache file already exists and is less than - // 10h old. Pre-seeding it here keeps this suite hermetic. - mkdirSync(join(workspaceDir, "supabase", ".temp"), { recursive: true }); - writeFileSync(join(workspaceDir, "supabase", ".temp", "cli-latest"), "v0.0.0"); - - // A bogus `--profile` for the two Management-API-gated delegates (`gen - // keys`, `functions download --legacy-bundle`): the unique profile name - // can never match a real stored credential, and the unreachable api_url - // means even a stray match still fails at connect. - bogusProfilePath = join(workspaceDir, "profile.yaml"); - writeFileSync( - bogusProfilePath, - [ - "name: cli-e2e-go-binary-surface-guard", - 'api_url: "http://127.0.0.1:1"', - 'dashboard_url: "http://127.0.0.1:1"', - "project_host: localhost", - ].join("\n"), - ); - }); - - afterAll(() => { - rmSync(workspaceDir, { recursive: true, force: true }); - }); - - function runGo(args: ReadonlyArray, envOverrides: Record = {}) { - const result = Bun.spawnSync([binary, ...args], { - cwd: workspaceDir, - env: { - PATH: process.env["PATH"] ?? "", - HOME: workspaceDir, - SUPABASE_HOME: workspaceDir, - // Belt-and-braces: no command exercised here should ever reach a - // real Docker daemon or send real telemetry. - DOCKER_HOST: "tcp://127.0.0.1:1", - SUPABASE_TELEMETRY_DISABLED: "1", - ...envOverrides, - }, - stdout: "pipe", - stderr: "pipe", - }); - return { - exitCode: result.exitCode, - stdout: result.stdout.toString(), - stderr: result.stderr.toString(), - }; - } - - // The complete spawn surface, mirrored from `GoProxy` call sites: - // - db diff (diff.handler.ts, `--use-pg-schema` delegate path) - // - db branch create|delete|list|switch (thin proxies) - // - db remote changes (thin proxy) - // - gen keys (keys.handler.ts) - // - functions download (shared/functions/download.ts, `--legacy-bundle`) - const RETAINED_COMMAND_PATHS: ReadonlyArray> = [ - ["db", "diff"], - ["db", "branch", "create"], - ["db", "branch", "delete"], - ["db", "branch", "list"], - ["db", "branch", "switch"], - ["db", "remote", "changes"], - ["gen", "keys"], - ["functions", "download"], - ]; - - describe("resolves every retained command path", () => { - for (const path of RETAINED_COMMAND_PATHS) { - test(`supabase-go ${path.join(" ")} --help exits 0`, () => { - const { exitCode, stdout, stderr } = runGo([...path, "--help"]); - const output = stdout + stderr; - expect(exitCode).toBe(0); - expect(output).not.toContain("unknown command"); - // A deleted nested subcommand doesn't error "unknown command": cobra - // only rejects unmatched args on the root command, so `db diff --help` - // with `diff` deleted would fall through to `db`'s own help (exit 0). - // Asserting the full command path appears in the usage output closes - // that gap — cobra only prints it once the whole path has resolved. - expect(output).toContain(`supabase ${path.join(" ")}`); - }, 5_000); - } - }); - - describe("accepts the exact argv shape the TS proxy builds", () => { - // `db diff` always provisions a Docker shadow database first, so the bogus - // DOCKER_HOST is what makes this fail fast, not the bogus --db-url. - test("db diff --use-pg-schema", () => { - const { exitCode, stderr } = runGo([ - "db", - "diff", - "--use-pg-schema", - "--db-url", - "postgresql://u:p@127.0.0.1:1/x", - "--schema", - "public", - ]); - expect(exitCode).toBe(1); - expect(stderr).not.toMatch(/unknown flag|invalid argument/i); - }, 5_000); - - // The only invocation in this suite exercising the full global-flag set at - // once. Also provisions a Docker shadow first, so DOCKER_HOST trips it. - test("db remote changes (full global flag set)", () => { - const { exitCode, stderr } = runGo([ - "--output", - "json", - "--profile", - "supabase-staging", - "--debug", - "--workdir", - workspaceDir, - "--experimental", - "--network-id", - "cli-e2e-test-net", - "--yes", - "--dns-resolver", - "https", - "--create-ticket", - "--agent", - "no", - "db", - "remote", - "changes", - "--db-url", - "postgresql://u:p@127.0.0.1:1/x", - "--schema", - "public", - ]); - expect(exitCode).toBe(1); - expect(stderr).not.toMatch(/unknown flag|invalid argument/i); - }, 5_000); - - // Gated behind a login check before any network call, so an isolated - // SUPABASE_HOME plus the bogus --profile fails fast without reaching a real API. - test("gen keys", () => { - const { exitCode, stderr } = runGo([ - "gen", - "keys", - "--project-ref", - "abcdefghijklmnopqrst", - "--override-name", - "db.host=CUSTOM_DB_HOST", - "--experimental", - "--profile", - bogusProfilePath, - ]); - expect(exitCode).toBe(1); - expect(stderr).not.toMatch(/unknown flag|invalid argument/i); - }, 5_000); - - // Same login-gate fail-fast as `gen keys`. - test("functions download --legacy-bundle", () => { - const { exitCode, stderr } = runGo([ - "functions", - "download", - "my-function", - "--project-ref", - "abcdefghijklmnopqrst", - "--legacy-bundle", - "--profile", - bogusProfilePath, - ]); - expect(exitCode).toBe(1); - expect(stderr).not.toMatch(/unknown flag|invalid argument/i); - }, 5_000); - }); - - describe("negative control: a deleted command still reports unknown", () => { - // `["db", "start"]` isn't used here even though it's a deleted command: - // like the nested-subcommand case above, `db` would fall through to its - // own help (exit 0) instead of erroring. Only a deleted top-level command - // reliably reproduces "unknown command". - for (const deletedCommand of ["inspect", "start"]) { - test(`supabase-go ${deletedCommand} reports unknown command`, () => { - const { exitCode, stderr } = runGo([deletedCommand]); - expect(exitCode).toBe(1); - expect(stderr).toContain("unknown command"); - }, 5_000); - } - }); -}); diff --git a/apps/cli-go/.gitignore b/apps/cli-go/.gitignore deleted file mode 100644 index 32520228b6..0000000000 --- a/apps/cli-go/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -# General -.DS_Store -.env - -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib -/cli -/main -/supabase-go - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Dependency directories (remove the comment below to include it) -# vendor/ - -# IDE -/.vscode -/.idea - -# NPM -node_modules -package-lock.json - -# Initialized by cli for local testing -/supabase diff --git a/apps/cli-go/.golangci.yml b/apps/cli-go/.golangci.yml deleted file mode 100644 index 711143ee31..0000000000 --- a/apps/cli-go/.golangci.yml +++ /dev/null @@ -1,49 +0,0 @@ -version: "2" -linters: - enable: - - dogsled - - dupl - - gosec - - misspell - - nakedret - - staticcheck - - unconvert - - unparam - - whitespace - settings: - dupl: - threshold: 250 - exclusions: - generated: lax - rules: - - text: "ST1003:" - linters: - - staticcheck - - path: _test\.go - linters: - - gosec - text: G101 - - path: internal/utils/docker\.go - linters: - - gosec - text: G117 - - presets: - - comments - - common-false-positives - - legacy - - std-error-handling - paths: - - third_party$ - - builtin$ - - examples$ -formatters: - enable: - - gofmt - - goimports - exclusions: - generated: lax - paths: - - third_party$ - - builtin$ - - examples$ diff --git a/apps/cli-go/CONTRIBUTING.md b/apps/cli-go/CONTRIBUTING.md deleted file mode 100644 index c53f8d6c91..0000000000 --- a/apps/cli-go/CONTRIBUTING.md +++ /dev/null @@ -1,50 +0,0 @@ -# Welcome to Supabase CLI contributing guide - -## Release process - -We release to stable channel every two weeks via `main` branch. - -We release to beta channel on merge to `develop` branch. - -Hotfixes are released manually. Follow these steps: - -1. Create a new branch named `N.N.x` from latest stable version. For eg. - 1. If stable is on `v1.2.3` and beta is on `v1.3.6`, create `1.2.x` branch. - 2. If stable is on `v1.3.1` and beta is on `v1.3.6`, create `1.3.x` branch (or simply release all patch versions). -2. Cherry-pick your hotfix on top of `N.N.x` branch. -3. Run the [Release (Beta)](https://github.com/supabase/cli/actions/workflows/release-beta.yml) workflow targetting `N.N.x` branch. -4. Verify your hotfix locally with `npx supabase@N.N.x help` -5. Edit [GitHub releases](https://github.com/supabase/cli/releases) to set your hotfix pre-release as latest stable. - -After promoting the next beta version to stable, previous `N.N.x` branches may be deleted. - -To revert a stable release, set a previous release to latest. This will update brew and scoop to an old version. There's no need to revert npm as it supports version pinning. - -## Unit testing - -All new code should aim to improve [test coverage](https://coveralls.io/github/supabase/cli). - -We use mock objects for unit testing code that interacts with external systems, such as - -- local filesystem (via [afero](https://github.com/spf13/afero)) -- Postgres database (via [pgmock](https://github.com/jackc/pgmock)) -- Supabase API (via [gock](https://github.com/h2non/gock)) - -Wrappers and test helper methods can be found under [internal/testing](internal/testing). - -Integration tests are created under [test](test). To run all tests: - -```bash -go test ./... -race -v -count=1 -failfast -``` - -## API client - -The Supabase API client is generated from OpenAPI spec. See [our guide](api/README.md) for updating the client and types. - -## Testing local pg-delta builds - -The Go binary no longer runs pg-delta. The TypeScript CLI bundles -`@supabase/pg-delta` in-process. To test a local pg-delta build, update the -`@supabase/pg-delta` dependency pin in `apps/cli/package.json` / -`pnpm-workspace.yaml`. diff --git a/apps/cli-go/api/README.md b/apps/cli-go/api/README.md deleted file mode 100644 index 326d08315b..0000000000 --- a/apps/cli-go/api/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Supabase OpenAPI Specification - -This directory contains the OpenAPI specification for Supabase Management APIs. - -`v1-openapi.yaml` is a committed snapshot of the upstream specification and is the input -`go generate` uses to build the Go [client](../pkg/api/client.gen.go) and -[types](../pkg/api/types.gen.go). `overlay.yaml` patches the specification on its way -through codegen, for shapes `oapi-codegen` cannot consume directly. - -Committing the snapshot keeps codegen reproducible: `go generate` needs no network -access, and CI's `Codegen` check fails only when a pull request leaves `pkg/api` out of -sync with the snapshot. Upstream changes arrive through the -[API Sync workflow](../../../.github/workflows/cli-go-api-sync.yml), which refreshes the -snapshot and regenerates the client together in one pull request, so a spec change no -longer breaks unrelated pull requests. - -## Updating the specification - -The specification is generated from our NestJS middleware. `v1-openapi.yaml` is -refreshed from **staging** (`https://api.supabase.green/api/v1-yaml`) by the API -Sync workflow, so staging is the snapshot's authoritative upstream and the Go -client tracks the API as staging exposes it. The production release is viewable -as [Swagger UI](https://api.supabase.com/api/v1), which is useful for reading the -shipped API but is not what the snapshot is generated from -- expect it to lag -the snapshot. - -Refreshing the snapshot by hand is rarely necessary, since API Sync does it -automatically. To pick up a change from local development before it reaches staging: - -1. Update the snapshot - -```bash -curl -fsSL -o api/v1-openapi.yaml http://127.0.0.1:8080/api/v1-yaml -``` - -2. Regenerate the Go client and API types - -```bash -go generate -``` - -3. [Optional] Add [properties](https://swagger.io/docs/specification/basic-structure/) that NestJS does not generate to `overlay.yaml` diff --git a/apps/cli-go/api/overlay.yaml b/apps/cli-go/api/overlay.yaml deleted file mode 100644 index 025cca4465..0000000000 --- a/apps/cli-go/api/overlay.yaml +++ /dev/null @@ -1,79 +0,0 @@ -overlay: 1.0.0 -info: - title: Overlay - version: 0.0.0 -actions: -- target: $.components.schemas.*.properties.password_required_characters.enum - description: Converts enum with special characters to a string - remove: true -- target: $.components.schemas.*.properties.password_required_characters - description: Optionally adds back the enum with escaped backslash and quotes - update: - enum: - - abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789 - - abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789 - - abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\\\:\"|<>?,./`~ - - '' -- target: $.components.schemas.*.properties.attribute_mapping.properties.keys.additionalProperties.properties.default.anyOf - description: Replaces request union type with interface for easier casting - remove: true -- target: $.components.schemas.*.properties.saml.properties.attribute_mapping.properties.keys.additionalProperties.properties.default.anyOf - description: Replaces response union type with interface for easier casting - remove: true -- target: $.components.schemas.*.properties.items.items.properties.saml.properties.attribute_mapping.properties.keys.additionalProperties.properties.default.anyOf - description: Replaces list union type with interface for easier casting - remove: true -- target: $.components.schemas.*.properties.saml.properties.name_id_format.enum - description: Converts enum with duplicated type to a string - remove: true -- target: $.components.schemas.*.properties.items.items.properties.saml.properties.name_id_format.enum - description: Converts list of enum with duplicated type to a string - remove: true -- target: $.components.schemas.*.properties.connectionString - description: Removes deprecated field that conflicts with naming convention - remove: true -- target: $.components.schemas.V1CreateProjectBody.properties.release_channel - description: Removes deprecated null-only field that oapi-codegen cannot map - remove: true -- target: $.components.schemas.V1CreateProjectBody.properties.postgres_engine - description: Removes deprecated null-only field that oapi-codegen cannot map - remove: true -- target: $.components.schemas.JitListAccessResponse_Output.properties.items.items.anyOf[0].properties.invite_id - description: Replaces null-only project user invite id with nullable UUID for oapi-codegen - update: - type: string - format: uuid - nullable: true -- target: $.components.schemas.JitListAccessResponse_Output.properties.items.items.anyOf[0].properties.expires_at - description: Replaces null-only project user invite expiry with nullable string for oapi-codegen - update: - type: string - nullable: true -- target: $.components.schemas.JitListAccessResponse_Output.properties.items.items.anyOf[1].properties.user_id - description: Replaces null-only invited user id with nullable UUID for oapi-codegen - update: - type: string - format: uuid - nullable: true -- target: $.paths.*.*.parameters[?(@.name=='branch_id_or_ref')] - update: - schema: - type: string -- target: $.paths.*.*.parameters[?(@.name=='services')].schema.anyOf - description: Removes comma-string-or-array union so the array-of-enum shape can be set directly - remove: true -- target: $.paths.*.*.parameters[?(@.name=='services')].schema - description: Replaces comma-string-or-array union with a plain array of enums for oapi-codegen - update: - type: array - items: - type: string - enum: - - auth - - db - - db_postgres_user - - pooler - - realtime - - rest - - storage - - pg_bouncer diff --git a/apps/cli-go/api/v1-openapi.yaml b/apps/cli-go/api/v1-openapi.yaml deleted file mode 100644 index 7145f616e0..0000000000 --- a/apps/cli-go/api/v1-openapi.yaml +++ /dev/null @@ -1,16788 +0,0 @@ -openapi: 3.0.0 -paths: - /v1/branches/{branch_id_or_ref}: - get: - description: Fetches configurations of the specified database branch - operationId: v1-get-a-branch-config - parameters: - - name: branch_id_or_ref - required: true - in: path - description: Branch ref or deprecated branch ID - schema: - example: abcdefghijklmnopqrst - anyOf: - - type: string - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - description: Project ref - example: abcdefghijklmnopqrst - - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - deprecated: true - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BranchDetailResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve database branch - security: - - bearer: [] - summary: Get database branch config - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:read' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - branching_development_read - - - branching_production_read - x-oauth-scope: environment:read - patch: - description: Updates the configuration of the specified database branch - operationId: v1-update-a-branch-config - parameters: - - name: branch_id_or_ref - required: true - in: path - description: Branch ref or deprecated branch ID - schema: - example: abcdefghijklmnopqrst - anyOf: - - type: string - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - description: Project ref - example: abcdefghijklmnopqrst - - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - deprecated: true - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateBranchBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BranchResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update database branch - security: - - bearer: [] - summary: Update database branch config - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:write' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - branching_development_write - - - branching_production_write - x-oauth-scope: environment:write - delete: - description: >- - Deletes the specified database branch. By default, deletes immediately. - Use force=false to schedule deletion with 1-hour grace period (only when - soft deletion is enabled). - operationId: v1-delete-a-branch - parameters: - - name: branch_id_or_ref - required: true - in: path - description: Branch ref or deprecated branch ID - schema: - example: abcdefghijklmnopqrst - anyOf: - - type: string - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - description: Project ref - example: abcdefghijklmnopqrst - - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - deprecated: true - - name: force - required: false - in: query - description: >- - If set to false, schedule deletion with 1-hour grace period (only - when soft deletion is enabled). - schema: - example: false - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BranchDeleteResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to delete database branch - security: - - bearer: [] - summary: Delete a database branch - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:write' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - branching_development_delete - - - branching_production_delete - x-oauth-scope: environment:write - /v1/branches/{branch_id_or_ref}/push: - post: - description: Pushes the specified database branch - operationId: v1-push-a-branch - parameters: - - name: branch_id_or_ref - required: true - in: path - description: Branch ref or deprecated branch ID - schema: - example: abcdefghijklmnopqrst - anyOf: - - type: string - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - description: Project ref - example: abcdefghijklmnopqrst - - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - deprecated: true - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/BranchActionBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BranchUpdateResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to push database branch - security: - - bearer: [] - summary: Pushes a database branch - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:write' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - branching_development_write - - - branching_production_write - x-oauth-scope: environment:write - /v1/branches/{branch_id_or_ref}/merge: - post: - description: Merges the specified database branch - operationId: v1-merge-a-branch - parameters: - - name: branch_id_or_ref - required: true - in: path - description: Branch ref or deprecated branch ID - schema: - example: abcdefghijklmnopqrst - anyOf: - - type: string - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - description: Project ref - example: abcdefghijklmnopqrst - - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - deprecated: true - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/BranchActionBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BranchUpdateResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to merge database branch - security: - - bearer: [] - summary: Merges a database branch - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:write' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - branching_development_write - - - branching_production_write - x-oauth-scope: environment:write - /v1/branches/{branch_id_or_ref}/reset: - post: - description: Resets the specified database branch - operationId: v1-reset-a-branch - parameters: - - name: branch_id_or_ref - required: true - in: path - description: Branch ref or deprecated branch ID - schema: - example: abcdefghijklmnopqrst - anyOf: - - type: string - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - description: Project ref - example: abcdefghijklmnopqrst - - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - deprecated: true - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/BranchActionBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BranchUpdateResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to reset database branch - security: - - bearer: [] - summary: Resets a database branch - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:write' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - branching_development_write - - - branching_production_write - x-oauth-scope: environment:write - /v1/branches/{branch_id_or_ref}/restore: - post: - description: Cancels scheduled deletion and restores the branch to active state - operationId: v1-restore-a-branch - parameters: - - name: branch_id_or_ref - required: true - in: path - description: Branch ref or deprecated branch ID - schema: - example: abcdefghijklmnopqrst - anyOf: - - type: string - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - description: Project ref - example: abcdefghijklmnopqrst - - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - deprecated: true - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BranchRestoreResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to restore database branch - security: - - bearer: [] - summary: Restore a scheduled branch deletion - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:write' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - branching_development_write - - - branching_production_write - x-oauth-scope: environment:write - /v1/branches/{branch_id_or_ref}/diff: - get: - description: |- - Diffs the specified database branch - - This endpoint is currently in its **Beta** stage. - operationId: v1-diff-a-branch - parameters: - - name: branch_id_or_ref - required: true - in: path - description: Branch ref or deprecated branch ID - schema: - example: abcdefghijklmnopqrst - anyOf: - - type: string - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - description: Project ref - example: abcdefghijklmnopqrst - - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - deprecated: true - - name: included_schemas - required: false - in: query - schema: - example: public,auth - type: string - - name: pgdelta - required: false - in: query - description: |- - Use pg-delta instead of Migra for diffing when true. - Boolean string. - - Truthy values: `true`, `1`, `yes`, `on`, `y`, `enabled` - - Falsy values: `false`, `0`, `no`, `off`, `n`, `disabled` - schema: - example: 'true' - type: string - responses: - '200': - content: - text/plain: - schema: - type: string - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to diff database branch - security: - - bearer: [] - summary: Diffs a database branch - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - branching_development_write - - - branching_production_write - x-oauth-scope: environment:write - x-scalar-stability: experimental - /v1/projects: - get: - description: Returns a list of all projects you've previously created. - operationId: v1-list-all-projects - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/V1ProjectWithDatabaseResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: List all projects - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:read' - position: after - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - projects_read - x-oauth-scope: projects:read - post: - operationId: v1-create-a-project - parameters: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1CreateProjectBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1ProjectResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Create a project - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:write' - position: after - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - organization_projects_create - x-oauth-scope: projects:write - /v1/projects/available-regions: - get: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-get-available-regions - parameters: - - name: organization_slug - required: true - in: query - description: Slug of your organization - schema: - example: tsrqponmlkjihgfedcba - type: string - - name: continent - required: false - in: query - description: >- - Continent code to determine regional recommendations: NA (North - America), SA (South America), EU (Europe), AF (Africa), AS (Asia), - OC (Oceania), AN (Antarctica) - schema: - example: NA - type: string - enum: - - NA - - SA - - EU - - AF - - AS - - OC - - AN - - name: desired_instance_size - required: false - in: query - description: >- - Desired instance size. Omit this field to always default to the - smallest possible size. - schema: - type: string - enum: - - nano - - micro - - small - - medium - - large - - xlarge - - 2xlarge - - 4xlarge - - 8xlarge - - 12xlarge - - 16xlarge - - 24xlarge - - 24xlarge_optimized_memory - - 24xlarge_optimized_cpu - - 24xlarge_high_memory - - 48xlarge - - 48xlarge_optimized_memory - - 48xlarge_optimized_cpu - - 48xlarge_high_memory - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/RegionsInfo_Output' - security: - - bearer: [] - summary: Gets the list of available regions that can be used for a new project - tags: - - Projects - x-badges: - - name: 'OAuth scope: organizations:read' - position: after - - name: Beta - position: before - x-endpoint-owners: - - infra - x-oauth-scope: organizations:read - x-scalar-stability: experimental - /v1/organizations: - get: - description: Returns a list of organizations that you currently belong to. - operationId: v1-list-all-organizations - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/OrganizationResponseV1_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Unexpected error listing organizations - security: - - bearer: [] - summary: List all organizations - tags: - - Organizations - x-badges: - - name: 'OAuth scope: organizations:read' - position: after - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - organizations_read - x-oauth-scope: organizations:read - post: - operationId: v1-create-an-organization - parameters: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateOrganizationV1' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/OrganizationResponseV1_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Unexpected error creating an organization - security: - - bearer: [] - summary: Create an organization - tags: - - Organizations - x-endpoint-owners: - - control-plane - - billing - x-fga-permissions: - - - organizations_create - /v1/oauth/authorize: - get: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-authorize-user - parameters: - - name: client_id - required: true - in: query - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 66666666-6666-4666-8666-666666666666 - type: string - - name: response_type - required: true - in: query - schema: - example: code - type: string - enum: - - code - - token - - id_token token - - name: redirect_uri - required: true - in: query - schema: - example: https://app.acme.com/auth/callback - type: string - - name: scope - required: false - in: query - schema: - example: projects:read projects:write - type: string - - name: state - required: false - in: query - schema: - example: st_9f4d3a206b2e4a7e8c91 - type: string - - name: response_mode - required: false - in: query - schema: - example: query - type: string - - name: code_challenge - required: false - in: query - schema: - example: Z_P4EKbGwIkA01e3Y5fp4tMCvn_Ae5nUw7qY7XwkTrQ - type: string - - name: code_challenge_method - required: false - in: query - schema: - example: S256 - type: string - enum: - - plain - - sha256 - - S256 - - name: organization_slug - required: false - in: query - description: Organization slug - schema: - pattern: ^[\w-]+$ - example: tsrqponmlkjihgfedcba - type: string - - name: target_flow - required: false - in: query - schema: - type: string - - name: resource - required: false - in: query - description: Resource indicator for MCP (Model Context Protocol) clients - schema: - format: uri - example: https://mcp.supabase.com/projects - type: string - responses: - '204': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - summary: Authorize user through oauth - tags: - - OAuth - x-badges: - - name: Beta - position: before - x-endpoint-owners: - - auth - - control-plane - x-scalar-stability: experimental - /v1/oauth/token: - post: - description: >- - Supports `authorization_code`, `refresh_token`, and - `urn:ietf:params:oauth:grant-type:jwt-bearer` grant types. The - `jwt-bearer` grant type (IDJAG — identity-directed JWT assertion) is in - beta and available on Team and Enterprise plans only. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-exchange-oauth-token - parameters: [] - requestBody: - required: true - content: - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/OAuthTokenBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/OAuthTokenResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - summary: Exchange auth code for user's access and refresh token - tags: - - OAuth - x-badges: - - name: Beta - position: before - x-endpoint-owners: - - auth - - control-plane - x-scalar-stability: experimental - /v1/oauth/revoke: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-revoke-token - parameters: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/OAuthRevokeTokenBody' - responses: - '204': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - summary: Revoke oauth app authorization and it's corresponding tokens - tags: - - OAuth - x-badges: - - name: Beta - position: before - x-endpoint-owners: - - auth - - control-plane - x-scalar-stability: experimental - /v1/oauth/authorize/project-claim: - get: - description: >- - Initiates the OAuth authorization flow for the specified provider. After - successful authentication, the user can claim ownership of the specified - project. - operationId: v1-oauth-authorize-project-claim - parameters: - - name: project_ref - required: true - in: query - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: client_id - required: true - in: query - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 66666666-6666-4666-8666-666666666666 - type: string - - name: response_type - required: true - in: query - schema: - example: code - type: string - enum: - - code - - token - - id_token token - - name: redirect_uri - required: true - in: query - schema: - example: https://app.acme.com/auth/callback - type: string - - name: state - required: false - in: query - schema: - example: st_9f4d3a206b2e4a7e8c91 - type: string - - name: response_mode - required: false - in: query - schema: - example: query - type: string - - name: code_challenge - required: false - in: query - schema: - example: Z_P4EKbGwIkA01e3Y5fp4tMCvn_Ae5nUw7qY7XwkTrQ - type: string - - name: code_challenge_method - required: false - in: query - schema: - example: S256 - type: string - enum: - - plain - - sha256 - - S256 - responses: - '204': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Authorize user through oauth and claim a project - tags: - - OAuth - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - organization_admin_write - - project_admin_write - /v1/snippets: - get: - operationId: v1-list-all-snippets - parameters: - - name: project_ref - required: false - in: query - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: cursor - required: false - in: query - schema: - type: string - - name: limit - required: false - in: query - schema: - type: string - minimum: 1 - maximum: 100 - - name: sort_by - required: false - in: query - schema: - enum: - - name - - inserted_at - type: string - - name: sort_order - required: false - in: query - schema: - enum: - - asc - - desc - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SnippetList_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to list user's SQL snippets - security: - - bearer: [] - summary: Lists SQL snippets for the logged in user - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - snippets_read - x-oauth-scope: database:read - /v1/snippets/{id}: - get: - operationId: v1-get-a-snippet - parameters: - - name: id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 44444444-4444-4444-8444-444444444444 - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SnippetResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve SQL snippet - security: - - bearer: [] - summary: Gets a specific SQL snippet - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - snippets_read - x-oauth-scope: database:read - /v1/profile: - get: - operationId: v1-get-profile - parameters: [] - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1ProfileResponse_Output' - security: - - bearer: [] - summary: Gets the user's profile - tags: - - Profile - x-endpoint-owners: - - control-plane - /v1/projects/{ref}/actions: - head: - description: Returns the total number of action runs of the specified project. - operationId: v1-count-action-runs - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - headers: - X-Total-Count: - schema: - type: integer - format: int64 - minimum: 0 - description: total count value - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to count action runs - security: - - bearer: [] - summary: Count the number of action runs - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:read' - position: after - x-fga-permissions: - - - action_runs_read - x-oauth-scope: environment:read - get: - description: Returns a paginated list of action runs of the specified project. - operationId: v1-list-action-runs - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: offset - required: false - in: query - schema: - minimum: 0 - example: 0 - type: number - - name: limit - required: false - in: query - schema: - minimum: 10 - example: 20 - type: number - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ListActionRunResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to list action runs - security: - - bearer: [] - summary: List all action runs - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:read' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - action_runs_read - x-oauth-scope: environment:read - /v1/projects/{ref}/actions/{run_id}: - get: - description: Returns the current status of the specified action run. - operationId: v1-get-action-run - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: run_id - required: true - in: path - description: Action Run ID - schema: - example: run_01hq3q9m7y5q7e4a7x2c8m1p4n - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ActionRunResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get action run status - security: - - bearer: [] - summary: Get the status of an action run - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:read' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - action_runs_read - x-oauth-scope: environment:read - /v1/projects/{ref}/actions/{run_id}/status: - patch: - description: Updates the status of an ongoing action run. - operationId: v1-update-action-run-status - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: run_id - required: true - in: path - description: Action Run ID - schema: - example: run_01hq3q9m7y5q7e4a7x2c8m1p4n - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateRunStatusBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateRunStatusResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update action run status - security: - - bearer: [] - summary: Update the status of an action run - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:write' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - action_runs_write - x-oauth-scope: environment:write - /v1/projects/{ref}/actions/{run_id}/logs: - get: - description: Returns the logs from the specified action run. - operationId: v1-get-action-run-logs - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: run_id - required: true - in: path - description: Action Run ID - schema: - example: run_01hq3q9m7y5q7e4a7x2c8m1p4n - type: string - responses: - '200': - content: - text/plain: - schema: - type: string - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get action run logs - security: - - bearer: [] - summary: Get the logs of an action run - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:read' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - action_runs_read - x-oauth-scope: environment:read - /v1/projects/{ref}/api-keys: - get: - operationId: v1-get-project-api-keys - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: reveal - required: false - in: query - description: |- - Boolean string. - - Truthy values: `true`, `1`, `yes`, `on`, `y`, `enabled` - - Falsy values: `false`, `0`, `no`, `off`, `n`, `disabled` - schema: - example: 'true' - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ApiKeyResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Get project api keys - tags: - - Secrets - x-badges: - - name: 'OAuth scope: secrets:read' - position: after - x-endpoint-owners: - - auth - - control-plane - x-fga-permissions: - - - api_gateway_keys_read - - - api_gateway_keys_read - - api_gateway_keys_secret_read - x-oauth-scope: secrets:read - post: - operationId: v1-create-project-api-key - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: reveal - required: false - in: query - description: |- - Boolean string. - - Truthy values: `true`, `1`, `yes`, `on`, `y`, `enabled` - - Falsy values: `false`, `0`, `no`, `off`, `n`, `disabled` - schema: - example: 'true' - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateApiKeyBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ApiKeyResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Creates a new API key for the project - tags: - - Secrets - x-badges: - - name: 'OAuth scope: secrets:write' - position: after - x-endpoint-owners: - - auth - - control-plane - x-fga-permissions: - - - api_gateway_keys_write - x-oauth-scope: secrets:write - /v1/projects/{ref}/api-keys/legacy: - get: - operationId: v1-get-project-legacy-api-keys - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/LegacyApiKeysResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: >- - Check whether JWT based legacy (anon, service_role) API keys are - enabled. This API endpoint will be removed in the future, check for HTTP - 404 Not Found. - tags: - - Secrets - x-badges: - - name: 'OAuth scope: secrets:read' - position: after - x-endpoint-owners: - - auth - - control-plane - x-fga-permissions: - - - api_gateway_keys_read - x-oauth-scope: secrets:read - put: - operationId: v1-update-project-legacy-api-keys - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: enabled - required: true - in: query - description: |- - Boolean string. - - Truthy values: `true`, `1`, `yes`, `on`, `y`, `enabled` - - Falsy values: `false`, `0`, `no`, `off`, `n`, `disabled` - schema: - example: 'true' - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/LegacyApiKeysResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: >- - Disable or re-enable JWT based legacy (anon, service_role) API keys. - This API endpoint will be removed in the future, check for HTTP 404 Not - Found. - tags: - - Secrets - x-badges: - - name: 'OAuth scope: secrets:write' - position: after - x-endpoint-owners: - - auth - - control-plane - x-fga-permissions: - - - api_gateway_keys_write - x-oauth-scope: secrets:write - /v1/projects/{ref}/api-keys/{id}: - patch: - operationId: v1-update-project-api-key - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 22222222-2222-4222-8222-222222222222 - type: string - - name: reveal - required: false - in: query - description: |- - Boolean string. - - Truthy values: `true`, `1`, `yes`, `on`, `y`, `enabled` - - Falsy values: `false`, `0`, `no`, `off`, `n`, `disabled` - schema: - example: 'true' - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateApiKeyBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ApiKeyResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Updates an API key for the project - tags: - - Secrets - x-badges: - - name: 'OAuth scope: secrets:write' - position: after - x-endpoint-owners: - - auth - - control-plane - x-fga-permissions: - - - api_gateway_keys_write - x-oauth-scope: secrets:write - get: - operationId: v1-get-project-api-key - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 22222222-2222-4222-8222-222222222222 - type: string - - name: reveal - required: false - in: query - description: |- - Boolean string. - - Truthy values: `true`, `1`, `yes`, `on`, `y`, `enabled` - - Falsy values: `false`, `0`, `no`, `off`, `n`, `disabled` - schema: - example: 'true' - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ApiKeyResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Get API key - tags: - - Secrets - x-badges: - - name: 'OAuth scope: secrets:read' - position: after - x-endpoint-owners: - - auth - - control-plane - x-fga-permissions: - - - api_gateway_keys_read - - - api_gateway_keys_read - - api_gateway_keys_secret_read - x-oauth-scope: secrets:read - delete: - operationId: v1-delete-project-api-key - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 22222222-2222-4222-8222-222222222222 - type: string - - name: reveal - required: false - in: query - description: Boolean string, true or false - schema: - example: true - type: string - - name: was_compromised - required: false - in: query - description: Boolean string, true or false - schema: - example: false - type: string - - name: reason - required: false - in: query - schema: - example: rotating_key - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ApiKeyResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Deletes an API key for the project - tags: - - Secrets - x-badges: - - name: 'OAuth scope: secrets:write' - position: after - x-endpoint-owners: - - auth - - control-plane - x-fga-permissions: - - - api_gateway_keys_write - x-oauth-scope: secrets:write - /v1/projects/{ref}/branches: - get: - description: Returns all database branches of the specified project. - operationId: v1-list-all-branches - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/BranchResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve database branches - security: - - bearer: [] - summary: List all database branches - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:read' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - branching_development_read - - - branching_production_read - x-oauth-scope: environment:read - post: - description: Creates a database branch from the specified project. - operationId: v1-create-a-branch - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateBranchBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BranchResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to create database branch - security: - - bearer: [] - summary: Create a database branch - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:write' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - branching_development_create - - - branching_production_create - x-oauth-scope: environment:write - delete: - description: Disables preview branching for the specified project - operationId: v1-disable-preview-branching - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to disable preview branching - security: - - bearer: [] - summary: Disables preview branching - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:write' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - branching_production_delete - x-oauth-scope: environment:write - /v1/projects/{ref}/branches/{name}: - get: - description: Fetches the specified database branch by its name. - operationId: v1-get-a-branch - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: name - required: true - in: path - schema: - example: preview-login-page - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BranchResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to fetch database branch - security: - - bearer: [] - summary: Get a database branch - tags: - - Environments - x-badges: - - name: 'OAuth scope: environment:read' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - branching_development_read - - - branching_production_read - x-oauth-scope: environment:read - /v1/projects/{ref}/custom-hostname: - get: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-get-hostname-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateCustomHostnameResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's custom hostname config - security: - - bearer: [] - summary: Gets project's custom hostname config - tags: - - Domains - x-badges: - - name: 'OAuth scope: domains:read' - position: after - - name: Beta - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - custom_domain_read - x-oauth-scope: domains:read - x-scalar-stability: experimental - delete: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-Delete hostname config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: remove_addon - required: false - in: query - description: >- - If true, also removes the custom domain add-on from the project - subscription. - schema: - type: string - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to delete project custom hostname configuration - security: - - bearer: [] - summary: Deletes a project's custom hostname configuration - tags: - - Domains - x-badges: - - name: 'OAuth scope: domains:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - custom_domain_write - x-oauth-scope: domains:write - x-scalar-stability: experimental - /v1/projects/{ref}/custom-hostname/initialize: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-update-hostname-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateCustomHostnameBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateCustomHostnameResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update project custom hostname configuration - security: - - bearer: [] - summary: Updates project's custom hostname configuration - tags: - - Domains - x-badges: - - name: 'OAuth scope: domains:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - custom_domain_write - x-oauth-scope: domains:write - x-scalar-stability: experimental - /v1/projects/{ref}/custom-hostname/reverify: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-verify-dns-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateCustomHostnameResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to verify project custom hostname configuration - security: - - bearer: [] - summary: >- - Attempts to verify the DNS configuration for project's custom hostname - configuration - tags: - - Domains - x-badges: - - name: 'OAuth scope: domains:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - custom_domain_write - x-oauth-scope: domains:write - x-scalar-stability: experimental - /v1/projects/{ref}/custom-hostname/activate: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-activate-custom-hostname - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateCustomHostnameResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to activate project custom hostname configuration - security: - - bearer: [] - summary: Activates a custom hostname for a project. - tags: - - Domains - x-badges: - - name: 'OAuth scope: domains:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - custom_domain_write - x-oauth-scope: domains:write - x-scalar-stability: experimental - /v1/projects/{ref}/jit-access: - get: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-get-jit-access-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $schema: https://json-schema.org/draft/2020-12/schema - oneOf: - - type: object - properties: - state: - type: string - enum: - - enabled - - disabled - appliedSuccessfully: - type: boolean - required: - - state - - type: object - properties: - state: - type: string - const: unavailable - unavailableReason: - type: string - enum: - - platform_unsupported - - postgres_upgrade_required - - ssl_enforcement_required - - temporarily_unavailable - required: - - state - - unavailableReason - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's temporary access configuration. - security: - - bearer: [] - summary: Get project's temporary access configuration. - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - - name: Beta - position: before - x-endpoint-owners: - - security - - control-plane - x-fga-permissions: - - - project_admin_read - x-oauth-scope: database:read - x-scalar-stability: experimental - put: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-update-jit-access-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/JitAccessRequestRequest' - responses: - '200': - description: '' - content: - application/json: - schema: - $schema: https://json-schema.org/draft/2020-12/schema - oneOf: - - type: object - properties: - state: - type: string - enum: - - enabled - - disabled - appliedSuccessfully: - type: boolean - required: - - state - - type: object - properties: - state: - type: string - const: unavailable - unavailableReason: - type: string - enum: - - platform_unsupported - - postgres_upgrade_required - - ssl_enforcement_required - - temporarily_unavailable - required: - - state - - unavailableReason - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update project's temporary access configuration. - security: - - bearer: [] - summary: Update project's temporary access configuration. - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - security - - control-plane - x-fga-permissions: - - - project_admin_write - x-oauth-scope: database:write - x-scalar-stability: experimental - /v1/projects/{ref}/network-bans/retrieve: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-list-all-network-bans - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/NetworkBanResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's network bans - security: - - bearer: [] - summary: Gets project's network bans - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:read' - position: after - - name: Beta - position: before - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - database_network_bans_read - x-oauth-scope: projects:read - x-scalar-stability: experimental - /v1/projects/{ref}/network-bans/retrieve/enriched: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-list-all-network-bans-enriched - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/NetworkBanResponseEnriched_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's enriched network bans - security: - - bearer: [] - summary: >- - Gets project's network bans with additional information about which - databases they affect - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:read' - position: after - - name: Beta - position: before - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - database_network_bans_read - x-oauth-scope: projects:read - x-scalar-stability: experimental - /v1/projects/{ref}/network-bans: - delete: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-delete-network-bans - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/RemoveNetworkBanRequest' - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to remove network bans. - security: - - bearer: [] - summary: Remove network bans. - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - database_network_bans_write - x-oauth-scope: projects:write - x-scalar-stability: experimental - /v1/projects/{ref}/network-restrictions: - get: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-get-network-restrictions - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/NetworkRestrictionsResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's network restrictions - security: - - bearer: [] - summary: Gets project's network restrictions - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:read' - position: after - - name: Beta - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - database_network_restrictions_read - x-oauth-scope: projects:read - x-scalar-stability: experimental - patch: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Alpha** stage. - operationId: v1-patch-network-restrictions - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/NetworkRestrictionsPatchRequest' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/NetworkRestrictionsV2Response_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update project network restrictions - security: - - bearer: [] - summary: Updates project's network restrictions by adding or removing CIDRs - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:write' - position: after - - name: Alpha - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - database_network_restrictions_write - x-oauth-scope: projects:write - x-scalar-stability: experimental - /v1/projects/{ref}/network-restrictions/apply: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-update-network-restrictions - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/NetworkRestrictionsRequest' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/NetworkRestrictionsResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update project network restrictions - security: - - bearer: [] - summary: Updates project's network restrictions - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - database_network_restrictions_write - x-oauth-scope: projects:write - x-scalar-stability: experimental - /v1/projects/{ref}/pgsodium: - get: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-get-pgsodium-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PgsodiumConfigResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's pgsodium config - security: - - bearer: [] - summary: Gets project's pgsodium config - tags: - - Secrets - x-badges: - - name: 'OAuth scope: secrets:read' - position: after - - name: Beta - position: before - x-endpoint-owners: - - infra - x-fga-permissions: - - - project_admin_write - x-oauth-scope: secrets:read - x-scalar-stability: experimental - put: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-update-pgsodium-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdatePgsodiumConfigBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PgsodiumConfigResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update project's pgsodium config - security: - - bearer: [] - summary: >- - Updates project's pgsodium config. Updating the root_key can cause all - data encrypted with the older key to become inaccessible. - tags: - - Secrets - x-badges: - - name: 'OAuth scope: secrets:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - infra - x-fga-permissions: - - - project_admin_write - x-oauth-scope: secrets:write - x-scalar-stability: experimental - /v1/projects/{ref}/postgrest: - get: - operationId: v1-get-postgrest-service-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PostgrestConfigWithJWTSecretResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's postgrest config - security: - - bearer: [] - summary: Gets project's postgrest config - tags: - - Rest - x-badges: - - name: 'OAuth scope: rest:read' - position: after - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - data_api_config_read - - - data_api_config_read - - data_api_config_secret_read - x-oauth-scope: rest:read - patch: - operationId: v1-update-postgrest-service-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1UpdatePostgrestConfigBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1PostgrestConfigResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update project's postgrest config - security: - - bearer: [] - summary: Updates project's postgrest config - tags: - - Rest - x-badges: - - name: 'OAuth scope: rest:write' - position: after - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - data_api_config_write - x-oauth-scope: rest:write - /v1/projects/{ref}: - get: - operationId: v1-get-project - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1ProjectWithDatabaseResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project - security: - - bearer: [] - summary: Gets a specific project that belongs to the authenticated user - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:read' - position: after - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - project_admin_read - x-oauth-scope: projects:read - delete: - operationId: v1-delete-a-project - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1ProjectRefResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Deletes the given project - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:write' - position: after - x-endpoint-owners: - - control-plane - - infra - - dev-workflows - x-fga-permissions: - - - project_admin_write - x-oauth-scope: projects:write - patch: - operationId: v1-update-a-project - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1UpdateProjectBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1ProjectRefResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update project - security: - - bearer: [] - summary: Updates the given project - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:write' - position: after - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - project_admin_write - x-oauth-scope: projects:write - /v1/projects/{ref}/secrets: - get: - description: Returns all secrets you've previously added to the specified project. - operationId: v1-list-all-secrets - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/SecretResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's secrets - security: - - bearer: [] - summary: List all secrets - tags: - - Secrets - x-badges: - - name: 'OAuth scope: secrets:read' - position: after - x-endpoint-owners: - - functions - x-fga-permissions: - - - edge_functions_secrets_read - x-oauth-scope: secrets:read - post: - description: Creates multiple secrets and adds them to the specified project. - operationId: v1-bulk-create-secrets - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateSecretBody' - responses: - '201': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to create project's secrets - security: - - bearer: [] - summary: Bulk create secrets - tags: - - Secrets - x-badges: - - name: 'OAuth scope: secrets:write' - position: after - x-endpoint-owners: - - functions - x-fga-permissions: - - - edge_functions_secrets_write - x-oauth-scope: secrets:write - delete: - description: Deletes all secrets with the given names from the specified project - operationId: v1-bulk-delete-secrets - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteSecretsBody' - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to delete secrets with given names - security: - - bearer: [] - summary: Bulk delete secrets - tags: - - Secrets - x-badges: - - name: 'OAuth scope: secrets:write' - position: after - x-endpoint-owners: - - functions - x-fga-permissions: - - - edge_functions_secrets_write - x-oauth-scope: secrets:write - /v1/projects/{ref}/ssl-enforcement: - get: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-get-ssl-enforcement-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SslEnforcementResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's SSL enforcement config - security: - - bearer: [] - summary: Get project's SSL enforcement configuration. - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - - name: Beta - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - database_ssl_config_read - x-oauth-scope: database:read - x-scalar-stability: experimental - put: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-update-ssl-enforcement-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SslEnforcementRequest' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SslEnforcementResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update project's SSL enforcement configuration. - security: - - bearer: [] - summary: Update project's SSL enforcement configuration. - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - database_ssl_config_write - x-oauth-scope: database:write - x-scalar-stability: experimental - /v1/projects/{ref}/types/typescript: - get: - description: Returns the TypeScript types of your schema for use with supabase-js. - operationId: v1-generate-typescript-types - parameters: - - name: included_schemas - required: false - in: query - schema: - default: public - example: public,auth - type: string - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/TypescriptResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to generate TypeScript types - security: - - bearer: [] - summary: Generate TypeScript types - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - database_read - x-oauth-scope: database:read - /v1/projects/{ref}/vanity-subdomain: - get: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-get-vanity-subdomain-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/VanitySubdomainConfigResponse_Output' - '400': - description: >- - This feature requires the Pro, Team, or Enterprise organization - plan. - content: - application/json: - schema: - $ref: '#/components/schemas/PlanGateErrorBody' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get project vanity subdomain configuration - security: - - bearer: [] - summary: Gets current vanity subdomain config - tags: - - Domains - x-allowed-plans: - - Pro - - Team - - Enterprise - x-badges: - - name: 'OAuth scope: domains:read' - position: after - - name: Beta - position: before - - name: Only available on Pro, Team, Enterprise - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - vanity_subdomain_read - x-oauth-scope: domains:read - x-scalar-stability: experimental - delete: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-deactivate-vanity-subdomain-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to delete project vanity subdomain configuration - security: - - bearer: [] - summary: Deletes a project's vanity subdomain configuration - tags: - - Domains - x-badges: - - name: 'OAuth scope: domains:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - vanity_subdomain_write - x-oauth-scope: domains:write - x-scalar-stability: experimental - /v1/projects/{ref}/vanity-subdomain/check-availability: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-check-vanity-subdomain-availability - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/VanitySubdomainBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SubdomainAvailabilityResponse_Output' - '400': - description: >- - This feature requires the Pro, Team, or Enterprise organization - plan. - content: - application/json: - schema: - $ref: '#/components/schemas/PlanGateErrorBody' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to check project vanity subdomain configuration - security: - - bearer: [] - summary: Checks vanity subdomain availability - tags: - - Domains - x-allowed-plans: - - Pro - - Team - - Enterprise - x-badges: - - name: 'OAuth scope: domains:write' - position: after - - name: Beta - position: before - - name: Only available on Pro, Team, Enterprise - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - vanity_subdomain_write - x-oauth-scope: domains:write - x-scalar-stability: experimental - /v1/projects/{ref}/vanity-subdomain/activate: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-activate-vanity-subdomain-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/VanitySubdomainBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ActivateVanitySubdomainResponse_Output' - '400': - description: >- - This feature requires the Pro, Team, or Enterprise organization - plan. - content: - application/json: - schema: - $ref: '#/components/schemas/PlanGateErrorBody' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to activate project vanity subdomain configuration - security: - - bearer: [] - summary: Activates a vanity subdomain for a project. - tags: - - Domains - x-allowed-plans: - - Pro - - Team - - Enterprise - x-badges: - - name: 'OAuth scope: domains:write' - position: after - - name: Beta - position: before - - name: Only available on Pro, Team, Enterprise - position: before - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - vanity_subdomain_write - x-oauth-scope: domains:write - x-scalar-stability: experimental - /v1/projects/{ref}/upgrade: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-upgrade-postgres-version - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpgradeDatabaseBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectUpgradeInitiateResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to initiate project upgrade - security: - - bearer: [] - summary: Upgrades the project's Postgres version - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - project_admin_write - - database_write - x-oauth-scope: projects:write - x-scalar-stability: experimental - /v1/projects/{ref}/upgrade/eligibility: - get: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-get-postgres-upgrade-eligibility - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectUpgradeEligibilityResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to determine project upgrade eligibility - security: - - bearer: [] - summary: Returns the project's eligibility for upgrades - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:read' - position: after - - name: Beta - position: before - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - project_admin_read - - database_read - x-oauth-scope: projects:read - x-scalar-stability: experimental - /v1/projects/{ref}/upgrade/status: - get: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-get-postgres-upgrade-status - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: tracking_id - required: false - in: query - schema: - example: 9f4d3a20-6b2e-4a7e-8c91-1d5f3e7a2b4c - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DatabaseUpgradeStatusResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project upgrade status - security: - - bearer: [] - summary: Gets the latest status of the project's upgrade - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:read' - position: after - - name: Beta - position: before - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - project_admin_read - - database_read - x-oauth-scope: projects:read - x-scalar-stability: experimental - /v1/projects/{ref}/readonly: - get: - operationId: v1-get-readonly-mode-status - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ReadOnlyStatusResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get project readonly mode status - security: - - bearer: [] - summary: Returns project's readonly mode status - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - control-plane - - infra - - support-tooling - x-fga-permissions: - - - database_readonly_config_read - x-oauth-scope: database:read - /v1/projects/{ref}/readonly/temporary-disable: - post: - operationId: v1-disable-readonly-mode-temporarily - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '201': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to disable project's readonly mode - security: - - bearer: [] - summary: Disables project's readonly mode for the next 15 minutes - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - x-endpoint-owners: - - control-plane - - infra - - support-tooling - x-fga-permissions: - - - database_readonly_config_write - x-oauth-scope: database:write - /v1/projects/{ref}/read-replicas/setup: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-setup-a-read-replica - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SetUpReadReplicaBody' - responses: - '204': - description: '' - '401': - description: Unauthorized - '402': - description: >- - This feature requires the Pro, Team, or Enterprise organization - plan. - content: - application/json: - schema: - $ref: '#/components/schemas/PlanGateErrorBody' - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to set up read replica - security: - - bearer: [] - summary: Set up a read replica - tags: - - Database - x-allowed-plans: - - Pro - - Team - - Enterprise - x-badges: - - name: Beta - position: before - - name: Only available on Pro, Team, Enterprise - position: before - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - infra_read_replicas_write - x-scalar-stability: experimental - /v1/projects/{ref}/read-replicas/remove: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-remove-a-read-replica - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/RemoveReadReplicaBody' - responses: - '204': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to remove read replica - security: - - bearer: [] - summary: Remove a read replica - tags: - - Database - x-badges: - - name: Beta - position: before - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - infra_read_replicas_write - x-scalar-stability: experimental - /v1/projects/{ref}/health: - get: - operationId: v1-get-services-health - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: services - required: true - in: query - description: Comma-separated list of enums or array of enums. - schema: - example: - - auth,db - - auth - anyOf: - - type: string - description: |- - Comma-separated list of enums: - - - `auth` - - `db` - - `db_postgres_user` - - `pooler` - - `realtime` - - `rest` - - `storage` - - `pg_bouncer` - example: - - auth,db - - auth - - type: array - items: - type: string - enum: - - auth - - db - - db_postgres_user - - pooler - - realtime - - rest - - storage - - pg_bouncer - description: Array of enums. - example: - - '{field}=auth&{field}=db' - - '{field}=auth' - - name: timeout_ms - required: false - in: query - schema: - minimum: 0 - maximum: 10000 - example: 2000 - type: integer - responses: - '200': - description: '' - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/V1ServiceHealthResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's service health status - security: - - bearer: [] - summary: Gets project's service health status - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:read' - position: after - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - project_admin_read - x-oauth-scope: projects:read - /v1/projects/{ref}/config/auth/signing-keys/legacy: - post: - operationId: v1-create-legacy-signing-key - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SigningKeyResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: >- - Set up the project's existing JWT secret as an in_use JWT signing key. - This endpoint will be removed in the future always check for HTTP 404 - Not Found. - tags: - - Auth - x-badges: - - name: 'OAuth scope: secrets:write' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_signing_keys_write - x-oauth-scope: secrets:write - get: - operationId: v1-get-legacy-signing-key - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SigningKeyResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: >- - Get the signing key information for the JWT secret imported as signing - key for this project. This endpoint will be removed in the future, check - for HTTP 404 Not Found. - tags: - - Auth - x-badges: - - name: 'OAuth scope: secrets:read' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_signing_keys_read - x-oauth-scope: secrets:read - /v1/projects/{ref}/config/auth/signing-keys: - post: - operationId: v1-create-project-signing-key - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateSigningKeyBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SigningKeyResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Create a new signing key for the project in standby status - tags: - - Auth - x-badges: - - name: 'OAuth scope: secrets:write' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_signing_keys_write - x-oauth-scope: secrets:write - get: - operationId: v1-get-project-signing-keys - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SigningKeysResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: List all signing keys for the project - tags: - - Auth - x-badges: - - name: 'OAuth scope: secrets:read' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_signing_keys_read - x-oauth-scope: secrets:read - /v1/projects/{ref}/config/auth/signing-keys/{id}: - get: - operationId: v1-get-project-signing-key - parameters: - - name: id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 33333333-3333-4333-8333-333333333333 - type: string - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SigningKeyResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Get information about a signing key - tags: - - Auth - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_signing_keys_read - delete: - operationId: v1-remove-project-signing-key - parameters: - - name: id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 33333333-3333-4333-8333-333333333333 - type: string - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SigningKeyResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: >- - Remove a signing key from a project. Only possible if the key has been - in revoked status for a while. - tags: - - Auth - x-badges: - - name: 'OAuth scope: secrets:write' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_signing_keys_write - x-oauth-scope: secrets:write - patch: - operationId: v1-update-project-signing-key - parameters: - - name: id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 33333333-3333-4333-8333-333333333333 - type: string - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateSigningKeyBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/SigningKeyResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Update a signing key, mainly its status - tags: - - Auth - x-badges: - - name: 'OAuth scope: secrets:write' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_signing_keys_write - x-oauth-scope: secrets:write - /v1/projects/{ref}/config/auth: - get: - operationId: v1-get-auth-service-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/AuthConfigResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's auth config - security: - - bearer: [] - summary: Gets project's auth config - tags: - - Auth - x-badges: - - name: 'OAuth scope: auth:read' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_config_read - x-oauth-scope: auth:read - patch: - operationId: v1-update-auth-service-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateAuthConfigBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/AuthConfigResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update project's auth config - security: - - bearer: [] - summary: Updates a project's auth config - tags: - - Auth - x-badges: - - name: 'OAuth scope: auth:write' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_config_write - - project_admin_write - x-oauth-scope: auth:write - /v1/projects/{ref}/config/auth/third-party-auth: - post: - operationId: v1-create-project-tpa-integration - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateThirdPartyAuthBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ThirdPartyAuth_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Creates a new third-party auth integration - tags: - - Auth - x-badges: - - name: 'OAuth scope: auth:write' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_config_write - x-oauth-scope: auth:write - get: - operationId: v1-list-project-tpa-integrations - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ThirdPartyAuth_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Lists all third-party auth integrations - tags: - - Auth - x-badges: - - name: 'OAuth scope: auth:read' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_config_read - x-oauth-scope: auth:read - /v1/projects/{ref}/config/auth/third-party-auth/{tpa_id}: - delete: - operationId: v1-delete-project-tpa-integration - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: tpa_id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 88888888-8888-4888-8888-888888888888 - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ThirdPartyAuth_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Removes a third-party auth integration - tags: - - Auth - x-badges: - - name: 'OAuth scope: auth:write' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_config_write - x-oauth-scope: auth:write - get: - operationId: v1-get-project-tpa-integration - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: tpa_id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 88888888-8888-4888-8888-888888888888 - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ThirdPartyAuth_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Get a third-party integration - tags: - - Auth - x-badges: - - name: 'OAuth scope: auth:read' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_config_read - x-oauth-scope: auth:read - /v1/projects/{ref}/pause: - post: - operationId: v1-pause-a-project - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Pauses the given project - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:write' - position: after - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - project_admin_write - x-oauth-scope: projects:write - /v1/projects/{ref}/restart: - post: - operationId: v1-restart-a-project - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Restarts the given project - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:write' - position: after - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - project_admin_write - x-oauth-scope: projects:write - /v1/projects/{ref}/restore: - get: - operationId: v1-list-available-restore-versions - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/GetProjectAvailableRestoreVersionsResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Lists available restore versions for the given project - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:read' - position: after - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - project_admin_read - x-oauth-scope: projects:read - post: - operationId: v1-restore-a-project - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Restores the given project - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:write' - position: after - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - project_admin_write - x-oauth-scope: projects:write - /v1/projects/{ref}/restore/cancel: - post: - operationId: v1-cancel-a-project-restoration - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Cancels the given project restoration - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:write' - position: after - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - project_admin_write - x-oauth-scope: projects:write - /v1/projects/{ref}/billing/addons: - get: - description: >- - Returns the billing addons that are currently applied, including the - active compute instance size, and lists every addon option that can be - provisioned with pricing metadata. - operationId: v1-list-project-addons - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ListProjectAddonsResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to list project addons - security: - - bearer: [] - summary: List billing addons and compute instance selections - tags: - - Billing - x-endpoint-owners: - - billing - x-fga-permissions: - - - infra_add_ons_read - patch: - description: >- - Selects an addon variant, for example scaling the project’s compute - instance up or down, and applies it to the project. - operationId: v1-apply-project-addon - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ApplyProjectAddonBody' - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to apply project addon - security: - - bearer: [] - summary: Apply or update billing addons, including compute instance size - tags: - - Billing - x-endpoint-owners: - - billing - x-fga-permissions: - - - infra_add_ons_write - /v1/projects/{ref}/billing/addons/{addon_variant}: - delete: - description: >- - Disables the selected addon variant, including rolling the compute - instance back to its previous size. - operationId: v1-remove-project-addon - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: addon_variant - required: true - in: path - schema: - example: pitr_7 - anyOf: - - type: string - enum: - - ci_micro - - ci_small - - ci_medium - - ci_large - - ci_xlarge - - ci_2xlarge - - ci_4xlarge - - ci_8xlarge - - ci_12xlarge - - ci_16xlarge - - ci_24xlarge - - ci_24xlarge_optimized_cpu - - ci_24xlarge_optimized_memory - - ci_24xlarge_high_memory - - ci_48xlarge - - ci_48xlarge_optimized_cpu - - ci_48xlarge_optimized_memory - - ci_48xlarge_high_memory - - type: string - enum: - - cd_default - - type: string - enum: - - pitr_7 - - pitr_14 - - pitr_28 - - type: string - enum: - - ipv4_default - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to remove project addon - security: - - bearer: [] - summary: Remove billing addons or revert compute instance sizing - tags: - - Billing - x-endpoint-owners: - - billing - x-fga-permissions: - - - infra_add_ons_write - /v1/projects/{ref}/claim-token: - get: - operationId: v1-get-project-claim-token - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectClaimTokenResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Gets project claim token - tags: - - Projects - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - project_admin_read - x-internal: true - post: - operationId: v1-create-project-claim-token - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/CreateProjectClaimTokenResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Creates project claim token - tags: - - Projects - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - organization_admin_write - - project_admin_write - x-internal: true - delete: - operationId: v1-delete-project-claim-token - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '204': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Revokes project claim token - tags: - - Projects - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - organization_admin_write - - project_admin_write - x-internal: true - /v1/projects/{ref}/advisors/performance: - get: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - operationId: v1-get-performance-advisors - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1ProjectAdvisorsResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Gets project performance advisors. - tags: - - Advisors - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - advisors_read - x-oauth-scope: database:read - x-scalar-stability: experimental - /v1/projects/{ref}/advisors/security: - get: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - operationId: v1-get-security-advisors - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: lint_type - required: false - in: query - schema: - example: sql - type: string - enum: - - sql - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1ProjectAdvisorsResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Gets project security advisors. - tags: - - Advisors - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - advisors_read - x-oauth-scope: database:read - x-scalar-stability: experimental - /v1/projects/{ref}/analytics/endpoints/logs.all: - get: - deprecated: true - description: >- - This endpoint has been removed and always responds with `410 Gone`. Use - `GET /v1/projects/{ref}/analytics/endpoints/logs` instead. See the - [migration - guide](https://supabase.com/changelog/48235-migration-of-supabase-management-api-logs-all-analytics-endpoint-to-logs-endpoint). - operationId: v1-get-project-logs-all - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '401': - description: Unauthorized - '403': - description: Forbidden action - '410': - description: This endpoint has been removed - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Gets project's logs - tags: - - Analytics - x-badges: - - name: 'OAuth scope: analytics:read' - position: after - x-endpoint-owners: - - observability - x-fga-permissions: - - - analytics_logs_read - x-oauth-scope: analytics:read - /v1/projects/{ref}/analytics/endpoints/logs: - get: - deprecated: false - description: > - Executes an SQL or LQL query on the project's unified logs stream. - - - Either the `iso_timestamp_start` and `iso_timestamp_end` parameters must - be provided. - - If both are not provided, only the last 1 minute of logs will be - queried. - - The timestamp range must be no more than 24 hours and is rounded to the - nearest minute. If the range is more than 24 hours, a validation error - will be thrown. - - - Filter by the `source` column to specify specific log sources, such as - edge_logs, postgres_logs, etc. - - - Note: SQL must be written in **ClickHouse SQL dialect**. - operationId: v1-get-project-logs - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: sql - required: false - in: query - description: >- - Custom SQL query to execute on the logs. See [querying - logs](https://supabase.com/docs/guides/monitoring-and-debugging/logs#querying-with-the-logs-explorer) - for more details. - schema: - example: select event_message from edge_logs limit 10 - type: string - - name: iso_timestamp_start - required: false - in: query - schema: - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$ - example: '2025-03-01T00:00:00Z' - type: string - - name: iso_timestamp_end - required: false - in: query - schema: - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$ - example: '2025-03-01T23:59:59Z' - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/AnalyticsResponse_Output' - '401': - description: Unauthorized - '402': - description: Usage exceeded. Enable additional usage to continue querying - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Gets all project's logs in a single log stream - tags: - - Analytics - x-badges: - - name: 'OAuth scope: analytics:read' - position: after - x-endpoint-owners: - - observability - x-fga-permissions: - - - analytics_logs_read - x-oauth-scope: analytics:read - /v1/projects/{ref}/analytics/endpoints/usage.api-counts: - get: - operationId: v1-get-project-usage-api-count - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: interval - required: false - in: query - schema: - example: 1day - type: string - enum: - - 15min - - 30min - - 1hr - - 3hr - - 1day - - 3day - - 7day - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1GetUsageApiCountResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get project's usage api counts - security: - - bearer: [] - summary: Gets project's usage api counts - tags: - - Analytics - x-endpoint-owners: - - observability - x-fga-permissions: - - - analytics_usage_read - /v1/projects/{ref}/analytics/endpoints/usage.api-requests-count: - get: - operationId: v1-get-project-usage-request-count - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1GetUsageApiRequestsCountResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get project's usage api requests count - security: - - bearer: [] - summary: Gets project's usage api requests count - tags: - - Analytics - x-endpoint-owners: - - observability - x-fga-permissions: - - - analytics_usage_read - /v1/projects/{ref}/analytics/endpoints/functions.combined-stats: - get: - operationId: v1-get-project-function-combined-stats - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: interval - required: true - in: query - schema: - example: 1hr - type: string - enum: - - 15min - - 1hr - - 3hr - - 1day - - name: function_id - required: true - in: query - schema: - example: 3c078cce-ad70-4148-9f37-4da362789053 - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/AnalyticsResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get project's function combined statistics - security: - - bearer: [] - summary: Gets a project's function combined statistics - tags: - - Analytics - x-endpoint-owners: - - observability - x-fga-permissions: - - - analytics_usage_read - /v1/projects/{ref}/analytics/endpoints/metrics: - get: - description: >- - Prometheus scrape endpoint. Returns metrics of a customer project in the - Prometheus open exposition format. - operationId: v1-scrape-project-metrics - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: Prometheus / OpenMetrics text exposition - content: - text/plain: - schema: - type: string - application/openmetrics-text: - schema: - type: string - '400': - description: >- - Project must be active and healthy, or metrics are not available for - this project - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to fetch project's metrics - security: - - bearer: [] - summary: Scrape a project's metrics - tags: - - Analytics - x-badges: - - name: 'OAuth scope: analytics:read' - position: after - x-endpoint-owners: - - observability - x-fga-permissions: - - - analytics_logs_read - x-oauth-scope: analytics:read - /v1/projects/{ref}/cli/login-role: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-create-login-role - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateRoleBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/CreateRoleResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to create login role - security: - - bearer: [] - summary: Create a login role for CLI with temporary password - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - database_write - x-oauth-scope: database:write - x-scalar-stability: experimental - delete: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-delete-login-roles - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteRolesResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to delete login roles - security: - - bearer: [] - summary: Delete existing login roles used by CLI - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - dev-workflows - x-fga-permissions: - - - database_write - x-oauth-scope: database:write - x-scalar-stability: experimental - /v1/projects/{ref}/database/migrations: - get: - operationId: v1-list-migration-history - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1ListMigrationsResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to list database migrations - security: - - bearer: [] - summary: List applied migration versions - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - database_migrations_read - x-oauth-scope: database:read - post: - operationId: v1-apply-a-migration - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: Idempotency-Key - required: false - in: header - description: A unique key to ensure the same migration is tracked only once. - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1CreateMigrationBody' - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to apply database migration - security: - - bearer: [] - summary: Apply a database migration - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - database_migrations_write - x-oauth-scope: database:write - put: - operationId: v1-upsert-a-migration - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: Idempotency-Key - required: false - in: header - description: A unique key to ensure the same migration is tracked only once. - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1UpsertMigrationBody' - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to upsert database migration - security: - - bearer: [] - summary: Upsert a database migration without applying - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - database_migrations_write - x-oauth-scope: database:write - delete: - operationId: v1-rollback-migrations - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: gte - required: true - in: query - description: Rollback migrations greater or equal to this version - schema: - pattern: ^\d+$ - example: '20250312000000' - type: string - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to rollback database migration - security: - - bearer: [] - summary: Rollback database migrations and remove them from history table - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - database_migrations_write - x-oauth-scope: database:write - /v1/projects/{ref}/database/migrations/{version}: - get: - operationId: v1-get-a-migration - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: version - required: true - in: path - schema: - pattern: ^\d+$ - example: '20250312000000' - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1GetMigrationResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get database migration - security: - - bearer: [] - summary: Fetch an existing entry from migration history - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - database_migrations_read - x-oauth-scope: database:read - patch: - operationId: v1-patch-a-migration - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: version - required: true - in: path - schema: - pattern: ^\d+$ - example: '20250312000000' - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1PatchMigrationBody' - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to patch database migration - security: - - bearer: [] - summary: Patch an existing entry in migration history - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - database_migrations_write - x-oauth-scope: database:write - /v1/projects/{ref}/database/query: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-run-a-query - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1RunQueryBody' - responses: - '201': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to run sql query - security: - - bearer: [] - summary: Run sql query - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - database_read - - - database_write - x-oauth-scope: database:write - x-scalar-stability: experimental - /v1/projects/{ref}/database/query/read-only: - post: - description: |- - All entity references must be schema qualified. - - This endpoint is currently in its **Beta** stage. - operationId: v1-read-only-query - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1ReadOnlyQueryBody' - responses: - '201': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to run read-only sql query - security: - - bearer: [] - summary: Run a sql query as supabase_read_only_user - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - - name: Beta - position: before - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - database_read - x-oauth-scope: database:read - x-scalar-stability: experimental - /v1/projects/{ref}/database/webhooks/enable: - post: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - - - This endpoint is currently in its **Beta** stage. - operationId: v1-enable-database-webhook - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '201': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to enable Database Webhooks on the project - security: - - bearer: [] - summary: Enables Database Webhooks on the project - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - - name: Beta - position: before - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - database_webhooks_config_write - x-oauth-scope: database:write - x-scalar-stability: experimental - /v1/projects/{ref}/database/context: - get: - description: >- - This is an **experimental** endpoint. It is subject to change or removal - in future versions. Use it with caution, as it may not remain supported - or stable. - operationId: v1-get-database-metadata - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/GetProjectDbMetadataResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Gets database metadata for the given project. - tags: - - Database - x-badges: - - name: 'OAuth scope: projects:read' - position: after - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - database_read - x-oauth-scope: projects:read - x-scalar-stability: experimental - /v1/projects/{ref}/database/password: - patch: - operationId: v1-update-database-password - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1UpdatePasswordBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1UpdatePasswordResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update database password - security: - - bearer: [] - summary: Updates the database password - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - database_config_write - x-oauth-scope: database:write - /v1/projects/{ref}/database/jit: - get: - description: Mappings of roles a user can assume in the project database - operationId: v1-get-jit-access - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/JitAccessResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to list database jit access - security: - - bearer: [] - summary: Get user-id to role mappings for JIT access - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - security - x-fga-permissions: - - - database_jit_read - x-oauth-scope: database:read - post: - description: Authorizes the request to assume a role in the project database - operationId: v1-authorize-jit-access - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AuthorizeJitAccessBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/JitAuthorizeAccessResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to authorize database jit access - security: - - bearer: [] - summary: Authorize user-id to role mappings for JIT access - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - security - x-fga-permissions: - - - database_jit_read - x-oauth-scope: database:read - put: - description: Modifies the roles that can be assumed and for how long - operationId: v1-update-jit-access - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateJitAccessBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/JitAccessResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update JIT access - security: - - bearer: [] - summary: Updates a user mapping for JIT access - tags: - - Database - x-endpoint-owners: - - security - x-fga-permissions: - - - database_jit_write - /v1/projects/{ref}/database/jit/list: - get: - description: Mappings of roles a user can assume in the project database - operationId: v1-list-jit-access - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/JitListAccessResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to list database jit access - security: - - bearer: [] - summary: List all user-id to role mappings for JIT access - tags: - - Database - x-endpoint-owners: - - security - x-fga-permissions: - - - database_jit_write - /v1/projects/{ref}/database/jit/invite: - post: - description: >- - Invites the external user and sets initial roles that can be assumed and - for how long - operationId: v1-invite-external-jit-access - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/InviteExternalUserJitAccessBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/InviteExternalUserJitResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to invite external user - security: - - bearer: [] - summary: Invites an external user to a database for JIT access - tags: - - Database - x-endpoint-owners: - - security - x-fga-permissions: - - - database_jit_write - /v1/projects/{ref}/database/jit/invite/accept: - post: - description: Accepts the invitation to JIT database access - operationId: v1-accept-invite-external-jit-access - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AcceptInviteExternalUserJitAccessBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/JitAccessResponse_Output' - '500': - description: Failed to accept invitation - security: - - bearer: [] - summary: Accepts invitation for JIT database access - tags: - - Database - x-endpoint-owners: - - security - /v1/projects/{ref}/database/jit/invite/{invite_id}: - delete: - description: Revokes and deletes the invitation - operationId: v1-delete-invite-external-jit-access - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: invite_id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 55555555-5555-4555-8555-555555555555 - type: string - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to revoke invite for external user - security: - - bearer: [] - summary: Deletes the invite for an external user to a database for JIT access - tags: - - Database - x-endpoint-owners: - - security - x-fga-permissions: - - - database_jit_write - /v1/projects/{ref}/database/jit/{user_id}: - delete: - description: Remove JIT mappings of a user, revoking all JIT database access - operationId: v1-delete-jit-access - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: user_id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 55555555-5555-4555-8555-555555555555 - type: string - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to remove JIT access - security: - - bearer: [] - summary: Delete JIT access by user-id - tags: - - Database - x-endpoint-owners: - - security - x-fga-permissions: - - - database_jit_write - /v1/projects/{ref}/database/openapi: - get: - description: >- - Returns the PostgREST OpenAPI specification for the project. This is the - replacement for querying `/rest/v1/` directly with the anon key. - operationId: v1-get-database-openapi - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: schema - required: false - in: query - description: The database schema to generate the OpenAPI spec for - schema: - default: public - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to fetch PostgREST OpenAPI spec - security: - - bearer: [] - summary: Get PostgREST OpenAPI spec - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - database_read - x-oauth-scope: database:read - /v1/projects/{ref}/functions: - get: - description: Returns all functions you've previously added to the specified project. - operationId: v1-list-all-functions - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/FunctionResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's functions - security: - - bearer: [] - summary: List all functions - tags: - - Edge Functions - x-badges: - - name: 'OAuth scope: edge_functions:read' - position: after - x-endpoint-owners: - - functions - x-fga-permissions: - - - edge_functions_read - x-oauth-scope: edge_functions:read - post: - deprecated: true - description: >- - This endpoint is deprecated - use the deploy endpoint. Creates a - function and adds it to the specified project. - operationId: v1-create-a-function - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: slug - required: false - in: query - schema: - pattern: ^[A-Za-z0-9_-]+$ - example: hello-world - type: string - - name: name - required: false - in: query - schema: - example: Hello World - type: string - - name: verify_jwt - required: false - in: query - schema: - example: true - type: string - - name: import_map - required: false - in: query - schema: - example: false - type: string - - name: entrypoint_path - required: false - in: query - schema: - example: index.ts - type: string - - name: import_map_path - required: false - in: query - schema: - example: import_map.json - type: string - - name: ezbr_sha256 - required: false - in: query - schema: - example: 44c691990518d25498f0fd80cf6631ecf2b58eb9c5eb2a087dd1688f2904dac7 - type: string - requestBody: - required: true - content: - application/vnd.denoland.eszip: - schema: - type: string - format: binary - application/json: - schema: - $ref: '#/components/schemas/V1CreateFunctionBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/FunctionResponse_Output' - '401': - description: Unauthorized - '402': - description: Maximum number of functions reached for Plan - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to create project's function - security: - - bearer: [] - summary: Create a function - tags: - - Edge Functions - x-badges: - - name: 'OAuth scope: edge_functions:write' - position: after - x-endpoint-owners: - - functions - x-fga-permissions: - - - edge_functions_write - x-oauth-scope: edge_functions:write - put: - description: >- - Bulk update functions. It will create a new function or replace - existing. The operation is idempotent. NOTE: You will need to manually - bump the version. - operationId: v1-bulk-update-functions - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/BulkUpdateFunctionBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/BulkUpdateFunctionResponse_Output' - '401': - description: Unauthorized - '402': - description: Maximum number of functions reached for Plan - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update functions - security: - - bearer: [] - summary: Bulk update functions - tags: - - Edge Functions - x-badges: - - name: 'OAuth scope: edge_functions:write' - position: after - x-endpoint-owners: - - functions - x-fga-permissions: - - - edge_functions_write - x-oauth-scope: edge_functions:write - /v1/projects/{ref}/functions/deploy: - post: - description: >- - A new endpoint to deploy functions. It will create if function does not - exist. - operationId: v1-deploy-a-function - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: slug - required: false - in: query - schema: - pattern: ^[A-Za-z][A-Za-z0-9_-]*$ - example: hello-world - type: string - - name: bundleOnly - required: false - in: query - schema: - example: false - type: string - requestBody: - required: true - content: - multipart/form-data: - schema: - $ref: '#/components/schemas/FunctionDeployBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DeployFunctionResponse_Output' - '401': - description: Unauthorized - '402': - description: Maximum number of functions reached for Plan - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to deploy function - security: - - bearer: [] - summary: Deploy a function - tags: - - Edge Functions - x-badges: - - name: 'OAuth scope: edge_functions:write' - position: after - x-endpoint-owners: - - functions - x-fga-permissions: - - - edge_functions_write - x-oauth-scope: edge_functions:write - /v1/projects/{ref}/functions/{function_slug}: - get: - description: Retrieves a function with the specified slug and project. - operationId: v1-get-a-function - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: function_slug - required: true - in: path - description: Function slug - schema: - pattern: ^[A-Za-z0-9_-]+$ - example: hello-world - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/FunctionSlugResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve function with given slug - security: - - bearer: [] - summary: Retrieve a function - tags: - - Edge Functions - x-badges: - - name: 'OAuth scope: edge_functions:read' - position: after - x-endpoint-owners: - - functions - x-fga-permissions: - - - edge_functions_read - x-oauth-scope: edge_functions:read - patch: - description: Updates a function with the specified slug and project. - operationId: v1-update-a-function - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: function_slug - required: true - in: path - description: Function slug - schema: - pattern: ^[A-Za-z0-9_-]+$ - example: hello-world - type: string - - name: slug - required: false - in: query - schema: - pattern: ^[A-Za-z0-9_-]+$ - example: hello-world - type: string - - name: name - required: false - in: query - schema: - example: Hello World - type: string - - name: verify_jwt - required: false - in: query - schema: - example: true - type: string - - name: import_map - required: false - in: query - schema: - example: false - type: string - - name: entrypoint_path - required: false - in: query - schema: - example: index.ts - type: string - - name: import_map_path - required: false - in: query - schema: - example: import_map.json - type: string - - name: ezbr_sha256 - required: false - in: query - schema: - example: 44c691990518d25498f0fd80cf6631ecf2b58eb9c5eb2a087dd1688f2904dac7 - type: string - requestBody: - required: true - content: - application/vnd.denoland.eszip: - schema: - type: string - format: binary - application/json: - schema: - $ref: '#/components/schemas/V1UpdateFunctionBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/FunctionSlugResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update function with given slug - security: - - bearer: [] - summary: Update a function - tags: - - Edge Functions - x-badges: - - name: 'OAuth scope: edge_functions:write' - position: after - x-endpoint-owners: - - functions - x-fga-permissions: - - - edge_functions_write - x-oauth-scope: edge_functions:write - delete: - description: Deletes a function with the specified slug from the specified project. - operationId: v1-delete-a-function - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: function_slug - required: true - in: path - description: Function slug - schema: - pattern: ^[A-Za-z0-9_-]+$ - example: hello-world - type: string - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to delete function with given slug - security: - - bearer: [] - summary: Delete a function - tags: - - Edge Functions - x-badges: - - name: 'OAuth scope: edge_functions:write' - position: after - x-endpoint-owners: - - functions - x-fga-permissions: - - - edge_functions_write - x-oauth-scope: edge_functions:write - /v1/projects/{ref}/functions/{function_slug}/body: - get: - description: Retrieves a function body for the specified slug and project. - operationId: v1-get-a-function-body - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: function_slug - required: true - in: path - description: Function slug - schema: - pattern: ^[A-Za-z0-9_-]+$ - example: hello-world - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/StreamableFile' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve function body with given slug - security: - - bearer: [] - summary: Retrieve a function body - tags: - - Edge Functions - x-badges: - - name: 'OAuth scope: edge_functions:read' - position: after - x-endpoint-owners: - - functions - x-fga-permissions: - - - edge_functions_read - x-oauth-scope: edge_functions:read - /v1/projects/{ref}/storage/buckets: - get: - operationId: v1-list-all-buckets - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/V1StorageBucketResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get list of buckets - security: - - bearer: [] - summary: Lists all buckets - tags: - - Storage - x-badges: - - name: 'OAuth scope: storage:read' - position: after - x-endpoint-owners: - - storage - x-fga-permissions: - - - storage_read - x-oauth-scope: storage:read - /v1/projects/{ref}/config/disk: - get: - operationId: v1-get-database-disk - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DiskResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get database disk attributes - security: - - bearer: [] - summary: Get database disk attributes - tags: - - Projects - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - infra_disk_config_read - post: - operationId: v1-modify-database-disk - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DiskRequestBody' - responses: - '201': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to modify database disk - security: - - bearer: [] - summary: Modify database disk - tags: - - Projects - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - infra_disk_config_write - /v1/projects/{ref}/config/disk/util: - get: - operationId: v1-get-disk-utilization - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DiskUtilMetricsResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get disk utilization - security: - - bearer: [] - summary: Get disk utilization - tags: - - Projects - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - infra_disk_config_read - /v1/projects/{ref}/config/disk/autoscale: - get: - operationId: v1-get-project-disk-autoscale-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DiskAutoscaleConfig_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get project disk autoscale config - security: - - bearer: [] - summary: Gets project disk autoscale config - tags: - - Projects - x-endpoint-owners: - - control-plane - - infra - x-fga-permissions: - - - infra_disk_config_read - /v1/projects/{ref}/config/storage: - get: - operationId: v1-get-storage-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/StorageConfigResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's storage config - security: - - bearer: [] - summary: Gets project's storage config - tags: - - Storage - x-endpoint-owners: - - storage - x-fga-permissions: - - - storage_config_read - patch: - operationId: v1-update-storage-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateStorageConfigBody' - responses: - '200': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update project's storage config - security: - - bearer: [] - summary: Updates project's storage config - tags: - - Storage - x-endpoint-owners: - - storage - x-fga-permissions: - - - storage_config_write - /v1/projects/{ref}/config/database/pgbouncer: - get: - operationId: v1-get-project-pgbouncer-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1PgbouncerConfigResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's pgbouncer config - summary: Get project's pgbouncer config - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - database_read - x-oauth-scope: database:read - /v1/projects/{ref}/config/database/pooler: - get: - operationId: v1-get-pooler-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/SupavisorConfigResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's supavisor config - security: - - bearer: [] - summary: Gets project's supavisor config - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - database_pooling_config_read - x-oauth-scope: database:read - patch: - operationId: v1-update-pooler-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateSupavisorConfigBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateSupavisorConfigResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update project's supavisor config - security: - - bearer: [] - summary: Updates project's supavisor config - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - database_pooling_config_write - x-oauth-scope: database:write - /v1/projects/{ref}/config/database/postgres: - get: - operationId: v1-get-postgres-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PostgresConfigResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve project's Postgres config - security: - - bearer: [] - summary: Gets project's Postgres config - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - database_config_read - x-oauth-scope: database:read - put: - operationId: v1-update-postgres-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdatePostgresConfigBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/PostgresConfigResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to update project's Postgres config - security: - - bearer: [] - summary: Updates project's Postgres config - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - x-endpoint-owners: - - infra - - control-plane - x-fga-permissions: - - - database_config_write - x-oauth-scope: database:write - /v1/projects/{ref}/config/realtime: - get: - operationId: v1-get-realtime-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: Gets project's realtime configuration - content: - application/json: - schema: - $ref: '#/components/schemas/RealtimeConfigResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Gets realtime configuration - tags: - - Realtime - x-endpoint-owners: - - realtime - x-fga-permissions: - - - realtime_config_read - patch: - operationId: v1-update-realtime-config - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateRealtimeConfigBody' - responses: - '204': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Updates realtime configuration - tags: - - Realtime - x-endpoint-owners: - - realtime - x-fga-permissions: - - - realtime_config_write - /v1/projects/{ref}/config/realtime/shutdown: - post: - operationId: v1-shutdown-realtime - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '204': - description: Realtime connections shutdown successfully - '401': - description: Unauthorized - '403': - description: Forbidden action - '404': - description: Tenant not found - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Shutdowns realtime connections for a project - tags: - - Realtime - x-endpoint-owners: - - realtime - x-fga-permissions: - - - realtime_config_write - /v1/projects/{ref}/config/auth/sso/providers: - post: - operationId: v1-create-a-sso-provider - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateProviderBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/CreateProviderResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '404': - description: SAML 2.0 support is not enabled for this project - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Creates a new SSO provider - tags: - - Auth - x-badges: - - name: 'OAuth scope: auth:write' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_config_write - x-oauth-scope: auth:write - get: - operationId: v1-list-all-sso-provider - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/ListProvidersResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '404': - description: SAML 2.0 support is not enabled for this project - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Lists all SSO providers - tags: - - Auth - x-badges: - - name: 'OAuth scope: auth:read' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_config_read - x-oauth-scope: auth:read - /v1/projects/{ref}/config/auth/sso/providers/{provider_id}: - get: - operationId: v1-get-a-sso-provider - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: provider_id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 77777777-7777-4777-8777-777777777777 - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/GetProviderResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '404': - description: >- - Either SAML 2.0 was not enabled for this project, or the provider - does not exist - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Gets a SSO provider by its UUID - tags: - - Auth - x-badges: - - name: 'OAuth scope: auth:read' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_config_read - x-oauth-scope: auth:read - put: - operationId: v1-update-a-sso-provider - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: provider_id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 77777777-7777-4777-8777-777777777777 - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateProviderBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateProviderResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '404': - description: >- - Either SAML 2.0 was not enabled for this project, or the provider - does not exist - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Updates a SSO provider by its UUID - tags: - - Auth - x-badges: - - name: 'OAuth scope: auth:write' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_config_write - x-oauth-scope: auth:write - delete: - operationId: v1-delete-a-sso-provider - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: provider_id - required: true - in: path - schema: - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - example: 77777777-7777-4777-8777-777777777777 - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteProviderResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '404': - description: >- - Either SAML 2.0 was not enabled for this project, or the provider - does not exist - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Removes a SSO provider by its UUID - tags: - - Auth - x-badges: - - name: 'OAuth scope: auth:write' - position: after - x-endpoint-owners: - - auth - x-fga-permissions: - - - auth_config_write - x-oauth-scope: auth:write - /v1/projects/{ref}/database/backups: - get: - operationId: v1-list-all-backups - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1BackupsResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get backups - security: - - bearer: [] - summary: Lists all backups - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - backups_read - x-oauth-scope: database:read - /v1/projects/{ref}/database/backups/restore-pitr: - post: - operationId: v1-restore-pitr-backup - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1RestorePitrBody' - responses: - '201': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Restores a PITR backup for a database - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - backups_write - x-oauth-scope: database:write - /v1/projects/{ref}/database/backups/restore-point: - post: - operationId: v1-create-restore-point - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1RestorePointPostBody' - responses: - '201': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1RestorePointResponse' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Initiates a creation of a restore point for a database - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - backups_write - x-internal: true - x-oauth-scope: database:write - get: - operationId: v1-get-restore-point - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - - name: name - required: false - in: query - schema: - maxLength: 20 - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1RestorePointResponse' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to get requested restore points - security: - - bearer: [] - summary: Get restore points for project - tags: - - Database - x-badges: - - name: 'OAuth scope: database:read' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - backups_read - x-internal: true - x-oauth-scope: database:read - /v1/projects/{ref}/database/backups/restore: - post: - operationId: v1-restore-physical-backup - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1RestoreBackupBody' - responses: - '201': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Restores a physical backup for a database - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - backups_write - x-internal: true - x-oauth-scope: database:write - /v1/projects/{ref}/database/backups/schedule: - get: - operationId: v1-get-backup-schedule - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1BackupScheduleResponse_Output' - '401': - description: Unauthorized - '402': - description: This feature requires the Enterprise organization plan. - content: - application/json: - schema: - $ref: '#/components/schemas/PlanGateErrorBody' - '403': - description: Forbidden action - '404': - description: Project or backup schedule not found - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve backup schedule - security: - - bearer: [] - summary: Gets the backup schedule for a project - tags: - - Database - x-allowed-plans: - - Enterprise - x-badges: - - name: 'OAuth scope: database:read' - position: after - - name: Only available on Enterprise - position: before - x-endpoint-owners: - - infra - x-fga-permissions: - - - backups_read - x-oauth-scope: database:read - patch: - description: >- - Sets the time at which the daily backup runs. The change takes effect on - the next backup window that includes the new time. If the new time has - already passed for today, the first backup at the new time will occur - the following day. It can only be updated 3 times per 24 hours. - operationId: v1-update-backup-schedule - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1UpdateBackupScheduleBody' - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1BackupScheduleResponse_Output' - '400': - description: Invalid schedule_for format - '401': - description: Unauthorized - '402': - description: This feature requires the Enterprise organization plan. - content: - application/json: - schema: - $ref: '#/components/schemas/PlanGateErrorBody' - '403': - description: Forbidden action - '404': - description: Project or backup schedule not found - '429': - description: Rate limit exceeded - '500': - description: Failed to update backup schedule - security: - - bearer: [] - summary: Updates the backup schedule time for a project - tags: - - Database - x-allowed-plans: - - Enterprise - x-badges: - - name: 'OAuth scope: database:write' - position: after - - name: Only available on Enterprise - position: before - x-endpoint-owners: - - infra - x-fga-permissions: - - - backups_write - x-oauth-scope: database:write - /v1/projects/{ref}/database/backups/undo: - post: - operationId: v1-undo - parameters: - - name: ref - required: true - in: path - description: Project ref - schema: - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - example: abcdefghijklmnopqrst - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/V1UndoBody' - responses: - '201': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Initiates an undo to a given restore point - tags: - - Database - x-badges: - - name: 'OAuth scope: database:write' - position: after - x-endpoint-owners: - - infra - x-fga-permissions: - - - backups_write - x-internal: true - x-oauth-scope: database:write - /v1/organizations/{slug}/entitlements: - get: - description: >- - Returns the entitlements available to the organization based on their - plan and any overrides. - operationId: v1-get-organization-entitlements - parameters: - - name: slug - required: true - in: path - description: Organization slug - schema: - pattern: ^[\w-]+$ - example: tsrqponmlkjihgfedcba - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1ListEntitlementsResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Get entitlements for an organization - tags: - - Organizations - x-badges: - - name: 'OAuth scope: organizations:read' - position: after - x-endpoint-owners: - - billing - x-fga-permissions: - - - organization_admin_read - x-oauth-scope: organizations:read - /v1/organizations/{slug}/members: - get: - operationId: v1-list-organization-members - parameters: - - name: slug - required: true - in: path - description: Organization slug - schema: - pattern: ^[\w-]+$ - example: tsrqponmlkjihgfedcba - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/V1OrganizationMemberResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: List members of an organization - tags: - - Organizations - x-badges: - - name: 'OAuth scope: organizations:read' - position: after - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - members_read - x-oauth-scope: organizations:read - /v1/organizations/{slug}: - get: - operationId: v1-get-an-organization - parameters: - - name: slug - required: true - in: path - description: Organization slug - schema: - pattern: ^[\w-]+$ - example: tsrqponmlkjihgfedcba - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/V1OrganizationSlugResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Gets information about the organization - tags: - - Organizations - x-badges: - - name: 'OAuth scope: organizations:read' - position: after - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - organization_admin_read - x-oauth-scope: organizations:read - /v1/organizations/{slug}/project-claim/{token}: - get: - operationId: v1-get-organization-project-claim - parameters: - - name: slug - required: true - in: path - description: Organization slug - schema: - pattern: ^[\w-]+$ - example: tsrqponmlkjihgfedcba - type: string - - name: token - required: true - in: path - schema: - example: 0123456789abcdef0123456789abcdef01234567 - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/OrganizationProjectClaimResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Gets project details for the specified organization and claim token - tags: - - Organizations - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - organization_admin_write - x-internal: true - post: - operationId: v1-claim-project-for-organization - parameters: - - name: slug - required: true - in: path - description: Organization slug - schema: - pattern: ^[\w-]+$ - example: tsrqponmlkjihgfedcba - type: string - - name: token - required: true - in: path - schema: - example: 0123456789abcdef0123456789abcdef01234567 - type: string - responses: - '204': - description: '' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - security: - - bearer: [] - summary: Claims project for the specified organization - tags: - - Organizations - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - organization_admin_write - x-internal: true - /v1/organizations/{slug}/projects: - get: - description: >- - Returns a paginated list of projects for the specified organization. - - - This endpoint uses offset-based pagination. Use the `offset` parameter - to skip a number of projects and the `limit` parameter to control the - number of projects returned per page. - operationId: v1-get-all-projects-for-organization - parameters: - - name: slug - required: true - in: path - description: Organization slug - schema: - pattern: ^[\w-]+$ - example: tsrqponmlkjihgfedcba - type: string - - name: offset - required: false - in: query - description: Number of projects to skip - schema: - minimum: 0 - maximum: 9007199254740991 - default: 0 - example: 0 - type: integer - - name: limit - required: false - in: query - description: Number of projects to return per page - schema: - minimum: 1 - maximum: 100 - default: 100 - example: 20 - type: integer - - name: search - required: false - in: query - description: Search projects by name - schema: - example: acme - type: string - - name: sort - required: false - in: query - description: Sort order for projects - schema: - default: name_asc - example: created_desc - type: string - enum: - - name_asc - - name_desc - - created_asc - - created_desc - - name: statuses - required: false - in: query - description: |- - A comma-separated list of project statuses to filter by. - - The following values are supported: `ACTIVE_HEALTHY`, `INACTIVE`. - schema: - example: ACTIVE_HEALTHY,INACTIVE - type: string - responses: - '200': - description: '' - content: - application/json: - schema: - $ref: '#/components/schemas/OrganizationProjectsResponse_Output' - '401': - description: Unauthorized - '403': - description: Forbidden action - '429': - description: Rate limit exceeded - '500': - description: Failed to retrieve projects - security: - - bearer: [] - summary: Gets all projects for the given organization - tags: - - Projects - x-badges: - - name: 'OAuth scope: projects:read' - position: after - x-endpoint-owners: - - control-plane - x-fga-permissions: - - - organization_projects_read - x-oauth-scope: projects:read -info: - title: Supabase API (v1) - description: >- - Supabase API generated from the OpenAPI specification.
Visit - [https://supabase.com/docs](https://supabase.com/docs) for a complete - documentation. - version: 1.0.0 - contact: {} -tags: - - name: Advisors - description: Advisors related endpoints - - name: Analytics - description: Analytics related endpoints - - name: Auth - description: Auth related endpoints - - name: Billing - description: Billing related endpoints - - name: Database - description: Database related endpoints - - name: Domains - description: Domains related endpoints - - name: Edge Functions - description: Edge related endpoints - - name: Environments - description: Environments related endpoints - - name: OAuth - description: OAuth related endpoints - - name: Organizations - description: Organizations related endpoints - - name: Projects - description: Projects related endpoints - - name: Rest - description: Rest related endpoints - - name: Secrets - description: Secrets related endpoints - - name: Storage - description: >- - Visit - [https://supabase.github.io/storage/](https://supabase.github.io/storage/) - for complete documentation. -servers: - - url: https://api.supabase.green -components: - securitySchemes: - bearer: - scheme: bearer - bearerFormat: JWT - type: http - oauth2: - type: oauth2 - flows: - authorizationCode: - authorizationUrl: https://api.supabase.green/v1/oauth/authorize - tokenUrl: https://api.supabase.green/v1/oauth/token - scopes: - analytics:read: Read access to analytics - analytics:write: Write access to analytics - analytics_config:read: Read access to analytics config - analytics_config:write: Write access to analytics config - auth:read: Read access to auth - auth:write: Write access to auth - database:read: Read access to database - database:write: Write access to database - domains:read: Read access to domains - domains:write: Write access to domains - edge_functions:read: Read access to edge functions - edge_functions:write: Write access to edge functions - environment:read: Read access to environment - environment:write: Write access to environment - organizations:read: Read access to organizations - organizations:write: Write access to organizations - projects:read: Read access to projects - projects:write: Write access to projects - rest:read: Read access to rest - rest:write: Write access to rest - secrets:read: Read access to secrets - secrets:write: Write access to secrets - storage:read: Read access to storage - storage:write: Write access to storage - description: >- - OAuth 2.0 authorization code flow for Supabase OAuth apps. Tokens carry - the scopes approved for the app by the organization. - schemas: - BranchDetailResponse_Output: - type: object - properties: - ref: - type: string - postgres_version: - type: string - postgres_engine: - type: string - release_channel: - type: string - status: - type: string - enum: - - INACTIVE - - ACTIVE_HEALTHY - - ACTIVE_UNHEALTHY - - COMING_UP - - UNKNOWN - - GOING_DOWN - - INIT_FAILED - - REMOVED - - RESTORING - - UPGRADING - - PAUSING - - RESTORE_FAILED - - RESTARTING - - PAUSE_FAILED - - RESIZING - db_host: - type: string - db_port: - type: integer - exclusiveMinimum: true - maximum: 9007199254740991 - minimum: 0 - db_user: - type: string - db_pass: - type: string - jwt_secret: - type: string - required: - - ref - - postgres_version - - postgres_engine - - release_channel - - status - - db_host - - db_port - UpdateBranchBody: - type: object - properties: - branch_name: - type: string - git_branch: - type: string - reset_on_push: - description: >- - This field is deprecated and will be ignored. Use v1-reset-a-branch - endpoint directly instead. - deprecated: true - type: boolean - persistent: - type: boolean - status: - type: string - enum: - - CREATING_PROJECT - - RUNNING_MIGRATIONS - - MIGRATIONS_PASSED - - MIGRATIONS_FAILED - - FUNCTIONS_DEPLOYED - - FUNCTIONS_FAILED - request_review: - type: boolean - notify_url: - type: string - format: uri - description: HTTP endpoint to receive branch status updates. - example: - branch_name: preview-login-page - git_branch: feature/login-page - persistent: true - request_review: true - notify_url: https://example.com/webhooks/branches - BranchResponse_Output: - type: object - properties: - id: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - name: - type: string - project_ref: - type: string - parent_project_ref: - type: string - is_default: - type: boolean - git_branch: - type: string - pr_number: - type: integer - format: int32 - minimum: -9007199254740991 - maximum: 9007199254740991 - latest_check_run_id: - description: This field is deprecated and will not be populated. - deprecated: true - type: number - persistent: - type: boolean - status: - type: string - enum: - - CREATING_PROJECT - - RUNNING_MIGRATIONS - - MIGRATIONS_PASSED - - MIGRATIONS_FAILED - - FUNCTIONS_DEPLOYED - - FUNCTIONS_FAILED - description: >- - This field is deprecated. List action runs to get branch status - instead. - deprecated: true - created_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - updated_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - review_requested_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - with_data: - type: boolean - notify_url: - type: string - format: uri - deletion_scheduled_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - preview_project_status: - type: string - enum: - - INACTIVE - - ACTIVE_HEALTHY - - ACTIVE_UNHEALTHY - - COMING_UP - - UNKNOWN - - GOING_DOWN - - INIT_FAILED - - REMOVED - - RESTORING - - UPGRADING - - PAUSING - - RESTORE_FAILED - - RESTARTING - - PAUSE_FAILED - - RESIZING - required: - - id - - name - - project_ref - - parent_project_ref - - is_default - - persistent - - status - - created_at - - updated_at - - with_data - BranchDeleteResponse_Output: - type: object - properties: - message: - type: string - enum: - - ok - required: - - message - BranchActionBody: - type: object - properties: - migration_version: - type: string - example: - migration_version: '20250312000000' - BranchUpdateResponse_Output: - type: object - properties: - workflow_run_id: - type: string - message: - type: string - enum: - - ok - required: - - workflow_run_id - - message - BranchRestoreResponse_Output: - type: object - properties: - message: - type: string - enum: - - Branch restoration initiated - required: - - message - V1ProjectWithDatabaseResponse_Output: - type: object - properties: - id: - type: string - deprecated: true - description: 'Deprecated: Use `ref` instead.' - ref: - type: string - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - description: Project ref - example: abcdefghijklmnopqrst - organization_id: - type: string - description: 'Deprecated: Use `organization_slug` instead.' - deprecated: true - organization_slug: - type: string - pattern: ^[\w-]+$ - description: Organization slug - example: tsrqponmlkjihgfedcba - name: - type: string - description: Name of your project - region: - type: string - description: Region of your project - created_at: - type: string - description: Creation timestamp - status: - type: string - enum: - - INACTIVE - - ACTIVE_HEALTHY - - ACTIVE_UNHEALTHY - - COMING_UP - - UNKNOWN - - GOING_DOWN - - INIT_FAILED - - REMOVED - - RESTORING - - UPGRADING - - PAUSING - - RESTORE_FAILED - - RESTARTING - - PAUSE_FAILED - - RESIZING - database: - type: object - properties: - host: - type: string - description: Database host - version: - type: string - description: Database version - postgres_engine: - type: string - description: Database engine - release_channel: - type: string - description: Release channel - required: - - host - - version - - postgres_engine - - release_channel - required: - - id - - ref - - organization_id - - organization_slug - - name - - region - - created_at - - status - - database - V1CreateProjectBody: - type: object - properties: - db_pass: - type: string - description: Database password - name: - type: string - maxLength: 256 - description: Name of your project - organization_id: - deprecated: true - description: 'Deprecated: Use `organization_slug` instead.' - type: string - organization_slug: - type: string - pattern: ^[\w-]+$ - description: Organization slug - example: tsrqponmlkjihgfedcba - plan: - deprecated: true - description: >- - Subscription Plan is now set on organization level and is ignored in - this request - type: string - enum: - - free - - pro - region: - description: >- - Region you want your server to reside in. Use region_selection - instead. - deprecated: true - enum: - - us-east-1 - - us-east-2 - - us-west-1 - - us-west-2 - - ap-east-1 - - ap-southeast-1 - - ap-northeast-1 - - ap-northeast-2 - - ap-southeast-2 - - eu-west-1 - - eu-west-2 - - eu-west-3 - - eu-north-1 - - eu-central-1 - - eu-central-2 - - ca-central-1 - - ap-south-1 - - sa-east-1 - type: string - region_selection: - description: >- - Region selection. Only one of region or region_selection can be - specified. - oneOf: - - type: object - properties: - type: - type: string - enum: - - specific - code: - type: string - description: >- - Specific region code. The codes supported are not a stable - API, and should be retrieved from the /available-regions - endpoint. - enum: - - us-east-1 - - us-east-2 - - us-west-1 - - us-west-2 - - ap-east-1 - - ap-southeast-1 - - ap-northeast-1 - - ap-northeast-2 - - ap-southeast-2 - - eu-west-1 - - eu-west-2 - - eu-west-3 - - eu-north-1 - - eu-central-1 - - eu-central-2 - - ca-central-1 - - ap-south-1 - - sa-east-1 - required: - - type - - code - - type: object - properties: - type: - type: string - enum: - - smartGroup - code: - type: string - enum: - - americas - - emea - - apac - description: >- - The Smart Region Group's code. The codes supported are not a - stable API, and should be retrieved from the - /available-regions endpoint. - required: - - type - - code - kps_enabled: - deprecated: true - description: This field is deprecated and is ignored in this request - type: boolean - desired_instance_size: - description: >- - Desired instance size. Omit this field to always default to the - smallest possible size. - type: string - enum: - - nano - - micro - - small - - medium - - large - - xlarge - - 2xlarge - - 4xlarge - - 8xlarge - - 12xlarge - - 16xlarge - - 24xlarge - - 24xlarge_optimized_memory - - 24xlarge_optimized_cpu - - 24xlarge_high_memory - - 48xlarge - - 48xlarge_optimized_memory - - 48xlarge_optimized_cpu - - 48xlarge_high_memory - template_url: - description: Template URL used to create the project from the CLI. - type: string - format: uri - release_channel: - deprecated: true - type: 'null' - postgres_engine: - deprecated: true - type: 'null' - high_availability: - description: '[Experimental] Whether to enable high availability for the project.' - type: boolean - required: - - db_pass - - name - - organization_slug - example: - db_pass: correct-horse-battery-staple - name: acme-prod - organization_slug: tsrqponmlkjihgfedcba - region: us-east-1 - additionalProperties: false - V1ProjectResponse_Output: - type: object - properties: - id: - type: string - deprecated: true - description: 'Deprecated: Use `ref` instead.' - ref: - type: string - minLength: 20 - maxLength: 20 - pattern: ^[a-z]+$ - description: Project ref - example: abcdefghijklmnopqrst - organization_id: - type: string - description: 'Deprecated: Use `organization_slug` instead.' - deprecated: true - organization_slug: - type: string - pattern: ^[\w-]+$ - description: Organization slug - example: tsrqponmlkjihgfedcba - name: - type: string - description: Name of your project - region: - type: string - description: Region of your project - created_at: - type: string - description: Creation timestamp - status: - type: string - enum: - - INACTIVE - - ACTIVE_HEALTHY - - ACTIVE_UNHEALTHY - - COMING_UP - - UNKNOWN - - GOING_DOWN - - INIT_FAILED - - REMOVED - - RESTORING - - UPGRADING - - PAUSING - - RESTORE_FAILED - - RESTARTING - - PAUSE_FAILED - - RESIZING - required: - - id - - ref - - organization_id - - organization_slug - - name - - region - - created_at - - status - RegionsInfo_Output: - type: object - properties: - recommendations: - type: object - properties: - smartGroup: - type: object - properties: - name: - type: string - code: - type: string - enum: - - americas - - emea - - apac - type: - type: string - enum: - - smartGroup - required: - - name - - code - - type - specific: - type: array - items: - type: object - properties: - name: - type: string - code: - type: string - enum: - - us-east-1 - - us-east-2 - - us-west-1 - - us-west-2 - - ap-southeast-1 - - ap-northeast-1 - - ap-northeast-2 - - ap-east-1 - - ap-southeast-2 - - eu-west-1 - - eu-west-2 - - eu-west-3 - - eu-north-1 - - eu-central-1 - - eu-central-2 - - ca-central-1 - - ap-south-1 - - sa-east-1 - type: - type: string - enum: - - specific - provider: - type: string - enum: - - AWS - - AWS_K8S - - AWS_NIMBUS - status: - type: string - enum: - - capacity - - other - required: - - name - - code - - type - - provider - required: - - smartGroup - - specific - all: - type: object - properties: - smartGroup: - type: array - items: - type: object - properties: - name: - type: string - code: - type: string - enum: - - americas - - emea - - apac - type: - type: string - enum: - - smartGroup - required: - - name - - code - - type - specific: - type: array - items: - type: object - properties: - name: - type: string - code: - type: string - enum: - - us-east-1 - - us-east-2 - - us-west-1 - - us-west-2 - - ap-southeast-1 - - ap-northeast-1 - - ap-northeast-2 - - ap-east-1 - - ap-southeast-2 - - eu-west-1 - - eu-west-2 - - eu-west-3 - - eu-north-1 - - eu-central-1 - - eu-central-2 - - ca-central-1 - - ap-south-1 - - sa-east-1 - type: - type: string - enum: - - specific - provider: - type: string - enum: - - AWS - - AWS_K8S - - AWS_NIMBUS - status: - type: string - enum: - - capacity - - other - required: - - name - - code - - type - - provider - required: - - smartGroup - - specific - required: - - recommendations - - all - OrganizationResponseV1_Output: - type: object - properties: - id: - type: string - description: 'Deprecated: Use `slug` instead.' - deprecated: true - slug: - type: string - pattern: ^[\w-]+$ - description: Organization slug - example: tsrqponmlkjihgfedcba - name: - type: string - required: - - id - - slug - - name - CreateOrganizationV1: - type: object - properties: - name: - type: string - maxLength: 256 - required: - - name - example: - name: Acme - additionalProperties: false - OAuthTokenBody: - type: object - properties: - grant_type: - type: string - enum: - - authorization_code - - refresh_token - - urn:ietf:params:oauth:grant-type:jwt-bearer - client_id: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - client_secret: - type: string - code: - type: string - code_verifier: - type: string - redirect_uri: - type: string - refresh_token: - type: string - assertion: - description: >- - IDJAG assertion JWT for - grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer. Beta - - available on Team and Enterprise plans only. - type: string - resource: - description: Resource indicator for MCP (Model Context Protocol) clients - type: string - format: uri - scope: - type: string - example: - grant_type: authorization_code - client_id: 66666666-6666-4666-8666-666666666666 - client_secret: sb_secret_live_example_9f4d3a206b2e4a7e8c91 - code: oauth_code_9f4d3a206b2e4a7e8c91 - code_verifier: qW0Z6d9pQnW0mL1dK1q9wFq6Yz2nV5rA8jT3mP7sH4c - redirect_uri: https://app.acme.com/auth/callback - scope: projects:read projects:write - additionalProperties: false - OAuthTokenResponse_Output: - type: object - properties: - access_token: - type: string - refresh_token: - description: >- - The `urn:ietf:params:oauth:grant-type:jwt-bearer` grant type issues - access tokens only, no refresh token is returned and the token - cannot be revoked via `/v1/oauth/revoke`. - type: string - expires_in: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - token_type: - type: string - enum: - - Bearer - required: - - access_token - - expires_in - - token_type - OAuthRevokeTokenBody: - type: object - properties: - client_id: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - client_secret: - type: string - refresh_token: - type: string - required: - - client_id - - client_secret - - refresh_token - example: - client_id: 66666666-6666-4666-8666-666666666666 - client_secret: sb_secret_live_example_9f4d3a206b2e4a7e8c91 - refresh_token: oauth_refresh_9f4d3a206b2e4a7e8c91 - additionalProperties: false - SnippetList_Output: - type: object - properties: - data: - type: array - items: - type: object - properties: - id: - type: string - inserted_at: - type: string - updated_at: - type: string - type: - type: string - enum: - - sql - visibility: - type: string - enum: - - user - - project - - org - - public - name: - type: string - description: - type: string - nullable: true - project: - type: object - properties: - id: - type: number - name: - type: string - required: - - id - - name - owner: - type: object - properties: - id: - type: number - username: - type: string - required: - - id - - username - updated_by: - type: object - properties: - id: - type: number - username: - type: string - required: - - id - - username - favorite: - type: boolean - required: - - id - - inserted_at - - updated_at - - type - - visibility - - name - - description - - project - - owner - - updated_by - - favorite - cursor: - type: string - required: - - data - SnippetResponse_Output: - type: object - properties: - id: - type: string - inserted_at: - type: string - updated_at: - type: string - type: - type: string - enum: - - sql - visibility: - type: string - enum: - - user - - project - - org - - public - name: - type: string - description: - type: string - nullable: true - project: - type: object - properties: - id: - type: number - name: - type: string - required: - - id - - name - owner: - type: object - properties: - id: - type: number - username: - type: string - required: - - id - - username - updated_by: - type: object - properties: - id: - type: number - username: - type: string - required: - - id - - username - favorite: - type: boolean - content: - type: object - properties: - favorite: - deprecated: true - description: 'Deprecated: Rely on root-level favorite property instead.' - type: boolean - schema_version: - type: string - sql: - type: string - required: - - sql - required: - - id - - inserted_at - - updated_at - - type - - visibility - - name - - description - - project - - owner - - updated_by - - favorite - - content - V1ProfileResponse_Output: - type: object - properties: - gotrue_id: - type: string - primary_email: - type: string - username: - type: string - required: - - gotrue_id - - primary_email - - username - ListActionRunResponse_Output: - type: array - items: - type: object - properties: - id: - type: string - branch_id: - type: string - run_steps: - type: array - items: - type: object - properties: - name: - type: string - enum: - - clone - - pull - - health - - configure - - migrate - - seed - - deploy - status: - type: string - enum: - - CREATED - - DEAD - - EXITED - - PAUSED - - REMOVING - - RESTARTING - - RUNNING - created_at: - type: string - updated_at: - type: string - required: - - name - - status - - created_at - - updated_at - git_config: - nullable: true - workdir: - type: string - nullable: true - check_run_id: - type: number - nullable: true - created_at: - type: string - updated_at: - type: string - required: - - id - - branch_id - - run_steps - - workdir - - check_run_id - - created_at - - updated_at - ActionRunResponse_Output: - type: object - properties: - id: - type: string - branch_id: - type: string - run_steps: - type: array - items: - type: object - properties: - name: - type: string - enum: - - clone - - pull - - health - - configure - - migrate - - seed - - deploy - status: - type: string - enum: - - CREATED - - DEAD - - EXITED - - PAUSED - - REMOVING - - RESTARTING - - RUNNING - created_at: - type: string - updated_at: - type: string - required: - - name - - status - - created_at - - updated_at - git_config: - nullable: true - workdir: - type: string - nullable: true - check_run_id: - type: number - nullable: true - created_at: - type: string - updated_at: - type: string - required: - - id - - branch_id - - run_steps - - workdir - - check_run_id - - created_at - - updated_at - UpdateRunStatusBody: - type: object - properties: - clone: - type: string - enum: - - CREATED - - DEAD - - EXITED - - PAUSED - - REMOVING - - RESTARTING - - RUNNING - pull: - type: string - enum: - - CREATED - - DEAD - - EXITED - - PAUSED - - REMOVING - - RESTARTING - - RUNNING - health: - type: string - enum: - - CREATED - - DEAD - - EXITED - - PAUSED - - REMOVING - - RESTARTING - - RUNNING - configure: - type: string - enum: - - CREATED - - DEAD - - EXITED - - PAUSED - - REMOVING - - RESTARTING - - RUNNING - migrate: - type: string - enum: - - CREATED - - DEAD - - EXITED - - PAUSED - - REMOVING - - RESTARTING - - RUNNING - seed: - type: string - enum: - - CREATED - - DEAD - - EXITED - - PAUSED - - REMOVING - - RESTARTING - - RUNNING - deploy: - type: string - enum: - - CREATED - - DEAD - - EXITED - - PAUSED - - REMOVING - - RESTARTING - - RUNNING - example: - clone: RUNNING - configure: RUNNING - migrate: RUNNING - deploy: CREATED - UpdateRunStatusResponse_Output: - type: object - properties: - message: - type: string - enum: - - ok - required: - - message - ApiKeyResponse_Output: - type: object - properties: - api_key: - type: string - nullable: true - id: - type: string - nullable: true - type: - type: string - enum: - - legacy - - publishable - - secret - - null - nullable: true - prefix: - type: string - nullable: true - name: - type: string - description: - type: string - nullable: true - hash: - type: string - nullable: true - secret_jwt_template: - type: object - propertyNames: - type: string - additionalProperties: {} - nullable: true - inserted_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - nullable: true - updated_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - nullable: true - required: - - name - LegacyApiKeysResponse_Output: - type: object - properties: - enabled: - type: boolean - required: - - enabled - CreateApiKeyBody: - type: object - properties: - type: - type: string - enum: - - publishable - - secret - name: - type: string - minLength: 4 - maxLength: 64 - pattern: ^[a-z_][a-z0-9_]+$ - description: - type: string - nullable: true - secret_jwt_template: - type: object - propertyNames: - type: string - additionalProperties: {} - nullable: true - required: - - type - - name - example: - type: secret - name: ci_secret_key - description: CI deploy key - UpdateApiKeyBody: - type: object - properties: - name: - type: string - minLength: 4 - maxLength: 64 - pattern: ^[a-z_][a-z0-9_]+$ - description: - type: string - nullable: true - secret_jwt_template: - type: object - propertyNames: - type: string - additionalProperties: {} - nullable: true - example: - name: ci_secret_key_rotated - description: Rotated after March release - CreateBranchBody: - type: object - properties: - branch_name: - type: string - minLength: 1 - git_branch: - type: string - is_default: - type: boolean - persistent: - type: boolean - region: - type: string - desired_instance_size: - type: string - enum: - - pico - - nano - - micro - - small - - medium - - large - - xlarge - - 2xlarge - - 4xlarge - - 8xlarge - - 12xlarge - - 16xlarge - - 24xlarge - - 24xlarge_optimized_memory - - 24xlarge_optimized_cpu - - 24xlarge_high_memory - - 48xlarge - - 48xlarge_optimized_memory - - 48xlarge_optimized_cpu - - 48xlarge_high_memory - release_channel: - type: string - enum: - - internal - - alpha - - beta - - ga - - withdrawn - - preview - description: Release channel. If not provided, GA will be used. - postgres_engine: - type: string - enum: - - '15' - - '17' - - 17-oriole - description: >- - Postgres engine version. If not provided, the latest version will be - used. - secrets: - type: object - additionalProperties: - type: string - with_data: - type: boolean - notify_url: - type: string - format: uri - description: HTTP endpoint to receive branch status updates. - required: - - branch_name - example: - branch_name: preview-login-page - git_branch: feature/login-page - persistent: true - with_data: false - notify_url: https://example.com/webhooks/branches - JsonValue_Output: - description: Any JSON-serializable value - anyOf: - - anyOf: - - type: string - - type: number - - type: boolean - nullable: true - - type: array - items: - $ref: '#/components/schemas/JsonValue_Output' - - type: object - additionalProperties: - $ref: '#/components/schemas/JsonValue_Output' - UpdateCustomHostnameResponse_Output: - type: object - properties: - status: - type: string - enum: - - 1_not_started - - 2_initiated - - 3_challenge_verified - - 4_origin_setup_completed - - 5_services_reconfigured - custom_hostname: - type: string - data: - type: object - properties: - success: - type: boolean - errors: - type: array - items: - $ref: '#/components/schemas/JsonValue_Output' - messages: - type: array - items: - $ref: '#/components/schemas/JsonValue_Output' - result: - type: object - properties: - id: - type: string - hostname: - type: string - ssl: - type: object - properties: - status: - type: string - validation_records: - type: array - items: - type: object - properties: - txt_name: - type: string - txt_value: - type: string - validation_errors: - type: array - items: - type: object - properties: - message: - type: string - required: - - message - ownership_verification: - type: object - properties: - type: - type: string - name: - type: string - value: - type: string - custom_origin_server: - type: string - verification_errors: - type: array - items: - type: string - status: - type: string - required: - - id - - hostname - required: - - success - - errors - - messages - - result - required: - - status - - data - UpdateCustomHostnameBody: - type: object - properties: - custom_hostname: - type: string - minLength: 1 - maxLength: 253 - required: - - custom_hostname - example: - custom_hostname: docs.example.com - JitAccessRequestRequest: - type: object - properties: - state: - type: string - enum: - - enabled - - disabled - required: - - state - example: - state: enabled - NetworkBanResponse_Output: - type: object - properties: - banned_ipv4_addresses: - type: array - items: - type: string - required: - - banned_ipv4_addresses - NetworkBanResponseEnriched_Output: - type: object - properties: - banned_ipv4_addresses: - type: array - items: - type: object - properties: - banned_address: - type: string - identifier: - type: string - type: - type: string - required: - - banned_address - - identifier - - type - required: - - banned_ipv4_addresses - RemoveNetworkBanRequest: - type: object - properties: - ipv4_addresses: - type: array - items: - type: string - description: List of IP addresses to unban. - requester_ip: - default: false - description: Include requester's public IP in the list of addresses to unban. - type: boolean - identifier: - type: string - required: - - ipv4_addresses - example: - ipv4_addresses: - - 203.0.113.10 - requester_ip: false - NetworkRestrictionsResponse_Output: - type: object - properties: - entitlement: - type: string - enum: - - disallowed - - allowed - config: - type: object - properties: - dbAllowedCidrs: - type: array - items: - type: string - dbAllowedCidrsV6: - type: array - items: - type: string - example: - dbAllowedCidrs: - - 203.0.113.0/24 - dbAllowedCidrsV6: - - 2001:db8::/32 - description: >- - At any given point in time, this is the config that the user has - requested be applied to their project. The `status` field indicates - if it has been applied to the project, or is pending. When an - updated config is received, the applied config is moved to - `old_config`. - old_config: - description: >- - Populated when a new config has been received, but not registered as - successfully applied to a project. - type: object - properties: - dbAllowedCidrs: - type: array - items: - type: string - dbAllowedCidrsV6: - type: array - items: - type: string - example: - dbAllowedCidrs: - - 203.0.113.0/24 - dbAllowedCidrsV6: - - 2001:db8::/32 - status: - type: string - enum: - - stored - - applied - updated_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - applied_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - required: - - entitlement - - config - - status - NetworkRestrictionsRequest: - type: object - properties: - dbAllowedCidrs: - type: array - items: - type: string - dbAllowedCidrsV6: - type: array - items: - type: string - example: - dbAllowedCidrs: - - 203.0.113.0/24 - dbAllowedCidrsV6: - - 2001:db8::/32 - NetworkRestrictionsPatchRequest: - type: object - properties: - add: - type: object - properties: - dbAllowedCidrs: - type: array - items: - type: string - dbAllowedCidrsV6: - type: array - items: - type: string - remove: - type: object - properties: - dbAllowedCidrs: - type: array - items: - type: string - dbAllowedCidrsV6: - type: array - items: - type: string - example: - add: - dbAllowedCidrs: - - 203.0.113.0/24 - remove: - dbAllowedCidrs: - - 198.51.100.0/24 - NetworkRestrictionsV2Response_Output: - type: object - properties: - entitlement: - type: string - enum: - - disallowed - - allowed - config: - type: object - properties: - dbAllowedCidrs: - type: array - items: - type: object - properties: - address: - type: string - type: - type: string - enum: - - v4 - - v6 - required: - - address - - type - description: >- - At any given point in time, this is the config that the user has - requested be applied to their project. The `status` field indicates - if it has been applied to the project, or is pending. When an - updated config is received, the applied config is moved to - `old_config`. - old_config: - description: >- - Populated when a new config has been received, but not registered as - successfully applied to a project. - type: object - properties: - dbAllowedCidrs: - type: array - items: - type: object - properties: - address: - type: string - type: - type: string - enum: - - v4 - - v6 - required: - - address - - type - updated_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - applied_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - status: - type: string - enum: - - stored - - applied - required: - - entitlement - - config - - status - PgsodiumConfigResponse_Output: - type: object - properties: - root_key: - type: string - description: 'The pgsodium root key: 32 bytes, hex-encoded (64 characters).' - required: - - root_key - example: - root_key: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - UpdatePgsodiumConfigBody: - type: object - properties: - root_key: - type: string - description: 'The pgsodium root key: 32 bytes, hex-encoded (64 characters).' - required: - - root_key - example: - root_key: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - PostgrestConfigWithJWTSecretResponse_Output: - type: object - properties: - db_schema: - type: string - max_rows: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - db_extra_search_path: - type: string - db_pool: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - description: >- - If `null`, the value is automatically configured based on compute - size. - nullable: true - db_pool_acquisition_timeout: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - description: If `null`, the value is automatically configured to 10. - nullable: true - jwt_secret: - type: string - required: - - db_schema - - max_rows - - db_extra_search_path - - db_pool - - db_pool_acquisition_timeout - V1UpdatePostgrestConfigBody: - type: object - properties: - db_extra_search_path: - type: string - db_schema: - type: string - max_rows: - type: integer - minimum: 0 - maximum: 1000000 - db_pool: - type: integer - minimum: 0 - maximum: 1000 - db_pool_acquisition_timeout: - type: integer - minimum: 0 - maximum: 60 - example: - db_schema: public,storage - db_pool: 20 - max_rows: 1000 - V1PostgrestConfigResponse_Output: - type: object - properties: - db_schema: - type: string - max_rows: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - db_extra_search_path: - type: string - db_pool: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - description: >- - If `null`, the value is automatically configured based on compute - size. - nullable: true - db_pool_acquisition_timeout: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - description: If `null`, the value is automatically configured to 10. - nullable: true - required: - - db_schema - - max_rows - - db_extra_search_path - - db_pool - - db_pool_acquisition_timeout - V1ProjectRefResponse_Output: - type: object - properties: - id: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - ref: - type: string - name: - type: string - required: - - id - - ref - - name - V1UpdateProjectBody: - type: object - properties: - name: - type: string - minLength: 1 - maxLength: 256 - required: - - name - example: - name: Acme Platform - SecretResponse_Output: - type: object - properties: - name: - type: string - value: - type: string - updated_at: - type: string - required: - - name - - value - CreateSecretBody: - maxItems: 100 - type: array - items: - type: object - properties: - name: - type: string - maxLength: 256 - pattern: ^(?!SUPABASE_).* - description: Secret name must not start with the SUPABASE_ prefix. - value: - type: string - maxLength: 24576 - required: - - name - - value - example: - - name: OPENAI_API_KEY - value: sk-example-secret - - name: STRIPE_WEBHOOK_SECRET - value: whsec_example - DeleteSecretsBody: - type: array - items: - type: string - example: - - OPENAI_API_KEY - SslEnforcementResponse_Output: - type: object - properties: - currentConfig: - type: object - properties: - database: - type: boolean - required: - - database - appliedSuccessfully: - type: boolean - required: - - currentConfig - - appliedSuccessfully - SslEnforcementRequest: - type: object - properties: - requestedConfig: - type: object - properties: - database: - type: boolean - required: - - database - required: - - requestedConfig - example: - requestedConfig: - database: true - TypescriptResponse_Output: - type: object - properties: - types: - type: string - required: - - types - VanitySubdomainConfigResponse_Output: - type: object - properties: - status: - type: string - enum: - - not-used - - custom-domain-used - - active - custom_domain: - type: string - minLength: 1 - required: - - status - PlanGateErrorBody: - type: object - properties: - message: - type: string - description: Human-readable explanation of the plan gate - error: - description: >- - Present on entitlement denials. Other errors with this status code - (validation, billing state) carry only message. - type: object - properties: - code: - type: string - description: Machine-readable marker for plan-gated denials - enum: - - entitlement_required - feature: - type: string - description: Entitlement feature key that failed the check - upgrade_url: - description: >- - Billing page URL for the organization, present when the org is - resolvable - type: string - required: - - code - - feature - required: - - message - VanitySubdomainBody: - type: object - properties: - vanity_subdomain: - type: string - maxLength: 63 - required: - - vanity_subdomain - example: - vanity_subdomain: acme-prod - SubdomainAvailabilityResponse_Output: - type: object - properties: - available: - type: boolean - required: - - available - ActivateVanitySubdomainResponse_Output: - type: object - properties: - custom_domain: - type: string - required: - - custom_domain - UpgradeDatabaseBody: - type: object - properties: - target_version: - type: string - release_channel: - type: string - enum: - - internal - - alpha - - beta - - ga - - withdrawn - - preview - required: - - target_version - example: - target_version: '17' - release_channel: ga - ProjectUpgradeInitiateResponse_Output: - type: object - properties: - tracking_id: - type: string - required: - - tracking_id - ProjectUpgradeEligibilityResponse_Output: - type: object - properties: - eligible: - type: boolean - current_app_version: - type: string - current_app_version_release_channel: - type: string - enum: - - internal - - alpha - - beta - - ga - - withdrawn - - preview - latest_app_version: - type: string - target_upgrade_versions: - type: array - items: - type: object - properties: - postgres_version: - type: string - enum: - - '13' - - '14' - - '15' - - '17' - - 17-oriole - release_channel: - type: string - enum: - - internal - - alpha - - beta - - ga - - withdrawn - - preview - app_version: - type: string - required: - - postgres_version - - release_channel - - app_version - duration_estimate_hours: - type: number - legacy_auth_custom_roles: - type: array - items: - type: string - objects_to_be_dropped: - type: array - items: - type: string - deprecated: true - description: Use validation_errors instead. - unsupported_extensions: - type: array - items: - type: string - deprecated: true - description: Use validation_errors instead. - user_defined_objects_in_internal_schemas: - type: array - items: - type: string - deprecated: true - description: Use validation_errors instead. - validation_errors: - type: array - items: - anyOf: - - type: object - properties: - type: - type: string - enum: - - objects_depending_on_pg_cron - dependents: - type: array - items: - type: string - required: - - type - - dependents - - type: object - properties: - type: - type: string - enum: - - indexes_referencing_ll_to_earth - schema_name: - type: string - table_name: - type: string - index_name: - type: string - required: - - type - - schema_name - - table_name - - index_name - - type: object - properties: - type: - type: string - enum: - - function_using_obsolete_lang - schema_name: - type: string - function_name: - type: string - lang_name: - type: string - required: - - type - - schema_name - - function_name - - lang_name - - type: object - properties: - type: - type: string - enum: - - unsupported_extension - extension_name: - type: string - required: - - type - - extension_name - - type: object - properties: - type: - type: string - enum: - - unsupported_fdw_handler - fdw_name: - type: string - fdw_handler_name: - type: string - required: - - type - - fdw_name - - fdw_handler_name - - type: object - properties: - type: - type: string - enum: - - unlogged_table_with_persistent_sequence - schema_name: - type: string - table_name: - type: string - sequence_name: - type: string - required: - - type - - schema_name - - table_name - - sequence_name - - type: object - properties: - type: - type: string - enum: - - user_defined_objects_in_internal_schemas - obj_type: - anyOf: - - type: string - enum: - - table - - type: string - enum: - - function - schema_name: - type: string - obj_name: - type: string - required: - - type - - obj_type - - schema_name - - obj_name - - type: object - properties: - type: - type: string - enum: - - active_replication_slot - slot_name: - type: string - required: - - type - - slot_name - - type: object - properties: - type: - type: string - enum: - - x86_architecture - required: - - type - - type: object - properties: - type: - type: string - enum: - - project_hibernating - required: - - type - warnings: - type: array - items: - oneOf: - - type: object - properties: - type: - type: string - enum: - - pg_graphql_introspection_change - required: - - type - - type: object - properties: - type: - type: string - enum: - - ltree_reindex_required - required: - - type - - type: object - properties: - type: - type: string - enum: - - operator_estimator_gate - required: - - type - - type: object - properties: - type: - type: string - enum: - - btree_gist_nan_reindex - required: - - type - required: - - eligible - - current_app_version - - current_app_version_release_channel - - latest_app_version - - target_upgrade_versions - - duration_estimate_hours - - legacy_auth_custom_roles - - objects_to_be_dropped - - unsupported_extensions - - user_defined_objects_in_internal_schemas - - validation_errors - - warnings - DatabaseUpgradeStatusResponse_Output: - type: object - properties: - databaseUpgradeStatus: - type: object - properties: - initiated_at: - type: string - latest_status_at: - type: string - target_version: - type: string - error: - type: string - enum: - - 1_upgraded_instance_launch_failed - - 2_volume_detachchment_from_upgraded_instance_failed - - 3_volume_attachment_to_original_instance_failed - - 4_data_upgrade_initiation_failed - - 5_data_upgrade_completion_failed - - 6_volume_detachchment_from_original_instance_failed - - 7_volume_attachment_to_upgraded_instance_failed - - 8_upgrade_completion_failed - - 9_post_physical_backup_failed - progress: - type: string - enum: - - 0_requested - - 1_started - - 2_launched_upgraded_instance - - 3_detached_volume_from_upgraded_instance - - 4_attached_volume_to_original_instance - - 5_initiated_data_upgrade - - 6_completed_data_upgrade - - 7_detached_volume_from_original_instance - - 8_attached_volume_to_upgraded_instance - - 9_completed_upgrade - - 10_completed_post_physical_backup - status: - type: number - required: - - initiated_at - - latest_status_at - - target_version - - status - nullable: true - required: - - databaseUpgradeStatus - ReadOnlyStatusResponse_Output: - type: object - properties: - enabled: - type: boolean - override_enabled: - type: boolean - override_active_until: - type: string - required: - - enabled - - override_enabled - - override_active_until - SetUpReadReplicaBody: - type: object - properties: - read_replica_region: - type: string - enum: - - us-east-1 - - us-east-2 - - us-west-1 - - us-west-2 - - ap-east-1 - - ap-southeast-1 - - ap-northeast-1 - - ap-northeast-2 - - ap-southeast-2 - - eu-west-1 - - eu-west-2 - - eu-west-3 - - eu-north-1 - - eu-central-1 - - eu-central-2 - - ca-central-1 - - ap-south-1 - - sa-east-1 - description: Region you want your read replica to reside in - required: - - read_replica_region - example: - read_replica_region: us-west-1 - RemoveReadReplicaBody: - type: object - properties: - database_identifier: - type: string - required: - - database_identifier - example: - database_identifier: abcdefghijklmnopqrst-rr-us-west-1-abcde - V1ServiceHealthResponse_Output: - type: object - properties: - name: - type: string - enum: - - auth - - db - - db_postgres_user - - pooler - - realtime - - rest - - storage - - pg_bouncer - healthy: - type: boolean - deprecated: true - description: Deprecated. Use `status` instead. - status: - type: string - enum: - - COMING_UP - - ACTIVE_HEALTHY - - UNHEALTHY - info: - anyOf: - - type: object - properties: - name: - type: string - enum: - - GoTrue - version: - type: string - description: - type: string - required: - - name - - version - - description - - type: object - properties: - healthy: - type: boolean - deprecated: true - description: Deprecated. Use `status` instead. - db_connected: - type: boolean - replication_connected: - type: boolean - connected_cluster: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - required: - - healthy - - db_connected - - replication_connected - - connected_cluster - - type: object - properties: - db_schema: - type: string - required: - - db_schema - error: - type: string - required: - - name - - healthy - - status - SigningKeyResponse_Output: - type: object - properties: - id: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - algorithm: - type: string - enum: - - EdDSA - - ES256 - - RS256 - - HS256 - status: - type: string - enum: - - in_use - - previously_used - - revoked - - standby - public_jwk: - nullable: true - created_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - updated_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - required: - - id - - algorithm - - status - - public_jwk - - created_at - - updated_at - CreateSigningKeyBody: - type: object - properties: - algorithm: - type: string - enum: - - EdDSA - - ES256 - - RS256 - - HS256 - status: - type: string - enum: - - in_use - - standby - private_jwk: - oneOf: - - type: object - properties: - kid: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - use: - type: string - enum: - - sig - key_ops: - minItems: 2 - maxItems: 2 - type: array - items: - type: string - enum: - - sign - - verify - ext: - type: boolean - enum: - - true - kty: - type: string - enum: - - RSA - alg: - type: string - enum: - - RS256 - 'n': - type: string - e: - type: string - enum: - - AQAB - d: - type: string - p: - type: string - q: - type: string - dp: - type: string - dq: - type: string - qi: - type: string - required: - - kty - - 'n' - - e - - d - - p - - q - - dp - - dq - - qi - additionalProperties: false - - type: object - properties: - kid: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - use: - type: string - enum: - - sig - key_ops: - minItems: 2 - maxItems: 2 - type: array - items: - type: string - enum: - - sign - - verify - ext: - type: boolean - enum: - - true - kty: - type: string - enum: - - EC - alg: - type: string - enum: - - ES256 - crv: - type: string - enum: - - P-256 - x: - type: string - 'y': - type: string - d: - type: string - required: - - kty - - crv - - x - - 'y' - - d - additionalProperties: false - - type: object - properties: - kid: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - use: - type: string - enum: - - sig - key_ops: - minItems: 2 - maxItems: 2 - type: array - items: - type: string - enum: - - sign - - verify - ext: - type: boolean - enum: - - true - kty: - type: string - enum: - - OKP - alg: - type: string - enum: - - EdDSA - crv: - type: string - enum: - - Ed25519 - x: - type: string - d: - type: string - required: - - kty - - crv - - x - - d - additionalProperties: false - - type: object - properties: - kid: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - use: - type: string - enum: - - sig - key_ops: - minItems: 2 - maxItems: 2 - type: array - items: - type: string - enum: - - sign - - verify - ext: - type: boolean - enum: - - true - kty: - type: string - enum: - - oct - alg: - type: string - enum: - - HS256 - k: - type: string - minLength: 16 - required: - - kty - - k - additionalProperties: false - required: - - algorithm - example: - algorithm: RS256 - status: standby - additionalProperties: false - SigningKeysResponse_Output: - type: object - properties: - keys: - type: array - items: - type: object - properties: - id: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - algorithm: - type: string - enum: - - EdDSA - - ES256 - - RS256 - - HS256 - status: - type: string - enum: - - in_use - - previously_used - - revoked - - standby - public_jwk: - nullable: true - created_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - updated_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - required: - - id - - algorithm - - status - - public_jwk - - created_at - - updated_at - required: - - keys - UpdateSigningKeyBody: - type: object - properties: - status: - type: string - enum: - - in_use - - previously_used - - revoked - - standby - required: - - status - example: - status: standby - additionalProperties: false - AuthConfigResponse_Output: - type: object - properties: - api_max_request_duration: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - db_max_pool_size: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - db_max_pool_size_unit: - type: string - enum: - - connections - - percent - - null - nullable: true - disable_signup: - type: boolean - nullable: true - external_anonymous_users_enabled: - type: boolean - nullable: true - external_apple_additional_client_ids: - type: string - nullable: true - external_apple_client_id: - type: string - nullable: true - external_apple_email_optional: - type: boolean - nullable: true - external_apple_enabled: - type: boolean - nullable: true - external_apple_secret: - type: string - nullable: true - external_azure_client_id: - type: string - nullable: true - external_azure_email_optional: - type: boolean - nullable: true - external_azure_enabled: - type: boolean - nullable: true - external_azure_secret: - type: string - nullable: true - external_azure_url: - type: string - nullable: true - external_bitbucket_client_id: - type: string - nullable: true - external_bitbucket_email_optional: - type: boolean - nullable: true - external_bitbucket_enabled: - type: boolean - nullable: true - external_bitbucket_secret: - type: string - nullable: true - external_discord_client_id: - type: string - nullable: true - external_discord_email_optional: - type: boolean - nullable: true - external_discord_enabled: - type: boolean - nullable: true - external_discord_secret: - type: string - nullable: true - external_email_enabled: - type: boolean - nullable: true - external_facebook_client_id: - type: string - nullable: true - external_facebook_email_optional: - type: boolean - nullable: true - external_facebook_enabled: - type: boolean - nullable: true - external_facebook_secret: - type: string - nullable: true - external_figma_client_id: - type: string - nullable: true - external_figma_email_optional: - type: boolean - nullable: true - external_figma_enabled: - type: boolean - nullable: true - external_figma_secret: - type: string - nullable: true - external_github_client_id: - type: string - nullable: true - external_github_email_optional: - type: boolean - nullable: true - external_github_enabled: - type: boolean - nullable: true - external_github_secret: - type: string - nullable: true - external_gitlab_client_id: - type: string - nullable: true - external_gitlab_email_optional: - type: boolean - nullable: true - external_gitlab_enabled: - type: boolean - nullable: true - external_gitlab_secret: - type: string - nullable: true - external_gitlab_url: - type: string - nullable: true - external_google_additional_client_ids: - type: string - nullable: true - external_google_client_id: - type: string - nullable: true - external_google_email_optional: - type: boolean - nullable: true - external_google_enabled: - type: boolean - nullable: true - external_google_secret: - type: string - nullable: true - external_google_skip_nonce_check: - type: boolean - nullable: true - external_kakao_client_id: - type: string - nullable: true - external_kakao_email_optional: - type: boolean - nullable: true - external_kakao_enabled: - type: boolean - nullable: true - external_kakao_secret: - type: string - nullable: true - external_keycloak_client_id: - type: string - nullable: true - external_keycloak_email_optional: - type: boolean - nullable: true - external_keycloak_enabled: - type: boolean - nullable: true - external_keycloak_secret: - type: string - nullable: true - external_keycloak_url: - type: string - nullable: true - external_linkedin_oidc_client_id: - type: string - nullable: true - external_linkedin_oidc_email_optional: - type: boolean - nullable: true - external_linkedin_oidc_enabled: - type: boolean - nullable: true - external_linkedin_oidc_secret: - type: string - nullable: true - external_slack_oidc_client_id: - type: string - nullable: true - external_slack_oidc_email_optional: - type: boolean - nullable: true - external_slack_oidc_enabled: - type: boolean - nullable: true - external_slack_oidc_secret: - type: string - nullable: true - external_notion_client_id: - type: string - nullable: true - external_notion_email_optional: - type: boolean - nullable: true - external_notion_enabled: - type: boolean - nullable: true - external_notion_secret: - type: string - nullable: true - external_phone_enabled: - type: boolean - nullable: true - external_slack_client_id: - type: string - nullable: true - external_slack_email_optional: - type: boolean - nullable: true - external_slack_enabled: - type: boolean - nullable: true - external_slack_secret: - type: string - nullable: true - external_spotify_client_id: - type: string - nullable: true - external_spotify_email_optional: - type: boolean - nullable: true - external_spotify_enabled: - type: boolean - nullable: true - external_spotify_secret: - type: string - nullable: true - external_twitch_client_id: - type: string - nullable: true - external_twitch_email_optional: - type: boolean - nullable: true - external_twitch_enabled: - type: boolean - nullable: true - external_twitch_secret: - type: string - nullable: true - external_twitter_client_id: - type: string - nullable: true - external_twitter_email_optional: - type: boolean - nullable: true - external_twitter_enabled: - type: boolean - nullable: true - external_twitter_secret: - type: string - nullable: true - external_x_client_id: - type: string - nullable: true - external_x_email_optional: - type: boolean - nullable: true - external_x_enabled: - type: boolean - nullable: true - external_x_secret: - type: string - nullable: true - external_workos_client_id: - type: string - nullable: true - external_workos_enabled: - type: boolean - nullable: true - external_workos_secret: - type: string - nullable: true - external_workos_url: - type: string - nullable: true - external_web3_solana_enabled: - type: boolean - nullable: true - external_web3_ethereum_enabled: - type: boolean - nullable: true - external_zoom_client_id: - type: string - nullable: true - external_zoom_email_optional: - type: boolean - nullable: true - external_zoom_enabled: - type: boolean - nullable: true - external_zoom_secret: - type: string - nullable: true - hook_custom_access_token_enabled: - type: boolean - nullable: true - hook_custom_access_token_uri: - type: string - nullable: true - hook_custom_access_token_secrets: - type: string - nullable: true - hook_mfa_verification_attempt_enabled: - type: boolean - nullable: true - hook_mfa_verification_attempt_uri: - type: string - nullable: true - hook_mfa_verification_attempt_secrets: - type: string - nullable: true - hook_password_verification_attempt_enabled: - type: boolean - nullable: true - hook_password_verification_attempt_uri: - type: string - nullable: true - hook_password_verification_attempt_secrets: - type: string - nullable: true - hook_send_sms_enabled: - type: boolean - nullable: true - hook_send_sms_uri: - type: string - nullable: true - hook_send_sms_secrets: - type: string - nullable: true - hook_send_email_enabled: - type: boolean - nullable: true - hook_send_email_uri: - type: string - nullable: true - hook_send_email_secrets: - type: string - nullable: true - hook_before_user_created_enabled: - type: boolean - nullable: true - hook_before_user_created_uri: - type: string - nullable: true - hook_before_user_created_secrets: - type: string - nullable: true - hook_after_user_created_enabled: - type: boolean - nullable: true - hook_after_user_created_uri: - type: string - nullable: true - hook_after_user_created_secrets: - type: string - nullable: true - jwt_exp: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - mailer_allow_unverified_email_sign_ins: - type: boolean - nullable: true - mailer_autoconfirm: - type: boolean - nullable: true - mailer_otp_exp: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - mailer_otp_length: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - mailer_secure_email_change_enabled: - type: boolean - nullable: true - mailer_subjects_confirmation: - type: string - nullable: true - mailer_subjects_email_change: - type: string - nullable: true - mailer_subjects_invite: - type: string - nullable: true - mailer_subjects_magic_link: - type: string - nullable: true - mailer_subjects_reauthentication: - type: string - nullable: true - mailer_subjects_recovery: - type: string - nullable: true - mailer_subjects_password_changed_notification: - type: string - nullable: true - mailer_subjects_email_changed_notification: - type: string - nullable: true - mailer_subjects_phone_changed_notification: - type: string - nullable: true - mailer_subjects_mfa_factor_enrolled_notification: - type: string - nullable: true - mailer_subjects_mfa_factor_unenrolled_notification: - type: string - nullable: true - mailer_subjects_identity_linked_notification: - type: string - nullable: true - mailer_subjects_identity_unlinked_notification: - type: string - nullable: true - mailer_templates_confirmation_content: - type: string - nullable: true - mailer_templates_email_change_content: - type: string - nullable: true - mailer_templates_invite_content: - type: string - nullable: true - mailer_templates_magic_link_content: - type: string - nullable: true - mailer_templates_reauthentication_content: - type: string - nullable: true - mailer_templates_recovery_content: - type: string - nullable: true - mailer_templates_password_changed_notification_content: - type: string - nullable: true - mailer_templates_email_changed_notification_content: - type: string - nullable: true - mailer_templates_phone_changed_notification_content: - type: string - nullable: true - mailer_templates_mfa_factor_enrolled_notification_content: - type: string - nullable: true - mailer_templates_mfa_factor_unenrolled_notification_content: - type: string - nullable: true - mailer_templates_identity_linked_notification_content: - type: string - nullable: true - mailer_templates_identity_unlinked_notification_content: - type: string - nullable: true - mailer_notifications_password_changed_enabled: - type: boolean - nullable: true - mailer_notifications_email_changed_enabled: - type: boolean - nullable: true - mailer_notifications_phone_changed_enabled: - type: boolean - nullable: true - mailer_notifications_mfa_factor_enrolled_enabled: - type: boolean - nullable: true - mailer_notifications_mfa_factor_unenrolled_enabled: - type: boolean - nullable: true - mailer_notifications_identity_linked_enabled: - type: boolean - nullable: true - mailer_notifications_identity_unlinked_enabled: - type: boolean - nullable: true - mfa_max_enrolled_factors: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - mfa_totp_enroll_enabled: - type: boolean - nullable: true - mfa_totp_verify_enabled: - type: boolean - nullable: true - mfa_phone_enroll_enabled: - type: boolean - nullable: true - mfa_phone_verify_enabled: - type: boolean - nullable: true - mfa_web_authn_enroll_enabled: - type: boolean - nullable: true - mfa_web_authn_verify_enabled: - type: boolean - nullable: true - passkey_enabled: - type: boolean - webauthn_rp_display_name: - type: string - nullable: true - webauthn_rp_id: - type: string - nullable: true - webauthn_rp_origins: - type: string - nullable: true - mfa_phone_otp_length: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - mfa_phone_template: - type: string - nullable: true - mfa_phone_max_frequency: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - nimbus_oauth_client_id: - type: string - nullable: true - nimbus_oauth_email_optional: - type: boolean - nullable: true - nimbus_oauth_client_secret: - type: string - nullable: true - password_hibp_enabled: - type: boolean - nullable: true - password_min_length: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - password_required_characters: - type: string - enum: - - abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789 - - abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789 - - abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\:"|<>?,./`~ - - '' - - null - nullable: true - rate_limit_anonymous_users: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - rate_limit_email_sent: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - rate_limit_sms_sent: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - rate_limit_token_refresh: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - rate_limit_verify: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - rate_limit_otp: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - rate_limit_web3: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - refresh_token_rotation_enabled: - type: boolean - nullable: true - saml_enabled: - type: boolean - nullable: true - saml_external_url: - type: string - nullable: true - saml_allow_encrypted_assertions: - type: boolean - nullable: true - security_sb_forwarded_for_enabled: - type: boolean - nullable: true - security_captcha_enabled: - type: boolean - nullable: true - security_captcha_provider: - type: string - enum: - - turnstile - - hcaptcha - - null - nullable: true - security_captcha_secret: - type: string - nullable: true - security_manual_linking_enabled: - type: boolean - nullable: true - security_refresh_token_reuse_interval: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - security_update_password_require_current_password: - type: boolean - nullable: true - security_update_password_require_reauthentication: - type: boolean - nullable: true - sessions_inactivity_timeout: - type: number - nullable: true - sessions_single_per_user: - type: boolean - nullable: true - sessions_tags: - type: string - nullable: true - sessions_timebox: - type: number - nullable: true - site_url: - type: string - nullable: true - sms_autoconfirm: - type: boolean - nullable: true - sms_max_frequency: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - sms_messagebird_access_key: - type: string - nullable: true - sms_messagebird_originator: - type: string - nullable: true - sms_otp_exp: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - sms_otp_length: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - sms_provider: - type: string - enum: - - messagebird - - textlocal - - twilio - - twilio_verify - - vonage - - null - nullable: true - sms_template: - type: string - nullable: true - sms_test_otp: - type: string - nullable: true - sms_test_otp_valid_until: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - nullable: true - sms_textlocal_api_key: - type: string - nullable: true - sms_textlocal_sender: - type: string - nullable: true - sms_twilio_account_sid: - type: string - nullable: true - sms_twilio_auth_token: - type: string - nullable: true - sms_twilio_content_sid: - type: string - nullable: true - sms_twilio_message_service_sid: - type: string - nullable: true - sms_twilio_verify_account_sid: - type: string - nullable: true - sms_twilio_verify_auth_token: - type: string - nullable: true - sms_twilio_verify_message_service_sid: - type: string - nullable: true - sms_vonage_api_key: - type: string - nullable: true - sms_vonage_api_secret: - type: string - nullable: true - sms_vonage_from: - type: string - nullable: true - smtp_admin_email: - type: string - format: email - pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$ - nullable: true - smtp_host: - type: string - nullable: true - smtp_max_frequency: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - smtp_pass: - type: string - nullable: true - smtp_port: - type: string - nullable: true - smtp_sender_name: - type: string - nullable: true - smtp_user: - type: string - nullable: true - uri_allow_list: - type: string - nullable: true - oauth_server_enabled: - type: boolean - oauth_server_allow_dynamic_registration: - type: boolean - oauth_server_authorization_path: - type: string - nullable: true - custom_oauth_enabled: - type: boolean - custom_oauth_max_providers: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - required: - - api_max_request_duration - - db_max_pool_size - - db_max_pool_size_unit - - disable_signup - - external_anonymous_users_enabled - - external_apple_additional_client_ids - - external_apple_client_id - - external_apple_email_optional - - external_apple_enabled - - external_apple_secret - - external_azure_client_id - - external_azure_email_optional - - external_azure_enabled - - external_azure_secret - - external_azure_url - - external_bitbucket_client_id - - external_bitbucket_email_optional - - external_bitbucket_enabled - - external_bitbucket_secret - - external_discord_client_id - - external_discord_email_optional - - external_discord_enabled - - external_discord_secret - - external_email_enabled - - external_facebook_client_id - - external_facebook_email_optional - - external_facebook_enabled - - external_facebook_secret - - external_figma_client_id - - external_figma_email_optional - - external_figma_enabled - - external_figma_secret - - external_github_client_id - - external_github_email_optional - - external_github_enabled - - external_github_secret - - external_gitlab_client_id - - external_gitlab_email_optional - - external_gitlab_enabled - - external_gitlab_secret - - external_gitlab_url - - external_google_additional_client_ids - - external_google_client_id - - external_google_email_optional - - external_google_enabled - - external_google_secret - - external_google_skip_nonce_check - - external_kakao_client_id - - external_kakao_email_optional - - external_kakao_enabled - - external_kakao_secret - - external_keycloak_client_id - - external_keycloak_email_optional - - external_keycloak_enabled - - external_keycloak_secret - - external_keycloak_url - - external_linkedin_oidc_client_id - - external_linkedin_oidc_email_optional - - external_linkedin_oidc_enabled - - external_linkedin_oidc_secret - - external_slack_oidc_client_id - - external_slack_oidc_email_optional - - external_slack_oidc_enabled - - external_slack_oidc_secret - - external_notion_client_id - - external_notion_email_optional - - external_notion_enabled - - external_notion_secret - - external_phone_enabled - - external_slack_client_id - - external_slack_email_optional - - external_slack_enabled - - external_slack_secret - - external_spotify_client_id - - external_spotify_email_optional - - external_spotify_enabled - - external_spotify_secret - - external_twitch_client_id - - external_twitch_email_optional - - external_twitch_enabled - - external_twitch_secret - - external_twitter_client_id - - external_twitter_email_optional - - external_twitter_enabled - - external_twitter_secret - - external_x_client_id - - external_x_email_optional - - external_x_enabled - - external_x_secret - - external_workos_client_id - - external_workos_enabled - - external_workos_secret - - external_workos_url - - external_web3_solana_enabled - - external_web3_ethereum_enabled - - external_zoom_client_id - - external_zoom_email_optional - - external_zoom_enabled - - external_zoom_secret - - hook_custom_access_token_enabled - - hook_custom_access_token_uri - - hook_custom_access_token_secrets - - hook_mfa_verification_attempt_enabled - - hook_mfa_verification_attempt_uri - - hook_mfa_verification_attempt_secrets - - hook_password_verification_attempt_enabled - - hook_password_verification_attempt_uri - - hook_password_verification_attempt_secrets - - hook_send_sms_enabled - - hook_send_sms_uri - - hook_send_sms_secrets - - hook_send_email_enabled - - hook_send_email_uri - - hook_send_email_secrets - - hook_before_user_created_enabled - - hook_before_user_created_uri - - hook_before_user_created_secrets - - hook_after_user_created_enabled - - hook_after_user_created_uri - - hook_after_user_created_secrets - - jwt_exp - - mailer_allow_unverified_email_sign_ins - - mailer_autoconfirm - - mailer_otp_exp - - mailer_otp_length - - mailer_secure_email_change_enabled - - mailer_subjects_confirmation - - mailer_subjects_email_change - - mailer_subjects_invite - - mailer_subjects_magic_link - - mailer_subjects_reauthentication - - mailer_subjects_recovery - - mailer_subjects_password_changed_notification - - mailer_subjects_email_changed_notification - - mailer_subjects_phone_changed_notification - - mailer_subjects_mfa_factor_enrolled_notification - - mailer_subjects_mfa_factor_unenrolled_notification - - mailer_subjects_identity_linked_notification - - mailer_subjects_identity_unlinked_notification - - mailer_templates_confirmation_content - - mailer_templates_email_change_content - - mailer_templates_invite_content - - mailer_templates_magic_link_content - - mailer_templates_reauthentication_content - - mailer_templates_recovery_content - - mailer_templates_password_changed_notification_content - - mailer_templates_email_changed_notification_content - - mailer_templates_phone_changed_notification_content - - mailer_templates_mfa_factor_enrolled_notification_content - - mailer_templates_mfa_factor_unenrolled_notification_content - - mailer_templates_identity_linked_notification_content - - mailer_templates_identity_unlinked_notification_content - - mailer_notifications_password_changed_enabled - - mailer_notifications_email_changed_enabled - - mailer_notifications_phone_changed_enabled - - mailer_notifications_mfa_factor_enrolled_enabled - - mailer_notifications_mfa_factor_unenrolled_enabled - - mailer_notifications_identity_linked_enabled - - mailer_notifications_identity_unlinked_enabled - - mfa_max_enrolled_factors - - mfa_totp_enroll_enabled - - mfa_totp_verify_enabled - - mfa_phone_enroll_enabled - - mfa_phone_verify_enabled - - mfa_web_authn_enroll_enabled - - mfa_web_authn_verify_enabled - - passkey_enabled - - webauthn_rp_display_name - - webauthn_rp_id - - webauthn_rp_origins - - mfa_phone_otp_length - - mfa_phone_template - - mfa_phone_max_frequency - - nimbus_oauth_client_id - - nimbus_oauth_email_optional - - nimbus_oauth_client_secret - - password_hibp_enabled - - password_min_length - - password_required_characters - - rate_limit_anonymous_users - - rate_limit_email_sent - - rate_limit_sms_sent - - rate_limit_token_refresh - - rate_limit_verify - - rate_limit_otp - - rate_limit_web3 - - refresh_token_rotation_enabled - - saml_enabled - - saml_external_url - - saml_allow_encrypted_assertions - - security_sb_forwarded_for_enabled - - security_captcha_enabled - - security_captcha_provider - - security_captcha_secret - - security_manual_linking_enabled - - security_refresh_token_reuse_interval - - security_update_password_require_current_password - - security_update_password_require_reauthentication - - sessions_inactivity_timeout - - sessions_single_per_user - - sessions_tags - - sessions_timebox - - site_url - - sms_autoconfirm - - sms_max_frequency - - sms_messagebird_access_key - - sms_messagebird_originator - - sms_otp_exp - - sms_otp_length - - sms_provider - - sms_template - - sms_test_otp - - sms_test_otp_valid_until - - sms_textlocal_api_key - - sms_textlocal_sender - - sms_twilio_account_sid - - sms_twilio_auth_token - - sms_twilio_content_sid - - sms_twilio_message_service_sid - - sms_twilio_verify_account_sid - - sms_twilio_verify_auth_token - - sms_twilio_verify_message_service_sid - - sms_vonage_api_key - - sms_vonage_api_secret - - sms_vonage_from - - smtp_admin_email - - smtp_host - - smtp_max_frequency - - smtp_pass - - smtp_port - - smtp_sender_name - - smtp_user - - uri_allow_list - - oauth_server_enabled - - oauth_server_allow_dynamic_registration - - oauth_server_authorization_path - - custom_oauth_enabled - - custom_oauth_max_providers - UpdateAuthConfigBody: - type: object - properties: - site_url: - type: string - pattern: ^[^,]+$ - nullable: true - disable_signup: - type: boolean - nullable: true - jwt_exp: - type: integer - minimum: 0 - maximum: 604800 - nullable: true - smtp_admin_email: - type: string - format: email - pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$ - nullable: true - smtp_host: - type: string - nullable: true - smtp_port: - type: string - nullable: true - smtp_user: - type: string - nullable: true - smtp_pass: - type: string - nullable: true - smtp_max_frequency: - type: integer - minimum: 0 - maximum: 32767 - nullable: true - smtp_sender_name: - type: string - nullable: true - mailer_allow_unverified_email_sign_ins: - type: boolean - nullable: true - mailer_autoconfirm: - type: boolean - nullable: true - mailer_subjects_invite: - type: string - nullable: true - mailer_subjects_confirmation: - type: string - nullable: true - mailer_subjects_recovery: - type: string - nullable: true - mailer_subjects_email_change: - type: string - nullable: true - mailer_subjects_magic_link: - type: string - nullable: true - mailer_subjects_reauthentication: - type: string - nullable: true - mailer_subjects_password_changed_notification: - type: string - nullable: true - mailer_subjects_email_changed_notification: - type: string - nullable: true - mailer_subjects_phone_changed_notification: - type: string - nullable: true - mailer_subjects_mfa_factor_enrolled_notification: - type: string - nullable: true - mailer_subjects_mfa_factor_unenrolled_notification: - type: string - nullable: true - mailer_subjects_identity_linked_notification: - type: string - nullable: true - mailer_subjects_identity_unlinked_notification: - type: string - nullable: true - mailer_templates_invite_content: - type: string - nullable: true - mailer_templates_confirmation_content: - type: string - nullable: true - mailer_templates_recovery_content: - type: string - nullable: true - mailer_templates_email_change_content: - type: string - nullable: true - mailer_templates_magic_link_content: - type: string - nullable: true - mailer_templates_reauthentication_content: - type: string - nullable: true - mailer_templates_password_changed_notification_content: - type: string - nullable: true - mailer_templates_email_changed_notification_content: - type: string - nullable: true - mailer_templates_phone_changed_notification_content: - type: string - nullable: true - mailer_templates_mfa_factor_enrolled_notification_content: - type: string - nullable: true - mailer_templates_mfa_factor_unenrolled_notification_content: - type: string - nullable: true - mailer_templates_identity_linked_notification_content: - type: string - nullable: true - mailer_templates_identity_unlinked_notification_content: - type: string - nullable: true - mailer_notifications_password_changed_enabled: - type: boolean - nullable: true - mailer_notifications_email_changed_enabled: - type: boolean - nullable: true - mailer_notifications_phone_changed_enabled: - type: boolean - nullable: true - mailer_notifications_mfa_factor_enrolled_enabled: - type: boolean - nullable: true - mailer_notifications_mfa_factor_unenrolled_enabled: - type: boolean - nullable: true - mailer_notifications_identity_linked_enabled: - type: boolean - nullable: true - mailer_notifications_identity_unlinked_enabled: - type: boolean - nullable: true - mfa_max_enrolled_factors: - type: integer - minimum: 0 - maximum: 2147483647 - nullable: true - uri_allow_list: - type: string - nullable: true - external_anonymous_users_enabled: - type: boolean - nullable: true - external_email_enabled: - type: boolean - nullable: true - external_phone_enabled: - type: boolean - nullable: true - saml_enabled: - type: boolean - nullable: true - saml_external_url: - type: string - pattern: ^[^,]+$ - nullable: true - security_sb_forwarded_for_enabled: - type: boolean - nullable: true - security_captcha_enabled: - type: boolean - nullable: true - security_captcha_provider: - type: string - enum: - - turnstile - - hcaptcha - - null - nullable: true - security_captcha_secret: - type: string - nullable: true - sessions_timebox: - type: number - minimum: 0 - description: Session timebox in hours. Maximum 8760 hours (1 year). - nullable: true - sessions_inactivity_timeout: - type: number - minimum: 0 - description: Session inactivity timeout in hours. Maximum 8760 hours (1 year). - nullable: true - sessions_single_per_user: - type: boolean - nullable: true - sessions_tags: - type: string - pattern: ^\s*([a-zA-Z0-9_-]+(\s*,+\s*)?)*\s*$ - nullable: true - rate_limit_anonymous_users: - type: integer - minimum: 1 - maximum: 2147483647 - nullable: true - rate_limit_email_sent: - type: integer - minimum: 1 - maximum: 2147483647 - nullable: true - rate_limit_sms_sent: - type: integer - minimum: 1 - maximum: 2147483647 - nullable: true - rate_limit_verify: - type: integer - minimum: 1 - maximum: 2147483647 - nullable: true - rate_limit_token_refresh: - type: integer - minimum: 1 - maximum: 2147483647 - nullable: true - rate_limit_otp: - type: integer - minimum: 1 - maximum: 2147483647 - nullable: true - rate_limit_web3: - type: integer - minimum: 1 - maximum: 2147483647 - nullable: true - mailer_secure_email_change_enabled: - type: boolean - nullable: true - refresh_token_rotation_enabled: - type: boolean - nullable: true - password_hibp_enabled: - type: boolean - nullable: true - password_min_length: - type: integer - minimum: 6 - maximum: 32767 - nullable: true - password_required_characters: - type: string - enum: - - abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789 - - abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789 - - abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\:"|<>?,./`~ - - '' - - null - nullable: true - security_manual_linking_enabled: - type: boolean - nullable: true - security_update_password_require_reauthentication: - type: boolean - nullable: true - security_update_password_require_current_password: - type: boolean - description: Require the user's current password when updating their password. - nullable: true - security_refresh_token_reuse_interval: - type: integer - minimum: 0 - maximum: 2147483647 - description: >- - Refresh token reuse interval in seconds. Maximum 300 seconds (5 - minutes). - nullable: true - mailer_otp_exp: - type: integer - minimum: 0 - maximum: 2147483647 - mailer_otp_length: - type: integer - minimum: 6 - maximum: 10 - nullable: true - sms_autoconfirm: - type: boolean - nullable: true - sms_max_frequency: - type: integer - minimum: 0 - maximum: 32767 - nullable: true - sms_otp_exp: - type: integer - minimum: 0 - maximum: 2147483647 - nullable: true - sms_otp_length: - type: integer - minimum: 0 - maximum: 32767 - sms_provider: - type: string - enum: - - messagebird - - textlocal - - twilio - - twilio_verify - - vonage - - null - nullable: true - sms_messagebird_access_key: - type: string - nullable: true - sms_messagebird_originator: - type: string - nullable: true - sms_test_otp: - type: string - pattern: ^([0-9]{1,15}=[0-9]+,?)*$ - nullable: true - sms_test_otp_valid_until: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - nullable: true - sms_textlocal_api_key: - type: string - nullable: true - sms_textlocal_sender: - type: string - nullable: true - sms_twilio_account_sid: - type: string - nullable: true - sms_twilio_auth_token: - type: string - nullable: true - sms_twilio_content_sid: - type: string - nullable: true - sms_twilio_message_service_sid: - type: string - nullable: true - sms_twilio_verify_account_sid: - type: string - nullable: true - sms_twilio_verify_auth_token: - type: string - nullable: true - sms_twilio_verify_message_service_sid: - type: string - nullable: true - sms_vonage_api_key: - type: string - nullable: true - sms_vonage_api_secret: - type: string - nullable: true - sms_vonage_from: - type: string - nullable: true - sms_template: - type: string - nullable: true - hook_mfa_verification_attempt_enabled: - type: boolean - nullable: true - hook_mfa_verification_attempt_uri: - type: string - nullable: true - hook_mfa_verification_attempt_secrets: - type: string - nullable: true - hook_password_verification_attempt_enabled: - type: boolean - nullable: true - hook_password_verification_attempt_uri: - type: string - nullable: true - hook_password_verification_attempt_secrets: - type: string - nullable: true - hook_custom_access_token_enabled: - type: boolean - nullable: true - hook_custom_access_token_uri: - type: string - nullable: true - hook_custom_access_token_secrets: - type: string - nullable: true - hook_send_sms_enabled: - type: boolean - nullable: true - hook_send_sms_uri: - type: string - nullable: true - hook_send_sms_secrets: - type: string - nullable: true - hook_send_email_enabled: - type: boolean - nullable: true - hook_send_email_uri: - type: string - nullable: true - hook_send_email_secrets: - type: string - nullable: true - hook_before_user_created_enabled: - type: boolean - nullable: true - hook_before_user_created_uri: - type: string - nullable: true - hook_before_user_created_secrets: - type: string - nullable: true - hook_after_user_created_enabled: - type: boolean - nullable: true - hook_after_user_created_uri: - type: string - nullable: true - hook_after_user_created_secrets: - type: string - nullable: true - external_apple_enabled: - type: boolean - nullable: true - external_apple_client_id: - type: string - nullable: true - external_apple_email_optional: - type: boolean - nullable: true - external_apple_secret: - type: string - nullable: true - external_apple_additional_client_ids: - type: string - nullable: true - external_azure_enabled: - type: boolean - nullable: true - external_azure_client_id: - type: string - nullable: true - external_azure_email_optional: - type: boolean - nullable: true - external_azure_secret: - type: string - nullable: true - external_azure_url: - type: string - nullable: true - external_bitbucket_enabled: - type: boolean - nullable: true - external_bitbucket_client_id: - type: string - nullable: true - external_bitbucket_email_optional: - type: boolean - nullable: true - external_bitbucket_secret: - type: string - nullable: true - external_discord_enabled: - type: boolean - nullable: true - external_discord_client_id: - type: string - nullable: true - external_discord_email_optional: - type: boolean - nullable: true - external_discord_secret: - type: string - nullable: true - external_facebook_enabled: - type: boolean - nullable: true - external_facebook_client_id: - type: string - nullable: true - external_facebook_email_optional: - type: boolean - nullable: true - external_facebook_secret: - type: string - nullable: true - external_figma_enabled: - type: boolean - nullable: true - external_figma_client_id: - type: string - nullable: true - external_figma_email_optional: - type: boolean - nullable: true - external_figma_secret: - type: string - nullable: true - external_github_enabled: - type: boolean - nullable: true - external_github_client_id: - type: string - nullable: true - external_github_email_optional: - type: boolean - nullable: true - external_github_secret: - type: string - nullable: true - external_gitlab_enabled: - type: boolean - nullable: true - external_gitlab_client_id: - type: string - nullable: true - external_gitlab_email_optional: - type: boolean - nullable: true - external_gitlab_secret: - type: string - nullable: true - external_gitlab_url: - type: string - nullable: true - external_google_enabled: - type: boolean - nullable: true - external_google_client_id: - type: string - nullable: true - external_google_email_optional: - type: boolean - nullable: true - external_google_secret: - type: string - nullable: true - external_google_additional_client_ids: - type: string - nullable: true - external_google_skip_nonce_check: - type: boolean - nullable: true - external_kakao_enabled: - type: boolean - nullable: true - external_kakao_client_id: - type: string - nullable: true - external_kakao_email_optional: - type: boolean - nullable: true - external_kakao_secret: - type: string - nullable: true - external_keycloak_enabled: - type: boolean - nullable: true - external_keycloak_client_id: - type: string - nullable: true - external_keycloak_email_optional: - type: boolean - nullable: true - external_keycloak_secret: - type: string - nullable: true - external_keycloak_url: - type: string - nullable: true - external_linkedin_oidc_enabled: - type: boolean - nullable: true - external_linkedin_oidc_client_id: - type: string - nullable: true - external_linkedin_oidc_email_optional: - type: boolean - nullable: true - external_linkedin_oidc_secret: - type: string - nullable: true - external_slack_oidc_enabled: - type: boolean - nullable: true - external_slack_oidc_client_id: - type: string - nullable: true - external_slack_oidc_email_optional: - type: boolean - nullable: true - external_slack_oidc_secret: - type: string - nullable: true - external_notion_enabled: - type: boolean - nullable: true - external_notion_client_id: - type: string - nullable: true - external_notion_email_optional: - type: boolean - nullable: true - external_notion_secret: - type: string - nullable: true - external_slack_enabled: - type: boolean - nullable: true - external_slack_client_id: - type: string - nullable: true - external_slack_email_optional: - type: boolean - nullable: true - external_slack_secret: - type: string - nullable: true - external_spotify_enabled: - type: boolean - nullable: true - external_spotify_client_id: - type: string - nullable: true - external_spotify_email_optional: - type: boolean - nullable: true - external_spotify_secret: - type: string - nullable: true - external_twitch_enabled: - type: boolean - nullable: true - external_twitch_client_id: - type: string - nullable: true - external_twitch_email_optional: - type: boolean - nullable: true - external_twitch_secret: - type: string - nullable: true - external_twitter_enabled: - type: boolean - nullable: true - external_twitter_client_id: - type: string - nullable: true - external_twitter_email_optional: - type: boolean - nullable: true - external_twitter_secret: - type: string - nullable: true - external_x_enabled: - type: boolean - nullable: true - external_x_client_id: - type: string - nullable: true - external_x_email_optional: - type: boolean - nullable: true - external_x_secret: - type: string - nullable: true - external_workos_enabled: - type: boolean - nullable: true - external_workos_client_id: - type: string - nullable: true - external_workos_secret: - type: string - nullable: true - external_workos_url: - type: string - nullable: true - external_web3_solana_enabled: - type: boolean - nullable: true - external_web3_ethereum_enabled: - type: boolean - nullable: true - external_zoom_enabled: - type: boolean - nullable: true - external_zoom_client_id: - type: string - nullable: true - external_zoom_email_optional: - type: boolean - nullable: true - external_zoom_secret: - type: string - nullable: true - db_max_pool_size: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - db_max_pool_size_unit: - type: string - enum: - - connections - - percent - - null - nullable: true - api_max_request_duration: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - mfa_totp_enroll_enabled: - type: boolean - nullable: true - mfa_totp_verify_enabled: - type: boolean - nullable: true - mfa_web_authn_enroll_enabled: - type: boolean - nullable: true - mfa_web_authn_verify_enabled: - type: boolean - nullable: true - passkey_enabled: - type: boolean - webauthn_rp_display_name: - type: string - nullable: true - webauthn_rp_id: - type: string - nullable: true - webauthn_rp_origins: - type: string - nullable: true - mfa_phone_enroll_enabled: - type: boolean - nullable: true - mfa_phone_verify_enabled: - type: boolean - nullable: true - mfa_phone_max_frequency: - type: integer - minimum: 0 - maximum: 32767 - nullable: true - mfa_phone_otp_length: - type: integer - minimum: 0 - maximum: 32767 - nullable: true - mfa_phone_template: - type: string - nullable: true - nimbus_oauth_client_id: - type: string - nullable: true - nimbus_oauth_client_secret: - type: string - nullable: true - oauth_server_enabled: - type: boolean - nullable: true - oauth_server_allow_dynamic_registration: - type: boolean - nullable: true - oauth_server_authorization_path: - type: string - nullable: true - custom_oauth_enabled: - type: boolean - example: - site_url: https://app.example.com - disable_signup: false - jwt_exp: 3600 - CreateThirdPartyAuthBody: - type: object - properties: - oidc_issuer_url: - type: string - jwks_url: - type: string - custom_jwks: {} - example: - oidc_issuer_url: https://login.acme.com - jwks_url: https://login.acme.com/.well-known/jwks.json - ThirdPartyAuth_Output: - type: object - properties: - id: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - type: - type: string - oidc_issuer_url: - type: string - nullable: true - jwks_url: - type: string - nullable: true - custom_jwks: - nullable: true - resolved_jwks: - nullable: true - inserted_at: - type: string - updated_at: - type: string - resolved_at: - type: string - nullable: true - required: - - id - - type - - inserted_at - - updated_at - GetProjectAvailableRestoreVersionsResponse_Output: - type: object - properties: - available_versions: - type: array - items: - type: object - properties: - version: - type: string - release_channel: - type: string - enum: - - internal - - alpha - - beta - - ga - - withdrawn - - preview - postgres_engine: - type: string - enum: - - '13' - - '14' - - '15' - - '17' - - 17-oriole - required: - - version - - release_channel - - postgres_engine - required: - - available_versions - ListProjectAddonsResponse_Output: - type: object - properties: - selected_addons: - type: array - items: - type: object - properties: - type: - type: string - enum: - - custom_domain - - compute_instance - - pitr - - ipv4 - - auth_mfa_phone - - auth_mfa_web_authn - - log_drain - - etl_pipeline - variant: - type: object - properties: - id: - anyOf: - - type: string - enum: - - ci_micro - - ci_small - - ci_medium - - ci_large - - ci_xlarge - - ci_2xlarge - - ci_4xlarge - - ci_8xlarge - - ci_12xlarge - - ci_16xlarge - - ci_24xlarge - - ci_24xlarge_optimized_cpu - - ci_24xlarge_optimized_memory - - ci_24xlarge_high_memory - - ci_48xlarge - - ci_48xlarge_optimized_cpu - - ci_48xlarge_optimized_memory - - ci_48xlarge_high_memory - - type: string - enum: - - cd_default - - type: string - enum: - - pitr_7 - - pitr_14 - - pitr_28 - - type: string - enum: - - ipv4_default - - type: string - enum: - - auth_mfa_phone_default - - type: string - enum: - - auth_mfa_web_authn_default - - type: string - enum: - - log_drain_default - - type: string - enum: - - etl_pipeline_default - name: - type: string - price: - type: object - properties: - description: - type: string - type: - type: string - enum: - - fixed - - usage - interval: - type: string - enum: - - monthly - - hourly - amount: - type: number - required: - - description - - type - - interval - - amount - meta: - $ref: '#/components/schemas/JsonValue_Output' - required: - - id - - name - - price - required: - - type - - variant - available_addons: - type: array - items: - type: object - properties: - type: - type: string - enum: - - custom_domain - - compute_instance - - pitr - - ipv4 - - auth_mfa_phone - - auth_mfa_web_authn - - log_drain - - etl_pipeline - name: - type: string - variants: - type: array - items: - type: object - properties: - id: - anyOf: - - type: string - enum: - - ci_micro - - ci_small - - ci_medium - - ci_large - - ci_xlarge - - ci_2xlarge - - ci_4xlarge - - ci_8xlarge - - ci_12xlarge - - ci_16xlarge - - ci_24xlarge - - ci_24xlarge_optimized_cpu - - ci_24xlarge_optimized_memory - - ci_24xlarge_high_memory - - ci_48xlarge - - ci_48xlarge_optimized_cpu - - ci_48xlarge_optimized_memory - - ci_48xlarge_high_memory - - type: string - enum: - - cd_default - - type: string - enum: - - pitr_7 - - pitr_14 - - pitr_28 - - type: string - enum: - - ipv4_default - - type: string - enum: - - auth_mfa_phone_default - - type: string - enum: - - auth_mfa_web_authn_default - - type: string - enum: - - log_drain_default - - type: string - enum: - - etl_pipeline_default - name: - type: string - price: - type: object - properties: - description: - type: string - type: - type: string - enum: - - fixed - - usage - interval: - type: string - enum: - - monthly - - hourly - amount: - type: number - required: - - description - - type - - interval - - amount - meta: - $ref: '#/components/schemas/JsonValue_Output' - required: - - id - - name - - price - required: - - type - - name - - variants - required: - - selected_addons - - available_addons - ApplyProjectAddonBody: - type: object - properties: - addon_variant: - anyOf: - - type: string - enum: - - ci_micro - - ci_small - - ci_medium - - ci_large - - ci_xlarge - - ci_2xlarge - - ci_4xlarge - - ci_8xlarge - - ci_12xlarge - - ci_16xlarge - - ci_24xlarge - - ci_24xlarge_optimized_cpu - - ci_24xlarge_optimized_memory - - ci_24xlarge_high_memory - - ci_48xlarge - - ci_48xlarge_optimized_cpu - - ci_48xlarge_optimized_memory - - ci_48xlarge_high_memory - - type: string - enum: - - cd_default - - type: string - enum: - - pitr_7 - - pitr_14 - - pitr_28 - - type: string - enum: - - ipv4_default - addon_type: - type: string - enum: - - custom_domain - - compute_instance - - pitr - - ipv4 - - auth_mfa_phone - - auth_mfa_web_authn - - log_drain - - etl_pipeline - required: - - addon_variant - - addon_type - example: - addon_variant: pitr_7 - addon_type: pitr - ProjectClaimTokenResponse_Output: - type: object - properties: - token_alias: - type: string - expires_at: - type: string - created_at: - type: string - created_by: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - required: - - token_alias - - expires_at - - created_at - - created_by - CreateProjectClaimTokenResponse_Output: - type: object - properties: - token: - type: string - token_alias: - type: string - expires_at: - type: string - created_at: - type: string - created_by: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - required: - - token - - token_alias - - expires_at - - created_at - - created_by - V1ProjectAdvisorsResponse_Output: - type: object - properties: - lints: - type: array - items: - type: object - properties: - name: - type: string - enum: - - unindexed_foreign_keys - - auth_users_exposed - - auth_rls_initplan - - no_primary_key - - unused_index - - multiple_permissive_policies - - policy_exists_rls_disabled - - rls_enabled_no_policy - - duplicate_index - - security_definer_view - - function_search_path_mutable - - rls_disabled_in_public - - extension_in_public - - rls_references_user_metadata - - materialized_view_in_api - - foreign_table_in_api - - unsupported_reg_types - - auth_otp_long_expiry - - auth_otp_short_length - - ssl_not_enforced - - log_connections_not_enabled - - network_restrictions_not_set - - password_requirements_min_length - - pitr_not_enabled - - auth_leaked_password_protection - - auth_insufficient_mfa_options - - auth_password_policy_missing - - leaked_service_key - - no_backup_admin - - vulnerable_postgres_version - - db_not_reachable - - db_connection_failing - - db_connection_limit_reached - - instance_telemetry_lost - - instance_db_down - - instance_alert_firing - - log_data_api_error_rate_high - - log_auth_error_rate_high - - log_storage_error_rate_high - - log_edge_function_error_rate_high - - project_not_active - - advisor_check_unavailable - title: - type: string - level: - type: string - enum: - - ERROR - - WARN - - INFO - facing: - type: string - enum: - - EXTERNAL - categories: - type: array - items: - type: string - enum: - - PERFORMANCE - - SECURITY - - HEALTH - x-ignore-array-items-must-be-objects: true - description: - type: string - detail: - type: string - remediation: - type: string - metadata: - type: object - properties: - schema: - type: string - name: - type: string - entity: - type: string - type: - type: string - enum: - - table - - view - - materialized view - - foreign table - - auth - - function - - extension - - compliance - - health - fkey_name: - type: string - fkey_columns: - x-ignore-array-items-must-be-objects: true - type: array - items: - type: number - cache_key: - type: string - observed_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$ - required: - - name - - title - - level - - facing - - categories - - description - - detail - - remediation - - cache_key - required: - - lints - AnalyticsResponse_Output: - type: object - properties: - result: - type: array - items: {} - error: - anyOf: - - type: string - - type: object - properties: - code: - type: number - errors: - type: array - items: - type: object - properties: - domain: - type: string - location: - type: string - locationType: - type: string - message: - type: string - reason: - type: string - required: - - domain - - location - - locationType - - message - - reason - message: - type: string - status: - type: string - required: - - code - - errors - - message - - status - V1GetUsageApiCountResponse_Output: - type: object - properties: - result: - type: array - items: - type: object - properties: - timestamp: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|))$ - total_auth_requests: - type: number - total_realtime_requests: - type: number - total_rest_requests: - type: number - total_storage_requests: - type: number - required: - - timestamp - - total_auth_requests - - total_realtime_requests - - total_rest_requests - - total_storage_requests - error: - anyOf: - - type: string - - type: object - properties: - code: - type: number - errors: - type: array - items: - type: object - properties: - domain: - type: string - location: - type: string - locationType: - type: string - message: - type: string - reason: - type: string - required: - - domain - - location - - locationType - - message - - reason - message: - type: string - status: - type: string - required: - - code - - errors - - message - - status - V1GetUsageApiRequestsCountResponse_Output: - type: object - properties: - result: - type: array - items: - type: object - properties: - count: - type: number - required: - - count - error: - anyOf: - - type: string - - type: object - properties: - code: - type: number - errors: - type: array - items: - type: object - properties: - domain: - type: string - location: - type: string - locationType: - type: string - message: - type: string - reason: - type: string - required: - - domain - - location - - locationType - - message - - reason - message: - type: string - status: - type: string - required: - - code - - errors - - message - - status - CreateRoleBody: - type: object - properties: - read_only: - type: boolean - required: - - read_only - example: - read_only: true - CreateRoleResponse_Output: - type: object - properties: - role: - type: string - minLength: 1 - password: - type: string - minLength: 1 - ttl_seconds: - type: integer - minimum: 1 - maximum: 9007199254740991 - format: int64 - required: - - role - - password - - ttl_seconds - DeleteRolesResponse_Output: - type: object - properties: - message: - type: string - enum: - - ok - required: - - message - V1ListMigrationsResponse_Output: - type: array - items: - type: object - properties: - version: - type: string - name: - type: string - required: - - version - V1CreateMigrationBody: - type: object - properties: - query: - type: string - minLength: 1 - name: - type: string - rollback: - type: string - required: - - query - example: - query: create table public.widgets(id bigint primary key); - name: create_widgets_table - rollback: drop table if exists public.widgets; - V1UpsertMigrationBody: - type: object - properties: - query: - type: string - minLength: 1 - name: - type: string - rollback: - type: string - required: - - query - example: - query: create table public.widgets(id bigint primary key); - name: create_widgets_table - rollback: drop table if exists public.widgets; - V1GetMigrationResponse_Output: - type: object - properties: - version: - type: string - name: - type: string - statements: - type: array - items: - type: string - rollback: - type: array - items: - type: string - created_by: - type: string - idempotency_key: - type: string - required: - - version - V1PatchMigrationBody: - type: object - properties: - name: - type: string - rollback: - type: string - example: - name: create_widgets_table - rollback: drop table if exists public.widgets; - V1RunQueryBody: - type: object - properties: - query: - type: string - minLength: 1 - parameters: - type: array - items: {} - read_only: - type: boolean - required: - - query - example: - query: select * from pg_stat_activity limit 1; - read_only: true - V1ReadOnlyQueryBody: - type: object - properties: - query: - type: string - minLength: 1 - parameters: - type: array - items: {} - required: - - query - example: - query: select * from pg_stat_activity limit 1; - GetProjectDbMetadataResponse_Output: - type: object - properties: - databases: - type: array - items: - type: object - properties: - name: - type: string - schemas: - type: array - items: - type: object - properties: - name: - type: string - required: - - name - required: - - name - - schemas - required: - - databases - V1UpdatePasswordBody: - type: object - properties: - password: - type: string - minLength: 4 - required: - - password - example: - password: correct-horse-battery-staple - V1UpdatePasswordResponse_Output: - type: object - properties: - message: - type: string - required: - - message - JitAccessResponse_Output: - type: object - properties: - user_id: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - user_roles: - type: array - items: - type: object - properties: - role: - type: string - expires_at: - type: number - allowed_networks: - type: object - properties: - allowed_cidrs: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv4 - pattern: ^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$ - required: - - cidr - allowed_cidrs_v6: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv6 - pattern: ^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$ - required: - - cidr - branches_only: - type: boolean - required: - - role - required: - - user_roles - AuthorizeJitAccessBody: - type: object - properties: - role: - type: string - minLength: 1 - rhost: - anyOf: - - type: string - format: ipv4 - pattern: ^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$ - - type: string - format: ipv6 - pattern: ^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$ - required: - - role - - rhost - example: - role: postgres - rhost: 203.0.113.10 - JitAuthorizeAccessResponse_Output: - type: object - properties: - user_id: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - act: - type: string - user_role: - type: object - properties: - role: - type: string - expires_at: - type: number - allowed_networks: - type: object - properties: - allowed_cidrs: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv4 - pattern: ^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$ - required: - - cidr - allowed_cidrs_v6: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv6 - pattern: ^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$ - required: - - cidr - branches_only: - type: boolean - required: - - role - required: - - user_role - JitListAccessResponse_Output: - type: object - properties: - items: - type: array - items: - anyOf: - - type: object - properties: - user_id: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - primary_email: - type: string - nullable: true - invite_id: - type: 'null' - expires_at: - type: 'null' - user_roles: - type: array - items: - type: object - properties: - role: - type: string - expires_at: - type: number - allowed_networks: - type: object - properties: - allowed_cidrs: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv4 - pattern: ^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$ - required: - - cidr - allowed_cidrs_v6: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv6 - pattern: ^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$ - required: - - cidr - branches_only: - type: boolean - required: - - role - required: - - user_id - - primary_email - - invite_id - - expires_at - - user_roles - - type: object - properties: - user_id: - type: 'null' - primary_email: - type: string - invite_id: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - expires_at: - type: string - user_roles: - type: array - items: - type: object - properties: - role: - type: string - expires_at: - type: number - allowed_networks: - type: object - properties: - allowed_cidrs: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv4 - pattern: ^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$ - required: - - cidr - allowed_cidrs_v6: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv6 - pattern: ^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$ - required: - - cidr - branches_only: - type: boolean - required: - - role - required: - - user_id - - primary_email - - invite_id - - expires_at - - user_roles - required: - - items - UpdateJitAccessBody: - type: object - properties: - user_id: - type: string - minLength: 1 - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - roles: - type: array - items: - type: object - properties: - role: - type: string - minLength: 1 - expires_at: - type: number - allowed_networks: - type: object - properties: - allowed_cidrs: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv4 - pattern: ^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$ - required: - - cidr - allowed_cidrs_v6: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv6 - pattern: ^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$ - required: - - cidr - branches_only: - type: boolean - required: - - role - required: - - user_id - - roles - example: - user_id: 55555555-5555-4555-8555-555555555555 - roles: - - role: postgres - expires_at: 1740787200 - allowed_networks: - allowed_cidrs: - - cidr: 203.0.113.0/24 - branches_only: false - InviteExternalUserJitAccessBody: - type: object - properties: - email: - type: string - minLength: 1 - format: email - pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$ - roles: - type: array - items: - type: object - properties: - role: - type: string - minLength: 1 - expires_at: - type: number - allowed_networks: - type: object - properties: - allowed_cidrs: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv4 - pattern: ^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$ - required: - - cidr - allowed_cidrs_v6: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv6 - pattern: ^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$ - required: - - cidr - branches_only: - type: boolean - required: - - role - required: - - email - - roles - example: - email: external-user@somedomain.xyz - roles: - - role: postgres - expires_at: 1740787200 - allowed_networks: - allowed_cidrs: - - cidr: 203.0.113.0/24 - branches_only: false - InviteExternalUserJitResponse_Output: - type: object - properties: - email: - type: string - format: email - pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$ - invite_id: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - user_roles: - type: array - items: - type: object - properties: - role: - type: string - expires_at: - type: number - allowed_networks: - type: object - properties: - allowed_cidrs: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv4 - pattern: ^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$ - required: - - cidr - allowed_cidrs_v6: - type: array - items: - type: object - properties: - cidr: - type: string - format: cidrv6 - pattern: ^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$ - required: - - cidr - branches_only: - type: boolean - required: - - role - required: - - email - - invite_id - - user_roles - AcceptInviteExternalUserJitAccessBody: - type: object - properties: - email: - type: string - minLength: 1 - format: email - pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$ - token: - type: string - minLength: 1 - required: - - email - - token - example: - email: external-user@somedomain.xyz - token: '' - FunctionResponse_Output: - type: object - properties: - id: - type: string - slug: - type: string - name: - type: string - status: - type: string - enum: - - ACTIVE - - REMOVED - - THROTTLED - version: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - created_at: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - format: int64 - updated_at: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - format: int64 - verify_jwt: - type: boolean - import_map: - type: boolean - entrypoint_path: - type: string - import_map_path: - type: string - ezbr_sha256: - type: string - required: - - id - - slug - - name - - status - - version - - created_at - - updated_at - V1CreateFunctionBody: - type: object - properties: - slug: - type: string - pattern: ^[A-Za-z][A-Za-z0-9_-]*$ - name: - type: string - body: - type: string - verify_jwt: - type: boolean - required: - - slug - - name - - body - example: - slug: hello-world - name: Hello World - body: Deno.serve(() => new Response('Hello, world!')) - verify_jwt: true - BulkUpdateFunctionBody: - type: array - items: - type: object - properties: - id: - type: string - slug: - type: string - pattern: ^[A-Za-z][A-Za-z0-9_-]*$ - name: - type: string - status: - type: string - enum: - - ACTIVE - - REMOVED - - THROTTLED - version: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - created_at: - type: integer - format: int64 - minimum: -9007199254740991 - maximum: 9007199254740991 - verify_jwt: - type: boolean - import_map: - type: boolean - entrypoint_path: - type: string - import_map_path: - type: string - ezbr_sha256: - type: string - required: - - id - - slug - - name - - status - - version - example: - - id: 3c078cce-ad70-4148-9f37-4da362789053 - slug: hello-world - name: Hello World - status: ACTIVE - version: 2 - verify_jwt: true - entrypoint_path: index.ts - BulkUpdateFunctionResponse_Output: - type: object - properties: - functions: - type: array - items: - type: object - properties: - id: - type: string - slug: - type: string - name: - type: string - status: - type: string - enum: - - ACTIVE - - REMOVED - - THROTTLED - version: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - created_at: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - format: int64 - updated_at: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - format: int64 - verify_jwt: - type: boolean - import_map: - type: boolean - entrypoint_path: - type: string - import_map_path: - type: string - ezbr_sha256: - type: string - required: - - id - - slug - - name - - status - - version - - created_at - - updated_at - required: - - functions - FunctionDeployBody: - type: object - properties: - file: - type: array - items: - type: string - format: binary - metadata: - type: object - properties: - entrypoint_path: - type: string - import_map_path: - type: string - static_patterns: - type: array - items: - type: string - verify_jwt: - type: boolean - name: - type: string - required: - - entrypoint_path - required: - - file - - metadata - example: - file: - - ./supabase/functions/hello-world/index.ts - metadata: - entrypoint_path: index.ts - verify_jwt: true - name: Hello World - DeployFunctionResponse_Output: - type: object - properties: - id: - type: string - slug: - type: string - name: - type: string - status: - type: string - enum: - - ACTIVE - - REMOVED - - THROTTLED - version: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - created_at: - type: integer - format: int64 - minimum: -9007199254740991 - maximum: 9007199254740991 - updated_at: - type: integer - format: int64 - minimum: -9007199254740991 - maximum: 9007199254740991 - verify_jwt: - type: boolean - import_map: - type: boolean - entrypoint_path: - type: string - import_map_path: - type: string - ezbr_sha256: - type: string - required: - - id - - slug - - name - - status - - version - FunctionSlugResponse_Output: - type: object - properties: - id: - type: string - slug: - type: string - name: - type: string - status: - type: string - enum: - - ACTIVE - - REMOVED - - THROTTLED - version: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - created_at: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - format: int64 - updated_at: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - format: int64 - verify_jwt: - type: boolean - import_map: - type: boolean - entrypoint_path: - type: string - import_map_path: - type: string - ezbr_sha256: - type: string - required: - - id - - slug - - name - - status - - version - - created_at - - updated_at - StreamableFile: - type: object - properties: {} - V1UpdateFunctionBody: - type: object - properties: - name: - type: string - body: - type: string - verify_jwt: - type: boolean - example: - name: Hello World - body: Deno.serve(() => new Response('Hello again!')) - verify_jwt: true - V1StorageBucketResponse_Output: - type: object - properties: - id: - type: string - name: - type: string - owner: - type: string - created_at: - type: string - updated_at: - type: string - public: - type: boolean - required: - - id - - name - - owner - - created_at - - updated_at - - public - DiskResponse_Output: - type: object - properties: - attributes: - anyOf: - - type: object - properties: - iops: - type: integer - exclusiveMinimum: true - maximum: 9007199254740991 - minimum: 0 - size_gb: - type: integer - exclusiveMinimum: true - maximum: 9007199254740991 - minimum: 0 - throughput_mibps: - type: integer - exclusiveMinimum: true - maximum: 9007199254740991 - minimum: 0 - type: - type: string - enum: - - gp3 - required: - - iops - - size_gb - - type - - type: object - properties: - iops: - type: integer - exclusiveMinimum: true - maximum: 9007199254740991 - minimum: 0 - size_gb: - type: integer - exclusiveMinimum: true - maximum: 9007199254740991 - minimum: 0 - type: - type: string - enum: - - io2 - required: - - iops - - size_gb - - type - last_modified_at: - type: string - required: - - attributes - DiskRequestBody: - type: object - properties: - attributes: - oneOf: - - type: object - properties: - iops: - type: integer - exclusiveMinimum: true - maximum: 9007199254740991 - minimum: 0 - size_gb: - type: integer - exclusiveMinimum: true - maximum: 9007199254740991 - minimum: 0 - throughput_mibps: - type: integer - exclusiveMinimum: true - maximum: 9007199254740991 - minimum: 0 - type: - type: string - enum: - - gp3 - required: - - iops - - size_gb - - type - - type: object - properties: - iops: - type: integer - exclusiveMinimum: true - maximum: 9007199254740991 - minimum: 0 - size_gb: - type: integer - exclusiveMinimum: true - maximum: 9007199254740991 - minimum: 0 - type: - type: string - enum: - - io2 - required: - - iops - - size_gb - - type - required: - - attributes - example: - attributes: - type: gp3 - size_gb: 100 - iops: 3000 - throughput_mibps: 125 - DiskUtilMetricsResponse_Output: - type: object - properties: - timestamp: - type: string - metrics: - type: object - properties: - fs_size_bytes: - type: number - fs_avail_bytes: - type: number - fs_used_bytes: - type: number - required: - - fs_size_bytes - - fs_avail_bytes - - fs_used_bytes - required: - - timestamp - - metrics - DiskAutoscaleConfig_Output: - type: object - properties: - growth_percent: - type: integer - exclusiveMinimum: 0 - maximum: 9007199254740991 - description: Growth percentage for disk autoscaling - nullable: true - min_increment_gb: - type: integer - exclusiveMinimum: 0 - maximum: 9007199254740991 - description: Minimum increment size for disk autoscaling in GB - nullable: true - max_size_gb: - type: integer - exclusiveMinimum: 0 - maximum: 9007199254740991 - description: Maximum limit the disk size will grow to in GB - nullable: true - required: - - growth_percent - - min_increment_gb - - max_size_gb - StorageConfigResponse_Output: - type: object - properties: - fileSizeLimit: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - format: int64 - features: - type: object - properties: - imageTransformation: - type: object - properties: - enabled: - type: boolean - required: - - enabled - s3Protocol: - type: object - properties: - enabled: - type: boolean - required: - - enabled - purgeCache: - type: object - properties: - enabled: - type: boolean - required: - - enabled - icebergCatalog: - type: object - properties: - enabled: - type: boolean - maxNamespaces: - type: integer - minimum: 0 - maximum: 9007199254740991 - maxTables: - type: integer - minimum: 0 - maximum: 9007199254740991 - maxCatalogs: - type: integer - minimum: 0 - maximum: 9007199254740991 - required: - - enabled - - maxNamespaces - - maxTables - - maxCatalogs - vectorBuckets: - type: object - properties: - enabled: - type: boolean - maxBuckets: - type: integer - minimum: 0 - maximum: 9007199254740991 - maxIndexes: - type: integer - minimum: 0 - maximum: 9007199254740991 - required: - - enabled - - maxBuckets - - maxIndexes - required: - - imageTransformation - - s3Protocol - - purgeCache - - icebergCatalog - - vectorBuckets - capabilities: - type: object - properties: - list_v2: - type: boolean - iceberg_catalog: - type: boolean - object_versioning: - type: boolean - required: - - list_v2 - - iceberg_catalog - - object_versioning - external: - type: object - properties: - upstreamTarget: - type: string - enum: - - main - - canary - required: - - upstreamTarget - migrationVersion: - type: string - nullable: true - required: - - fileSizeLimit - - features - - capabilities - - external - - migrationVersion - UpdateStorageConfigBody: - type: object - properties: - fileSizeLimit: - type: integer - format: int64 - minimum: 0 - maximum: 536870912000 - features: - type: object - properties: - imageTransformation: - type: object - properties: - enabled: - type: boolean - required: - - enabled - s3Protocol: - type: object - properties: - enabled: - type: boolean - required: - - enabled - purgeCache: - type: object - properties: - enabled: - type: boolean - required: - - enabled - icebergCatalog: - type: object - properties: - enabled: - type: boolean - maxNamespaces: - type: integer - minimum: 0 - maximum: 9007199254740991 - maxTables: - type: integer - minimum: 0 - maximum: 9007199254740991 - maxCatalogs: - type: integer - minimum: 0 - maximum: 9007199254740991 - required: - - enabled - - maxNamespaces - - maxTables - - maxCatalogs - vectorBuckets: - type: object - properties: - enabled: - type: boolean - maxBuckets: - type: integer - minimum: 0 - maximum: 9007199254740991 - maxIndexes: - type: integer - minimum: 0 - maximum: 9007199254740991 - required: - - enabled - - maxBuckets - - maxIndexes - external: - type: object - properties: - upstreamTarget: - type: string - enum: - - main - - canary - required: - - upstreamTarget - example: - fileSizeLimit: 10485760 - features: - imageTransformation: - enabled: true - additionalProperties: false - V1PgbouncerConfigResponse_Output: - type: object - properties: - default_pool_size: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - ignore_startup_parameters: - type: string - max_client_conn: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - pool_mode: - type: string - enum: - - transaction - - session - - statement - connection_string: - type: string - server_idle_timeout: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - server_lifetime: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - query_wait_timeout: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - reserve_pool_size: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - SupavisorConfigResponse_Output: - type: object - properties: - identifier: - type: string - database_type: - type: string - enum: - - PRIMARY - - READ_REPLICA - is_using_scram_auth: - type: boolean - db_user: - type: string - db_host: - type: string - db_port: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - db_name: - type: string - connection_string: - type: string - connectionString: - type: string - description: Use connection_string instead - default_pool_size: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - max_client_conn: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - pool_mode: - type: string - enum: - - transaction - - session - required: - - identifier - - database_type - - is_using_scram_auth - - db_user - - db_host - - db_port - - db_name - - connection_string - - connectionString - - default_pool_size - - max_client_conn - - pool_mode - UpdateSupavisorConfigBody: - type: object - properties: - default_pool_size: - type: integer - minimum: 0 - maximum: 3000 - nullable: true - pool_mode: - description: Dedicated pooler mode for the project - type: string - enum: - - transaction - - session - example: - default_pool_size: 25 - pool_mode: transaction - UpdateSupavisorConfigResponse_Output: - type: object - properties: - default_pool_size: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - nullable: true - pool_mode: - type: string - required: - - default_pool_size - - pool_mode - PostgresConfigResponse_Output: - type: object - properties: - effective_cache_size: - type: string - logical_decoding_work_mem: - type: string - pg_stat_statements.max: - type: integer - minimum: 100 - maximum: 45000 - pg_net.batch_size: - type: integer - minimum: 0 - maximum: 32767 - autovacuum_max_workers: - type: integer - minimum: 1 - maximum: 262143 - autovacuum_work_mem: - type: string - cron.log_statement: - type: boolean - log_autovacuum_min_duration: - type: string - description: 'Default unit: ms' - pattern: ^(-?[0-9]+(?:\.[0-9]+)?)(us|ms|s|min|h|d)?$ - log_checkpoints: - type: boolean - log_connections: - type: boolean - log_disconnections: - type: boolean - log_duration: - type: boolean - log_lock_waits: - type: boolean - log_recovery_conflict_waits: - type: boolean - log_replication_commands: - type: boolean - log_startup_progress_interval: - type: string - description: 'Default unit: ms' - pattern: ^(-?[0-9]+(?:\.[0-9]+)?)(us|ms|s|min|h|d)?$ - log_temp_files: - type: string - maintenance_work_mem: - type: string - track_activity_query_size: - type: string - max_connections: - type: integer - minimum: 1 - maximum: 262143 - max_locks_per_transaction: - type: integer - minimum: 10 - maximum: 2147483640 - max_logical_replication_workers: - type: integer - minimum: 0 - maximum: 262143 - max_parallel_maintenance_workers: - type: integer - minimum: 0 - maximum: 1024 - max_parallel_workers: - type: integer - minimum: 0 - maximum: 1024 - max_parallel_workers_per_gather: - type: integer - minimum: 0 - maximum: 1024 - max_replication_slots: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - max_slot_wal_keep_size: - type: string - max_standby_archive_delay: - type: string - max_standby_streaming_delay: - type: string - max_sync_workers_per_subscription: - type: integer - minimum: 0 - maximum: 262143 - max_wal_size: - type: string - max_wal_senders: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - max_worker_processes: - type: integer - minimum: 0 - maximum: 262143 - session_replication_role: - type: string - enum: - - origin - - replica - - local - shared_buffers: - type: string - statement_timeout: - type: string - description: 'Default unit: ms' - pattern: ^(-?[0-9]+(?:\.[0-9]+)?)(us|ms|s|min|h|d)?$ - track_commit_timestamp: - type: boolean - wal_keep_size: - type: string - wal_sender_timeout: - type: string - description: 'Default unit: ms' - pattern: ^(-?[0-9]+(?:\.[0-9]+)?)(us|ms|s|min|h|d)?$ - work_mem: - type: string - checkpoint_timeout: - type: string - description: 'Default unit: s' - pattern: ^(-?[0-9]+(?:\.[0-9]+)?)(us|ms|s|min|h|d)?$ - hot_standby_feedback: - type: boolean - UpdatePostgresConfigBody: - type: object - properties: - effective_cache_size: - type: string - logical_decoding_work_mem: - type: string - pg_stat_statements.max: - type: integer - minimum: 100 - maximum: 45000 - pg_net.batch_size: - type: integer - minimum: 0 - maximum: 32767 - autovacuum_max_workers: - type: integer - minimum: 1 - maximum: 262143 - autovacuum_work_mem: - type: string - cron.log_statement: - type: boolean - log_autovacuum_min_duration: - type: string - description: 'Default unit: ms' - pattern: ^(-?[0-9]+(?:\.[0-9]+)?)(us|ms|s|min|h|d)?$ - log_checkpoints: - type: boolean - log_connections: - type: boolean - log_disconnections: - type: boolean - log_duration: - type: boolean - log_lock_waits: - type: boolean - log_recovery_conflict_waits: - type: boolean - log_replication_commands: - type: boolean - log_startup_progress_interval: - type: string - description: 'Default unit: ms' - pattern: ^(-?[0-9]+(?:\.[0-9]+)?)(us|ms|s|min|h|d)?$ - log_temp_files: - type: string - maintenance_work_mem: - type: string - track_activity_query_size: - type: string - max_connections: - type: integer - minimum: 1 - maximum: 262143 - max_locks_per_transaction: - type: integer - minimum: 10 - maximum: 2147483640 - max_logical_replication_workers: - type: integer - minimum: 0 - maximum: 262143 - max_parallel_maintenance_workers: - type: integer - minimum: 0 - maximum: 1024 - max_parallel_workers: - type: integer - minimum: 0 - maximum: 1024 - max_parallel_workers_per_gather: - type: integer - minimum: 0 - maximum: 1024 - max_replication_slots: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - max_slot_wal_keep_size: - type: string - max_standby_archive_delay: - type: string - max_standby_streaming_delay: - type: string - max_sync_workers_per_subscription: - type: integer - minimum: 0 - maximum: 262143 - max_wal_size: - type: string - max_wal_senders: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - max_worker_processes: - type: integer - minimum: 0 - maximum: 262143 - session_replication_role: - type: string - enum: - - origin - - replica - - local - shared_buffers: - type: string - statement_timeout: - type: string - description: 'Default unit: ms' - pattern: ^(-?[0-9]+(?:\.[0-9]+)?)(us|ms|s|min|h|d)?$ - track_commit_timestamp: - type: boolean - wal_keep_size: - type: string - wal_sender_timeout: - type: string - description: 'Default unit: ms' - pattern: ^(-?[0-9]+(?:\.[0-9]+)?)(us|ms|s|min|h|d)?$ - work_mem: - type: string - checkpoint_timeout: - type: string - description: 'Default unit: s' - pattern: ^(-?[0-9]+(?:\.[0-9]+)?)(us|ms|s|min|h|d)?$ - hot_standby_feedback: - type: boolean - restart_database: - type: boolean - example: - max_connections: 120 - shared_buffers: 256MB - work_mem: 4MB - statement_timeout: 60000ms - additionalProperties: false - RealtimeConfigResponse_Output: - type: object - properties: - private_only: - type: boolean - description: Whether to only allow private channels - nullable: true - connection_pool: - type: integer - minimum: 1 - maximum: 100 - description: Sets connection pool size for Realtime Authorization - nullable: true - postgres_changes_pool: - type: integer - minimum: 1 - maximum: 100 - description: >- - Sets connection pool size used to create Postgres Changes - subscriptions - nullable: true - max_concurrent_users: - type: integer - minimum: 1 - maximum: 300000 - description: Sets maximum number of concurrent users rate limit - nullable: true - max_events_per_second: - type: integer - minimum: 1 - maximum: 50000 - description: Sets maximum number of events per second rate per channel limit - nullable: true - max_bytes_per_second: - type: integer - minimum: 1 - maximum: 10000000 - description: Sets maximum number of bytes per second rate per channel limit - nullable: true - max_channels_per_client: - type: integer - minimum: 1 - maximum: 10000 - description: Sets maximum number of channels per client rate limit - nullable: true - max_joins_per_second: - type: integer - minimum: 1 - maximum: 5000 - description: Sets maximum number of joins per second rate limit - nullable: true - max_presence_events_per_second: - type: integer - minimum: 1 - maximum: 5000 - description: Sets maximum number of presence events per second rate limit - nullable: true - max_payload_size_in_kb: - type: integer - minimum: 1 - maximum: 10000 - description: Sets maximum number of payload size in KB rate limit - nullable: true - suspend: - type: boolean - description: >- - Disables the Realtime service for this project when true. Set to - false to re-enable it. - nullable: true - presence_enabled: - type: boolean - description: Whether to enable presence - admin_suspended_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - description: If set, the Realtime service has been suspended by an admin. - nullable: true - required: - - private_only - - connection_pool - - postgres_changes_pool - - max_concurrent_users - - max_events_per_second - - max_bytes_per_second - - max_channels_per_client - - max_joins_per_second - - max_presence_events_per_second - - max_payload_size_in_kb - - suspend - - presence_enabled - - admin_suspended_at - UpdateRealtimeConfigBody: - type: object - properties: - private_only: - type: boolean - description: Whether to only allow private channels - connection_pool: - type: integer - minimum: 1 - maximum: 100 - description: Sets connection pool size for Realtime Authorization - postgres_changes_pool: - type: integer - minimum: 1 - maximum: 100 - description: >- - Sets connection pool size used to create Postgres Changes - subscriptions - max_concurrent_users: - type: integer - minimum: 1 - maximum: 300000 - description: Sets maximum number of concurrent users rate limit - max_events_per_second: - type: integer - minimum: 1 - maximum: 50000 - description: Sets maximum number of events per second rate per channel limit - max_bytes_per_second: - type: integer - minimum: 1 - maximum: 10000000 - description: Sets maximum number of bytes per second rate per channel limit - max_channels_per_client: - type: integer - minimum: 1 - maximum: 10000 - description: Sets maximum number of channels per client rate limit - max_joins_per_second: - type: integer - minimum: 1 - maximum: 5000 - description: Sets maximum number of joins per second rate limit - max_presence_events_per_second: - type: integer - minimum: 1 - maximum: 5000 - description: Sets maximum number of presence events per second rate limit - max_payload_size_in_kb: - type: integer - minimum: 1 - maximum: 10000 - description: Sets maximum number of payload size in KB rate limit - suspend: - type: boolean - description: >- - Disables the Realtime service for this project when true. Set to - false to re-enable it. - presence_enabled: - type: boolean - description: Whether to enable presence - example: - private_only: false - max_concurrent_users: 1000 - max_channels_per_client: 100 - additionalProperties: false - CreateProviderBody: - type: object - properties: - type: - type: string - enum: - - saml - description: What type of provider will be created - metadata_xml: - type: string - metadata_url: - type: string - domains: - type: array - items: - type: string - attribute_mapping: - type: object - properties: - keys: - type: object - additionalProperties: - type: object - properties: - name: - type: string - names: - type: array - items: - type: string - array: - type: boolean - default: - anyOf: - - type: object - properties: {} - - type: number - - type: string - - type: boolean - required: - - keys - name_id_format: - type: string - enum: - - urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified - - urn:oasis:names:tc:SAML:2.0:nameid-format:transient - - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - - urn:oasis:names:tc:SAML:2.0:nameid-format:persistent - required: - - type - example: - type: saml - metadata_url: https://sso.acme.com/metadata.xml - domains: - - acme.com - attribute_mapping: - keys: - email: - name: email - first_name: - name: first_name - last_name: - name: last_name - CreateProviderResponse_Output: - type: object - properties: - id: - type: string - saml: - type: object - properties: - entity_id: - type: string - metadata_url: - type: string - metadata_xml: - type: string - attribute_mapping: - type: object - properties: - keys: - type: object - additionalProperties: - type: object - properties: - name: - type: string - names: - type: array - items: - type: string - array: - type: boolean - default: - anyOf: - - type: object - properties: {} - - type: number - - type: string - - type: boolean - required: - - keys - name_id_format: - type: string - enum: - - urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified - - urn:oasis:names:tc:SAML:2.0:nameid-format:transient - - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - - urn:oasis:names:tc:SAML:2.0:nameid-format:persistent - required: - - entity_id - domains: - type: array - items: - type: object - properties: - domain: - type: string - created_at: - type: string - updated_at: - type: string - created_at: - type: string - updated_at: - type: string - required: - - id - ListProvidersResponse_Output: - type: object - properties: - items: - type: array - items: - type: object - properties: - id: - type: string - saml: - type: object - properties: - entity_id: - type: string - metadata_url: - type: string - metadata_xml: - type: string - attribute_mapping: - type: object - properties: - keys: - type: object - additionalProperties: - type: object - properties: - name: - type: string - names: - type: array - items: - type: string - array: - type: boolean - default: - anyOf: - - type: object - properties: {} - - type: number - - type: string - - type: boolean - required: - - keys - name_id_format: - type: string - enum: - - urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified - - urn:oasis:names:tc:SAML:2.0:nameid-format:transient - - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - - urn:oasis:names:tc:SAML:2.0:nameid-format:persistent - required: - - entity_id - domains: - type: array - items: - type: object - properties: - domain: - type: string - created_at: - type: string - updated_at: - type: string - created_at: - type: string - updated_at: - type: string - required: - - id - required: - - items - GetProviderResponse_Output: - type: object - properties: - id: - type: string - saml: - type: object - properties: - entity_id: - type: string - metadata_url: - type: string - metadata_xml: - type: string - attribute_mapping: - type: object - properties: - keys: - type: object - additionalProperties: - type: object - properties: - name: - type: string - names: - type: array - items: - type: string - array: - type: boolean - default: - anyOf: - - type: object - properties: {} - - type: number - - type: string - - type: boolean - required: - - keys - name_id_format: - type: string - enum: - - urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified - - urn:oasis:names:tc:SAML:2.0:nameid-format:transient - - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - - urn:oasis:names:tc:SAML:2.0:nameid-format:persistent - required: - - entity_id - domains: - type: array - items: - type: object - properties: - domain: - type: string - created_at: - type: string - updated_at: - type: string - created_at: - type: string - updated_at: - type: string - required: - - id - UpdateProviderBody: - type: object - properties: - metadata_xml: - type: string - metadata_url: - type: string - domains: - type: array - items: - type: string - attribute_mapping: - type: object - properties: - keys: - type: object - additionalProperties: - type: object - properties: - name: - type: string - names: - type: array - items: - type: string - array: - type: boolean - default: - anyOf: - - type: object - properties: {} - - type: number - - type: string - - type: boolean - required: - - keys - name_id_format: - type: string - enum: - - urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified - - urn:oasis:names:tc:SAML:2.0:nameid-format:transient - - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - - urn:oasis:names:tc:SAML:2.0:nameid-format:persistent - example: - metadata_url: https://sso.acme.com/metadata.xml - domains: - - acme.com - - contractors.acme.com - UpdateProviderResponse_Output: - type: object - properties: - id: - type: string - saml: - type: object - properties: - entity_id: - type: string - metadata_url: - type: string - metadata_xml: - type: string - attribute_mapping: - type: object - properties: - keys: - type: object - additionalProperties: - type: object - properties: - name: - type: string - names: - type: array - items: - type: string - array: - type: boolean - default: - anyOf: - - type: object - properties: {} - - type: number - - type: string - - type: boolean - required: - - keys - name_id_format: - type: string - enum: - - urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified - - urn:oasis:names:tc:SAML:2.0:nameid-format:transient - - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - - urn:oasis:names:tc:SAML:2.0:nameid-format:persistent - required: - - entity_id - domains: - type: array - items: - type: object - properties: - domain: - type: string - created_at: - type: string - updated_at: - type: string - created_at: - type: string - updated_at: - type: string - required: - - id - DeleteProviderResponse_Output: - type: object - properties: - id: - type: string - saml: - type: object - properties: - entity_id: - type: string - metadata_url: - type: string - metadata_xml: - type: string - attribute_mapping: - type: object - properties: - keys: - type: object - additionalProperties: - type: object - properties: - name: - type: string - names: - type: array - items: - type: string - array: - type: boolean - default: - anyOf: - - type: object - properties: {} - - type: number - - type: string - - type: boolean - required: - - keys - name_id_format: - type: string - enum: - - urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified - - urn:oasis:names:tc:SAML:2.0:nameid-format:transient - - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - - urn:oasis:names:tc:SAML:2.0:nameid-format:persistent - required: - - entity_id - domains: - type: array - items: - type: object - properties: - domain: - type: string - created_at: - type: string - updated_at: - type: string - created_at: - type: string - updated_at: - type: string - required: - - id - V1BackupsResponse_Output: - type: object - properties: - region: - type: string - walg_enabled: - type: boolean - pitr_enabled: - type: boolean - backups: - type: array - items: - type: object - properties: - id: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - is_physical_backup: - type: boolean - status: - type: string - enum: - - COMPLETED - - FAILED - - PENDING - - REMOVED - - ARCHIVED - - CANCELLED - inserted_at: - type: string - required: - - id - - is_physical_backup - - status - - inserted_at - physical_backup_data: - type: object - properties: - earliest_physical_backup_date_unix: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - latest_physical_backup_date_unix: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - required: - - region - - walg_enabled - - pitr_enabled - - backups - - physical_backup_data - V1RestorePitrBody: - type: object - properties: - recovery_time_target_unix: - type: integer - minimum: 0 - maximum: 9007199254740991 - format: int64 - required: - - recovery_time_target_unix - example: - recovery_time_target_unix: 1740787200 - V1RestorePointPostBody: - type: object - properties: - name: - type: string - maxLength: 20 - required: - - name - example: - name: before-upgrade - V1RestorePointResponse: - type: object - properties: - name: - type: string - status: - type: string - enum: - - AVAILABLE - - PENDING - - REMOVED - - FAILED - completed_on: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - nullable: true - required: - - name - - status - - completed_on - V1RestoreBackupBody: - type: object - properties: - id: - type: integer - minimum: -9007199254740991 - maximum: 9007199254740991 - required: - - id - example: - id: 12345 - V1BackupScheduleResponse_Output: - type: object - properties: - schedule_for: - type: string - pattern: ^(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?$ - description: 'Time of day to schedule daily backups, in UTC. Format: HH:MM:SS.' - example: '04:00:00' - updated_at: - type: string - format: date-time - pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ - description: Timestamp of when the backup schedule was last updated. - example: '2026-05-04T14:40:44+00:00' - required: - - schedule_for - - updated_at - V1UpdateBackupScheduleBody: - type: object - properties: - schedule_for: - type: string - pattern: ^(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?$ - description: 'Time of day to schedule daily backups, in UTC. Format: HH:MM:SS.' - example: '04:00:00' - required: - - schedule_for - example: - schedule_for: '04:00:00' - V1UndoBody: - type: object - properties: - name: - type: string - maxLength: 20 - required: - - name - example: - name: before-upgrade - V1ListEntitlementsResponse_Output: - type: object - properties: - entitlements: - type: array - items: - type: object - properties: - feature: - type: object - properties: - key: - type: string - enum: - - instances.compute_update_available_sizes - - instances.read_replicas - - instances.disk_modifications - - instances.high_availability - - instances.orioledb - - replication.etl - - storage.max_file_size - - storage.max_file_size.configurable - - storage.image_transformations - - storage.vector_buckets - - storage.iceberg_catalog - - storage.purge_cache - - security.audit_logs_days - - security.questionnaire - - security.soc2_report - - security.iso27001_certificate - - security.private_link - - security.enforce_mfa - - log.retention_days - - custom_domain - - vanity_subdomain - - ipv4 - - pitr.available_variants - - log_drains - - audit_log_drains - - branching_limit - - branching_persistent - - auth.mfa_phone - - auth.mfa_web_authn - - auth.mfa_enhanced_security - - auth.hooks - - auth.platform.sso - - auth.custom_jwt_template - - auth.saml_2 - - auth.user_sessions - - auth.leaked_password_protection - - auth.advanced_auth_settings - - auth.performance_settings - - auth.password_hibp - - auth.custom_oauth.max_providers - - backup.retention_days - - backup.restore_to_new_project - - backup.schedule - - function.max_count - - function.size_limit_mb - - realtime.max_concurrent_users - - realtime.max_events_per_second - - realtime.max_joins_per_second - - realtime.max_channels_per_client - - realtime.max_bytes_per_second - - realtime.max_presence_events_per_second - - realtime.max_payload_size_in_kb - - project_scoped_roles - - security.member_roles - - project_pausing - - project_cloning - - project_restore_after_expiry - - assistant.advance_model - - integrations.github_connections - - integrations.github_push_webhooks_limit - - dedicated_pooler - - observability.dashboard_advanced_metrics - - api.members.invitations - - api.members.roles - type: - type: string - enum: - - boolean - - numeric - - set - required: - - key - - type - hasAccess: - type: boolean - type: - type: string - enum: - - boolean - - numeric - - set - config: - anyOf: - - type: object - properties: - enabled: - type: boolean - required: - - enabled - - type: object - properties: - enabled: - type: boolean - value: - type: number - unlimited: - type: boolean - unit: - type: string - required: - - enabled - - value - - unlimited - - unit - - type: object - properties: - enabled: - type: boolean - set: - type: array - items: - type: string - required: - - enabled - - set - required: - - feature - - hasAccess - - type - - config - required: - - entitlements - V1OrganizationMemberResponse_Output: - type: object - properties: - user_id: - type: string - user_name: - type: string - email: - type: string - role_name: - type: string - mfa_enabled: - type: boolean - avatar_url: - type: string - nullable: true - required: - - user_id - - user_name - - mfa_enabled - - avatar_url - V1OrganizationSlugResponse_Output: - type: object - properties: - id: - type: string - name: - type: string - plan: - type: string - enum: - - free - - pro - - team - - enterprise - - platform - opt_in_tags: - type: array - items: - enum: - - AI_SQL_GENERATOR_OPT_IN - - AI_DATA_GENERATOR_OPT_IN - - AI_LOG_GENERATOR_OPT_IN - allowed_release_channels: - type: array - items: - type: string - enum: - - internal - - alpha - - beta - - ga - - withdrawn - - preview - required: - - id - - name - - opt_in_tags - - allowed_release_channels - OrganizationProjectClaimResponse_Output: - type: object - properties: - project: - type: object - properties: - ref: - type: string - name: - type: string - required: - - ref - - name - preview: - type: object - properties: - valid: - type: boolean - warnings: - type: array - items: - type: object - properties: - key: - type: string - message: - type: string - required: - - key - - message - errors: - type: array - items: - type: object - properties: - key: - type: string - message: - type: string - required: - - key - - message - info: - type: array - items: - type: object - properties: - key: - type: string - message: - type: string - required: - - key - - message - members_exceeding_free_project_limit: - type: array - items: - type: object - properties: - name: - type: string - limit: - type: number - required: - - name - - limit - source_subscription_plan: - type: string - enum: - - free - - pro - - team - - enterprise - - platform - target_subscription_plan: - type: string - enum: - - free - - pro - - team - - enterprise - - platform - - null - nullable: true - required: - - valid - - warnings - - errors - - info - - members_exceeding_free_project_limit - - source_subscription_plan - - target_subscription_plan - expires_at: - type: string - created_at: - type: string - created_by: - type: string - format: uuid - pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ - required: - - project - - preview - - expires_at - - created_at - - created_by - OrganizationProjectsResponse_Output: - type: object - properties: - projects: - type: array - items: - type: object - properties: - ref: - type: string - name: - type: string - cloud_provider: - type: string - region: - type: string - is_branch: - type: boolean - status: - type: string - enum: - - INACTIVE - - ACTIVE_HEALTHY - - ACTIVE_UNHEALTHY - - COMING_UP - - UNKNOWN - - GOING_DOWN - - INIT_FAILED - - REMOVED - - RESTORING - - UPGRADING - - PAUSING - - RESTORE_FAILED - - RESTARTING - - PAUSE_FAILED - - RESIZING - inserted_at: - type: string - databases: - type: array - items: - type: object - properties: - infra_compute_size: - type: string - enum: - - pico - - nano - - micro - - small - - medium - - large - - xlarge - - 2xlarge - - 4xlarge - - 8xlarge - - 12xlarge - - 16xlarge - - 24xlarge - - 24xlarge_optimized_memory - - 24xlarge_optimized_cpu - - 24xlarge_high_memory - - 48xlarge - - 48xlarge_optimized_memory - - 48xlarge_optimized_cpu - - 48xlarge_high_memory - region: - type: string - status: - type: string - enum: - - ACTIVE_HEALTHY - - ACTIVE_UNHEALTHY - - COMING_UP - - GOING_DOWN - - INIT_FAILED - - REMOVED - - RESTORING - - UNKNOWN - - INIT_READ_REPLICA - - INIT_READ_REPLICA_FAILED - - RESTARTING - - RESIZING - cloud_provider: - type: string - identifier: - type: string - type: - type: string - enum: - - PRIMARY - - READ_REPLICA - disk_volume_size_gb: - type: number - disk_type: - type: string - enum: - - gp3 - - io2 - disk_throughput_mbps: - type: number - disk_last_modified_at: - type: string - required: - - region - - status - - cloud_provider - - identifier - - type - required: - - ref - - name - - cloud_provider - - region - - is_branch - - status - - inserted_at - - databases - pagination: - type: object - properties: - count: - type: number - description: >- - Total number of projects. Use this to calculate the total number - of pages. - limit: - type: number - description: Maximum number of projects per page - offset: - type: number - description: Number of projects skipped in this response - required: - - count - - limit - - offset - required: - - projects - - pagination diff --git a/apps/cli-go/cmd/db.go b/apps/cli-go/cmd/db.go deleted file mode 100644 index 044a639783..0000000000 --- a/apps/cli-go/cmd/db.go +++ /dev/null @@ -1,136 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - - "github.com/spf13/afero" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/supabase/cli/internal/db/diff" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/flags" - "github.com/supabase/cli/legacy/branch/create" - "github.com/supabase/cli/legacy/branch/delete" - "github.com/supabase/cli/legacy/branch/list" - "github.com/supabase/cli/legacy/branch/switch_" -) - -var ( - dbCmd = &cobra.Command{ - GroupID: groupLocalDev, - Use: "db", - Short: "Manage Postgres databases", - } - - dbBranchCmd = &cobra.Command{ - Hidden: true, - Use: "branch", - Short: "Manage local database branches", - Long: "Manage local database branches. Each branch is associated with a separate local database. Forking remote databases is NOT supported.", - } - - dbBranchCreateCmd = &cobra.Command{ - Deprecated: "use \"branches create \" instead.\n", - Use: "create ", - Short: "Create a branch", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - return create.Run(args[0], afero.NewOsFs()) - }, - } - - dbBranchDeleteCmd = &cobra.Command{ - Deprecated: "use \"branches delete \" instead.\n", - Use: "delete ", - Short: "Delete a branch", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - return delete.Run(args[0], afero.NewOsFs()) - }, - } - - dbBranchListCmd = &cobra.Command{ - Deprecated: "use \"branches list\" instead.\n", - Use: "list", - Short: "List branches", - RunE: func(cmd *cobra.Command, args []string) error { - return list.Run(afero.NewOsFs(), os.Stdout) - }, - } - - dbSwitchCmd = &cobra.Command{ - Deprecated: "use \"branches create \" instead.\n", - Use: "switch ", - Short: "Switch the active branch", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - return switch_.Run(cmd.Context(), args[0], afero.NewOsFs()) - }, - } - - // Bound so the TS `--use-pg-schema` proxy can forward these without unknown-flag errors. - usePgSchema bool - outputPath string - schema []string - file string - dbPassword string - - dbDiffCmd = &cobra.Command{ - Use: "diff", - Short: "Diffs the local database for schema changes", - RunE: func(cmd *cobra.Command, args []string) error { - // TypeScript only proxies `--use-pg-schema` (stripe/pg-schema-diff). - // Other engines run in-process in the TS CLI. - fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), "--use-pg-schema flag is experimental and may not include all entities, such as views and grants.") - return diff.Run(cmd.Context(), schema, file, flags.DbConfig, diff.DiffPgSchema, afero.NewOsFs()) - }, - } - - dbRemoteCmd = &cobra.Command{ - Hidden: true, - Use: "remote", - Short: "Manage remote databases", - } - - dbRemoteChangesCmd = &cobra.Command{ - Deprecated: "use \"db diff --use-migra --linked\" instead.\n", - Use: "changes", - Short: "Show changes on the remote database", - Long: "Show changes on the remote database since last migration.", - RunE: func(cmd *cobra.Command, args []string) error { - return diff.Run(cmd.Context(), schema, file, flags.DbConfig, diff.DiffSchemaMigra, afero.NewOsFs()) - }, - } -) - -func init() { - // Build branch command - dbBranchCmd.AddCommand(dbBranchCreateCmd) - dbBranchCmd.AddCommand(dbBranchDeleteCmd) - dbBranchCmd.AddCommand(dbBranchListCmd) - dbBranchCmd.AddCommand(dbSwitchCmd) - dbCmd.AddCommand(dbBranchCmd) - // Build diff command - diffFlags := dbDiffCmd.Flags() - diffFlags.BoolVar(&usePgSchema, "use-pg-schema", false, "Use pg-schema-diff to generate schema diff.") - diffFlags.StringVarP(&outputPath, "output", "o", "", "Write explicit diff output to a file path.") - diffFlags.String("db-url", "", "Diffs against the database specified by the connection string (must be percent-encoded).") - diffFlags.Bool("linked", false, "Diffs local migration files against the linked project.") - diffFlags.Bool("local", true, "Diffs local migration files against the local database.") - dbDiffCmd.MarkFlagsMutuallyExclusive("db-url", "linked", "local") - diffFlags.StringVarP(&file, "file", "f", "", "Saves schema diff to a new migration file.") - diffFlags.StringSliceVarP(&schema, "schema", "s", []string{}, "Comma separated list of schema to include.") - dbCmd.AddCommand(dbDiffCmd) - // Build remote command - remoteFlags := dbRemoteCmd.PersistentFlags() - remoteFlags.StringSliceVarP(&schema, "schema", "s", []string{}, "Comma separated list of schema to include.") - remoteFlags.String("db-url", "", "Connect using the specified Postgres URL (must be percent-encoded).") - remoteFlags.Bool("linked", true, "Connect to the linked project.") - dbRemoteCmd.MarkFlagsMutuallyExclusive("db-url", "linked") - remoteFlags.StringVarP(&dbPassword, "password", "p", "", "Password to your remote Postgres database.") - cobra.CheckErr(viper.BindPFlag("DB_PASSWORD", remoteFlags.Lookup("password"))) - dbRemoteCmd.AddCommand(dbRemoteChangesCmd) - dbCmd.AddCommand(dbRemoteCmd) - rootCmd.AddCommand(dbCmd) -} diff --git a/apps/cli-go/cmd/functions.go b/apps/cli-go/cmd/functions.go deleted file mode 100644 index 3434c5ceb8..0000000000 --- a/apps/cli-go/cmd/functions.go +++ /dev/null @@ -1,51 +0,0 @@ -package cmd - -import ( - "github.com/spf13/afero" - "github.com/spf13/cobra" - "github.com/supabase/cli/internal/functions/download" - "github.com/supabase/cli/internal/utils/flags" -) - -var ( - functionsCmd = &cobra.Command{ - GroupID: groupManagementAPI, - Use: "functions", - Short: "Manage Supabase Edge functions", - } - - functionsDownloadCmd = &cobra.Command{ - Use: "download [Function name]", - Short: "Download a Function from Supabase", - Long: "Download the source code for a Function from the linked Supabase project. If no function name is provided, downloads all functions.", - Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - if useApi { - useDocker = false - } - slug := "" - if len(args) > 0 { - slug = args[0] - } - return download.Run(cmd.Context(), slug, flags.ProjectRef, useLegacyBundle, useDocker, afero.NewOsFs()) - }, - } - - useApi bool - useDocker bool - useLegacyBundle bool -) - -func init() { - downloadFlags := functionsDownloadCmd.Flags() - downloadFlags.StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.") - markFlagTelemetrySafe(downloadFlags.Lookup("project-ref")) - downloadFlags.BoolVar(&useLegacyBundle, "legacy-bundle", false, "Use legacy bundling mechanism.") - downloadFlags.BoolVar(&useApi, "use-api", false, "Unbundle functions server-side without using Docker.") - downloadFlags.BoolVar(&useDocker, "use-docker", true, "Use Docker to unbundle functions client-side.") - functionsDownloadCmd.MarkFlagsMutuallyExclusive("use-api", "use-docker", "legacy-bundle") - cobra.CheckErr(downloadFlags.MarkHidden("legacy-bundle")) - cobra.CheckErr(downloadFlags.MarkHidden("use-docker")) - functionsCmd.AddCommand(functionsDownloadCmd) - rootCmd.AddCommand(functionsCmd) -} diff --git a/apps/cli-go/cmd/gen.go b/apps/cli-go/cmd/gen.go deleted file mode 100644 index 9ccdbc862d..0000000000 --- a/apps/cli-go/cmd/gen.go +++ /dev/null @@ -1,54 +0,0 @@ -package cmd - -import ( - env "github.com/Netflix/go-env" - "github.com/spf13/afero" - "github.com/spf13/cobra" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/flags" - "github.com/supabase/cli/legacy/keys" -) - -var ( - genCmd = &cobra.Command{ - GroupID: groupLocalDev, - Use: "gen", - Short: "Run code generation tools", - } - - keyNames keys.CustomName - override []string - - genKeysCmd = &cobra.Command{ - Deprecated: `use "gen signing-key" instead.`, - Use: "keys", - Short: "Generate keys for preview branch", - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - es, err := env.EnvironToEnvSet(override) - if err != nil { - return err - } - if err := env.Unmarshal(es, &keyNames); err != nil { - return err - } - cmd.GroupID = groupManagementAPI - return cmd.Root().PersistentPreRunE(cmd, args) - }, - RunE: func(cmd *cobra.Command, args []string) error { - format := utils.OutputFormat.Value - if format == utils.OutputPretty { - format = utils.OutputEnv - } - return keys.Run(cmd.Context(), flags.ProjectRef, format, keyNames, afero.NewOsFs()) - }, - } -) - -func init() { - keyFlags := genKeysCmd.Flags() - keyFlags.StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.") - markFlagTelemetrySafe(keyFlags.Lookup("project-ref")) - keyFlags.StringSliceVar(&override, "override-name", []string{}, "Override specific variable names.") - genCmd.AddCommand(genKeysCmd) - rootCmd.AddCommand(genCmd) -} diff --git a/apps/cli-go/cmd/root.go b/apps/cli-go/cmd/root.go deleted file mode 100644 index 97aafb07fd..0000000000 --- a/apps/cli-go/cmd/root.go +++ /dev/null @@ -1,379 +0,0 @@ -package cmd - -import ( - "context" - "fmt" - "net" - "net/http" - "net/url" - "os" - "os/signal" - "strconv" - "strings" - "sync" - "time" - - "github.com/getsentry/sentry-go" - "github.com/go-errors/errors" - "github.com/spf13/afero" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/supabase/cli/internal/debug" - "github.com/supabase/cli/internal/telemetry" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/flags" - "golang.org/x/mod/semver" -) - -const ( - groupQuickStart = "quick-start" - groupLocalDev = "local-dev" - groupManagementAPI = "management-api" -) - -func IsManagementAPI(cmd *cobra.Command) bool { - for cmd != cmd.Root() { - if cmd.GroupID == groupManagementAPI { - return true - } - // Find the last assigned group - if len(cmd.GroupID) > 0 { - break - } - cmd = cmd.Parent() - } - return false -} - -func promptLogin(fsys afero.Fs) error { - if _, err := utils.LoadAccessTokenFS(fsys); err == utils.ErrMissingToken { - utils.CmdSuggestion = fmt.Sprintf("Run %s first.", utils.Aqua("supabase login")) - return errors.New("You need to be logged-in in order to use Management API commands.") - } else { - return err - } -} - -var experimental = []*cobra.Command{ - genKeysCmd, -} - -func IsExperimental(cmd *cobra.Command) bool { - for _, exp := range experimental { - if cmd == exp || cmd.Parent() == exp { - return true - } - } - return false -} - -var ( - sentryOpts = sentry.ClientOptions{ - Dsn: utils.SentryDsn, - Release: utils.Version, - ServerName: "", - // Set TracesSampleRate to 1.0 to capture 100% - // of transactions for performance monitoring. - // We recommend adjusting this value in production, - TracesSampleRate: 1.0, - } - - createTicket bool - - rootCmd = &cobra.Command{ - Use: "supabase", - Short: "Supabase CLI " + utils.Version, - Version: utils.Version, - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - if IsExperimental(cmd) && !viper.GetBool("EXPERIMENTAL") { - return errors.New("must set the --experimental flag to run this command") - } - cmd.SilenceUsage = true - // Load profile before changing workdir - ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt) - fsys := afero.NewOsFs() - if err := utils.LoadProfile(ctx, fsys); err != nil { - return err - } - if err := utils.ChangeWorkDir(fsys); err != nil { - return err - } - // Add common flags - if IsManagementAPI(cmd) { - if err := promptLogin(fsys); err != nil { - return err - } - if cmd.Flags().Lookup("project-ref") != nil { - if err := flags.ParseProjectRef(ctx, fsys); err != nil { - return err - } - } - } - if err := flags.ParseDatabaseConfig(ctx, cmd.Flags(), fsys); err != nil { - return err - } - // Prepare context - if viper.GetBool("DEBUG") { - http.DefaultTransport = debug.NewTransport() - fmt.Fprintln(os.Stderr, cmd.Root().Short) - fmt.Fprintf(os.Stderr, "Using profile: %s (%s)\n", utils.CurrentProfile.Name, utils.CurrentProfile.ProjectHost) - } - isTTY := telemetryIsTTY() - isCI := telemetryIsCI() - isAgent := telemetryIsAgent() - envSignals := telemetryEnvSignals() - service, err := telemetry.NewService(fsys, telemetry.Options{ - Now: time.Now, - IsTTY: isTTY, - IsCI: isCI, - IsAgent: isAgent, - EnvSignals: envSignals, - CLIName: utils.Version, - }) - if err != nil { - fmt.Fprintln(utils.GetDebugLogger(), err) - } else { - ctx = telemetry.WithService(ctx, service) - } - if service != nil { - var stitchOnce sync.Once - utils.OnGotrueID = func(gotrueID string) { - stitchOnce.Do(func() { - if err := service.ObserveAuthenticatedUser(gotrueID); err != nil { - fmt.Fprintln(utils.GetDebugLogger(), err) - } - }) - } - } - ctx = telemetry.WithCommandContext(ctx, commandAnalyticsContext(cmd)) - cmd.SetContext(ctx) - // Setup sentry last to ignore errors from parsing cli flags - apiHost, err := url.Parse(utils.GetSupabaseAPIHost()) - if err != nil { - return err - } - sentryOpts.Environment = apiHost.Host - return sentry.Init(sentryOpts) - }, - SilenceErrors: true, - } -) - -func Execute() { - defer recoverAndExit() - startedAt := time.Now() - executedCmd, err := rootCmd.ExecuteC() - if executedCmd != nil { - if service := telemetry.FromContext(executedCmd.Context()); service != nil { - ensureProjectGroupsCached(executedCmd.Context(), service) - _ = service.Capture(executedCmd.Context(), telemetry.EventCommandExecuted, map[string]any{ - telemetry.PropExitCode: exitCode(err), - telemetry.PropDurationMs: time.Since(startedAt).Milliseconds(), - telemetry.PropOutputFormat: utils.OutputFormat.Value, - }, nil) - _ = service.Close() - } - } - if err != nil { - panic(err) - } - // Check upgrade last because --version flag is initialised after execute - ctx := rootCmd.Context() - if executedCmd != nil { - ctx = executedCmd.Context() - } - if hint := utils.SuggestClaudePlugin(); hint != "" { - fmt.Fprintln(os.Stderr, hint) - } - if updateNotifierEnabled() { - version, err := checkUpgrade(ctx, afero.NewOsFs()) - if err != nil { - fmt.Fprintln(utils.GetDebugLogger(), err) - } - if semver.Compare(version, "v"+utils.Version) > 0 { - fmt.Fprintln(os.Stderr, suggestUpgrade(version)) - } - } - if len(utils.CmdSuggestion) > 0 { - fmt.Fprintln(os.Stderr, utils.CmdSuggestion) - } -} - -// ensureProjectGroupsCached populates the telemetry linked-project cache when -// a project ref is available but no cache exists. This ensures org/project -// PostHog groups are attached to all CLI events, not just those after `supabase link`. -// -// Does not overwrite an existing cache — `supabase link` is the authoritative source. -// Checks auth before calling the API to avoid the log.Fatalln in GetSupabase(). -func ensureProjectGroupsCached(ctx context.Context, service *telemetry.Service) { - ref := flags.ProjectRef - if ref == "" { - return - } - fsys := afero.NewOsFs() - if telemetry.HasLinkedProject(fsys) { - return - } - if _, err := utils.LoadAccessTokenFS(fsys); err != nil { - return - } - resp, err := utils.GetSupabase().V1GetProjectWithResponse(ctx, ref) - if err != nil { - fmt.Fprintln(utils.GetDebugLogger(), err) - return - } - if resp.JSON200 == nil { - return - } - telemetry.CacheProjectAndIdentifyGroups(*resp.JSON200, service, fsys) -} - -func exitCode(err error) int { - if err != nil { - return 1 - } - return 0 -} - -func updateNotifierEnabled() bool { - // Read the env directly instead of viper: the upgrade check also runs for - // --help and --version, which skip the cobra initializer that binds env vars. - disabled, _ := strconv.ParseBool(os.Getenv("SUPABASE_NO_UPDATE_NOTIFIER")) - return !disabled -} - -func checkUpgrade(ctx context.Context, fsys afero.Fs) (string, error) { - if shouldFetchRelease(fsys) { - version, err := utils.GetLatestRelease(ctx) - if exists, _ := afero.DirExists(fsys, utils.SupabaseDirPath); exists { - // If user is offline, write an empty file to skip subsequent checks - err = utils.WriteFile(utils.CliVersionPath, []byte(version), fsys) - } - return version, err - } - version, err := afero.ReadFile(fsys, utils.CliVersionPath) - if err != nil { - return "", errors.Errorf("failed to read cli version: %w", err) - } - return string(version), nil -} - -func shouldFetchRelease(fsys afero.Fs) bool { - // Always fetch latest release when using --version flag - if vf := rootCmd.Flag("version"); vf != nil && vf.Changed { - return true - } - if fi, err := fsys.Stat(utils.CliVersionPath); err == nil { - expiry := fi.ModTime().Add(time.Hour * 10) - // Skip if last checked is less than 10 hours ago - return time.Now().After(expiry) - } - return true -} - -func suggestUpgrade(version string) string { - const guide = "https://supabase.com/docs/guides/cli/getting-started#updating-the-supabase-cli" - return fmt.Sprintf(`A new version of Supabase CLI is available: %s (currently installed v%s) -We recommend updating regularly for new features and bug fixes: %s`, utils.Yellow(version), utils.Version, utils.Bold(guide)) -} - -func recoverAndExit() { - err := recover() - if err == nil { - return - } - var msg string - switch err := err.(type) { - case string: - msg = err - case error: - if !errors.Is(err, context.Canceled) && - len(utils.CmdSuggestion) == 0 && - !viper.GetBool("DEBUG") { - utils.CmdSuggestion = utils.SuggestDebugFlag - } - if e, ok := err.(*errors.Error); ok && len(utils.Version) == 0 { - fmt.Fprintln(os.Stderr, string(e.Stack())) - } - msg = err.Error() - default: - msg = fmt.Sprintf("%#v", err) - } - // Log error to console - fmt.Fprintln(os.Stderr, utils.Red(msg)) - if len(utils.CmdSuggestion) > 0 { - fmt.Fprintln(os.Stderr, utils.CmdSuggestion) - } - // Report error to sentry - if createTicket && len(utils.SentryDsn) > 0 { - sentry.ConfigureScope(addSentryScope) - eventId := sentry.CurrentHub().Recover(err) - if eventId != nil && sentry.Flush(2*time.Second) { - fmt.Fprintln(os.Stderr, "Sent crash report:", *eventId) - fmt.Fprintln(os.Stderr, "Quote the crash ID above when filing a bug report: https://github.com/supabase/cli/issues/new/choose") - } - } - os.Exit(1) -} - -func init() { - cobra.OnInitialize(func() { - viper.SetEnvPrefix("SUPABASE") - viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) - viper.AutomaticEnv() - }) - - // Shell tab-completion is fully native in the TS shim now (CLI-1965); the - // TS entrypoint intercepts completion/__complete/__completeNoDesc before - // ever delegating to this binary, so cobra's own completion command is - // unreachable dead weight here. This only removes the visible - // `completion ` command — cobra's ExecuteC() unconditionally - // (re-)registers the hidden __complete/__completeNoDesc responder on - // every run with no opt-out (command.go's initCompleteCmd), so that - // protocol handler stays present but, same as above, unreachable through - // the shipped CLI. - rootCmd.CompletionOptions.DisableDefaultCmd = true - - flags := rootCmd.PersistentFlags() - flags.Bool("yes", false, "answer yes to all prompts") - flags.Bool("debug", false, "output debug logs to stderr") - flags.String("workdir", "", "path to a Supabase project directory") - flags.Bool("experimental", false, "enable experimental features") - flags.String("network-id", "", "use the specified docker network instead of a generated one") - flags.String("profile", "supabase", "use a specific profile for connecting to Supabase API") - flags.VarP(&utils.OutputFormat, "output", "o", "output format of status variables") - flags.Var(&utils.DNSResolver, "dns-resolver", "lookup domain names using the specified resolver") - flags.BoolVar(&createTicket, "create-ticket", false, "create a support ticket for any CLI error") - flags.VarP(&utils.AgentMode, "agent", "", "Override agent detection: yes, no, or auto (default auto)") - cobra.CheckErr(viper.BindPFlags(flags)) - - rootCmd.SetVersionTemplate("{{.Version}}\n") - rootCmd.AddGroup(&cobra.Group{ID: groupQuickStart, Title: "Quick Start:"}) - rootCmd.AddGroup(&cobra.Group{ID: groupLocalDev, Title: "Local Development:"}) - rootCmd.AddGroup(&cobra.Group{ID: groupManagementAPI, Title: "Management APIs:"}) -} - -// instantiate new rootCmd is a bit tricky with cobra, but it can be done later with the following -// approach for example: https://github.com/portworx/pxc/tree/master/cmd -func GetRootCmd() *cobra.Command { - return rootCmd -} - -func addSentryScope(scope *sentry.Scope) { - serviceImages := utils.Config.GetServiceImages() - imageToVersion := make(map[string]any, len(serviceImages)) - for _, image := range serviceImages { - parts := strings.Split(image, ":") - // Bypasses sentry's IP sanitization rule, ie. 15.1.0.147 - if net.ParseIP(parts[1]) != nil { - imageToVersion[parts[0]] = "v" + parts[1] - } else { - imageToVersion[parts[0]] = parts[1] - } - } - scope.SetContext("Services", imageToVersion) - scope.SetContext("Config", map[string]any{ - "Image Registry": utils.GetRegistry(), - "Project ID": flags.ProjectRef, - }) -} diff --git a/apps/cli-go/cmd/root_analytics.go b/apps/cli-go/cmd/root_analytics.go deleted file mode 100644 index bf9735ec3c..0000000000 --- a/apps/cli-go/cmd/root_analytics.go +++ /dev/null @@ -1,180 +0,0 @@ -package cmd - -import ( - "os" - "sort" - "strconv" - "strings" - - "github.com/google/uuid" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/supabase/cli/internal/telemetry" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/agent" - "golang.org/x/term" -) - -const ( - telemetrySafeValueAnnotation = "supabase.com/telemetry-safe-value" - redactedTelemetryValue = "" - maxTelemetryEnvValueLength = 80 -) - -func commandAnalyticsContext(cmd *cobra.Command) telemetry.CommandContext { - return telemetry.CommandContext{ - RunID: uuid.NewString(), - Command: commandName(cmd), - Flags: changedFlagValues(cmd), - } -} - -func commandName(cmd *cobra.Command) string { - path := strings.TrimSpace(cmd.CommandPath()) - rootName := strings.TrimSpace(cmd.Root().Name()) - if path == rootName || path == "" { - return rootName - } - return strings.TrimSpace(strings.TrimPrefix(path, rootName)) -} - -func changedFlagValues(cmd *cobra.Command) map[string]any { - flags := changedFlags(cmd) - if len(flags) == 0 { - return nil - } - values := make(map[string]any, len(flags)) - for _, flag := range flags { - values[flag.Name] = telemetryFlagValue(flag) - } - return values -} - -func changedFlags(cmd *cobra.Command) []*pflag.Flag { - seen := make(map[string]struct{}) - var result []*pflag.Flag - collect := func(flags *pflag.FlagSet) { - if flags == nil { - return - } - flags.Visit(func(flag *pflag.Flag) { - if _, ok := seen[flag.Name]; ok { - return - } - seen[flag.Name] = struct{}{} - result = append(result, flag) - }) - } - for current := cmd; current != nil; current = current.Parent() { - collect(current.PersistentFlags()) - } - collect(cmd.Flags()) - sort.Slice(result, func(i, j int) bool { - return result[i].Name < result[j].Name - }) - return result -} - -func markFlagTelemetrySafe(flag *pflag.Flag) { - if flag == nil { - return - } - if flag.Annotations == nil { - flag.Annotations = map[string][]string{} - } - flag.Annotations[telemetrySafeValueAnnotation] = []string{"true"} -} - -func telemetryFlagValue(flag *pflag.Flag) any { - if flag == nil { - return nil - } - if isTelemetrySafeFlag(flag) || isBooleanFlag(flag) || isEnumFlag(flag) { - return actualTelemetryFlagValue(flag) - } - return redactedTelemetryValue -} - -func isTelemetrySafeFlag(flag *pflag.Flag) bool { - if flag == nil || flag.Annotations == nil { - return false - } - values, ok := flag.Annotations[telemetrySafeValueAnnotation] - return ok && len(values) > 0 && values[0] == "true" -} - -func isBooleanFlag(flag *pflag.Flag) bool { - return flag != nil && flag.Value.Type() == "bool" -} - -func isEnumFlag(flag *pflag.Flag) bool { - if flag == nil { - return false - } - _, ok := flag.Value.(*utils.EnumFlag) - return ok -} - -func actualTelemetryFlagValue(flag *pflag.Flag) any { - if isBooleanFlag(flag) { - value, err := strconv.ParseBool(flag.Value.String()) - if err == nil { - return value - } - } - return flag.Value.String() -} - -func telemetryIsCI() bool { - return os.Getenv("CI") != "" || - os.Getenv("GITHUB_ACTIONS") != "" || - os.Getenv("BUILDKITE") != "" || - os.Getenv("TF_BUILD") != "" || - os.Getenv("JENKINS_URL") != "" || - os.Getenv("GITLAB_CI") != "" -} - -func telemetryIsTTY() bool { - return term.IsTerminal(int(os.Stdout.Fd())) //nolint:gosec // G115: stdout fd is a small int on supported platforms -} - -func telemetryIsAgent() bool { - return agent.IsAgent() -} - -func telemetryEnvSignals() map[string]any { - return envSignals(telemetry.EnvSignalPresenceKeys[:], telemetry.EnvSignalValueKeys[:]) -} - -func envSignals(presenceKeys []string, valueKeys []string) map[string]any { - signals := make(map[string]any, len(presenceKeys)+len(valueKeys)) - for _, key := range presenceKeys { - if hasTelemetryEnvValue(key) { - signals[key] = true - } - } - for _, key := range valueKeys { - if value := telemetryEnvValue(key); value != "" { - signals[key] = value - } - } - if len(signals) == 0 { - return nil - } - return signals -} - -func hasTelemetryEnvValue(key string) bool { - return strings.TrimSpace(os.Getenv(key)) != "" -} - -func telemetryEnvValue(key string) string { - value := strings.TrimSpace(os.Getenv(key)) - if value == "" { - return "" - } - if len(value) > maxTelemetryEnvValueLength { - return value[:maxTelemetryEnvValueLength] - } - return value -} diff --git a/apps/cli-go/cmd/root_test.go b/apps/cli-go/cmd/root_test.go deleted file mode 100644 index ea82156213..0000000000 --- a/apps/cli-go/cmd/root_test.go +++ /dev/null @@ -1,137 +0,0 @@ -package cmd - -import ( - "strings" - "testing" - - "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/telemetry" - "github.com/supabase/cli/internal/utils" -) - -func clearTelemetryEnv(t *testing.T) { - for _, key := range telemetry.EnvSignalPresenceKeys { - t.Setenv(key, "") - } - for _, key := range telemetry.EnvSignalValueKeys { - t.Setenv(key, "") - } -} - -func TestCommandAnalyticsContext(t *testing.T) { - root := &cobra.Command{Use: "supabase"} - var projectRef string - var password string - var debug bool - output := utils.EnumFlag{ - Allowed: []string{"json", "table"}, - Value: "table", - } - child := &cobra.Command{ - Use: "link", - RunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - } - root.PersistentFlags().BoolVar(&debug, "debug", false, "") - child.Flags().StringVar(&projectRef, "project-ref", "", "") - child.Flags().StringVar(&password, "password", "", "") - child.Flags().Var(&output, "output", "") - child.Flags().AddFlag(root.PersistentFlags().Lookup("debug")) - markFlagTelemetrySafe(child.Flags().Lookup("project-ref")) - root.AddCommand(child) - - require.NoError(t, root.PersistentFlags().Set("debug", "true")) - require.NoError(t, child.Flags().Set("project-ref", "proj_123")) - require.NoError(t, child.Flags().Set("password", "hunter2")) - require.NoError(t, child.Flags().Set("output", "json")) - - ctx := commandAnalyticsContext(child) - - assert.Equal(t, "link", ctx.Command) - assert.Equal(t, map[string]any{ - "debug": true, - "output": "json", - "password": redactedTelemetryValue, - "project-ref": "proj_123", - }, ctx.Flags) - assert.NotContains(t, ctx.Flags, "linked") - assert.NotEmpty(t, ctx.RunID) -} - -func TestCommandName(t *testing.T) { - root := &cobra.Command{Use: "supabase"} - parent := &cobra.Command{Use: "db"} - child := &cobra.Command{Use: "push"} - root.AddCommand(parent) - parent.AddCommand(child) - - assert.Equal(t, "db push", commandName(child)) - assert.Equal(t, "supabase", commandName(root)) -} - -func TestTelemetryIsAgent(t *testing.T) { - t.Run("returns true for agent env", func(t *testing.T) { - clearTelemetryEnv(t) - t.Setenv("CLAUDE_CODE", "1") - utils.AgentMode.Value = "auto" - t.Cleanup(func() { - utils.AgentMode.Value = "auto" - }) - - assert.True(t, telemetryIsAgent()) - }) - - t.Run("returns false with no agent env", func(t *testing.T) { - clearTelemetryEnv(t) - utils.AgentMode.Value = "auto" - t.Cleanup(func() { - utils.AgentMode.Value = "auto" - }) - - assert.False(t, telemetryIsAgent()) - }) -} - -func TestTelemetryEnvSignals(t *testing.T) { - clearTelemetryEnv(t) - t.Setenv("CURSOR_AGENT", "1") - t.Setenv("TERM_PROGRAM", " iTerm.app ") - - signals := telemetryEnvSignals() - - assert.Equal(t, true, signals["CURSOR_AGENT"]) - assert.Equal(t, "iTerm.app", signals["TERM_PROGRAM"]) - assert.NotContains(t, signals, "AI_AGENT") -} - -func TestEnvSignals(t *testing.T) { - clearTelemetryEnv(t) - t.Setenv("AI_AGENT", " ") - t.Setenv("TERM_PROGRAM", " iTerm.app ") - t.Setenv("TERM", strings.Repeat("x", 100)) - - signals := envSignals([]string{"AI_AGENT"}, []string{"TERM_PROGRAM", "TERM"}) - - assert.Equal(t, "iTerm.app", signals["TERM_PROGRAM"]) - assert.Equal(t, strings.Repeat("x", 80), signals["TERM"]) - assert.NotContains(t, signals, "AI_AGENT") -} - -func TestUpdateNotifierEnabled(t *testing.T) { - for value, wantEnabled := range map[string]bool{ - "": true, - "0": true, - "false": true, - "garbage": true, - "1": false, - "true": false, - "TRUE": false, - "t": false, - } { - t.Setenv("SUPABASE_NO_UPDATE_NOTIFIER", value) - assert.Equal(t, wantEnabled, updateNotifierEnabled(), "value %q", value) - } -} diff --git a/apps/cli-go/fsevents/fsevents.go b/apps/cli-go/fsevents/fsevents.go deleted file mode 100644 index d75d319324..0000000000 --- a/apps/cli-go/fsevents/fsevents.go +++ /dev/null @@ -1,117 +0,0 @@ -//go:build darwin - -// Package fsevents provides file system notifications on macOS. -package fsevents - -import ( - "syscall" - "time" -) - -// Event represents a single file system notification. -type Event struct { - // Path holds the path to the item that's changed, relative - // to its device's root. - // Use DeviceForPath to determine the absolute path that's - // being referred to. - Path string - - // Flags holds details what has happened. - Flags EventFlags - - // ID holds the event ID. - // - // Each event ID comes from the most recent event being reported - // in the corresponding directory named in the EventStream.Paths field - // Event IDs all come from a single global source. - // They are guaranteed to always be increasing, usually in leaps - // and bounds, even across system reboots and moving drives from - // one machine to another. If you were to - // stop processing events from this stream after this event - // and resume processing them later from a newly-created - // EventStream, this is the value you would pass for the - // EventStream.EventID along with Resume=true. - ID uint64 -} - -// DeviceForPath returns the device ID for the specified volume. -func DeviceForPath(path string) (int32, error) { - stat := syscall.Stat_t{} - if err := syscall.Lstat(path, &stat); err != nil { - return 0, err - } - return stat.Dev, nil -} - -// EventStream is the primary interface to FSEvents -// You can provide your own event channel if you wish (or one will be -// created on Start). -// -// es := &EventStream{Paths: []string{"/tmp"}, Flags: 0} -// es.Start() -// es.Stop() -// ... -type EventStream struct { - // Events holds the channel on which events will be sent. - // It's initialized by EventStream.Start if nil. - Events chan []Event - - // Paths holds the set of paths to watch, each - // specifying the root of a filesystem hierarchy to be - // watched for modifications. - Paths []string - - // Flags specifies what events to receive on the stream. - Flags CreateFlags - - // Resume specifies that watching should resume from the event - // specified by EventID. - Resume bool - - // EventID holds the most recent event ID. - // - // NOTE: this is updated asynchronously by the - // watcher and should not be accessed while - // the stream has been started. - EventID uint64 - - // Latency holds the number of seconds the service should wait after hearing - // about an event from the kernel before passing it along to the - // client via its callback. Specifying a larger value may result - // in more effective temporal coalescing, resulting in fewer - // callbacks and greater overall efficiency. - Latency time.Duration - - // When Device is non-zero, the watcher will watch events on the - // device with this ID, and the paths in the Paths field are - // interpreted relative to the device's root. - // - // The device ID is the same as the st_dev field from a stat - // structure of a file on that device or the f_fsid[0] field of - // a statfs structure. - Device int32 -} - -// Start listening to an event stream. This creates es.Events if it's not already -// a valid channel. -func (es *EventStream) Start() error { - return nil -} - -// Flush flushes events that have occurred but haven't been delivered. -// If sync is true, it will block until all the events have been delivered, -// otherwise it will return immediately. -func (es *EventStream) Flush(sync bool) { -} - -// Stop stops listening to the event stream. -func (es *EventStream) Stop() { -} - -// Restart restarts the event listener. This -// can be used to change the current watch flags. -func (es *EventStream) Restart() error { - es.Stop() - es.Resume = true - return es.Start() -} diff --git a/apps/cli-go/fsevents/go.mod b/apps/cli-go/fsevents/go.mod deleted file mode 100644 index 41e670c561..0000000000 --- a/apps/cli-go/fsevents/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -go 1.24.10 - -module github.com/fsnotify/fsevents diff --git a/apps/cli-go/fsevents/wrap.go b/apps/cli-go/fsevents/wrap.go deleted file mode 100644 index f12125b7e3..0000000000 --- a/apps/cli-go/fsevents/wrap.go +++ /dev/null @@ -1,40 +0,0 @@ -//go:build darwin - -package fsevents - -// CreateFlags specifies what events will be seen in an event stream. -type CreateFlags uint32 - -const ( - // IgnoreSelf doesn't send events triggered by the current process (macOS 10.6+). - // - // Don't send events that were triggered by the current process. - // This is useful for reducing the volume of events that are - // sent. It is only useful if your process might modify the file - // system hierarchy beneath the path(s) being monitored. Note: - // this has no effect on historical events, i.e., those - // delivered before the HistoryDone sentinel event. - IgnoreSelf = CreateFlags(0) - - // FileEvents sends events about individual files, generating significantly - // more events (macOS 10.7+) than directory level notifications. - FileEvents = CreateFlags(1) -) - -// EventFlags passed to the FSEventStreamCallback function. -// These correspond directly to the flags as described here: -// https://developer.apple.com/documentation/coreservices/1455361-fseventstreameventflags -type EventFlags uint32 - -const ( - // ItemCreated indicates that a file or directory has been created. - ItemCreated = EventFlags(0) - - // ItemIsDir indicates that the item is a directory. - ItemIsDir = EventFlags(1) -) - -// LatestEventID returns the most recently generated event ID, system-wide. -func LatestEventID() uint64 { - return 0 -} diff --git a/apps/cli-go/go.mod b/apps/cli-go/go.mod deleted file mode 100644 index add818204b..0000000000 --- a/apps/cli-go/go.mod +++ /dev/null @@ -1,379 +0,0 @@ -module github.com/supabase/cli - -go 1.26.0 - -require ( - github.com/BurntSushi/toml v1.6.0 - github.com/Netflix/go-env v0.1.2 - github.com/andybalholm/brotli v1.2.3 - github.com/cenkalti/backoff/v4 v4.3.0 - github.com/charmbracelet/bubbles v1.0.0 - github.com/charmbracelet/bubbletea v1.3.10 - github.com/charmbracelet/glamour v1.0.0 - github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 - github.com/compose-spec/compose-go/v2 v2.9.1 - github.com/containerd/errdefs v1.0.0 - github.com/containers/common v0.64.2 - github.com/docker/cli v28.5.2+incompatible - github.com/docker/docker v28.5.2+incompatible - github.com/docker/go-connections v0.8.1 - github.com/docker/go-units v0.5.0 - github.com/getsentry/sentry-go v0.49.0 - github.com/go-errors/errors v1.5.1 - github.com/go-git/go-git/v5 v5.19.2 - github.com/go-playground/validator/v10 v10.30.4 - github.com/go-viper/mapstructure/v2 v2.5.0 - github.com/google/go-github/v62 v62.0.0 - github.com/google/go-querystring v1.2.0 - github.com/google/uuid v1.6.0 - github.com/h2non/gock v1.2.0 - github.com/jackc/pgconn v1.14.3 - github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 - github.com/jackc/pgproto3/v2 v2.3.3 - github.com/jackc/pgx/v4 v4.18.3 - github.com/joho/godotenv v1.5.1 - github.com/muesli/reflow v0.3.0 - github.com/oapi-codegen/nullable v1.2.0 - github.com/olekukonko/tablewriter v1.1.4 - github.com/posthog/posthog-go v1.25.1 - github.com/spf13/afero v1.15.0 - github.com/spf13/cobra v1.10.2 - github.com/spf13/pflag v1.0.10 - github.com/spf13/viper v1.21.0 - github.com/stretchr/testify v1.12.1 - github.com/stripe/pg-schema-diff v1.0.9 - github.com/supabase/cli/pkg v1.0.0 - github.com/zalando/go-keyring v0.2.8 - go.opentelemetry.io/otel v1.46.0 - golang.org/x/mod v0.41.0 - golang.org/x/net v0.58.0 - golang.org/x/oauth2 v0.37.0 - golang.org/x/term v0.45.0 - google.golang.org/grpc v1.83.2 - gopkg.in/yaml.v3 v3.0.1 -) - -require ( - 4d63.com/gocheckcompilerdirectives v1.3.0 // indirect - 4d63.com/gochecknoglobals v0.2.2 // indirect - charm.land/lipgloss/v2 v2.0.3 // indirect - codeberg.org/chavacava/garif v0.2.0 // indirect - codeberg.org/polyfloyd/go-errorlint v1.9.0 // indirect - dario.cat/mergo v1.0.2 // indirect - dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect - dev.gaijin.team/go/golib v0.6.0 // indirect - github.com/4meepo/tagalign v1.4.3 // indirect - github.com/Abirdcfly/dupword v0.1.7 // indirect - github.com/AdminBenni/iota-mixing v1.0.0 // indirect - github.com/AlwxSin/noinlineerr v1.0.5 // indirect - github.com/Antonboom/errname v1.1.1 // indirect - github.com/Antonboom/nilnil v1.1.1 // indirect - github.com/Antonboom/testifylint v1.6.4 // indirect - github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect - github.com/ClickHouse/clickhouse-go-linter v1.2.0 // indirect - github.com/Djarvur/go-err113 v0.1.1 // indirect - github.com/Masterminds/semver/v3 v3.5.0 // indirect - github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/MirrexOne/unqueryvet v1.5.4 // indirect - github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect - github.com/ProtonMail/go-crypto v1.3.0 // indirect - github.com/alecthomas/chroma/v2 v2.24.1 // indirect - github.com/alecthomas/go-check-sumtype v0.3.1 // indirect - github.com/alexkohler/nakedret/v2 v2.0.6 // indirect - github.com/alexkohler/prealloc v1.1.0 // indirect - github.com/alfatraining/structtag v1.0.0 // indirect - github.com/alingse/asasalint v0.0.11 // indirect - github.com/alingse/nilnesserr v0.2.0 // indirect - github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect - github.com/ashanbrown/forbidigo/v2 v2.3.1 // indirect - github.com/ashanbrown/makezero/v2 v2.2.1 // indirect - github.com/atotto/clipboard v0.1.4 // indirect - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/aymerick/douceur v0.2.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/bitfield/gotestdox v0.2.2 // indirect - github.com/bkielbasa/cyclop v1.2.3 // indirect - github.com/blizzy78/varnamelen v0.8.0 // indirect - github.com/bombsimon/wsl/v4 v4.7.0 // indirect - github.com/bombsimon/wsl/v5 v5.8.0 // indirect - github.com/breml/bidichk v0.3.3 // indirect - github.com/breml/errchkjson v0.4.1 // indirect - github.com/butuzov/ireturn v0.4.1 // indirect - github.com/butuzov/mirror v1.3.0 // indirect - github.com/catenacyber/perfsprint v0.10.1 // indirect - github.com/ccojocar/zxcvbn-go v1.0.4 // indirect - github.com/cenkalti/backoff/v5 v5.0.3 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/charithe/durationcheck v0.0.11 // indirect - github.com/charmbracelet/colorprofile v0.4.3 // indirect - github.com/charmbracelet/harmonica v0.2.0 // indirect - github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318 // indirect - github.com/charmbracelet/x/ansi v0.11.7 // indirect - github.com/charmbracelet/x/cellbuf v0.0.15 // indirect - github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect - github.com/charmbracelet/x/term v0.2.2 // indirect - github.com/charmbracelet/x/termios v0.1.1 // indirect - github.com/charmbracelet/x/windows v0.2.2 // indirect - github.com/ckaznocha/intrange v0.3.1 // indirect - github.com/clipperhouse/displaywidth v0.11.0 // indirect - github.com/clipperhouse/uax29/v2 v2.7.0 // indirect - github.com/cloudflare/circl v1.6.3 // indirect - github.com/containerd/errdefs/pkg v0.3.0 // indirect - github.com/containers/storage v1.59.1 // indirect - github.com/creack/pty v1.1.24 // indirect - github.com/curioswitch/go-reassign v0.3.0 // indirect - github.com/cyphar/filepath-securejoin v0.6.1 // indirect - github.com/daixiang0/gci v0.13.7 // indirect - github.com/danieljoos/wincred v1.2.3 // indirect - github.com/dave/dst v0.27.3 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect - github.com/denis-tingaikin/go-header v0.5.0 // indirect - github.com/distribution/reference v0.6.0 // indirect - github.com/dlclark/regexp2 v1.12.0 // indirect - github.com/dnephin/pflag v1.0.7 // indirect - github.com/docker/docker-credential-helpers v0.9.5 // indirect - github.com/docker/go-metrics v0.0.1 // indirect - github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect - github.com/ecies/go/v2 v2.0.11 // indirect - github.com/emirpasic/gods v1.18.1 // indirect - github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect - github.com/ethereum/go-ethereum v1.17.0 // indirect - github.com/ettle/strcase v0.2.0 // indirect - github.com/fatih/color v1.19.0 // indirect - github.com/fatih/structtag v1.2.0 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/firefart/nonamedreturns v1.0.6 // indirect - github.com/fsnotify/fsnotify v1.10.1 // indirect - github.com/fvbommel/sortorder v1.1.0 // indirect - github.com/fzipp/gocyclo v0.6.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.15 // indirect - github.com/getkin/kin-openapi v0.144.0 // indirect - github.com/ghostiam/protogetter v0.3.20 // indirect - github.com/go-critic/go-critic v0.14.3 // indirect - github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.9.0 // indirect - github.com/go-logr/logr v1.4.4 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.22.5 // indirect - github.com/go-openapi/swag/jsonname v0.25.5 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-toolsmith/astcast v1.1.0 // indirect - github.com/go-toolsmith/astcopy v1.1.0 // indirect - github.com/go-toolsmith/astequal v1.2.0 // indirect - github.com/go-toolsmith/astfmt v1.1.0 // indirect - github.com/go-toolsmith/astp v1.1.0 // indirect - github.com/go-toolsmith/strparse v1.1.0 // indirect - github.com/go-toolsmith/typep v1.1.0 // indirect - github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect - github.com/gobwas/glob v0.2.3 // indirect - github.com/goccy/go-json v0.10.6 // indirect - github.com/godbus/dbus/v5 v5.2.2 // indirect - github.com/godoc-lint/godoc-lint v0.11.2 // indirect - github.com/gofrs/flock v0.13.0 // indirect - github.com/golang-jwt/jwt/v5 v5.3.1 // indirect - github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect - github.com/golangci/asciicheck v0.5.0 // indirect - github.com/golangci/dupl v0.0.0-20260401084720-c99c5cf5c202 // indirect - github.com/golangci/go-printf-func-name v0.1.1 // indirect - github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect - github.com/golangci/golangci-lint/v2 v2.12.2 // indirect - github.com/golangci/golines v0.15.0 // indirect - github.com/golangci/misspell v0.8.0 // indirect - github.com/golangci/plugin-module-register v0.1.2 // indirect - github.com/golangci/revgrep v0.8.0 // indirect - github.com/golangci/rowserrcheck v0.0.0-20260419091836-c5f79b8a11ba // indirect - github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect - github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect - github.com/google/go-cmp v0.7.0 // indirect - github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/gordonklaus/ineffassign v0.2.0 // indirect - github.com/gorilla/css v1.0.1 // indirect - github.com/gostaticanalysis/analysisutil v0.7.1 // indirect - github.com/gostaticanalysis/comment v1.5.0 // indirect - github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect - github.com/gostaticanalysis/nilerr v0.1.2 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect - github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect - github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect - github.com/hashicorp/go-version v1.9.0 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/hexops/gotextdiff v1.0.3 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgio v1.0.0 // indirect - github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 // indirect - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgtype v1.14.4 // indirect - github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/jgautheron/goconst v1.10.0 // indirect - github.com/jjti/go-spancheck v0.6.5 // indirect - github.com/julz/importas v0.2.0 // indirect - github.com/karamaru-alpha/copyloopvar v1.2.2 // indirect - github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/kisielk/errcheck v1.10.0 // indirect - github.com/kkHAIKE/contextcheck v1.1.6 // indirect - github.com/klauspost/compress v1.18.5 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/kulti/thelper v0.7.1 // indirect - github.com/kunwardeep/paralleltest v1.0.15 // indirect - github.com/lasiar/canonicalheader v1.1.2 // indirect - github.com/ldez/exptostd v0.4.5 // indirect - github.com/ldez/gomoddirectives v0.8.0 // indirect - github.com/ldez/grignotin v0.10.1 // indirect - github.com/ldez/structtags v0.6.1 // indirect - github.com/ldez/tagliatelle v0.7.2 // indirect - github.com/ldez/usetesting v0.5.0 // indirect - github.com/leodido/go-urn v1.5.0 // indirect - github.com/leonklingele/grouper v1.1.2 // indirect - github.com/lib/pq v1.12.3 // indirect - github.com/lucasb-eyer/go-colorful v1.4.0 // indirect - github.com/macabu/inamedparam v0.2.0 // indirect - github.com/manuelarte/embeddedstructfieldcheck v0.4.0 // indirect - github.com/manuelarte/funcorder v0.6.0 // indirect - github.com/maratori/testableexamples v1.0.1 // indirect - github.com/maratori/testpackage v1.1.2 // indirect - github.com/matoous/godox v1.1.0 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.23 // indirect - github.com/mattn/go-shellwords v1.0.12 // indirect - github.com/mgechev/revive v1.15.0 // indirect - github.com/microcosm-cc/bluemonday v1.0.27 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect - github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/sys/atomicwriter v0.1.0 // indirect - github.com/moby/sys/sequential v0.6.0 // indirect - github.com/moby/term v0.5.2 // indirect - github.com/moricho/tparallel v0.3.2 // indirect - github.com/morikuni/aec v1.1.0 // indirect - github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect - github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.16.0 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/nakabonne/nestif v0.3.1 // indirect - github.com/nishanths/exhaustive v0.12.0 // indirect - github.com/nishanths/predeclared v0.2.2 // indirect - github.com/nunnatsa/ginkgolinter v0.23.0 // indirect - github.com/oapi-codegen/oapi-codegen/v2 v2.7.1 // indirect - github.com/oapi-codegen/runtime v1.7.0 // indirect - github.com/oasdiff/yaml v0.1.1 // indirect - github.com/oasdiff/yaml3 v0.0.14 // indirect - github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect - github.com/olekukonko/errors v1.2.0 // indirect - github.com/olekukonko/ll v0.1.6 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/otiai10/copy v1.14.1 // indirect - github.com/pelletier/go-toml/v2 v2.3.1 // indirect - github.com/pjbgf/sha1cd v0.6.0 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.23.2 // indirect - github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.67.4 // indirect - github.com/prometheus/procfs v0.19.2 // indirect - github.com/quasilyte/go-ruleguard v0.4.5 // indirect - github.com/quasilyte/go-ruleguard/dsl v0.3.23 // indirect - github.com/quasilyte/gogrep v0.5.0 // indirect - github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect - github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect - github.com/raeperd/recvcheck v0.2.0 // indirect - github.com/rivo/uniseg v0.4.7 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/ryancurrah/gomodguard v1.4.1 // indirect - github.com/ryancurrah/gomodguard/v2 v2.1.3 // indirect - github.com/ryanrolds/sqlclosecheck v0.6.0 // indirect - github.com/sagikazarmark/locafero v0.12.0 // indirect - github.com/sahilm/fuzzy v0.1.1 // indirect - github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect - github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect - github.com/sashamelentyev/interfacebloat v1.1.0 // indirect - github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect - github.com/securego/gosec/v2 v2.26.1 // indirect - github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect - github.com/sirupsen/logrus v1.9.4 // indirect - github.com/sivchari/containedctx v1.0.3 // indirect - github.com/skeema/knownhosts v1.3.1 // indirect - github.com/sonatard/noctx v0.5.1 // indirect - github.com/sourcegraph/go-diff v0.8.0 // indirect - github.com/speakeasy-api/jsonpath v0.6.3 // indirect - github.com/speakeasy-api/openapi v1.19.2 // indirect - github.com/spf13/cast v1.10.0 // indirect - github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect - github.com/stbenjam/no-sprintf-host-port v0.3.1 // indirect - github.com/stretchr/objx v0.5.3 // indirect - github.com/subosito/gotenv v1.6.0 // indirect - github.com/tetafro/godot v1.5.6 // indirect - github.com/theupdateframework/notary v0.7.0 // indirect - github.com/tidwall/jsonc v0.3.3 // indirect - github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4 // indirect - github.com/timonwong/loggercheck v0.11.0 // indirect - github.com/tomarrell/wrapcheck/v2 v2.12.0 // indirect - github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect - github.com/ultraware/funlen v0.2.0 // indirect - github.com/ultraware/whitespace v0.2.0 // indirect - github.com/uudashr/gocognit v1.2.1 // indirect - github.com/uudashr/iface v1.4.2 // indirect - github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect - github.com/xanzy/ssh-agent v0.3.3 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect - github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xeipuuv/gojsonschema v1.2.0 // indirect - github.com/xen0n/gosmopolitan v1.3.0 // indirect - github.com/xhit/go-str2duration/v2 v2.1.0 // indirect - github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - github.com/yagipy/maintidx v1.0.0 // indirect - github.com/yeya24/promlinter v0.3.0 // indirect - github.com/ykadowak/zerologlint v0.1.5 // indirect - github.com/yuin/goldmark v1.7.13 // indirect - github.com/yuin/goldmark-emoji v1.0.6 // indirect - gitlab.com/bosi/decorder v0.4.2 // indirect - go-simpler.org/musttag v0.14.0 // indirect - go-simpler.org/sloglint v0.12.0 // indirect - go.augendre.info/arangolint v0.4.0 // indirect - go.augendre.info/fatcontext v0.9.0 // indirect - go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect - go.opentelemetry.io/otel/metric v1.46.0 // indirect - go.opentelemetry.io/otel/sdk v1.44.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect - go.opentelemetry.io/otel/trace v1.46.0 // indirect - go.opentelemetry.io/proto/otlp v1.10.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect - go.yaml.in/yaml/v2 v2.4.3 // indirect - go.yaml.in/yaml/v3 v3.0.5 // indirect - golang.org/x/crypto v0.55.0 // indirect - golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 // indirect - golang.org/x/sync v0.22.0 // indirect - golang.org/x/sys v0.47.0 // indirect - golang.org/x/text v0.41.0 // indirect - golang.org/x/tools v0.49.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect - google.golang.org/protobuf v1.36.11 // indirect - gopkg.in/warnings.v0 v0.1.2 // indirect - gotest.tools/gotestsum v1.12.2 // indirect - honnef.co/go/tools v0.7.0 // indirect - mvdan.cc/gofumpt v0.9.2 // indirect - mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 // indirect -) - -replace github.com/supabase/cli/pkg v1.0.0 => ./pkg - -replace github.com/fsnotify/fsevents v0.2.0 => ./fsevents - -tool ( - github.com/golangci/golangci-lint/v2/cmd/golangci-lint - github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen - gotest.tools/gotestsum -) diff --git a/apps/cli-go/go.sum b/apps/cli-go/go.sum deleted file mode 100644 index 9cf1bac004..0000000000 --- a/apps/cli-go/go.sum +++ /dev/null @@ -1,1232 +0,0 @@ -4d63.com/gocheckcompilerdirectives v1.3.0 h1:Ew5y5CtcAAQeTVKUVFrE7EwHMrTO6BggtEj8BZSjZ3A= -4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY= -4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU= -4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0= -charm.land/lipgloss/v2 v2.0.3 h1:yM2zJ4Cf5Y51b7RHIwioil4ApI/aypFXXVHSwlM6RzU= -charm.land/lipgloss/v2 v2.0.3/go.mod h1:7myLU9iG/3xluAWzpY/fSxYYHCgoKTie7laxk6ATwXA= -codeberg.org/chavacava/garif v0.2.0 h1:F0tVjhYbuOCnvNcU3YSpO6b3Waw6Bimy4K0mM8y6MfY= -codeberg.org/chavacava/garif v0.2.0/go.mod h1:P2BPbVbT4QcvLZrORc2T29szK3xEOlnl0GiPTJmEqBQ= -codeberg.org/polyfloyd/go-errorlint v1.9.0 h1:VkdEEmA1VBpH6ecQoMR4LdphVI3fA4RrCh2an7YmodI= -codeberg.org/polyfloyd/go-errorlint v1.9.0/go.mod h1:GPRRu2LzVijNn4YkrZYJfatQIdS+TrcK8rL5Xs24qw8= -dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= -dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= -dev.gaijin.team/go/exhaustruct/v4 v4.0.0 h1:873r7aNneqoBB3IaFIzhvt2RFYTuHgmMjoKfwODoI1Y= -dev.gaijin.team/go/exhaustruct/v4 v4.0.0/go.mod h1:aZ/k2o4Y05aMJtiux15x8iXaumE88YdiB0Ai4fXOzPI= -dev.gaijin.team/go/golib v0.6.0 h1:v6nnznFTs4bppib/NyU1PQxobwDHwCXXl15P7DV5Zgo= -dev.gaijin.team/go/golib v0.6.0/go.mod h1:uY1mShx8Z/aNHWDyAkZTkX+uCi5PdX7KsG1eDQa2AVE= -github.com/4meepo/tagalign v1.4.3 h1:Bnu7jGWwbfpAie2vyl63Zup5KuRv21olsPIha53BJr8= -github.com/4meepo/tagalign v1.4.3/go.mod h1:00WwRjiuSbrRJnSVeGWPLp2epS5Q/l4UEy0apLLS37c= -github.com/Abirdcfly/dupword v0.1.7 h1:2j8sInznrje4I0CMisSL6ipEBkeJUJAmK1/lfoNGWrQ= -github.com/Abirdcfly/dupword v0.1.7/go.mod h1:K0DkBeOebJ4VyOICFdppB23Q0YMOgVafM0zYW0n9lF4= -github.com/AdminBenni/iota-mixing v1.0.0 h1:Os6lpjG2dp/AE5fYBPAA1zfa2qMdCAWwPMCgpwKq7wo= -github.com/AdminBenni/iota-mixing v1.0.0/go.mod h1:i4+tpAaB+qMVIV9OK3m4/DAynOd5bQFaOu+2AhtBCNY= -github.com/AlwxSin/noinlineerr v1.0.5 h1:RUjt63wk1AYWTXtVXbSqemlbVTb23JOSRiNsshj7TbY= -github.com/AlwxSin/noinlineerr v1.0.5/go.mod h1:+QgkkoYrMH7RHvcdxdlI7vYYEdgeoFOVjU9sUhw/rQc= -github.com/Antonboom/errname v1.1.1 h1:bllB7mlIbTVzO9jmSWVWLjxTEbGBVQ1Ff/ClQgtPw9Q= -github.com/Antonboom/errname v1.1.1/go.mod h1:gjhe24xoxXp0ScLtHzjiXp0Exi1RFLKJb0bVBtWKCWQ= -github.com/Antonboom/nilnil v1.1.1 h1:9Mdr6BYd8WHCDngQnNVV0b554xyisFioEKi30sksufQ= -github.com/Antonboom/nilnil v1.1.1/go.mod h1:yCyAmSw3doopbOWhJlVci+HuyNRuHJKIv6V2oYQa8II= -github.com/Antonboom/testifylint v1.6.4 h1:gs9fUEy+egzxkEbq9P4cpcMB6/G0DYdMeiFS87UiqmQ= -github.com/Antonboom/testifylint v1.6.4/go.mod h1:YO33FROXX2OoUfwjz8g+gUxQXio5i9qpVy7nXGbxDD4= -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= -github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/ClickHouse/clickhouse-go-linter v1.2.0 h1:zbm174up3hTKjp0wKZVnTzRiG7tSF5XZF0FJG/MuCBI= -github.com/ClickHouse/clickhouse-go-linter v1.2.0/go.mod h1:pLorS7ffPTfuUV9M0SJgfHA/h/WQPQUk2FWG9x74cQ4= -github.com/Djarvur/go-err113 v0.1.1 h1:eHfopDqXRwAi+YmCUas75ZE0+hoBHJ2GQNLYRSxao4g= -github.com/Djarvur/go-err113 v0.1.1/go.mod h1:IaWJdYFLg76t2ihfflPZnM1LIQszWOsFDh2hhhAVF6k= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= -github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/MirrexOne/unqueryvet v1.5.4 h1:38QOxShO7JmMWT+eCdDMbcUgGCOeJphVkzzRgyLJgsQ= -github.com/MirrexOne/unqueryvet v1.5.4/go.mod h1:fs9Zq6eh1LRIhsDIsxf9PONVUjYdFHdtkHIgZdJnyPU= -github.com/Netflix/go-env v0.1.2 h1:0DRoLR9lECQ9Zqvkswuebm3jJ/2enaDX6Ei8/Z+EnK0= -github.com/Netflix/go-env v0.1.2/go.mod h1:WlIhYi++8FlKNJtrop1mjXYAJMzv1f43K4MqCoh0yGE= -github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4= -github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo= -github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= -github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= -github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= -github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= -github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= -github.com/alecthomas/chroma/v2 v2.24.1 h1:m5ffpfZbIb++k8AqFEKy9uVgY12xIQtBsQlc6DfZJQM= -github.com/alecthomas/chroma/v2 v2.24.1/go.mod h1:l+ohZ9xRXIbGe7cIW+YZgOGbvuVLjMps/FYN/CwuabI= -github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU= -github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E= -github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= -github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alexkohler/nakedret/v2 v2.0.6 h1:ME3Qef1/KIKr3kWX3nti3hhgNxw6aqN5pZmQiFSsuzQ= -github.com/alexkohler/nakedret/v2 v2.0.6/go.mod h1:l3RKju/IzOMQHmsEvXwkqMDzHHvurNQfAgE1eVmT40Q= -github.com/alexkohler/prealloc v1.1.0 h1:cKGRBqlXw5iyQGLYhrXrDlcHxugXpTq4tQ5c91wkf8M= -github.com/alexkohler/prealloc v1.1.0/go.mod h1:fT39Jge3bQrfA7nPMDngUfvUbQGQeJyGQnR+913SCig= -github.com/alfatraining/structtag v1.0.0 h1:2qmcUqNcCoyVJ0up879K614L9PazjBSFruTB0GOFjCc= -github.com/alfatraining/structtag v1.0.0/go.mod h1:p3Xi5SwzTi+Ryj64DqjLWz7XurHxbGsq6y3ubePJPus= -github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= -github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= -github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEWd/w= -github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= -github.com/andybalholm/brotli v1.2.3 h1:8H1qwOkl2LPfjf3YezB90JnCliZb6SInJ/OJkEbA5NQ= -github.com/andybalholm/brotli v1.2.3/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= -github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/ashanbrown/forbidigo/v2 v2.3.1 h1:KAZijvQ7zeIBKbhikT4jCm0TLYXC4u78bTiLh/8JROI= -github.com/ashanbrown/forbidigo/v2 v2.3.1/go.mod h1:2QDkLTzU6TV937eFROamXrW92M3paehdae4HCDCOZCM= -github.com/ashanbrown/makezero/v2 v2.2.1 h1:A7uU8dgB1PA9aelTxHMfHIQ8Qev8AB3JLxJUBUsejqM= -github.com/ashanbrown/makezero/v2 v2.2.1/go.mod h1:aEGT/9q3S8DHeE57C88z2a6xydvgx8J5hgXIGWgo0MY= -github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= -github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= -github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o= -github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w= -github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= -github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bitfield/gotestdox v0.2.2 h1:x6RcPAbBbErKLnapz1QeAlf3ospg8efBsedU93CDsnE= -github.com/bitfield/gotestdox v0.2.2/go.mod h1:D+gwtS0urjBrzguAkTM2wodsTQYFHdpx8eqRJ3N+9pY= -github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w= -github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo= -github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= -github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= -github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bombsimon/wsl/v4 v4.7.0 h1:1Ilm9JBPRczjyUs6hvOPKvd7VL1Q++PL8M0SXBDf+jQ= -github.com/bombsimon/wsl/v4 v4.7.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg= -github.com/bombsimon/wsl/v5 v5.8.0 h1:JTkyfs4yl8SPejrCF2GdABXE+mO1WvM7iUYzRWlsxDs= -github.com/bombsimon/wsl/v5 v5.8.0/go.mod h1:AbOLsulgkqP4ZnitHf9gwPtCOGlrzkk0jb0uNxRSY0o= -github.com/breml/bidichk v0.3.3 h1:WSM67ztRusf1sMoqH6/c4OBCUlRVTKq+CbSeo0R17sE= -github.com/breml/bidichk v0.3.3/go.mod h1:ISbsut8OnjB367j5NseXEGGgO/th206dVa427kR8YTE= -github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDwg= -github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s= -github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/butuzov/ireturn v0.4.1 h1:vWb3NO4t77iku/sjCQ/2pHTQeOmxEhjIriJqRLg1Y+I= -github.com/butuzov/ireturn v0.4.1/go.mod h1:q+DXKzTDV5guNuXLnIab9fKXizTn2miZHLhxH7V/GB4= -github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc= -github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI= -github.com/catenacyber/perfsprint v0.10.1 h1:u7Riei30bk46XsG8nknMhKLXG9BcXz3+3tl/WpKm0PQ= -github.com/catenacyber/perfsprint v0.10.1/go.mod h1:DJTGsi/Zufpuus6XPGJyKOTMELe347o6akPvWG9Zcsc= -github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNrUcc= -github.com/ccojocar/zxcvbn-go v1.0.4/go.mod h1:3GxGX+rHmueTUMvm5ium7irpyjmm7ikxYFOSJB21Das= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= -github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charithe/durationcheck v0.0.11 h1:g1/EX1eIiKS57NTWsYtHDZ/APfeXKhye1DidBcABctk= -github.com/charithe/durationcheck v0.0.11/go.mod h1:x5iZaixRNl8ctbM+3B2RrPG5t856TxRyVQEnbIEM2X4= -github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc= -github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E= -github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= -github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= -github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q= -github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q= -github.com/charmbracelet/glamour v1.0.0 h1:AWMLOVFHTsysl4WV8T8QgkQ0s/ZNZo7CiE4WKhk8l08= -github.com/charmbracelet/glamour v1.0.0/go.mod h1:DSdohgOBkMr2ZQNhw4LZxSGpx3SvpeujNoXrQyH2hxo= -github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= -github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE= -github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA= -github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318 h1:OqDqxQZliC7C8adA7KjelW3OjtAxREfeHkNcd66wpeI= -github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318/go.mod h1:Y6kE2GzHfkyQQVCSL9r2hwokSrIlHGzZG+71+wDYSZI= -github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI= -github.com/charmbracelet/x/ansi v0.11.7/go.mod h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ= -github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI= -github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q= -github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA= -github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I= -github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI= -github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU= -github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= -github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= -github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY= -github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo= -github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM= -github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/ckaznocha/intrange v0.3.1 h1:j1onQyXvHUsPWujDH6WIjhyH26gkRt/txNlV7LspvJs= -github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nWh/s6SHIJJk= -github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8= -github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0= -github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= -github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= -github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= -github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= -github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= -github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/compose-spec/compose-go/v2 v2.9.1 h1:8UwI+ujNU+9Ffkf/YgAm/qM9/eU7Jn8nHzWG721W4rs= -github.com/compose-spec/compose-go/v2 v2.9.1/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE= -github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= -github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= -github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containers/common v0.64.2 h1:1xepE7QwQggUXxmyQ1Dbh6Cn0yd7ktk14sN3McSWf5I= -github.com/containers/common v0.64.2/go.mod h1:o29GfYy4tefUuShm8mOn2AiL5Mpzdio+viHI7n24KJ4= -github.com/containers/storage v1.59.1 h1:11Zu68MXsEQGBBd+GadPrHPpWeqjKS8hJDGiAHgIqDs= -github.com/containers/storage v1.59.1/go.mod h1:KoAYHnAjP3/cTsRS+mmWZGkufSY2GACiKQ4V3ZLQnR0= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= -github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= -github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs= -github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88= -github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= -github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= -github.com/daixiang0/gci v0.13.7 h1:+0bG5eK9vlI08J+J/NWGbWPTNiXPG4WhNLJOkSxWITQ= -github.com/daixiang0/gci v0.13.7/go.mod h1:812WVN6JLFY9S6Tv76twqmNqevN0pa3SX3nih0brVzQ= -github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ= -github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= -github.com/dave/dst v0.27.3 h1:P1HPoMza3cMEquVf9kKy8yXsFirry4zEnWOdYPOoIzY= -github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= -github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo= -github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= -github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= -github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= -github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8= -github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk= -github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE= -github.com/docker/cli v28.5.2+incompatible h1:XmG99IHcBmIAoC1PPg9eLBZPlTrNUAijsHLm8PjhBlg= -github.com/docker/cli v28.5.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= -github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= -github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= -github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= -github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= -github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-connections v0.8.1 h1:JibmG5hULs5qXSr/cp/w3Pw5fZuStt4MOHMUExb29/M= -github.com/docker/go-connections v0.8.1/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58= -github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w= -github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q= -github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= -github.com/ecies/go/v2 v2.0.11 h1:xYhtMdLiqNi02oLirFmLyNbVXw6250h3WM6zJryQdiM= -github.com/ecies/go/v2 v2.0.11/go.mod h1:LPRzoefP0Tam+1uesQOq3Gtb6M2OwlFUnXBTtBAKfDQ= -github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= -github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= -github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= -github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= -github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= -github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= -github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= -github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= -github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= -github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= -github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= -github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E= -github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= -github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= -github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= -github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= -github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= -github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= -github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI= -github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ= -github.com/getkin/kin-openapi v0.144.0 h1:hIRcTH+KjLfkLpYU6bSSfdFpi0fZi1fp+hSPi4aQu9Y= -github.com/getkin/kin-openapi v0.144.0/go.mod h1:3BH9M9XDe/y9M5DSvEocVYAYq1w0qrhJHjC/vZi0AaY= -github.com/getsentry/sentry-go v0.49.0 h1:Ehejknu1l023Ub7QoRBVLAI7g3Jnhqku4oWx4B4Sh5s= -github.com/getsentry/sentry-go v0.49.0/go.mod h1:nuMJAoCfe1u0Bts2ocyNI+TW8HT84vRMqwA5Qq/SKUI= -github.com/ghostiam/protogetter v0.3.20 h1:oW7OPFit2FxZOpmMRPP9FffU4uUpfeE/rEdE1f+MzD0= -github.com/ghostiam/protogetter v0.3.20/go.mod h1:FjIu5Yfs6FT391m+Fjp3fbAYJ6rkL/J6ySpZBfnODuI= -github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= -github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= -github.com/go-critic/go-critic v0.14.3 h1:5R1qH2iFeo4I/RJU8vTezdqs08Egi4u5p6vOESA0pog= -github.com/go-critic/go-critic v0.14.3/go.mod h1:xwntfW6SYAd7h1OqDzmN6hBX/JxsEKl5up/Y2bsxgVQ= -github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= -github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= -github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA= -github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw= -github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= -github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.19.2 h1:wkfn7vOlUBu8ivAWKBWisTiwJK4jYHzTF8Ndv1LyGqY= -github.com/go-git/go-git/v5 v5.19.2/go.mod h1:QqCBE1EFN5ddFmrliLQ3/ntRCUjZU3EJuwuB/jWEHjk= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= -github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= -github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= -github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= -github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= -github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM= -github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.30.4 h1:9Rcod2ZPO6mOEG6b4GqyoHE/H6//Ze0RuhOo1hT1x0w= -github.com/go-playground/validator/v10 v10.30.4/go.mod h1:numpT+RPLE91R9oYWMY/R9zRgJBewr3IXHko4OISPpk= -github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= -github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= -github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8= -github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU= -github.com/go-toolsmith/astcopy v1.1.0 h1:YGwBN0WM+ekI/6SS6+52zLDEf8Yvp3n2seZITCUBt5s= -github.com/go-toolsmith/astcopy v1.1.0/go.mod h1:hXM6gan18VA1T/daUEHCFcYiW8Ai1tIwIzHY6srfEAw= -github.com/go-toolsmith/astequal v1.0.3/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4= -github.com/go-toolsmith/astequal v1.1.0/go.mod h1:sedf7VIdCL22LD8qIvv7Nn9MuWJruQA/ysswh64lffQ= -github.com/go-toolsmith/astequal v1.2.0 h1:3Fs3CYZ1k9Vo4FzFhwwewC3CHISHDnVUPC4x0bI2+Cw= -github.com/go-toolsmith/astequal v1.2.0/go.mod h1:c8NZ3+kSFtFY/8lPso4v8LuJjdJiUFVnSuU3s0qrrDY= -github.com/go-toolsmith/astfmt v1.1.0 h1:iJVPDPp6/7AaeLJEruMsBUlOYCmvg0MoCfJprsOmcco= -github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlNMV634mhwuQ4= -github.com/go-toolsmith/astp v1.1.0 h1:dXPuCl6u2llURjdPLLDxJeZInAeZ0/eZwFJmqZMnpQA= -github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA= -github.com/go-toolsmith/pkgload v1.2.2 h1:0CtmHq/02QhxcF7E9N5LIFcYFsMR5rdovfqTtRKkgIk= -github.com/go-toolsmith/pkgload v1.2.2/go.mod h1:R2hxLNRKuAsiXCo2i5J6ZQPhnPMOVtU+f0arbFPWCus= -github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQiyP2Bvw= -github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= -github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus= -github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig= -github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= -github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY= -github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= -github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= -github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= -github.com/godoc-lint/godoc-lint v0.11.2 h1:Bp0FkJWoSdNsBikdNgIcgtaoo+xz6I/Y9s5WSBQUeeM= -github.com/godoc-lint/godoc-lint v0.11.2/go.mod h1:iVpGdL1JCikNH2gGeAn3Hh+AgN5Gx/I/cxV+91L41jo= -github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= -github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= -github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= -github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= -github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golangci/asciicheck v0.5.0 h1:jczN/BorERZwK8oiFBOGvlGPknhvq0bjnysTj4nUfo0= -github.com/golangci/asciicheck v0.5.0/go.mod h1:5RMNAInbNFw2krqN6ibBxN/zfRFa9S6tA1nPdM0l8qQ= -github.com/golangci/dupl v0.0.0-20260401084720-c99c5cf5c202 h1:CbTB8KpqnViI6lIXxp03Oclc4VFHi3K4BWC1TacsZ+A= -github.com/golangci/dupl v0.0.0-20260401084720-c99c5cf5c202/go.mod h1:NUw9Zr2Sy7+HxzdjIULge71wI6yEg1lWQr7Evcu8K0E= -github.com/golangci/go-printf-func-name v0.1.1 h1:hIYTFJqAGp1iwoIfsNTpoq1xZAarogrvjO9AfiW3B4U= -github.com/golangci/go-printf-func-name v0.1.1/go.mod h1:Es64MpWEZbh0UBtTAICOZiB+miW53w/K9Or/4QogJss= -github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE= -github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY= -github.com/golangci/golangci-lint/v2 v2.12.2 h1:7+d1uY0bq1MU2UV3R5pW5Q7QWdcoq4naMRXM+gsJKrs= -github.com/golangci/golangci-lint/v2 v2.12.2/go.mod h1:opqHHuIcTG2R+4akzWMd4o1BnD9/1LcjICWOujr91U8= -github.com/golangci/golines v0.15.0 h1:Qnph25g8Y1c5fdo1X7GaRDGgnMHgnxh4Gk4VfPTtRx0= -github.com/golangci/golines v0.15.0/go.mod h1:AZjXd23tbHMpowhtnGlj9KCNsysj72aeZVVHnVcZx10= -github.com/golangci/misspell v0.8.0 h1:qvxQhiE2/5z+BVRo1kwYA8yGz+lOlu5Jfvtx2b04Jbg= -github.com/golangci/misspell v0.8.0/go.mod h1:WZyyI2P3hxPY2UVHs3cS8YcllAeyfquQcKfdeE9AFVg= -github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= -github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= -github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s= -github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= -github.com/golangci/rowserrcheck v0.0.0-20260419091836-c5f79b8a11ba h1:lqtcnSMDuuJdu/LrKWi5RJzpSNLOJXYe/nzQutTI5kg= -github.com/golangci/rowserrcheck v0.0.0-20260419091836-c5f79b8a11ba/go.mod h1:sCBNcpRmhJCtbFGz49+IM3ETTFf7QdJ30AeYCd43NKk= -github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e h1:ai0EfmVYE2bRA5htgAG9r7s3tHsfjIhN98WshBTJ9jM= -github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e/go.mod h1:Vrn4B5oR9qRwM+f54koyeH3yzphlecwERs0el27Fr/s= -github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM= -github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e/go.mod h1:h+wZwLjUTJnm/P2rwlbJdRPZXOzaT36/FwnPnY2inzc= -github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-github/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4= -github.com/google/go-github/v62 v62.0.0/go.mod h1:EMxeUqGJq2xRu9DYBMwel/mr7kZrzUOfQmmpYrZn2a4= -github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= -github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc= -github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= -github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gordonklaus/ineffassign v0.2.0 h1:Uths4KnmwxNJNzq87fwQQDDnbNb7De00VOk9Nu0TySs= -github.com/gordonklaus/ineffassign v0.2.0/go.mod h1:TIpymnagPSexySzs7F9FnO1XFTy8IT3a59vmZp5Y9Lw= -github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= -github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= -github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= -github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= -github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= -github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= -github.com/gostaticanalysis/comment v1.5.0 h1:X82FLl+TswsUMpMh17srGRuKaaXprTaytmEpgnKIDu8= -github.com/gostaticanalysis/comment v1.5.0/go.mod h1:V6eb3gpCv9GNVqb6amXzEUX3jXLVK/AdA+IrAMSqvEc= -github.com/gostaticanalysis/forcetypeassert v0.2.0 h1:uSnWrrUEYDr86OCxWa4/Tp2jeYDlogZiZHzGkWFefTk= -github.com/gostaticanalysis/forcetypeassert v0.2.0/go.mod h1:M5iPavzE9pPqWyeiVXSFghQjljW1+l/Uke3PXHS6ILY= -github.com/gostaticanalysis/nilerr v0.1.2 h1:S6nk8a9N8g062nsx63kUkF6AzbHGw7zzyHMcpu52xQU= -github.com/gostaticanalysis/nilerr v0.1.2/go.mod h1:A19UHhoY3y8ahoL7YKz6sdjDtduwTSI4CsymaC2htPA= -github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= -github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8= -github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= -github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= -github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo= -github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA= -github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= -github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= -github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= -github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= -github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0= -github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= -github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= -github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8= -github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= -github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= -github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jgautheron/goconst v1.10.0 h1:Ptt+OoE4NaEWKhLrWrrN3IpZdGLiqaf7WLnEX/iv4Jw= -github.com/jgautheron/goconst v1.10.0/go.mod h1:0p+wv1lFOiUr0IlNNT1nrm6+8DB8u2sU6KHGzFRXHDc= -github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= -github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jjti/go-spancheck v0.6.5 h1:lmi7pKxa37oKYIMScialXUK6hP3iY5F1gu+mLBPgYB8= -github.com/jjti/go-spancheck v0.6.5/go.mod h1:aEogkeatBrbYsyW6y5TgDfihCulDYciL1B7rG2vSsrU= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= -github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ= -github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY= -github.com/karamaru-alpha/copyloopvar v1.2.2 h1:yfNQvP9YaGQR7VaWLYcfZUlRP2eo2vhExWKxD/fP6q0= -github.com/karamaru-alpha/copyloopvar v1.2.2/go.mod h1:oY4rGZqZ879JkJMtX3RRkcXRkmUvH0x35ykgaKgsgJY= -github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= -github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kisielk/errcheck v1.10.0 h1:Lvs/YAHP24YKg08LA8oDw2z9fJVme090RAXd90S+rrw= -github.com/kisielk/errcheck v1.10.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE= -github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg= -github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= -github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kulti/thelper v0.7.1 h1:fI8QITAoFVLx+y+vSyuLBP+rcVIB8jKooNSCT2EiI98= -github.com/kulti/thelper v0.7.1/go.mod h1:NsMjfQEy6sd+9Kfw8kCP61W1I0nerGSYSFnGaxQkcbs= -github.com/kunwardeep/paralleltest v1.0.15 h1:ZMk4Qt306tHIgKISHWFJAO1IDQJLc6uDyJMLyncOb6w= -github.com/kunwardeep/paralleltest v1.0.15/go.mod h1:di4moFqtfz3ToSKxhNjhOZL+696QtJGCFe132CbBLGk= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4= -github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI= -github.com/ldez/exptostd v0.4.5 h1:kv2ZGUVI6VwRfp/+bcQ6Nbx0ghFWcGIKInkG/oFn1aQ= -github.com/ldez/exptostd v0.4.5/go.mod h1:QRjHRMXJrCTIm9WxVNH6VW7oN7KrGSht69bIRwvdFsM= -github.com/ldez/gomoddirectives v0.8.0 h1:JqIuTtgvFC2RdH1s357vrE23WJF2cpDCPFgA/TWDGpk= -github.com/ldez/gomoddirectives v0.8.0/go.mod h1:jutzamvZR4XYJLr0d5Honycp4Gy6GEg2mS9+2YX3F1Q= -github.com/ldez/grignotin v0.10.1 h1:keYi9rYsgbvqAZGI1liek5c+jv9UUjbvdj3Tbn5fn4o= -github.com/ldez/grignotin v0.10.1/go.mod h1:UlDbXFCARrXbWGNGP3S5vsysNXAPhnSuBufpTEbwOas= -github.com/ldez/structtags v0.6.1 h1:bUooFLbXx41tW8SvkfwfFkkjPYvFFs59AAMgVg6DUBk= -github.com/ldez/structtags v0.6.1/go.mod h1:YDxVSgDy/MON6ariaxLF2X09bh19qL7MtGBN5MrvbdY= -github.com/ldez/tagliatelle v0.7.2 h1:KuOlL70/fu9paxuxbeqlicJnCspCRjH0x8FW+NfgYUk= -github.com/ldez/tagliatelle v0.7.2/go.mod h1:PtGgm163ZplJfZMZ2sf5nhUT170rSuPgBimoyYtdaSI= -github.com/ldez/usetesting v0.5.0 h1:3/QtzZObBKLy1F4F8jLuKJiKBjjVFi1IavpoWbmqLwc= -github.com/ldez/usetesting v0.5.0/go.mod h1:Spnb4Qppf8JTuRgblLrEWb7IE6rDmUpGvxY3iRrzvDQ= -github.com/leodido/go-urn v1.5.0 h1:pLqT2kq1zpHW/1D18QMjMpdtX7cekxqtJJjg5ANyWw0= -github.com/leodido/go-urn v1.5.0/go.mod h1:9BORnCDhdPBJNDEX+w1bJisa8yOKYi116VeO96s4ifE= -github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY= -github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= -github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= -github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= -github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4= -github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/macabu/inamedparam v0.2.0 h1:VyPYpOc10nkhI2qeNUdh3Zket4fcZjEWe35poddBCpE= -github.com/macabu/inamedparam v0.2.0/go.mod h1:+Pee9/YfGe5LJ62pYXqB89lJ+0k5bsR8Wgz/C0Zlq3U= -github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/manuelarte/embeddedstructfieldcheck v0.4.0 h1:3mAIyaGRtjK6EO9E73JlXLtiy7ha80b2ZVGyacxgfww= -github.com/manuelarte/embeddedstructfieldcheck v0.4.0/go.mod h1:z8dFSyXqp+fC6NLDSljRJeNQJJDWnY7RoWFzV3PC6UM= -github.com/manuelarte/funcorder v0.6.0 h1:0hBngc4fa1IgNiI65A7sFGkMvoMCc878RjqB5V7rWP0= -github.com/manuelarte/funcorder v0.6.0/go.mod h1:id3NDhXdQBmeqXH7eVC6Z89xS6JxvZ8kF9xUxpArU/g= -github.com/maratori/testableexamples v1.0.1 h1:HfOQXs+XgfeRBJ+Wz0XfH+FHnoY9TVqL6Fcevpzy4q8= -github.com/maratori/testableexamples v1.0.1/go.mod h1:XE2F/nQs7B9N08JgyRmdGjYVGqxWwClLPCGSQhXQSrQ= -github.com/maratori/testpackage v1.1.2 h1:ffDSh+AgqluCLMXhM19f/cpvQAKygKAJXFl9aUjmbqs= -github.com/maratori/testpackage v1.1.2/go.mod h1:8F24GdVDFW5Ew43Et02jamrVMNXLUNaOynhDssITGfc= -github.com/matoous/godox v1.1.0 h1:W5mqwbyWrwZv6OQ5Z1a/DHGMOvXYCBP3+Ht7KMoJhq4= -github.com/matoous/godox v1.1.0/go.mod h1:jgE/3fUXiTurkdHOLT5WEkThTSuE7yxHv5iWPa80afs= -github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= -github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= -github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw= -github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= -github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mgechev/revive v1.15.0 h1:vJ0HzSBzfNyPbHKolgiFjHxLek9KUijhqh42yGoqZ8Q= -github.com/mgechev/revive v1.15.0/go.mod h1:LlAKO3QQe9OJ0pVZzI2GPa8CbXGZ/9lNpCGvK4T/a8A= -github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= -github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= -github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= -github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= -github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= -github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= -github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= -github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= -github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= -github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= -github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= -github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= -github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= -github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ= -github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= -github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= -github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= -github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= -github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg= -github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= -github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= -github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nunnatsa/ginkgolinter v0.23.0 h1:x3o4DGYOWbBMP/VdNQKgSj+25aJKx2Pe6lHr8gBcgf8= -github.com/nunnatsa/ginkgolinter v0.23.0/go.mod h1:9qN1+0akwXEccwV1CAcCDfcoBlWXHB+ML9884pL4SZ4= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oapi-codegen/nullable v1.2.0 h1:VflFkDW980KhBPiFF7nWSyjg+r4Obqj8lXipV0UkP5w= -github.com/oapi-codegen/nullable v1.2.0/go.mod h1:KUZ3vUzkmEKY90ksAmit2+5juDIhIZhfDl+0PwOQlFY= -github.com/oapi-codegen/oapi-codegen/v2 v2.7.1 h1:a7Ab7YlpqkVG5HKrTaeFstm32Z5QOnyjnbsCO0jiMYM= -github.com/oapi-codegen/oapi-codegen/v2 v2.7.1/go.mod h1:qzFy6iuobJw/hD1aRILee4G87/ShmhR0xYCwcUtZMCw= -github.com/oapi-codegen/runtime v1.7.0 h1:t7358VYPvNbWJ9gdAkIK/smVeHpBf6yp8VTsaZsb/7k= -github.com/oapi-codegen/runtime v1.7.0/go.mod h1:GwV7hC2hviaMzj+ITfHVRESK5J2W/GefVwIND/bMGvU= -github.com/oasdiff/yaml v0.1.1 h1:6nHx+pn9gBRM6YpBlFZFQGCCd1nuvqOBtTD3KKTgGxY= -github.com/oasdiff/yaml v0.1.1/go.mod h1:EYJNoyktvWMJ0Hmhx+6qTaqMOsalUaRGT8Sj1hNcegU= -github.com/oasdiff/yaml3 v0.0.14 h1:aLJee3hxBK2H5wdXd9iPcIXb93Nty1Ge0pT171eHtkw= -github.com/oasdiff/yaml3 v0.0.14/go.mod h1:csto2xfDjYccdUn/yw/bPjj/cYTdp6HtFA0J4TWG+gg= -github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= -github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= -github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo= -github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= -github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA= -github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88= -github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I= -github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.28.2 h1:DTrMfpqxiNUyQ3Y0zhn1n3cOO2euFgQPYIpkWwxVFps= -github.com/onsi/ginkgo/v2 v2.28.2/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= -github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= -github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= -github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= -github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= -github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I= -github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= -github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= -github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= -github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= -github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= -github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= -github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= -github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= -github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU= -github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posthog/posthog-go v1.25.1 h1:jo5cW5GGoY41noy33Jn6A2N9Z5yrIGKHBr4DjAvnCrc= -github.com/posthog/posthog-go v1.25.1/go.mod h1:rN0C9Urenhytgo3KQ/QVorGkyGfpouquDTSs2Ujzr+g= -github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= -github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= -github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= -github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= -github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= -github.com/quasilyte/go-ruleguard v0.4.5 h1:AGY0tiOT5hJX9BTdx/xBdoCubQUAE2grkqY2lSwvZcA= -github.com/quasilyte/go-ruleguard v0.4.5/go.mod h1:Vl05zJ538vcEEwu16V/Hdu7IYZWyKSwIy4c88Ro1kRE= -github.com/quasilyte/go-ruleguard/dsl v0.3.23 h1:lxjt5B6ZCiBeeNO8/oQsegE6fLeCzuMRoVWSkXC4uvY= -github.com/quasilyte/go-ruleguard/dsl v0.3.23/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= -github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= -github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng= -github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU= -github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= -github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= -github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= -github.com/raeperd/recvcheck v0.2.0 h1:GnU+NsbiCqdC2XX5+vMZzP+jAJC5fht7rcVTAhX74UI= -github.com/raeperd/recvcheck v0.2.0/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryancurrah/gomodguard v1.4.1 h1:eWC8eUMNZ/wM/PWuZBv7JxxqT5fiIKSIyTvjb7Elr+g= -github.com/ryancurrah/gomodguard v1.4.1/go.mod h1:qnMJwV1hX9m+YJseXEBhd2s90+1Xn6x9dLz11ualI1I= -github.com/ryancurrah/gomodguard/v2 v2.1.3 h1:E7sz3PJwE9Ba1reVxSpF6XLCPJZ74Kfw/LabTNM4GIA= -github.com/ryancurrah/gomodguard/v2 v2.1.3/go.mod h1:CQicdLGatWMxLX53JzoBjYlsNZhHbmLv2AVa0s2aivU= -github.com/ryanrolds/sqlclosecheck v0.6.0 h1:pEyL9okISdg1F1SEpJNlrEotkTGerv5BMk7U4AG0eVg= -github.com/ryanrolds/sqlclosecheck v0.6.0/go.mod h1:xyX16hsDaCMXHrMJ3JMzGf5OpDfHTOTTQrT7HOFUmeU= -github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= -github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= -github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= -github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= -github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0= -github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= -github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= -github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= -github.com/sashamelentyev/usestdlibvars v1.29.0 h1:8J0MoRrw4/NAXtjQqTHrbW9NN+3iMf7Knkq057v4XOQ= -github.com/sashamelentyev/usestdlibvars v1.29.0/go.mod h1:8PpnjHMk5VdeWlVb4wCdrB8PNbLqZ3wBZTZWkrpZZL8= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/securego/gosec/v2 v2.26.1 h1:gdkttGhQFVehqRJ8grKH4DrpqM/QlPKNHBnl8QgcEC4= -github.com/securego/gosec/v2 v2.26.1/go.mod h1:57UW4p0uoP3kxoTkhoo3axLdVAi+OWrLg/Ax/kdqtPE= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= -github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= -github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= -github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= -github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= -github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= -github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= -github.com/sonatard/noctx v0.5.1 h1:wklWg9c9ZYugOAk7qG4yP4PBrlQsmSLPTvW1K4PRQMs= -github.com/sonatard/noctx v0.5.1/go.mod h1:64XdbzFb18XL4LporKXp8poqZtPKbCrqQ402CV+kJas= -github.com/sourcegraph/go-diff v0.8.0 h1:ipIyu4cTsLbIrln4l0qtHA3r0a7gyK4ntKjtQytHhvY= -github.com/sourcegraph/go-diff v0.8.0/go.mod h1:hWlcO7Al+UZStZAP8rBumHpCK5ZHQ5BXsMls8p4+F5E= -github.com/speakeasy-api/jsonpath v0.6.3 h1:c+QPwzAOdrWvzycuc9HFsIZcxKIaWcNpC+xhOW9rJxU= -github.com/speakeasy-api/jsonpath v0.6.3/go.mod h1:2cXloNuQ+RSXi5HTRaeBh7JEmjRXTiaKpFTdZiL7URI= -github.com/speakeasy-api/openapi v1.19.2 h1:md90tE71/M8jS3cuRlsuWP5Aed4xoG5PSRvXeZgCv/M= -github.com/speakeasy-api/openapi v1.19.2/go.mod h1:UfKa7FqE4jgexJZuj51MmdHAFGmDv0Zaw3+yOd81YKU= -github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= -github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= -github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= -github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= -github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= -github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= -github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= -github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= -github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= -github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= -github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= -github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= -github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= -github.com/stbenjam/no-sprintf-host-port v0.3.1 h1:AyX7+dxI4IdLBPtDbsGAyqiTSLpCP9hWRrXQDU4Cm/g= -github.com/stbenjam/no-sprintf-host-port v0.3.1/go.mod h1:ODbZesTCHMVKthBHskvUUexdcNHAQRXk9NpSsL8p/HQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= -github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= -github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= -github.com/stripe/pg-schema-diff v1.0.9 h1:oI1MPWAEuvuc/TCeGoJJ6sc+70Rs+epFM3LXvTFZxYE= -github.com/stripe/pg-schema-diff v1.0.9/go.mod h1:3IctPaAqm+0LtWw/GiwyRoRlU1/N/+00+eXVk0KZIHs= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= -github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= -github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= -github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.5.6 h1:IEkrFCwXaYHlOn4mGzGS3F3dkP6m9t0jpwqBFPIkKiA= -github.com/tetafro/godot v1.5.6/go.mod h1:eOkMrVQurDui411nBY2FA05EYH01r14LuWY/NrVDVcU= -github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c= -github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw= -github.com/tidwall/jsonc v0.3.3 h1:RVQqL3xFfDkKKXIDsrBiVQiEpBtxoKbmMXONb2H/y2w= -github.com/tidwall/jsonc v0.3.3/go.mod h1:dw+3CIxqHi+t8eFSpzzMlcVYxKp08UP5CD8/uSFCyJE= -github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4 h1:SiHe5XLTn9sFWJ5pBwJ5FN/4j34q9ZlOAD//kMoMYp0= -github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4/go.mod h1:sDHLK7rb/59v/ZxZ7KtymgcoxuUMxjXq8gtu9VMOK8M= -github.com/timonwong/loggercheck v0.11.0 h1:jdaMpYBl+Uq9mWPXv1r8jc5fC3gyXx4/WGwTnnNKn4M= -github.com/timonwong/loggercheck v0.11.0/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8= -github.com/tomarrell/wrapcheck/v2 v2.12.0 h1:H/qQ1aNWz/eeIhxKAFvkfIA+N7YDvq6TWVFL27Of9is= -github.com/tomarrell/wrapcheck/v2 v2.12.0/go.mod h1:AQhQuZd0p7b6rfW+vUwHm5OMCGgp63moQ9Qr/0BpIWo= -github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= -github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= -github.com/ultraware/funlen v0.2.0 h1:gCHmCn+d2/1SemTdYMiKLAHFYxTYz7z9VIDRaTGyLkI= -github.com/ultraware/funlen v0.2.0/go.mod h1:ZE0q4TsJ8T1SQcjmkhN/w+MceuatI6pBFSxxyteHIJA= -github.com/ultraware/whitespace v0.2.0 h1:TYowo2m9Nfj1baEQBjuHzvMRbp19i+RCcRYrSWoFa+g= -github.com/ultraware/whitespace v0.2.0/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= -github.com/uudashr/gocognit v1.2.1 h1:CSJynt5txTnORn/DkhiB4mZjwPuifyASC8/6Q0I/QS4= -github.com/uudashr/gocognit v1.2.1/go.mod h1:acaubQc6xYlXFEMb9nWX2dYBzJ/bIjEkc1zzvyIZg5Q= -github.com/uudashr/iface v1.4.2 h1:06Vq5RKVYThBsj0Bnw4oasMjD1r+7CE/bcKOA8dVSvg= -github.com/uudashr/iface v1.4.2/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg= -github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk= -github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ= -github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= -github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM= -github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4= -github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= -github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= -github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= -github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= -github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= -github.com/yeya24/promlinter v0.3.0 h1:JVDbMp08lVCP7Y6NP3qHroGAO6z2yGKQtS5JsjqtoFs= -github.com/yeya24/promlinter v0.3.0/go.mod h1:cDfJQQYv9uYciW60QT0eeHlFodotkYZlL+YcPQN+mW4= -github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoolgdiw= -github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= -github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= -github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs= -github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA= -github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPccHs= -github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo= -gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8= -go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ= -go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= -go-simpler.org/musttag v0.14.0 h1:XGySZATqQYSEV3/YTy+iX+aofbZZllJaqwFWs+RTtSo= -go-simpler.org/musttag v0.14.0/go.mod h1:uP8EymctQjJ4Z1kUnjX0u2l60WfUdQxCwSNKzE1JEOE= -go-simpler.org/sloglint v0.12.0 h1:UzWDlLWNE5FLqsvyq3tWYHuQMbqrervOhT8qPl4Mmw4= -go-simpler.org/sloglint v0.12.0/go.mod h1:jBjjC2bm8rYrs88oTRlFX497kWjJsyZWYoNaXkGRI6I= -go.augendre.info/arangolint v0.4.0 h1:xSCZjRoS93nXazBSg5d0OGCi9APPLNMmmLrC995tR50= -go.augendre.info/arangolint v0.4.0/go.mod h1:l+f/b4plABuFISuKnTGD4RioXiCCgghv2xqst/xOvAA= -go.augendre.info/fatcontext v0.9.0 h1:Gt5jGD4Zcj8CDMVzjOJITlSb9cEch54hjRRlN3qDojE= -go.augendre.info/fatcontext v0.9.0/go.mod h1:L94brOAT1OOUNue6ph/2HnwxoNlds9aXDF2FcUntbNw= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ= -go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= -go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 h1:cEf8jF6WbuGQWUVcqgyWtTR0kOOAWY1DYZ+UhvdmQPw= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0/go.mod h1:k1lzV5n5U3HkGvTCJHraTAGJ7MqsgL1wrGwTj1Isfiw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= -go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8= -go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o= -go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA= -go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk= -go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= -go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= -go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= -go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= -go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c= -go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI= -go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= -go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= -go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= -go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= -golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M= -golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= -golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= -golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= -golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 h1:qWFG1Dj7TBjOjOvhEOkmyGPVoquqUKnIU0lEVLp8xyk= -golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c= -golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= -golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= -golang.org/x/oauth2 v0.37.0 h1:JUlcxA8oAtauLfiH8FX2/FkAWHAdi0QtGCGc+hofE98= -golang.org/x/oauth2 v0.37.0/go.mod h1:IxwZNxUULJmpBFf9K/9NTMSIfZZuvuTy1gGxhigP/58= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= -golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= -golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= -golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= -golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= -golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= -golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= -golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= -golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= -golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= -golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= -gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= -google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= -google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/gotestsum v1.12.2 h1:eli4tu9Q2D/ogDsEGSr8XfQfl7mT0JsGOG6DFtUiZ/Q= -gotest.tools/gotestsum v1.12.2/go.mod h1:kjRtCglPZVsSU0hFHX3M5VWBM6Y63emHuB14ER1/sow= -gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= -gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.7.0 h1:w6WUp1VbkqPEgLz4rkBzH/CSU6HkoqNLp6GstyTx3lU= -honnef.co/go/tools v0.7.0/go.mod h1:pm29oPxeP3P82ISxZDgIYeOaf9ta6Pi0EWvCFoLG2vc= -mvdan.cc/gofumpt v0.9.2 h1:zsEMWL8SVKGHNztrx6uZrXdp7AX8r421Vvp23sz7ik4= -mvdan.cc/gofumpt v0.9.2/go.mod h1:iB7Hn+ai8lPvofHd9ZFGVg2GOr8sBUw1QUWjNbmIL/s= -mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 h1:ssMzja7PDPJV8FStj7hq9IKiuiKhgz9ErWw+m68e7DI= -mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15/go.mod h1:4M5MMXl2kW6fivUT6yRGpLLPNfuGtU2Z0cPvFquGDYU= diff --git a/apps/cli-go/internal/db/diff/diff.go b/apps/cli-go/internal/db/diff/diff.go deleted file mode 100644 index f4c05fb801..0000000000 --- a/apps/cli-go/internal/db/diff/diff.go +++ /dev/null @@ -1,254 +0,0 @@ -package diff - -import ( - "context" - "fmt" - "io" - "io/fs" - "os" - "path/filepath" - "regexp" - "sort" - "strconv" - "strings" - "time" - - "github.com/cenkalti/backoff/v4" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" - "github.com/docker/go-connections/nat" - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/db/start" - "github.com/supabase/cli/internal/migration/new" - "github.com/supabase/cli/internal/utils" - configpkg "github.com/supabase/cli/pkg/config" - "github.com/supabase/cli/pkg/migration" - "github.com/supabase/cli/pkg/parser" -) - -type DiffFunc func(context.Context, pgconn.Config, pgconn.Config, []string, ...func(*pgx.ConnConfig)) (string, error) - -// DatabaseDiff is the result of diffing a target database against a shadow baseline. -type DatabaseDiff struct { - SQL string -} - -func Run(ctx context.Context, schema []string, file string, config pgconn.Config, differ DiffFunc, fsys afero.Fs, options ...func(*pgx.ConnConfig)) (err error) { - result, err := DiffDatabase(ctx, schema, config, os.Stderr, fsys, differ, options...) - if err != nil { - return err - } - out := result.SQL - branch := utils.GetGitBranch(fsys) - fmt.Fprintln(os.Stderr, "Finished "+utils.Aqua("supabase db diff")+" on branch "+utils.Aqua(branch)+".\n") - if err := SaveDiff(result, file, fsys); err != nil { - return err - } - drops := findDropStatements(out) - if len(drops) > 0 { - fmt.Fprintln(os.Stderr, "Found drop statements in schema diff. Please double check if these are expected:") - fmt.Fprintln(os.Stderr, utils.Yellow(strings.Join(drops, "\n"))) - } - return nil -} - -func loadDeclaredSchemas(fsys afero.Fs) ([]string, error) { - if schemas := utils.Config.Db.Migrations.SchemaPaths; len(schemas) > 0 { - return schemas.SQLFiles( - afero.NewIOFS(fsys), - configpkg.WithSkipEmptyGlobs(), - configpkg.WithErrorOnAllSkippedGlobs(), - ) - } - // When pg-delta is enabled, declarative path is the source of truth (config or default). - if utils.IsPgDeltaEnabled() { - declDir := utils.GetDeclarativeDir() - if exists, err := afero.DirExists(fsys, declDir); err == nil && exists { - var declared []string - if err := afero.Walk(fsys, declDir, func(path string, info fs.FileInfo, err error) error { - if err != nil { - return err - } - if info.Mode().IsRegular() && filepath.Ext(info.Name()) == ".sql" { - declared = append(declared, path) - } - return nil - }); err != nil { - return nil, errors.Errorf("failed to walk declarative dir: %w", err) - } - sort.Strings(declared) - return declared, nil - } - } - if exists, err := afero.DirExists(fsys, utils.SchemasDir); err != nil { - return nil, errors.Errorf("failed to check schemas: %w", err) - } else if !exists { - return nil, nil - } - var declared []string - if err := afero.Walk(fsys, utils.SchemasDir, func(path string, info fs.FileInfo, err error) error { - if err != nil { - return err - } - if info.Mode().IsRegular() && filepath.Ext(info.Name()) == ".sql" { - declared = append(declared, path) - } - return nil - }); err != nil { - return nil, errors.Errorf("failed to walk dir: %w", err) - } - // Keep file application order deterministic so diff output stays stable across - // filesystems and operating systems. This is only if no schema paths in config are set. - sort.Strings(declared) - return declared, nil -} - -var warnDiff = `WARNING: The diff tool is not foolproof, so you may need to manually rearrange and modify the generated migration. -Run ` + utils.Aqua("supabase db reset") + ` to verify that the new migration does not generate errors.` - -func SaveDiff(result DatabaseDiff, file string, fsys afero.Fs) error { - out := result.SQL - if len(out) < 2 { - fmt.Fprintln(os.Stderr, "No schema changes found") - } else if len(file) > 0 { - path := new.GetMigrationPath(utils.GetCurrentTimestamp(), file) - if err := utils.WriteFile(path, []byte(out), fsys); err != nil { - return err - } - fmt.Fprintln(os.Stderr, warnDiff) - } else { - fmt.Println(out) - } - return nil -} - -// https://github.com/djrobstep/migra/blob/master/migra/statements.py#L6 -var dropStatementPattern = regexp.MustCompile(`(?i)drop\s+`) - -func findDropStatements(out string) []string { - lines, err := parser.SplitAndTrim(strings.NewReader(out)) - if err != nil { - return nil - } - var drops []string - for _, line := range lines { - if dropStatementPattern.MatchString(line) { - drops = append(drops, line) - } - } - return drops -} - -func CreateShadowDatabase(ctx context.Context, port uint16) (string, error) { - // Disable background workers in shadow database - config := start.NewContainerConfig("-c", "max_worker_processes=0") - hostPort := strconv.FormatUint(uint64(port), 10) - hostConfig := container.HostConfig{ - PortBindings: nat.PortMap{"5432/tcp": []nat.PortBinding{{HostPort: hostPort}}}, - AutoRemove: true, - } - networkingConfig := network.NetworkingConfig{} - if utils.Config.Db.MajorVersion <= 14 { - hostConfig.Tmpfs = map[string]string{"/docker-entrypoint-initdb.d": ""} - } - return utils.DockerStart(ctx, config, hostConfig, networkingConfig, "") -} - -func ConnectShadowDatabase(ctx context.Context, timeout time.Duration, options ...func(*pgx.ConnConfig)) (conn *pgx.Conn, err error) { - // Retry until connected, cancelled, or timeout - policy := start.NewBackoffPolicy(ctx, timeout) - config := pgconn.Config{Port: utils.Config.Db.ShadowPort} - connect := func() (*pgx.Conn, error) { - return utils.ConnectLocalPostgres(ctx, config, options...) - } - return backoff.RetryWithData(connect, policy) -} - -// Required to bypass pg_cron check: https://github.com/citusdata/pg_cron/blob/main/pg_cron.sql#L3 -const CREATE_TEMPLATE = "CREATE DATABASE contrib_regression TEMPLATE postgres" - -// setupShadowConn applies the Supabase platform schema (auth, storage, realtime, -// etc.) to an already-connected shadow database and creates the pg_cron template -// database. It deliberately stops short of applying user migrations so that -// callers which only need the platform baseline (declarative apply) share the -// exact same starting point as callers that also replay migrations. -func setupShadowConn(ctx context.Context, conn *pgx.Conn, container string, fsys afero.Fs) error { - if err := start.SetupDatabase(ctx, conn, container[:12], os.Stderr, fsys); err != nil { - return err - } - if _, err := conn.Exec(ctx, CREATE_TEMPLATE); err != nil { - return errors.Errorf("failed to create template database: %w", err) - } - return nil -} - -// SetupShadowDatabase provisions the Supabase platform baseline (service schemas -// such as auth/storage/realtime) on a freshly created shadow database, without -// applying user migrations. Declarative apply uses this so the shadow matches the -// real database closely enough for Supabase-managed dependencies (auth.sessions, -// auth.jwt(), ...) to resolve. -func SetupShadowDatabase(ctx context.Context, container string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - conn, err := ConnectShadowDatabase(ctx, 10*time.Second, options...) - if err != nil { - return err - } - defer conn.Close(context.Background()) - return setupShadowConn(ctx, conn, container, fsys) -} - -func MigrateShadowDatabase(ctx context.Context, container string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - migrations, err := migration.ListLocalMigrations(utils.MigrationsDir, afero.NewIOFS(fsys)) - if err != nil { - return err - } - conn, err := ConnectShadowDatabase(ctx, 10*time.Second, options...) - if err != nil { - return err - } - defer conn.Close(context.Background()) - if err := setupShadowConn(ctx, conn, container, fsys); err != nil { - return err - } - return migration.ApplyMigrations(ctx, migrations, conn, afero.NewIOFS(fsys)) -} - -func DiffDatabase(ctx context.Context, schema []string, config pgconn.Config, w io.Writer, fsys afero.Fs, differ DiffFunc, options ...func(*pgx.ConnConfig)) (DatabaseDiff, error) { - fmt.Fprintln(w, "Creating shadow database...") - shadowSource, err := PrepareShadowSource(ctx, utils.IsLocalDatabase(config), fsys, options...) - if err != nil { - return DatabaseDiff{}, err - } - defer utils.DockerRemove(shadowSource.Container) - shadowConfig := shadowSource.Source - if shadowSource.TargetOverride != nil { - config = *shadowSource.TargetOverride - } - if len(schema) > 0 { - fmt.Fprintln(w, "Diffing schemas:", strings.Join(schema, ",")) - } else { - fmt.Fprintln(w, "Diffing schemas...") - } - output, err := differ(ctx, shadowConfig, config, schema, options...) - if err != nil { - return DatabaseDiff{}, err - } - return DatabaseDiff{SQL: output}, nil -} - -func migrateBaseDatabase(ctx context.Context, config pgconn.Config, migrations []string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - fmt.Fprintln(os.Stderr, "Creating local database from declarative schemas:") - msg := make([]string, len(migrations)) - for i, m := range migrations { - msg[i] = fmt.Sprintf(" • %s", utils.Bold(m)) - } - fmt.Fprintln(os.Stderr, strings.Join(msg, "\n")) - conn, err := utils.ConnectLocalPostgres(ctx, config, options...) - if err != nil { - return err - } - defer conn.Close(context.Background()) - return migration.SeedGlobals(ctx, migrations, conn, afero.NewIOFS(fsys)) -} diff --git a/apps/cli-go/internal/db/diff/diff_test.go b/apps/cli-go/internal/db/diff/diff_test.go deleted file mode 100644 index af0cacf610..0000000000 --- a/apps/cli-go/internal/db/diff/diff_test.go +++ /dev/null @@ -1,516 +0,0 @@ -package diff - -import ( - "context" - "errors" - "io" - "net/http" - "os" - "path/filepath" - "testing" - "time" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/h2non/gock" - "github.com/jackc/pgconn" - "github.com/jackc/pgerrcode" - "github.com/jackc/pgx/v4" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/testing/fstest" - "github.com/supabase/cli/internal/testing/helper" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/flags" - pkgconfig "github.com/supabase/cli/pkg/config" - "github.com/supabase/cli/pkg/migration" - "github.com/supabase/cli/pkg/pgtest" -) - -var dbConfig = pgconn.Config{ - Host: "db.supabase.co", - Port: 5432, - User: "admin", - Password: "password", - Database: "postgres", -} - -func TestLoadDeclaredSchemas(t *testing.T) { - t.Run("respects schema_paths order when pg-delta declarative dir exists", func(t *testing.T) { - originalConfig := utils.Config - t.Cleanup(func() { utils.Config = originalConfig }) - utils.Config.Db.Migrations.SchemaPaths = pkgconfig.Glob{ - "supabase/schemas/z_function.sql", - "supabase/schemas/a_table.sql", - } - utils.Config.Experimental.PgDelta = &pkgconfig.PgDeltaConfig{ - Enabled: true, - DeclarativeSchemaPath: utils.SchemasDir, - } - fsys := afero.NewMemMapFs() - require.NoError(t, fsys.MkdirAll(utils.SchemasDir, 0755)) - require.NoError(t, afero.WriteFile(fsys, "supabase/schemas/a_table.sql", []byte("create table a();"), 0644)) - require.NoError(t, afero.WriteFile(fsys, "supabase/schemas/z_function.sql", []byte("create function z() returns void language sql as $$ select 1 $$;"), 0644)) - - declared, err := loadDeclaredSchemas(fsys) - - require.NoError(t, err) - assert.Equal(t, []string{ - "supabase/schemas/z_function.sql", - "supabase/schemas/a_table.sql", - }, declared) - }) - - t.Run("expands schema_paths directory entries deterministically", func(t *testing.T) { - originalConfig := utils.Config - t.Cleanup(func() { utils.Config = originalConfig }) - utils.Config.Db.Migrations.SchemaPaths = pkgconfig.Glob{utils.DeclarativeDir} - fsys := afero.NewMemMapFs() - require.NoError(t, fsys.MkdirAll(filepath.Join(utils.DeclarativeDir, "nested"), 0755)) - require.NoError(t, afero.WriteFile(fsys, filepath.Join(utils.DeclarativeDir, "nested", "b.sql"), []byte("select 2;"), 0644)) - require.NoError(t, afero.WriteFile(fsys, filepath.Join(utils.DeclarativeDir, "a.sql"), []byte("select 1;"), 0644)) - - declared, err := loadDeclaredSchemas(fsys) - - require.NoError(t, err) - assert.Equal(t, []string{ - filepath.Join(utils.DeclarativeDir, "a.sql"), - filepath.Join(utils.DeclarativeDir, "nested", "b.sql"), - }, declared) - }) -} - -func TestRun(t *testing.T) { - t.Run("runs migra diff", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, flags.LoadConfig(fsys)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Db.Image), "test-shadow-db") - gock.New(utils.Docker.DaemonHost()). - Delete("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db"). - Reply(http.StatusOK) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - Health: &container.Health{Status: types.Healthy}, - }, - }}) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Realtime.Image), "test-shadow-realtime") - require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-shadow-realtime", "")) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Storage.Image), "test-shadow-storage") - require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-shadow-storage", "")) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Auth.Image), "test-shadow-auth") - require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-shadow-auth", "")) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.EdgeRuntime.Image), "test-migra") - diff := "create table test();" - require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-migra", diff)) - // Setup mock postgres: with auto_expose_new_tables unset, the shadow database setup - // revokes the default Data API GRANTs before creating the regression template. - conn := pgtest.NewConn() - helper.MockApiPrivilegesRevoke(conn). - Query(CREATE_TEMPLATE). - Reply("CREATE DATABASE") - defer conn.Close(t) - // Run test - err := Run(context.Background(), []string{"public"}, "file", dbConfig, DiffSchemaMigra, fsys, func(cc *pgx.ConnConfig) { - if cc.Host == dbConfig.Host { - // Fake a SSL error when connecting to target database - cc.LookupFunc = func(ctx context.Context, host string) (addrs []string, err error) { - return nil, errors.New("server refused TLS connection") - } - } else { - // Hijack connection to shadow database - conn.Intercept(cc) - } - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - // Check diff file - files, err := afero.ReadDir(fsys, utils.MigrationsDir) - assert.NoError(t, err) - assert.Equal(t, 1, len(files)) - diffPath := filepath.Join(utils.MigrationsDir, files[0].Name()) - contents, err := afero.ReadFile(fsys, diffPath) - assert.NoError(t, err) - assert.Equal(t, []byte(diff), contents) - }) - - t.Run("throws error on failure to diff target", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/images/" + utils.GetRegistryImageUrl(utils.Config.Db.Image) + "/json"). - ReplyError(errors.New("network error")) - // Run test - err := Run(context.Background(), []string{"public"}, "file", dbConfig, DiffSchemaMigra, fsys) - // Check error - assert.ErrorContains(t, err, "network error") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestMigrateShadow(t *testing.T) { - utils.Config.Db.MajorVersion = 14 - - t.Run("migrates shadow database", func(t *testing.T) { - utils.Config.Db.ShadowPort = 54320 - utils.GlobalsSql = "create schema public" - utils.InitialSchemaPg14Sql = "create schema private" - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := filepath.Join(utils.MigrationsDir, "0_test.sql") - sql := "create schema test" - require.NoError(t, afero.WriteFile(fsys, path, []byte(sql), 0644)) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(utils.GlobalsSql). - Reply("CREATE SCHEMA"). - Query(utils.InitialSchemaPg14Sql). - Reply("CREATE SCHEMA") - helper.MockApiPrivilegesRevoke(conn). - Query(CREATE_TEMPLATE). - Reply("CREATE DATABASE") - helper.MockMigrationHistory(conn). - Query("RESET ALL"). - Reply("RESET"). - Query(sql). - Reply("CREATE SCHEMA"). - Query(migration.INSERT_MIGRATION_VERSION, "0", "test", []string{sql}). - Reply("INSERT 0 1") - // Run test - err := MigrateShadowDatabase(context.Background(), "test-shadow-db", fsys, conn.Intercept) - // Check error - assert.NoError(t, err) - }) - - t.Run("throws error on timeout", func(t *testing.T) { - utils.Config.Db.ShadowPort = 54320 - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup cancelled context - ctx, cancel := context.WithCancel(context.Background()) - cancel() - // Run test - err := MigrateShadowDatabase(ctx, "", fsys) - // Check error - assert.ErrorIs(t, err, context.Canceled) - }) - - t.Run("throws error on permission denied", func(t *testing.T) { - // Setup in-memory fs - fsys := &fstest.OpenErrorFs{DenyPath: utils.MigrationsDir} - // Run test - err := MigrateShadowDatabase(context.Background(), "", fsys) - // Check error - assert.ErrorIs(t, err, os.ErrPermission) - }) - - t.Run("throws error on globals schema", func(t *testing.T) { - utils.Config.Db.ShadowPort = 54320 - utils.GlobalsSql = "create schema public" - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(utils.GlobalsSql). - ReplyError(pgerrcode.DuplicateSchema, `schema "public" already exists`) - // Run test - err := MigrateShadowDatabase(context.Background(), "test-shadow-db", fsys, conn.Intercept) - // Check error - assert.ErrorContains(t, err, `ERROR: schema "public" already exists (SQLSTATE 42P06)`) - }) -} - -func TestSetupShadowDatabase(t *testing.T) { - utils.Config.Db.MajorVersion = 14 - - t.Run("sets up platform baseline without applying migrations", func(t *testing.T) { - utils.Config.Db.ShadowPort = 54320 - utils.GlobalsSql = "create schema public" - utils.InitialSchemaPg14Sql = "create schema private" - // A migration exists on disk, but SetupShadowDatabase must not apply it: - // the mock below only scripts the platform setup + template, so any - // migration-history query would surface as an unmatched request. - fsys := afero.NewMemMapFs() - path := filepath.Join(utils.MigrationsDir, "0_test.sql") - require.NoError(t, afero.WriteFile(fsys, path, []byte("create schema test"), 0644)) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(utils.GlobalsSql). - Reply("CREATE SCHEMA"). - Query(utils.InitialSchemaPg14Sql). - Reply("CREATE SCHEMA") - helper.MockApiPrivilegesRevoke(conn). - Query(CREATE_TEMPLATE). - Reply("CREATE DATABASE") - // Run test - err := SetupShadowDatabase(context.Background(), "test-shadow-db", fsys, conn.Intercept) - // Check error - assert.NoError(t, err) - }) - - t.Run("throws error on globals schema", func(t *testing.T) { - utils.Config.Db.ShadowPort = 54320 - utils.GlobalsSql = "create schema public" - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(utils.GlobalsSql). - ReplyError(pgerrcode.DuplicateSchema, `schema "public" already exists`) - // Run test - err := SetupShadowDatabase(context.Background(), "test-shadow-db", afero.NewMemMapFs(), conn.Intercept) - // Check error - assert.ErrorContains(t, err, `ERROR: schema "public" already exists (SQLSTATE 42P06)`) - }) -} - -func TestDiffDatabase(t *testing.T) { - utils.Config.Db.MajorVersion = 14 - utils.Config.Db.ShadowPort = 54320 - utils.GlobalsSql = "create schema public" - utils.InitialSchemaPg14Sql = "create schema private" - - t.Run("throws error on failure to create shadow", func(t *testing.T) { - errNetwork := errors.New("network error") - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/images/" + utils.GetRegistryImageUrl(utils.Config.Db.Image) + "/json"). - ReplyError(errNetwork) - // Run test - result, err := DiffDatabase(context.Background(), []string{"public"}, dbConfig, io.Discard, fsys, DiffSchemaMigra) - // Check error - assert.Empty(t, result) - assert.ErrorIs(t, err, errNetwork) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on health check failure", func(t *testing.T) { - utils.Config.Db.HealthTimeout = time.Millisecond - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Db.Image), "test-shadow-db") - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: false, - Status: "exited", - }, - }}) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db/logs"). - Reply(http.StatusServiceUnavailable) - gock.New(utils.Docker.DaemonHost()). - Delete("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db"). - Reply(http.StatusOK) - // Run test - result, err := DiffDatabase(context.Background(), []string{"public"}, dbConfig, io.Discard, fsys, DiffSchemaMigra) - // Check error - assert.Empty(t, result) - assert.ErrorContains(t, err, "test-shadow-db container is not running: exited") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on failure to migrate shadow", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Db.Image), "test-shadow-db") - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - Health: &container.Health{Status: types.Healthy}, - }, - }}) - gock.New(utils.Docker.DaemonHost()). - Delete("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db"). - Reply(http.StatusOK) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(utils.GlobalsSql). - ReplyError(pgerrcode.DuplicateSchema, `schema "public" already exists`) - // Run test - result, err := DiffDatabase(context.Background(), []string{"public"}, dbConfig, io.Discard, fsys, DiffSchemaMigra, conn.Intercept) - // Check error - assert.Empty(t, result) - assert.ErrorContains(t, err, `ERROR: schema "public" already exists (SQLSTATE 42P06) -At statement: 0 -create schema public`) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on failure to diff target", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := filepath.Join(utils.MigrationsDir, "0_test.sql") - sql := "create schema test" - require.NoError(t, afero.WriteFile(fsys, path, []byte(sql), 0644)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Db.Image), "test-shadow-db") - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - Health: &container.Health{Status: types.Healthy}, - }, - }}) - gock.New(utils.Docker.DaemonHost()). - Delete("/v" + utils.Docker.ClientVersion() + "/containers/test-shadow-db"). - Reply(http.StatusOK) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.EdgeRuntime.Image), "test-migra") - // The edge-runtime diff waits for the container to exit via inspect before - // reading its logs (it must not follow the log stream — that hangs under - // podman, supabase/pg-toolbelt#312), so the diff failure here surfaces from - // the log read rather than the followed stream. - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/test-migra/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ExitCode: 0}, - }}) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/test-migra/logs"). - ReplyError(errors.New("network error")) - gock.New(utils.Docker.DaemonHost()). - Delete("/v" + utils.Docker.ClientVersion() + "/containers/test-migra"). - Reply(http.StatusOK) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(utils.GlobalsSql). - Reply("CREATE SCHEMA"). - Query(utils.InitialSchemaPg14Sql). - Reply("CREATE SCHEMA") - helper.MockApiPrivilegesRevoke(conn). - Query(CREATE_TEMPLATE). - Reply("CREATE DATABASE") - helper.MockMigrationHistory(conn). - Query("RESET ALL"). - Reply("RESET"). - Query(sql). - Reply("CREATE SCHEMA"). - Query(migration.INSERT_MIGRATION_VERSION, "0", "test", []string{sql}). - Reply("INSERT 0 1") - // Run test - result, err := DiffDatabase(context.Background(), []string{"public"}, dbConfig, io.Discard, fsys, DiffSchemaMigra, func(cc *pgx.ConnConfig) { - if cc.Host == dbConfig.Host { - // Fake a SSL error when connecting to target database - cc.LookupFunc = func(ctx context.Context, host string) (addrs []string, err error) { - return nil, errors.New("server refused TLS connection") - } - } else { - // Hijack connection to shadow database - conn.Intercept(cc) - } - }) - // Check error - assert.Empty(t, result) - assert.ErrorContains(t, err, "error diffing schema") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestDropStatements(t *testing.T) { - drops := findDropStatements("create table t(); drop table t; alter table t drop column c") - assert.Equal(t, []string{"drop table t", "alter table t drop column c"}, drops) -} - -func TestLoadSchemas(t *testing.T) { - expected := []string{ - filepath.Join(utils.SchemasDir, "comment", "model.sql"), - filepath.Join(utils.SchemasDir, "model.sql"), - filepath.Join(utils.SchemasDir, "reaction", "dislike", "model.sql"), - filepath.Join(utils.SchemasDir, "reaction", "like", "model.sql"), - } - fsys := afero.NewMemMapFs() - for _, fp := range expected { - require.NoError(t, afero.WriteFile(fsys, fp, nil, 0644)) - } - // Run test - schemas, err := loadDeclaredSchemas(fsys) - // Check error - assert.NoError(t, err) - assert.ElementsMatch(t, expected, schemas) -} - -func TestLoadSchemasSkipsEmptySchemaPathGlobs(t *testing.T) { - fsys := afero.NewMemMapFs() - matched := filepath.Join(utils.SupabaseDirPath, "schemas", "tables", "players.sql") - require.NoError(t, afero.WriteFile(fsys, matched, nil, 0644)) - utils.Config.Db.Migrations.SchemaPaths = []string{ - filepath.Join(utils.SupabaseDirPath, "schemas", "tables", "*.sql"), - filepath.Join(utils.SupabaseDirPath, "schemas", "materialized_views", "*.sql"), - } - t.Cleanup(func() { - utils.Config.Db.Migrations.SchemaPaths = nil - }) - - schemas, err := loadDeclaredSchemas(fsys) - - assert.NoError(t, err) - assert.Equal(t, []string{filepath.ToSlash(matched)}, schemas) -} - -func TestLoadSchemasErrorsOnMissingLiteralSchemaPath(t *testing.T) { - fsys := afero.NewMemMapFs() - utils.Config.Db.Migrations.SchemaPaths = []string{ - filepath.Join(utils.SupabaseDirPath, "schemas", "tables", "players.sql"), - } - t.Cleanup(func() { - utils.Config.Db.Migrations.SchemaPaths = nil - }) - - schemas, err := loadDeclaredSchemas(fsys) - - assert.ErrorContains(t, err, "no files matched pattern") - assert.Empty(t, schemas) -} - -func TestLoadSchemasErrorsWhenAllSchemaPathGlobsAreEmpty(t *testing.T) { - fsys := afero.NewMemMapFs() - utils.Config.Db.Migrations.SchemaPaths = []string{ - filepath.Join(utils.SupabaseDirPath, "schemas", "tables", "*.sql"), - filepath.Join(utils.SupabaseDirPath, "schemas", "views", "*.sql"), - } - t.Cleanup(func() { - utils.Config.Db.Migrations.SchemaPaths = nil - }) - - schemas, err := loadDeclaredSchemas(fsys) - - assert.ErrorContains(t, err, "no files matched pattern") - assert.Empty(t, schemas) -} diff --git a/apps/cli-go/internal/db/diff/migra.go b/apps/cli-go/internal/db/diff/migra.go deleted file mode 100644 index b5c001825b..0000000000 --- a/apps/cli-go/internal/db/diff/migra.go +++ /dev/null @@ -1,162 +0,0 @@ -package diff - -import ( - "bytes" - "context" - _ "embed" - "strings" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" - "github.com/supabase/cli/internal/gen/types" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/config" - "github.com/supabase/cli/pkg/migration" -) - -var ( - //go:embed templates/migra.sh - diffSchemaScript string - //go:embed templates/migra.ts - diffSchemaTypeScript string - - managedSchemas = []string{ - // Local development - "_analytics", - "_realtime", - "_supavisor", - // Owned by extensions - "cron", - "graphql", - "graphql_public", - "net", - "pgroonga", - "pgtle", - "repack", - "tiger_data", - "vault", - // Deprecated extensions - "pgsodium", - "pgsodium_masks", - "timescaledb_experimental", - "timescaledb_information", - "_timescaledb_cache", - "_timescaledb_catalog", - "_timescaledb_config", - "_timescaledb_debug", - "_timescaledb_functions", - "_timescaledb_internal", - // Managed by Supabase - "pgbouncer", - "supabase_functions", - "supabase_migrations", - } -) - -// Diffs local database schema against shadow, dumps output to stdout. -func DiffSchemaMigraBash(ctx context.Context, source, target pgconn.Config, schema []string, options ...func(*pgx.ConnConfig)) (string, error) { - // Load all user defined schemas - if len(schema) == 0 { - var err error - if schema, err = loadSchema(ctx, target, options...); err != nil { - return "", err - } - } - env := []string{ - "SOURCE=" + utils.ToPostgresURL(source), - "TARGET=" + utils.ToPostgresURL(target), - } - if types.IsSSLDebugEnabled() { - env = append(env, "SUPABASE_SSL_DEBUG=true") - } - // Passing in script string means command line args must be set manually, ie. "$@" - args := "set -- " + strings.Join(schema, " ") + ";" - cmd := []string{"/bin/sh", "-c", args + diffSchemaScript} - var out, stderr bytes.Buffer - if err := utils.DockerRunOnceWithConfig( - ctx, - container.Config{ - Image: config.Images.Migra, - Env: env, - Cmd: cmd, - }, - container.HostConfig{ - NetworkMode: network.NetworkHost, - }, - network.NetworkingConfig{}, - "", - &out, - &stderr, - ); err != nil { - return "", errors.Errorf("error diffing schema: %w:\n%s", err, stderr.String()) - } - return out.String(), nil -} - -func loadSchema(ctx context.Context, config pgconn.Config, options ...func(*pgx.ConnConfig)) ([]string, error) { - conn, err := utils.ConnectByConfig(ctx, config, options...) - if err != nil { - return nil, err - } - defer conn.Close(context.Background()) - // RLS policies in auth and storage schemas can be included with -s flag - return migration.ListUserSchemas(ctx, conn) -} - -func DiffSchemaMigra(ctx context.Context, source, target pgconn.Config, schema []string, options ...func(*pgx.ConnConfig)) (string, error) { - env := []string{ - "SOURCE=" + utils.ToPostgresURL(source), - "TARGET=" + utils.ToPostgresURL(target), - } - debugf := func(string, ...any) {} - if types.IsSSLDebugEnabled() { - debugf = types.LogSSLDebugf - env = append(env, "SUPABASE_SSL_DEBUG=true") - debugf("DiffSchemaMigra source_host=%s source_port=%d target_host=%s target_port=%d target_db=%s", - source.Host, - source.Port, - target.Host, - target.Port, - target.Database, - ) - debugf("DiffSchemaMigra docker_daemon=%s image=%s", utils.Docker.DaemonHost(), utils.Config.EdgeRuntime.Image) - } - if ca, err := types.GetRootCA(ctx, utils.ToPostgresURL(target), options...); err != nil { - debugf("DiffSchemaMigra GetRootCA error=%v", err) - return "", err - } else if len(ca) > 0 { - env = append(env, "SSL_CA="+ca) - debugf("DiffSchemaMigra GetRootCA ca_bundle_len=%d", len(ca)) - } - if len(schema) > 0 { - env = append(env, "INCLUDED_SCHEMAS="+strings.Join(schema, ",")) - } else { - env = append(env, "EXCLUDED_SCHEMAS="+strings.Join(managedSchemas, ",")) - } - // Migra also executes via Edge Runtime because the TypeScript implementation - // shares the same containerized execution environment as other diff engines. - // The helper remains in package diff to avoid coupling migra code paths to - // pg-delta-specific packages. - binds := []string{utils.EdgeRuntimeId + ":/root/.cache/deno:rw"} - var stdout, stderr bytes.Buffer - if err := utils.RunEdgeRuntimeScript(ctx, env, diffSchemaTypeScript, binds, "error diffing schema", &stdout, &stderr); err != nil { - if shouldFallbackToLegacyMigra(err) { - debugf("DiffSchemaMigra falling back to legacy migra after edge-runtime OOM") - return DiffSchemaMigraBash(ctx, source, target, schema, options...) - } - return "", err - } - return stdout.String(), nil -} - -func shouldFallbackToLegacyMigra(err error) bool { - if err == nil { - return false - } - message := err.Error() - return strings.Contains(message, "Fatal JavaScript out of memory") || - strings.Contains(message, "Ineffective mark-compacts near heap limit") -} diff --git a/apps/cli-go/internal/db/diff/pgschema.go b/apps/cli-go/internal/db/diff/pgschema.go deleted file mode 100644 index 5126ca96ed..0000000000 --- a/apps/cli-go/internal/db/diff/pgschema.go +++ /dev/null @@ -1,58 +0,0 @@ -package diff - -import ( - "context" - "database/sql" - "fmt" - "strings" - - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" - pgschema "github.com/stripe/pg-schema-diff/pkg/diff" - "github.com/supabase/cli/internal/utils" -) - -func DiffPgSchema(ctx context.Context, source, target pgconn.Config, schema []string, _ ...func(*pgx.ConnConfig)) (string, error) { - dbSrc, err := sql.Open("pgx", utils.ToPostgresURL(source)) - if err != nil { - return "", errors.Errorf("failed to open source database: %w", err) - } - defer dbSrc.Close() - dbDst, err := sql.Open("pgx", utils.ToPostgresURL(target)) - if err != nil { - return "", errors.Errorf("failed to open target database: %w", err) - } - defer dbDst.Close() - // Generate DDL based on schema plan - opts := []pgschema.PlanOpt{pgschema.WithDoNotValidatePlan()} - if len(schema) > 0 { - opts = append(opts, pgschema.WithIncludeSchemas(schema...)) - } else { - opts = append(opts, - pgschema.WithExcludeSchemas(managedSchemas...), - pgschema.WithExcludeSchemas( - "topology", // unsupported due to views - "realtime", // unsupported due to partitioned table - "storage", // unsupported due to unique index - ), - ) - } - plan, err := pgschema.Generate( - ctx, - pgschema.DBSchemaSource(dbSrc), - pgschema.DBSchemaSource(dbDst), - opts..., - ) - if err != nil { - return "", errors.Errorf("failed to generate plan: %w", err) - } - var lines []string - for _, stat := range plan.Statements { - for _, harzard := range stat.Hazards { - lines = append(lines, fmt.Sprintf("-- %s", harzard)) - } - lines = append(lines, fmt.Sprintf("%s;\n", stat.DDL)) - } - return fmt.Sprintln(strings.Join(lines, "\n")), nil -} diff --git a/apps/cli-go/internal/db/diff/save_diff_test.go b/apps/cli-go/internal/db/diff/save_diff_test.go deleted file mode 100644 index e03df73eec..0000000000 --- a/apps/cli-go/internal/db/diff/save_diff_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package diff - -import ( - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/utils" -) - -func TestSaveDiff(t *testing.T) { - t.Run("reports no changes on empty diff", func(t *testing.T) { - fsys := afero.NewMemMapFs() - require.NoError(t, SaveDiff(DatabaseDiff{SQL: ""}, "my_diff", fsys)) - entries, err := afero.ReadDir(fsys, utils.MigrationsDir) - assert.Error(t, err) - assert.Empty(t, entries) - }) - - t.Run("writes a single migration file", func(t *testing.T) { - fsys := afero.NewMemMapFs() - require.NoError(t, SaveDiff(DatabaseDiff{SQL: "create table a ();"}, "my_diff", fsys)) - entries, err := afero.ReadDir(fsys, utils.MigrationsDir) - require.NoError(t, err) - require.Len(t, entries, 1) - assert.Regexp(t, `^\d{14}_my_diff\.sql$`, entries[0].Name()) - contents, err := afero.ReadFile(fsys, utils.MigrationsDir+"/"+entries[0].Name()) - require.NoError(t, err) - assert.Equal(t, "create table a ();", string(contents)) - }) - - t.Run("prints diff to stdout when no file is given", func(t *testing.T) { - fsys := afero.NewMemMapFs() - require.NoError(t, SaveDiff(DatabaseDiff{SQL: "create table a ();"}, "", fsys)) - entries, _ := afero.ReadDir(fsys, utils.MigrationsDir) - assert.Empty(t, entries) - }) - - t.Run("creates nested parent directories for a nested name", func(t *testing.T) { - fsys := afero.NewMemMapFs() - require.NoError(t, SaveDiff(DatabaseDiff{SQL: "create table a ();"}, "snapshots/remote", fsys)) - matches, err := afero.Glob(fsys, utils.MigrationsDir+"/*_snapshots/remote.sql") - require.NoError(t, err) - require.Len(t, matches, 1) - contents, err := afero.ReadFile(fsys, matches[0]) - require.NoError(t, err) - assert.Equal(t, "create table a ();", string(contents)) - }) -} diff --git a/apps/cli-go/internal/db/diff/shadow.go b/apps/cli-go/internal/db/diff/shadow.go deleted file mode 100644 index 7cafbeaf1e..0000000000 --- a/apps/cli-go/internal/db/diff/shadow.go +++ /dev/null @@ -1,74 +0,0 @@ -package diff - -import ( - "context" - - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/db/start" - "github.com/supabase/cli/internal/utils" -) - -// ShadowSource is a provisioned shadow database, left running for an external -// caller to diff against and then remove. It mirrors the shadow that -// DiffDatabase prepares as the diff "source". -type ShadowSource struct { - // Container is the shadow database container id; the caller MUST remove it - // (e.g. `docker rm -f `) when the diff completes. - Container string - // Source is the connection config for the diff source (the shadow with the - // platform baseline + local migrations applied). - Source pgconn.Config - // TargetOverride, when non-nil, replaces the diff target with a second - // shadow database (contrib_regression with declarative schemas applied). - TargetOverride *pgconn.Config -} - -// PrepareShadowSource provisions the shadow database that DiffDatabase diffs -// against, but returns it running instead of diffing + removing. targetLocal -// mirrors utils.IsLocalDatabase(config). On error the shadow container is -// removed. Declared schemas are applied with the migra seed path; pg-delta -// apply lives in the TypeScript CLI. -func PrepareShadowSource(ctx context.Context, targetLocal bool, fsys afero.Fs, options ...func(*pgx.ConnConfig)) (ShadowSource, error) { - shadow, err := CreateShadowDatabase(ctx, utils.Config.Db.ShadowPort) - if err != nil { - return ShadowSource{}, err - } - ok := false - defer func() { - if !ok { - utils.DockerRemove(shadow) - } - }() - if err := start.WaitForHealthyService(ctx, utils.Config.Db.HealthTimeout, shadow); err != nil { - return ShadowSource{}, err - } - if err := MigrateShadowDatabase(ctx, shadow, fsys, options...); err != nil { - return ShadowSource{}, err - } - shadowConfig := pgconn.Config{ - Host: utils.Config.Hostname, - Port: utils.Config.Db.ShadowPort, - User: "postgres", - Password: utils.Config.Db.Password, - Database: "postgres", - } - var targetOverride *pgconn.Config - if targetLocal { - declared, err := loadDeclaredSchemas(fsys) - if err != nil { - return ShadowSource{}, err - } - if len(declared) > 0 { - override := shadowConfig - override.Database = "contrib_regression" - if err := migrateBaseDatabase(ctx, override, declared, fsys, options...); err != nil { - return ShadowSource{}, err - } - targetOverride = &override - } - } - ok = true - return ShadowSource{Container: shadow, Source: shadowConfig, TargetOverride: targetOverride}, nil -} diff --git a/apps/cli-go/internal/db/diff/templates/migra.sh b/apps/cli-go/internal/db/diff/templates/migra.sh deleted file mode 100755 index e630bd1147..0000000000 --- a/apps/cli-go/internal/db/diff/templates/migra.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh -set -eu - -if [ "${SUPABASE_SSL_DEBUG:-}" = "true" ]; then - [ -n "${SOURCE:-}" ] && source_set=true || source_set=false - [ -n "${TARGET:-}" ] && target_set=true || target_set=false - echo "[ssl-debug] migra.sh uname=$(uname -a)" >&2 - echo "[ssl-debug] migra.sh source_set=$source_set target_set=$target_set schemas=$*" >&2 -fi - -# migra doesn't shutdown gracefully, so kill it ourselves -trap 'kill -9 %1' TERM - -run_migra() { - # additional flags for diffing extensions - [ "$schema" = "extensions" ] && set -- --create-extensions-only --ignore-extension-versions "$@" - migra --with-privileges --unsafe --schema="$schema" "$@" -} - -# accepts command line args as a list of schema to generate -for schema in "$@"; do - # migra exits 2 when differences are found - run_migra "$SOURCE" "$TARGET" || status=$? - if [ "${SUPABASE_SSL_DEBUG:-}" = "true" ]; then - echo "[ssl-debug] migra.sh schema=$schema exit_status=${status:-0}" >&2 - fi - if [ ${status:-2} -ne 2 ]; then - exit $status - fi -done diff --git a/apps/cli-go/internal/db/diff/templates/migra.ts b/apps/cli-go/internal/db/diff/templates/migra.ts deleted file mode 100644 index d67124140f..0000000000 --- a/apps/cli-go/internal/db/diff/templates/migra.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { createClient } from "npm:@pgkit/client"; -import { Migration } from "npm:@pgkit/migra"; - -// Avoids error on self-signed certificate -const ca = Deno.env.get("SSL_CA"); -const source = Deno.env.get("SOURCE"); -const target = Deno.env.get("TARGET"); -const sslDebug = Deno.env.get("SUPABASE_SSL_DEBUG")?.toLowerCase() === "true"; - -function redactPostgresUrl(raw: string | undefined): string { - if (!raw) return ""; - try { - const u = new URL(raw); - if (u.password) u.password = "xxxxx"; - return u.toString(); - } catch { - return ""; - } -} - -if (sslDebug) { - console.error( - `[ssl-debug] migra.ts deno=${Deno.version.deno} v8=${Deno.version.v8} os=${Deno.build.os}`, - ); - console.error( - `[ssl-debug] migra.ts source=${redactPostgresUrl(source)} target=${redactPostgresUrl(target)}`, - ); - console.error( - `[ssl-debug] migra.ts ssl_ca_set=${ca != null} ssl_ca_len=${ca?.length ?? 0}`, - ); -} - -type PoolClient = { - query: (stmt: string) => Promise; - on: (event: "error", listener: () => void) => void; - off: (event: "error", listener: () => void) => void; -}; -// Step down from login role to postgres and force schema qualified references for -// pg_get_expr on every pooled connection, including one reopened after an idle timeout -const verify = (stmt: string) => (client: PoolClient, done: (err?: Error) => void) => { - // pg-pool drops its error listener during verify, so a socket error must reject, not throw - const ignore = () => {}; - client.on("error", ignore); - client - .query(stmt) - .finally(() => client.off("error", ignore)) - .then( - () => done(), - (err: Error) => { - err.message = `${stmt}: ${err.message}`; - done(err); - }, - ); -}; -const clientBase = createClient(source, { - pgpOptions: { connect: { verify: verify("set search_path = ''") } }, -}); -const clientHead = createClient(target, { - pgpOptions: { - connect: { - ssl: ca && { ca }, - verify: verify("set role postgres; set search_path = ''"), - }, - }, -}); -const includedSchemas = Deno.env.get("INCLUDED_SCHEMAS")?.split(",") ?? []; -const excludedSchemas = Deno.env.get("EXCLUDED_SCHEMAS")?.split(",") ?? []; - -const managedSchemas = ["auth", "realtime", "storage"]; -const extensionSchemas = [ - "pg_catalog", - "extensions", - "pgmq", - "tiger", - "topology", -]; - -try { - const result: string[] = []; - for (const schema of includedSchemas) { - const m = await Migration.create(clientBase, clientHead, { - schema, - ignore_extension_versions: true, - }); - m.set_safety(false); - if (managedSchemas.includes(schema)) { - m.add(m.changes.triggers({ drops_only: true })); - m.add(m.changes.rlspolicies({ drops_only: true })); - m.add(m.changes.rlspolicies({ creations_only: true })); - m.add(m.changes.triggers({ creations_only: true })); - } else { - m.add_all_changes(true); - } - result.push(m.sql); - } - if (includedSchemas.length === 0) { - // Migra does not ignore custom types and triggers created by extensions, so we diff - // them separately. This workaround only applies to a known list of managed schemas. - for (const schema of extensionSchemas) { - const e = await Migration.create(clientBase, clientHead, { - schema, - ignore_extension_versions: true, - }); - e.set_safety(false); - e.add(e.changes.schemas({ creations_only: true })); - e.add_extension_changes(); - result.push(e.sql); - } - // Diff user defined entities in non-managed schemas, including extensions. - const m = await Migration.create(clientBase, clientHead, { - exclude_schema: [ - ...managedSchemas, - ...extensionSchemas, - ...excludedSchemas, - ], - ignore_extension_versions: true, - }); - m.set_safety(false); - m.add_all_changes(true); - result.push(m.sql); - // For managed schemas, we want to include triggers and RLS policies only. - for (const schema of managedSchemas) { - const s = await Migration.create(clientBase, clientHead, { - schema, - ignore_extension_versions: true, - }); - s.set_safety(false); - s.add(s.changes.triggers({ drops_only: true })); - s.add(s.changes.rlspolicies({ drops_only: true })); - s.add(s.changes.rlspolicies({ creations_only: true })); - s.add(s.changes.triggers({ creations_only: true })); - result.push(s.sql); - } - } - console.log(result.join("")); -} catch (e) { - if (sslDebug) { - if (e instanceof Error) { - console.error( - `[ssl-debug] migra.ts error_name=${e.name} message=${e.message} stack=${e.stack ?? ""}`, - ); - } else { - console.error(`[ssl-debug] migra.ts error=${String(e)}`); - } - } - console.error(e); - console.error("PGDELTA_SCRIPT_ERROR"); -} finally { - await Promise.all([clientHead.end(), clientBase.end()]); -} diff --git a/apps/cli-go/internal/db/reset/reset.go b/apps/cli-go/internal/db/reset/reset.go deleted file mode 100644 index 7ac3f8e4f1..0000000000 --- a/apps/cli-go/internal/db/reset/reset.go +++ /dev/null @@ -1,313 +0,0 @@ -package reset - -import ( - "bytes" - "context" - _ "embed" - "fmt" - "io" - "os" - "strconv" - "strings" - "time" - - "github.com/cenkalti/backoff/v4" - "github.com/containerd/errdefs" - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgerrcode" - "github.com/jackc/pgx/v4" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/db/start" - "github.com/supabase/cli/internal/migration/apply" - "github.com/supabase/cli/internal/migration/down" - "github.com/supabase/cli/internal/migration/list" - "github.com/supabase/cli/internal/migration/repair" - "github.com/supabase/cli/internal/seed/buckets" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/migration" -) - -func Run(ctx context.Context, version string, last uint, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - if len(version) > 0 { - if _, err := strconv.Atoi(version); err != nil { - return errors.New(repair.ErrInvalidVersion) - } - if _, err := repair.GetMigrationFile(version, fsys); err != nil { - return err - } - } else if last > 0 { - localMigrations, err := list.LoadLocalVersions(fsys) - if err != nil { - return err - } - if total := uint(len(localMigrations)); last < total { - version = localMigrations[total-last-1] - } else { - // Negative skips all migrations - version = "-" - } - } - if !utils.IsLocalDatabase(config) { - return resetRemote(ctx, version, config, fsys, options...) - } - // Config file is loaded before parsing --linked or --local flags - if err := utils.AssertSupabaseDbIsRunning(); err != nil { - return err - } - // Reset postgres database because extensions (pg_cron, pg_net) require postgres - if err := resetDatabase(ctx, version, fsys, options...); err != nil { - return err - } - // Seed objects from supabase/buckets directory - if resp, err := utils.Docker.ContainerInspect(ctx, utils.StorageId); err == nil { - if resp.State.Health == nil || resp.State.Health.Status != types.Healthy { - if err := start.WaitForHealthyService(ctx, 30*time.Second, utils.StorageId); err != nil { - return err - } - } - if err := buckets.Run(ctx, "", false, fsys); err != nil { - return err - } - } - branch := utils.GetGitBranch(fsys) - fmt.Fprintln(os.Stderr, "Finished "+utils.Aqua("supabase db reset")+" on branch "+utils.Aqua(branch)+".") - return nil -} - -func resetDatabase(ctx context.Context, version string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - fmt.Fprintln(os.Stderr, "Resetting local database"+toLogMessage(version)) - if utils.Config.Db.MajorVersion <= 14 { - return resetDatabase14(ctx, version, fsys, options...) - } - return resetDatabase15(ctx, version, fsys, options...) -} - -func toLogMessage(version string) string { - if len(version) > 0 { - return " to version: " + version - } - return "..." -} - -func resetDatabase14(ctx context.Context, version string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - if err := recreateDatabase(ctx, options...); err != nil { - return err - } - if err := initDatabase(ctx, options...); err != nil { - return err - } - if err := RestartDatabase(ctx, os.Stderr); err != nil { - return err - } - conn, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{}, options...) - if err != nil { - return err - } - defer conn.Close(context.Background()) - return apply.MigrateAndSeed(ctx, version, conn, fsys) -} - -func resetDatabase15(ctx context.Context, version string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - if err := utils.Docker.ContainerRemove(ctx, utils.DbId, container.RemoveOptions{Force: true}); err != nil { - return errors.Errorf("failed to remove container: %w", err) - } - if err := utils.Docker.VolumeRemove(ctx, utils.DbId, true); err != nil { - return errors.Errorf("failed to remove volume: %w", err) - } - config := start.NewContainerConfig() - hostConfig := start.NewHostConfig() - networkingConfig := network.NetworkingConfig{ - EndpointsConfig: map[string]*network.EndpointSettings{ - utils.NetId: { - Aliases: utils.DbAliases, - }, - }, - } - fmt.Fprintln(os.Stderr, "Recreating database...") - if _, err := utils.DockerStart(ctx, config, hostConfig, networkingConfig, utils.DbId); err != nil { - return err - } - if err := start.WaitForHealthyService(ctx, utils.Config.Db.HealthTimeout, utils.DbId); err != nil { - return err - } - if err := start.SetupLocalDatabase(ctx, version, fsys, os.Stderr, options...); err != nil { - return err - } - fmt.Fprintln(os.Stderr, "Restarting containers...") - return restartServices(ctx) -} - -func initDatabase(ctx context.Context, options ...func(*pgx.ConnConfig)) error { - conn, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{User: utils.SUPERUSER_ROLE}, options...) - if err != nil { - return err - } - defer conn.Close(context.Background()) - if err := start.InitSchema14(ctx, conn); err != nil { - return err - } - return start.ApplyApiPrivileges(ctx, conn) -} - -// Recreate postgres database by connecting to template1 -func recreateDatabase(ctx context.Context, options ...func(*pgx.ConnConfig)) error { - conn, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{User: utils.SUPERUSER_ROLE, Database: "template1"}, options...) - if err != nil { - return err - } - defer conn.Close(context.Background()) - if err := DisconnectClients(ctx, conn); err != nil { - return err - } - // We are not dropping roles here because they are cluster level entities. Use stop && start instead. - sql := migration.MigrationFile{ - Statements: []string{ - "DROP DATABASE IF EXISTS postgres WITH (FORCE)", - "CREATE DATABASE postgres WITH OWNER postgres", - "DROP DATABASE IF EXISTS _supabase WITH (FORCE)", - "CREATE DATABASE _supabase WITH OWNER postgres", - }, - } - return sql.ExecBatch(ctx, conn) -} - -const ( - TERMINATE_BACKENDS = "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname IN ('postgres', '_supabase')" - COUNT_REPLICATION_SLOTS = "SELECT COUNT(*) FROM pg_replication_slots WHERE database IN ('postgres', '_supabase')" -) - -func DisconnectClients(ctx context.Context, conn *pgx.Conn) error { - // Must be executed separately because looping in transaction is unsupported - // https://dba.stackexchange.com/a/11895 - disconn := migration.MigrationFile{ - Statements: []string{ - "ALTER DATABASE postgres ALLOW_CONNECTIONS false", - "ALTER DATABASE _supabase ALLOW_CONNECTIONS false", - TERMINATE_BACKENDS, - }, - } - if err := disconn.ExecBatch(ctx, conn); err != nil { - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) && pgErr.Code != pgerrcode.InvalidCatalogName { - return errors.Errorf("failed to disconnect clients: %w", err) - } - } - // Wait for WAL senders to drop their replication slots - policy := start.NewBackoffPolicy(ctx, 10*time.Second) - waitForDrop := func() error { - var count int - if err := conn.QueryRow(ctx, COUNT_REPLICATION_SLOTS).Scan(&count); err != nil { - err = errors.Errorf("failed to count replication slots: %w", err) - return &backoff.PermanentError{Err: err} - } else if count > 0 { - return errors.Errorf("replication slots still active: %d", count) - } - return nil - } - return backoff.Retry(waitForDrop, policy) -} - -func RestartDatabase(ctx context.Context, w io.Writer) error { - fmt.Fprintln(w, "Restarting containers...") - // Some extensions must be manually restarted after pg_terminate_backend - // Ref: https://github.com/citusdata/pg_cron/issues/99 - if err := utils.Docker.ContainerRestart(ctx, utils.DbId, container.StopOptions{}); err != nil { - return errors.Errorf("failed to restart container: %w", err) - } - if err := start.WaitForHealthyService(ctx, utils.Config.Db.HealthTimeout, utils.DbId); err != nil { - return err - } - return restartServices(ctx) -} - -func restartServices(ctx context.Context) error { - // No need to restart PostgREST because it automatically reconnects and listens for schema changes - services := listServicesToRestart() - result := utils.WaitAll(services, func(id string) error { - if err := utils.Docker.ContainerRestart(ctx, id, container.StopOptions{}); err != nil && !errdefs.IsNotFound(err) { - return errors.Errorf("failed to restart %s: %w", id, err) - } - return nil - }) - // Do not wait for service healthy as those services may be excluded from starting - if err := errors.Join(result...); err != nil { - return err - } - return reloadKong(ctx) -} - -func listServicesToRestart() []string { - return []string{utils.StorageId, utils.GotrueId, utils.RealtimeId, utils.PoolerId} -} - -// reloadKong reloads Kong after the restarts above so its nginx re-resolves each -// upstream container's address. Kong caches resolved addresses for the life of a -// worker process, and a restarted container can come back on a different one, -// leaving the gateway returning 502 for that route until Kong restarts. An -// in-place reload (the `functions serve` pattern) keeps the gateway serving -// throughout. https://github.com/supabase/cli/issues/6016 -func reloadKong(ctx context.Context) error { - resp, err := utils.Docker.ContainerInspect(ctx, utils.KongId) - if errdefs.IsNotFound(err) { - // Kong may be excluded from the stack. - return nil - } else if err != nil { - return suggestKongRecovery(errors.Errorf("failed to inspect kong: %w", err)) - } - if !resp.State.Running { - // A stopped gateway has no stale cache to flush. - return nil - } - var out bytes.Buffer - // Reload re-renders nginx.conf from Kong's default template, so it needs the - // template bring-up wrote (start.go:589-592) handed back — otherwise it drops - // that template's email_templates server. https://github.com/supabase/cli/issues/6059 - if err := utils.DockerExecOnceWithStream(ctx, utils.KongId, "", nil, []string{"kong", "reload", "--nginx-conf", "/home/kong/custom_nginx.template"}, &out, &out); err != nil { - if msg := strings.TrimSpace(out.String()); len(msg) > 0 { - return suggestKongRecovery(errors.Errorf("failed to reload kong: %w:\n%s", err, msg)) - } - return suggestKongRecovery(errors.Errorf("failed to reload kong: %w", err)) - } - return nil -} - -// suggestKongRecovery decorates a gateway-left-unconfirmed failure with the -// advisory next step; the caller-neutral wording also covers branch switch, -// which shares RestartDatabase. -func suggestKongRecovery(err error) error { - utils.CmdSuggestion = fmt.Sprintf( - "Local services restarted, but API routes may return 502 until the gateway reloads.\nTry restarting it with %s, and check %s if the failure persists.", - utils.Aqua("docker restart "+utils.KongId), - utils.Aqua("docker logs "+utils.KongId), - ) - return err -} - -func resetRemote(ctx context.Context, version string, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - msg := "Do you want to reset the remote database?" - if shouldReset, err := utils.NewConsole().PromptYesNo(ctx, msg, false); err != nil { - return err - } else if !shouldReset { - return errors.New(context.Canceled) - } - fmt.Fprintln(os.Stderr, "Resetting remote database"+toLogMessage(version)) - conn, err := utils.ConnectByConfigStream(ctx, config, io.Discard, options...) - if err != nil { - return err - } - defer conn.Close(context.Background()) - return down.ResetAll(ctx, version, conn, fsys) -} - -func LikeEscapeSchema(schemas []string) (result []string) { - // Treat _ as literal, * as any character - replacer := strings.NewReplacer("_", `\_`, "*", "%") - for _, sch := range schemas { - result = append(result, replacer.Replace(sch)) - } - return result -} diff --git a/apps/cli-go/internal/db/reset/reset_test.go b/apps/cli-go/internal/db/reset/reset_test.go deleted file mode 100644 index 5d672ec15f..0000000000 --- a/apps/cli-go/internal/db/reset/reset_test.go +++ /dev/null @@ -1,568 +0,0 @@ -package reset - -import ( - "context" - "encoding/json" - "errors" - "io" - "net/http" - "testing" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/h2non/gock" - "github.com/jackc/pgconn" - "github.com/jackc/pgerrcode" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/testing/fstest" - "github.com/supabase/cli/internal/testing/helper" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/pgtest" - "github.com/supabase/cli/pkg/storage" -) - -func TestResetCommand(t *testing.T) { - utils.Config.Hostname = "127.0.0.1" - utils.Config.Db.Port = 5432 - - var dbConfig = pgconn.Config{ - Host: utils.Config.Hostname, - Port: utils.Config.Db.Port, - User: "admin", - Password: "password", - Database: "postgres", - } - - t.Run("seeds storage after reset", func(t *testing.T) { - utils.DbId = "test-reset" - utils.Config.Db.MajorVersion = 15 - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId). - Reply(http.StatusOK). - JSON(container.InspectResponse{}) - gock.New(utils.Docker.DaemonHost()). - Delete("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId). - Reply(http.StatusOK) - gock.New(utils.Docker.DaemonHost()). - Delete("/v" + utils.Docker.ClientVersion() + "/volumes/" + utils.DbId). - Reply(http.StatusOK) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Db.Image), utils.DbId) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - Health: &container.Health{Status: types.Healthy}, - }, - }}) - // Setup mock postgres: with auto_expose_new_tables unset, the default Data API GRANTs - // are revoked by default during database setup. - conn := pgtest.NewConn() - defer conn.Close(t) - helper.MockApiPrivilegesRevoke(conn) - // Restarts services - utils.StorageId = "test-storage" - utils.GotrueId = "test-auth" - utils.RealtimeId = "test-realtime" - utils.PoolerId = "test-pooler" - utils.KongId = "test-kong" - for _, container := range listServicesToRestart() { - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart"). - Reply(http.StatusOK) - } - // Kong is not running so the reload is skipped (a successful exec attach is not gock-mockable, see TestExecOnce) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.KongId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{Running: false}, - }}) - // Seeds storage - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.StorageId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - Health: &container.Health{Status: types.Healthy}, - }, - }}) - gock.New(utils.Config.Api.ExternalUrl). - Get("/storage/v1/bucket"). - Reply(http.StatusOK). - JSON([]storage.BucketResponse{}) - // Run test - err := Run(context.Background(), "", 0, dbConfig, fsys, conn.Intercept) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on context canceled", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - err := Run(context.Background(), "", 0, pgconn.Config{Host: "db.supabase.co"}, fsys) - // Check error - assert.ErrorIs(t, err, context.Canceled) - }) - - t.Run("throws error on invalid port", func(t *testing.T) { - t.Cleanup(fstest.MockStdin(t, "y")) - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - err := Run(context.Background(), "", 0, pgconn.Config{Host: "db.supabase.co"}, fsys) - // Check error - assert.ErrorContains(t, err, "invalid port (outside range)") - }) - - t.Run("throws error on db is not started", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers"). - Reply(http.StatusNotFound) - // Run test - err := Run(context.Background(), "", 0, dbConfig, fsys) - // Check error - assert.ErrorIs(t, err, utils.ErrNotRunning) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on failure to recreate", func(t *testing.T) { - utils.DbId = "test-reset" - utils.Config.Db.MajorVersion = 15 - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId). - Reply(http.StatusOK). - JSON(container.InspectResponse{}) - gock.New(utils.Docker.DaemonHost()). - Delete("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId). - ReplyError(errors.New("network error")) - // Run test - err := Run(context.Background(), "", 0, dbConfig, fsys) - // Check error - assert.ErrorContains(t, err, "network error") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestInitDatabase(t *testing.T) { - t.Run("initializes postgres database", func(t *testing.T) { - utils.Config.Db.Port = 54322 - utils.InitialSchemaPg14Sql = "create schema private" - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(utils.InitialSchemaPg14Sql). - Reply("CREATE SCHEMA") - helper.MockApiPrivilegesRevoke(conn) - // Run test - assert.NoError(t, initDatabase(context.Background(), conn.Intercept)) - }) - - t.Run("throws error on connect failure", func(t *testing.T) { - utils.Config.Db.Port = 0 - // Run test - err := initDatabase(context.Background()) - // Check error - assert.ErrorContains(t, err, "invalid port (outside range)") - }) - - t.Run("throws error on duplicate schema", func(t *testing.T) { - utils.Config.Db.Port = 54322 - utils.InitialSchemaPg14Sql = "create schema private" - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(utils.InitialSchemaPg14Sql). - ReplyError(pgerrcode.DuplicateSchema, `schema "public" already exists`) - // Run test - err := initDatabase(context.Background(), conn.Intercept) - // Check error - assert.ErrorContains(t, err, `ERROR: schema "public" already exists (SQLSTATE 42P06)`) - }) -} - -func TestRecreateDatabase(t *testing.T) { - t.Run("resets postgres database", func(t *testing.T) { - utils.Config.Db.Port = 54322 - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query("ALTER DATABASE postgres ALLOW_CONNECTIONS false"). - Reply("ALTER DATABASE"). - Query("ALTER DATABASE _supabase ALLOW_CONNECTIONS false"). - Reply("ALTER DATABASE"). - Query(TERMINATE_BACKENDS). - Reply("SELECT 1"). - Query(COUNT_REPLICATION_SLOTS). - Reply("SELECT 1", []any{0}). - Query("DROP DATABASE IF EXISTS postgres WITH (FORCE)"). - Reply("DROP DATABASE"). - Query("CREATE DATABASE postgres WITH OWNER postgres"). - Reply("CREATE DATABASE"). - Query("DROP DATABASE IF EXISTS _supabase WITH (FORCE)"). - Reply("DROP DATABASE"). - Query("CREATE DATABASE _supabase WITH OWNER postgres"). - Reply("CREATE DATABASE") - // Run test - assert.NoError(t, recreateDatabase(context.Background(), conn.Intercept)) - }) - - t.Run("throws error on invalid port", func(t *testing.T) { - utils.Config.Db.Port = 0 - assert.ErrorContains(t, recreateDatabase(context.Background()), "invalid port") - }) - - t.Run("continues on disconnecting missing database", func(t *testing.T) { - utils.Config.Db.Port = 54322 - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query("ALTER DATABASE postgres ALLOW_CONNECTIONS false"). - Reply("ALTER DATABASE"). - Query("ALTER DATABASE _supabase ALLOW_CONNECTIONS false"). - ReplyError(pgerrcode.InvalidCatalogName, `database "_supabase" does not exist`). - Query(TERMINATE_BACKENDS). - Query(COUNT_REPLICATION_SLOTS). - ReplyError(pgerrcode.UndefinedTable, `relation "pg_replication_slots" does not exist`) - // Run test - err := recreateDatabase(context.Background(), conn.Intercept) - // Check error - assert.ErrorContains(t, err, `ERROR: relation "pg_replication_slots" does not exist (SQLSTATE 42P01)`) - }) - - t.Run("throws error on failure to disconnect", func(t *testing.T) { - utils.Config.Db.Port = 54322 - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query("ALTER DATABASE postgres ALLOW_CONNECTIONS false"). - ReplyError(pgerrcode.InvalidParameterValue, `cannot disallow connections for current database`). - Query("ALTER DATABASE _supabase ALLOW_CONNECTIONS false"). - Query(TERMINATE_BACKENDS) - // Run test - err := recreateDatabase(context.Background(), conn.Intercept) - // Check error - assert.ErrorContains(t, err, "ERROR: cannot disallow connections for current database (SQLSTATE 22023)") - }) - - t.Run("throws error on failure to drop", func(t *testing.T) { - utils.Config.Db.Port = 54322 - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query("ALTER DATABASE postgres ALLOW_CONNECTIONS false"). - Reply("ALTER DATABASE"). - Query("ALTER DATABASE _supabase ALLOW_CONNECTIONS false"). - Reply("ALTER DATABASE"). - Query(TERMINATE_BACKENDS). - Reply("SELECT 1"). - Query(COUNT_REPLICATION_SLOTS). - Reply("SELECT 1", []any{0}). - Query("DROP DATABASE IF EXISTS postgres WITH (FORCE)"). - ReplyError(pgerrcode.ObjectInUse, `database "postgres" is used by an active logical replication slot`). - Query("CREATE DATABASE postgres WITH OWNER postgres"). - Query("DROP DATABASE IF EXISTS _supabase WITH (FORCE)"). - Reply("DROP DATABASE"). - Query("CREATE DATABASE _supabase WITH OWNER postgres"). - Reply("CREATE DATABASE") - err := recreateDatabase(context.Background(), conn.Intercept) - // Check error - assert.ErrorContains(t, err, `ERROR: database "postgres" is used by an active logical replication slot (SQLSTATE 55006)`) - }) -} - -func TestRestartDatabase(t *testing.T) { - t.Run("restarts affected services", func(t *testing.T) { - utils.DbId = "test-reset" - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - // Restarts postgres - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/restart"). - Reply(http.StatusOK) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - Health: &container.Health{Status: types.Healthy}, - }, - }}) - // Restarts services - utils.StorageId = "test-storage" - utils.GotrueId = "test-auth" - utils.RealtimeId = "test-realtime" - utils.PoolerId = "test-pooler" - utils.KongId = "test-kong" - for _, container := range listServicesToRestart() { - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart"). - Reply(http.StatusOK) - } - // Kong is not running so the reload is skipped (a successful exec attach is not gock-mockable, see TestExecOnce) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.KongId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{Running: false}, - }}) - // Run test - err := RestartDatabase(context.Background(), io.Discard) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on service restart failure", func(t *testing.T) { - utils.DbId = "test-reset" - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - // Restarts postgres - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/restart"). - Reply(http.StatusOK) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - Health: &container.Health{Status: types.Healthy}, - }, - }}) - // Restarts services - utils.StorageId = "test-storage" - utils.GotrueId = "test-auth" - utils.RealtimeId = "test-realtime" - utils.PoolerId = "test-pooler" - for _, container := range []string{utils.StorageId, utils.GotrueId, utils.RealtimeId} { - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart"). - Reply(http.StatusServiceUnavailable) - } - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.PoolerId + "/restart"). - Reply(http.StatusNotFound) - // Run test - err := RestartDatabase(context.Background(), io.Discard) - // Check error - assert.ErrorContains(t, err, "failed to restart "+utils.StorageId) - assert.ErrorContains(t, err, "failed to restart "+utils.GotrueId) - assert.ErrorContains(t, err, "failed to restart "+utils.RealtimeId) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("skips kong reload when kong is not running", func(t *testing.T) { - utils.DbId = "test-reset" - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - // Restarts postgres - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/restart"). - Reply(http.StatusOK) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - Health: &container.Health{Status: types.Healthy}, - }, - }}) - // Restarts services - utils.StorageId = "test-storage" - utils.GotrueId = "test-auth" - utils.RealtimeId = "test-realtime" - utils.PoolerId = "test-pooler" - utils.KongId = "test-kong" - for _, container := range listServicesToRestart() { - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart"). - Reply(http.StatusOK) - } - // Kong is excluded from the stack: no exec follows - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.KongId + "/json"). - Reply(http.StatusNotFound) - // Run test - err := RestartDatabase(context.Background(), io.Discard) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on kong inspect failure", func(t *testing.T) { - utils.DbId = "test-reset" - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - // Restarts postgres - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/restart"). - Reply(http.StatusOK) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - Health: &container.Health{Status: types.Healthy}, - }, - }}) - // Restarts services - utils.StorageId = "test-storage" - utils.GotrueId = "test-auth" - utils.RealtimeId = "test-realtime" - utils.PoolerId = "test-pooler" - utils.KongId = "test-kong" - for _, container := range listServicesToRestart() { - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart"). - Reply(http.StatusOK) - } - // A daemon error is not the excluded-kong skip case - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.KongId + "/json"). - Reply(http.StatusServiceUnavailable) - // Run test - err := RestartDatabase(context.Background(), io.Discard) - // Check error - assert.ErrorContains(t, err, "failed to inspect kong") - assert.Contains(t, utils.CmdSuggestion, "API routes may return 502") - assert.Contains(t, utils.CmdSuggestion, "docker restart test-kong") - t.Cleanup(func() { utils.CmdSuggestion = "" }) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on kong reload failure", func(t *testing.T) { - utils.DbId = "test-reset" - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - // Restarts postgres - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/restart"). - Reply(http.StatusOK) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - Health: &container.Health{Status: types.Healthy}, - }, - }}) - // Restarts services - utils.StorageId = "test-storage" - utils.GotrueId = "test-auth" - utils.RealtimeId = "test-realtime" - utils.PoolerId = "test-pooler" - utils.KongId = "test-kong" - for _, container := range listServicesToRestart() { - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart"). - Reply(http.StatusOK) - } - // Kong is up but the reload exec fails - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.KongId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{Running: true}, - }}) - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.KongId + "/exec"). - Reply(http.StatusServiceUnavailable) - // The reload must carry bring-up's template — see reloadKong (#6059). - execPath := "/v" + utils.Docker.ClientVersion() + "/containers/" + utils.KongId + "/exec" - observed := 0 - gock.Observe(func(r *http.Request, mock gock.Mock) { - if r.URL.Path != execPath { - return - } - observed += 1 - var body container.ExecOptions - assert.NoError(t, json.NewDecoder(r.Body).Decode(&body)) - assert.Equal(t, []string{"kong", "reload", "--nginx-conf", "/home/kong/custom_nginx.template"}, body.Cmd) - }) - t.Cleanup(func() { gock.Observe(nil) }) - // Run test - err := RestartDatabase(context.Background(), io.Discard) - // Check error - assert.Equal(t, 1, observed) - assert.ErrorContains(t, err, "failed to reload kong") - assert.Contains(t, utils.CmdSuggestion, "API routes may return 502") - assert.Contains(t, utils.CmdSuggestion, "docker restart test-kong") - t.Cleanup(func() { utils.CmdSuggestion = "" }) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on db restart failure", func(t *testing.T) { - utils.DbId = "test-reset" - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - // Restarts postgres - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/restart"). - Reply(http.StatusServiceUnavailable) - // Run test - err := RestartDatabase(context.Background(), io.Discard) - // Check error - assert.ErrorContains(t, err, "failed to restart container") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on health check timeout", func(t *testing.T) { - utils.DbId = "test-reset" - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/test-reset/restart"). - Reply(http.StatusOK) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/test-reset/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: false, - Status: "exited", - }, - }}) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/test-reset/logs"). - Reply(http.StatusServiceUnavailable) - // Run test - err := RestartDatabase(context.Background(), io.Discard) - // Check error - assert.ErrorContains(t, err, "test-reset container is not running: exited") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} diff --git a/apps/cli-go/internal/db/start/start.go b/apps/cli-go/internal/db/start/start.go deleted file mode 100644 index d4c97ba1d5..0000000000 --- a/apps/cli-go/internal/db/start/start.go +++ /dev/null @@ -1,425 +0,0 @@ -package start - -import ( - "context" - _ "embed" - "fmt" - "io" - "os" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/cenkalti/backoff/v4" - "github.com/containerd/errdefs" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" - "github.com/docker/go-connections/nat" - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/migration/apply" - "github.com/supabase/cli/internal/status" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/flags" - "github.com/supabase/cli/pkg/config" - "github.com/supabase/cli/pkg/migration" - "github.com/supabase/cli/pkg/vault" -) - -var ( - //go:embed templates/schema.sql - initialSchema string - //go:embed templates/webhook.sql - webhookSchema string - //go:embed templates/_supabase.sql - _supabaseSchema string - //go:embed templates/restore.sh - restoreScript string -) - -func Run(ctx context.Context, fromBackup string, fsys afero.Fs) error { - if err := flags.LoadConfig(fsys); err != nil { - return err - } - if err := utils.AssertSupabaseDbIsRunning(); err == nil { - fmt.Fprintln(os.Stderr, "Postgres database is already running.") - return nil - } else if !errors.Is(err, utils.ErrNotRunning) { - return err - } - err := StartDatabase(ctx, fromBackup, fsys, os.Stderr) - if err != nil { - if err := utils.DockerRemoveAll(context.Background(), os.Stderr, utils.Config.ProjectId); err != nil { - fmt.Fprintln(os.Stderr, err) - } - } - return err -} - -func NewContainerConfig(args ...string) container.Config { - env := []string{ - "POSTGRES_PASSWORD=" + utils.Config.Db.Password, - "POSTGRES_HOST=/var/run/postgresql", - "JWT_SECRET=" + utils.Config.Auth.JwtSecret.Value, - fmt.Sprintf("JWT_EXP=%d", utils.Config.Auth.JwtExpiry), - } - if len(utils.Config.Db.OrioleDBVersion) > 0 { - env = append(env, - "POSTGRES_INITDB_ARGS=--lc-collate=C --lc-ctype=C", - fmt.Sprintf("S3_ENABLED=%t", true), - "S3_HOST="+utils.Config.Experimental.S3Host, - "S3_REGION="+utils.Config.Experimental.S3Region, - "S3_ACCESS_KEY="+utils.Config.Experimental.S3AccessKey, - "S3_SECRET_KEY="+utils.Config.Experimental.S3SecretKey, - ) - } else if i := strings.IndexByte(utils.Config.Db.Image, ':'); config.VersionCompare(utils.Config.Db.Image[i+1:], "15.8.1.005") < 0 { - env = append(env, "POSTGRES_INITDB_ARGS=--lc-collate=C.UTF-8") - } - config := container.Config{ - Image: utils.Config.Db.Image, - Env: env, - Healthcheck: &container.HealthConfig{ - Test: []string{"CMD", "pg_isready", "-U", "postgres", "-h", "127.0.0.1", "-p", "5432"}, - Interval: 10 * time.Second, - Timeout: 2 * time.Second, - Retries: 3, - }, - Entrypoint: []string{"sh", "-c", ` -cat <<'EOF' > /etc/postgresql.schema.sql && \ -cat <<'EOF' > /etc/postgresql-custom/pgsodium_root.key && \ -cat <<'EOF' >> /etc/postgresql/postgresql.conf && \ -docker-entrypoint.sh postgres -D /etc/postgresql ` + strings.Join(args, " ") + ` -` + initialSchema + ` -` + webhookSchema + ` -` + _supabaseSchema + ` -EOF -` + utils.Config.Db.RootKey.Value + ` -EOF -` + utils.Config.Db.Settings.ToPostgresConfig() + ` -EOF`}, - } - if utils.Config.Db.MajorVersion <= 14 { - config.Entrypoint = []string{"sh", "-c", ` -cat <<'EOF' > /docker-entrypoint-initdb.d/supabase_schema.sql && \ -cat <<'EOF' >> /etc/postgresql/postgresql.conf && \ -docker-entrypoint.sh postgres -D /etc/postgresql ` + strings.Join(args, " ") + ` -` + _supabaseSchema + ` -EOF -` + utils.Config.Db.Settings.ToPostgresConfig() + ` -EOF`} - } - return config -} - -func NewHostConfig() container.HostConfig { - hostPort := strconv.FormatUint(uint64(utils.Config.Db.Port), 10) - hostConfig := container.HostConfig{ - PortBindings: nat.PortMap{"5432/tcp": []nat.PortBinding{{HostPort: hostPort}}}, - RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, - Binds: []string{ - utils.DbId + ":/var/lib/postgresql/data", - }, - } - if utils.Config.Db.MajorVersion <= 14 { - hostConfig.Tmpfs = map[string]string{"/docker-entrypoint-initdb.d": ""} - } - return hostConfig -} - -func StartDatabase(ctx context.Context, fromBackup string, fsys afero.Fs, w io.Writer, options ...func(*pgx.ConnConfig)) error { - config := NewContainerConfig() - hostConfig := NewHostConfig() - networkingConfig := network.NetworkingConfig{ - EndpointsConfig: map[string]*network.EndpointSettings{ - utils.NetId: { - Aliases: utils.DbAliases, - }, - }, - } - if len(fromBackup) > 0 { - config.Entrypoint = []string{"sh", "-c", ` -cat <<'EOF' > /etc/postgresql.schema.sql && \ -cat <<'EOF' > /docker-entrypoint-initdb.d/migrate.sh && \ -cat <<'EOF' > /etc/postgresql-custom/pgsodium_root.key && \ -cat <<'EOF' >> /etc/postgresql/postgresql.conf && \ -docker-entrypoint.sh postgres -D /etc/postgresql -` + initialSchema + ` -` + _supabaseSchema + ` -EOF -` + restoreScript + ` -EOF -` + utils.Config.Db.RootKey.Value + ` -EOF -` + utils.Config.Db.Settings.ToPostgresConfig() + ` -cron.launch_active_jobs = off -EOF`} - if !filepath.IsAbs(fromBackup) { - fromBackup = filepath.Join(utils.CurrentDirAbs, fromBackup) - } - hostConfig.Binds = append(hostConfig.Binds, utils.ToDockerPath(fromBackup)+":/etc/backup.sql:ro") - } - // Creating volume will not override existing volume, so we must inspect explicitly - _, err := utils.Docker.VolumeInspect(ctx, utils.DbId) - utils.NoBackupVolume = errdefs.IsNotFound(err) - if utils.NoBackupVolume { - fmt.Fprintln(w, "Starting database...") - } else if len(fromBackup) > 0 { - utils.CmdSuggestion = fmt.Sprintf("Run %s to remove existing docker volumes.", utils.Aqua("supabase stop --no-backup")) - return errors.Errorf("backup volume already exists") - } else { - fmt.Fprintln(w, "Starting database from backup...") - } - if _, err := utils.DockerStart(ctx, config, hostConfig, networkingConfig, utils.DbId); err != nil { - return err - } - // Ignore health check because restoring a large backup may take longer than 2 minutes - if err := WaitForHealthyService(ctx, utils.Config.Db.HealthTimeout, utils.DbId); err != nil && len(fromBackup) == 0 { - return err - } - // Initialize if we are on PG14 and there's no existing db volume - if utils.NoBackupVolume && len(fromBackup) == 0 { - if err := SetupLocalDatabase(ctx, "", fsys, w, options...); err != nil { - return err - } - } - return initCurrentBranch(fsys) -} - -func NewBackoffPolicy(ctx context.Context, timeout time.Duration) backoff.BackOff { - policy := backoff.WithMaxRetries( - backoff.NewConstantBackOff(time.Second), - uint64(timeout.Seconds()), - ) - return backoff.WithContext(policy, ctx) -} - -func WaitForHealthyService(ctx context.Context, timeout time.Duration, started ...string) error { - probe := func() error { - var errHealth []error - var unhealthy []string - for _, container := range started { - if err := status.IsServiceReady(ctx, container); err != nil { - unhealthy = append(unhealthy, container) - errHealth = append(errHealth, err) - } - } - started = unhealthy - return errors.Join(errHealth...) - } - policy := NewBackoffPolicy(ctx, timeout) - err := backoff.Retry(probe, policy) - if err != nil && !errors.Is(err, context.Canceled) { - // Print container logs for easier debugging - for _, containerId := range started { - fmt.Fprintln(os.Stderr, containerId, "container logs:") - if err := utils.DockerStreamLogsOnce(context.Background(), containerId, os.Stderr, os.Stderr); err != nil { - fmt.Fprintln(os.Stderr, err) - } - } - } - return err -} - -func IsUnhealthyError(err error) bool { - // Health check always returns a joinError - _, ok := err.(interface{ Unwrap() []error }) - return ok -} - -func initCurrentBranch(fsys afero.Fs) error { - // Create _current_branch file to avoid breaking db branch commands - if _, err := fsys.Stat(utils.CurrBranchPath); err == nil { - return nil - } else if !errors.Is(err, os.ErrNotExist) { - return errors.Errorf("failed init current branch: %w", err) - } - return utils.WriteFile(utils.CurrBranchPath, []byte("main"), fsys) -} - -func initSchema(ctx context.Context, conn *pgx.Conn, host string, w io.Writer) error { - fmt.Fprintln(w, "Initialising schema...") - if utils.Config.Db.MajorVersion <= 14 { - if file, err := migration.NewMigrationFromReader(strings.NewReader(utils.GlobalsSql)); err != nil { - return err - } else if err := file.ExecBatch(ctx, conn); err != nil { - return err - } - return InitSchema14(ctx, conn) - } - return initSchema15(ctx, host) -} - -func InitSchema14(ctx context.Context, conn *pgx.Conn) error { - sql := utils.InitialSchemaPg14Sql - if utils.Config.Db.MajorVersion == 13 { - sql = utils.InitialSchemaPg13Sql - } - file, err := migration.NewMigrationFromReader(strings.NewReader(sql)) - if err != nil { - return err - } - return file.ExecBatch(ctx, conn) -} - -func initRealtimeJob(host, jwks string) utils.DockerJob { - return utils.DockerJob{ - Image: utils.Config.Realtime.Image, - Env: []string{ - "PORT=4000", - "DB_HOST=" + host, - "DB_PORT=5432", - "DB_USER=" + utils.SUPERUSER_ROLE, - "DB_PASSWORD=" + utils.Config.Db.Password, - "DB_NAME=postgres", - "DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime", - "DB_ENC_KEY=" + utils.Config.Realtime.EncryptionKey, - fmt.Sprintf("API_JWT_JWKS=%s", jwks), - "API_JWT_SECRET=" + utils.Config.Auth.JwtSecret.Value, - "METRICS_JWT_SECRET=" + utils.Config.Auth.JwtSecret.Value, - "APP_NAME=realtime", - "SECRET_KEY_BASE=" + utils.Config.Realtime.SecretKeyBase, - "ERL_AFLAGS=" + utils.ToRealtimeEnv(utils.Config.Realtime.IpVersion), - "DNS_NODES=''", - "RLIMIT_NOFILE=", - "SEED_SELF_HOST=true", - "RUN_JANITOR=true", - fmt.Sprintf("MAX_HEADER_LENGTH=%d", utils.Config.Realtime.MaxHeaderLength), - }, - Cmd: []string{"/app/bin/realtime", "eval", fmt.Sprintf(`{:ok, _} = Application.ensure_all_started(:realtime) -{:ok, _} = Realtime.Tenants.health_check("%s")`, utils.Config.Realtime.TenantId)}, - } -} - -func initStorageJob(host string) utils.DockerJob { - return utils.DockerJob{ - Image: utils.Config.Storage.Image, - Env: []string{ - "DB_INSTALL_ROLES=false", - "DB_MIGRATIONS_FREEZE_AT=" + utils.Config.Storage.TargetMigration, - "ANON_KEY=" + utils.Config.Auth.AnonKey.Value, - "SERVICE_KEY=" + utils.Config.Auth.ServiceRoleKey.Value, - "PGRST_JWT_SECRET=" + utils.Config.Auth.JwtSecret.Value, - fmt.Sprintf("DATABASE_URL=postgresql://supabase_storage_admin:%s@%s:5432/postgres", utils.Config.Db.Password, host), - fmt.Sprintf("FILE_SIZE_LIMIT=%v", utils.Config.Storage.FileSizeLimit), - "STORAGE_BACKEND=file", - "STORAGE_FILE_BACKEND_PATH=/mnt", - "TENANT_ID=stub", - // TODO: https://github.com/supabase/storage-api/issues/55 - "REGION=stub", - "GLOBAL_S3_BUCKET=stub", - }, - Cmd: []string{"node", "dist/scripts/migrate-call.js"}, - } -} - -func initAuthJob(host string) utils.DockerJob { - return utils.DockerJob{ - Image: utils.Config.Auth.Image, - Env: []string{ - "API_EXTERNAL_URL=" + utils.Config.AuthExternalURL(), - "GOTRUE_LOG_LEVEL=error", - "GOTRUE_DB_DRIVER=postgres", - fmt.Sprintf("GOTRUE_DB_DATABASE_URL=postgresql://supabase_auth_admin:%s@%s:5432/postgres", utils.Config.Db.Password, host), - "GOTRUE_SITE_URL=" + utils.Config.Auth.SiteUrl, - "GOTRUE_JWT_SECRET=" + utils.Config.Auth.JwtSecret.Value, - }, - Cmd: []string{"gotrue", "migrate"}, - } -} - -func initSchema15(ctx context.Context, host string) error { - // Apply service migrations - var initJobs []utils.DockerJob - if utils.Config.Realtime.Enabled { - jwks, err := utils.Config.Auth.ResolveJWKS(context.Background()) - if err != nil { - return err - } - initJobs = append(initJobs, initRealtimeJob(host, jwks)) - } - if utils.Config.Storage.Enabled { - initJobs = append(initJobs, initStorageJob(host)) - } - if utils.Config.Auth.Enabled { - initJobs = append(initJobs, initAuthJob(host)) - } - logger := utils.GetDebugLogger() - for _, job := range initJobs { - if err := utils.DockerRunJob(ctx, job, io.Discard, logger); err != nil { - return err - } - } - return nil -} - -func SetupLocalDatabase(ctx context.Context, version string, fsys afero.Fs, w io.Writer, options ...func(*pgx.ConnConfig)) error { - conn, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{}, options...) - if err != nil { - return err - } - defer conn.Close(context.Background()) - if err := SetupDatabase(ctx, conn, utils.DbId, w, fsys); err != nil { - return err - } - if err := apply.MigrateAndSeed(ctx, version, conn, fsys); err != nil { - return err - } - return nil -} - -func SetupDatabase(ctx context.Context, conn *pgx.Conn, host string, w io.Writer, fsys afero.Fs) error { - if err := initSchema(ctx, conn, host, w); err != nil { - return err - } - if err := ApplyApiPrivileges(ctx, conn); err != nil { - return err - } - // Create vault secrets first so roles.sql can reference them - if err := vault.UpsertVaultSecrets(ctx, utils.Config.Db.Vault, conn); err != nil { - return err - } - err := migration.SeedGlobals(ctx, []string{utils.CustomRolesPath}, conn, afero.NewIOFS(fsys)) - if errors.Is(err, os.ErrNotExist) { - return nil - } - return err -} - -// RevokeDefaultDataApiPrivilegesSql matches the SQL that Studio runs at cloud project creation -// when the "Default privileges for new entities" toggle is off. It removes the default GRANTs -// applied by the initial schema so newly-created entities in `public` owned by `postgres` are -// not exposed through the Data API roles until explicit GRANTs are issued. -const RevokeDefaultDataApiPrivilegesSql = ` -alter default privileges for role postgres in schema public - revoke select, insert, update, delete on tables from anon, authenticated, service_role; -alter default privileges for role postgres in schema public - revoke usage, select on sequences from anon, authenticated, service_role; -alter default privileges for role postgres in schema public - revoke execute on functions from anon, authenticated, service_role; -` - -// ApplyApiPrivileges adjusts the default privileges on the `public` schema to match the -// `[api].auto_expose_new_tables` flag in config.toml. The flag is tri-state to give users a -// safe migration window: -// -// - unset (default): revoke the default Data API GRANTs so newly-created entities in `public` -// require explicit GRANTs to surface through the Data API, matching the new cloud default. -// The implicit default flipped to false on May 30, 2026; the flag is removed entirely in -// October 2026 (always-revoked behaviour). -// - true: explicit opt-in to the legacy behaviour of keeping the bundled initial-schema GRANTs -// in place. Deprecated: config loading warns that the flag is removed on October 30, 2026. -// - false: revoke the default Data API GRANTs (same as unset). Kept so users can pin the new -// behaviour explicitly during the deprecation window. -func ApplyApiPrivileges(ctx context.Context, conn *pgx.Conn) error { - if utils.Config.Api.AutoExposeNewTables != nil && *utils.Config.Api.AutoExposeNewTables { - return nil - } - file, err := migration.NewMigrationFromReader(strings.NewReader(RevokeDefaultDataApiPrivilegesSql)) - if err != nil { - return err - } - return file.ExecBatch(ctx, conn) -} diff --git a/apps/cli-go/internal/db/start/start_test.go b/apps/cli-go/internal/db/start/start_test.go deleted file mode 100644 index 805691b8ec..0000000000 --- a/apps/cli-go/internal/db/start/start_test.go +++ /dev/null @@ -1,434 +0,0 @@ -package start - -import ( - "context" - "errors" - "io" - "net/http" - "os" - "testing" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/volume" - "github.com/h2non/gock" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/testing/fstest" - "github.com/supabase/cli/internal/testing/helper" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/cast" - "github.com/supabase/cli/pkg/pgtest" -) - -func TestInitBranch(t *testing.T) { - t.Run("throws error on permission denied", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) - // Run test - err := initCurrentBranch(fsys) - // Check error - assert.ErrorContains(t, err, "operation not permitted") - }) - - t.Run("throws error on stat failure", func(t *testing.T) { - // Setup in-memory fs - fsys := &fstest.StatErrorFs{DenyPath: utils.CurrBranchPath} - // Run test - err := initCurrentBranch(fsys) - // Check error - assert.ErrorContains(t, err, "permission denied") - }) - - t.Run("throws error on write failure", func(t *testing.T) { - // Setup in-memory fs - fsys := &fstest.OpenErrorFs{DenyPath: utils.CurrBranchPath} - // Run test - err := initCurrentBranch(fsys) - // Check error - assert.ErrorContains(t, err, "permission denied") - }) -} - -func TestStartDatabase(t *testing.T) { - t.Run("initialize main branch", func(t *testing.T) { - utils.Config.Db.MajorVersion = 15 - utils.DbId = "supabase_db_test" - utils.Config.Db.Port = 5432 - // Setup in-memory fs - fsys := afero.NewMemMapFs() - roles := "create role test" - require.NoError(t, afero.WriteFile(fsys, utils.CustomRolesPath, []byte(roles), 0644)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/volumes/" + utils.DbId). - Reply(http.StatusNotFound). - JSON(volume.Volume{}) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Db.Image), utils.DbId) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - Health: &container.Health{Status: types.Healthy}, - }, - }}) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Realtime.Image), "test-realtime") - require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-realtime", "")) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Storage.Image), "test-storage") - require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-storage", "")) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Auth.Image), "test-auth") - require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-auth", "")) - // Setup mock postgres: with auto_expose_new_tables unset, the implicit default revokes - // the default Data API GRANTs before seeding roles. - conn := pgtest.NewConn() - defer conn.Close(t) - helper.MockApiPrivilegesRevoke(conn). - Query(roles). - Reply("CREATE ROLE") - // Run test - err := StartDatabase(context.Background(), "", fsys, io.Discard, conn.Intercept) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - // Check current branch - contents, err := afero.ReadFile(fsys, utils.CurrBranchPath) - assert.NoError(t, err) - assert.Equal(t, []byte("main"), contents) - }) - - t.Run("recover from backup volume", func(t *testing.T) { - utils.Config.Db.MajorVersion = 14 - utils.DbId = "supabase_db_test" - utils.Config.Db.Port = 5432 - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/volumes/" + utils.DbId). - Reply(http.StatusOK). - JSON(volume.Volume{}) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Db.Image), utils.DbId) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - Health: &container.Health{Status: types.Healthy}, - }, - }}) - // Run test - err := StartDatabase(context.Background(), "", fsys, io.Discard) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - // Check current branch - contents, err := afero.ReadFile(fsys, utils.CurrBranchPath) - assert.NoError(t, err) - assert.Equal(t, []byte("main"), contents) - }) - - t.Run("throws error on start failure", func(t *testing.T) { - utils.Config.Db.MajorVersion = 15 - utils.DbId = "supabase_db_test" - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/volumes/" + utils.DbId). - ReplyError(errors.New("network error")) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/images/" + utils.GetRegistryImageUrl(utils.Config.Db.Image) + "/json"). - Reply(http.StatusServiceUnavailable) - // Run test - err := StartDatabase(context.Background(), "", fsys, io.Discard) - // Check error - assert.ErrorContains(t, err, "request returned 503 Service Unavailable for API route and version") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestStartCommand(t *testing.T) { - t.Run("throws error on malformed config", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, afero.WriteFile(fsys, utils.ConfigPath, []byte("malformed"), 0644)) - // Run test - err := Run(context.Background(), "", fsys) - // Check error - assert.ErrorContains(t, err, "toml: expected = after a key, but the document ends there") - }) - - t.Run("throws error on missing docker", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers"). - ReplyError(errors.New("network error")) - // Run test - err := Run(context.Background(), "", fsys) - // Check error - assert.ErrorContains(t, err, "network error") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("exits if already started", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/"). - Reply(http.StatusOK). - JSON(container.InspectResponse{}) - // Run test - err := Run(context.Background(), "", fsys) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on start failure", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/"). - Reply(http.StatusNotFound) - // Fail to start - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/volumes/"). - ReplyError(errors.New("network error")) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/images/" + utils.GetRegistryImageUrl(utils.Config.Db.Image) + "/json"). - ReplyError(errors.New("network error")) - // Cleanup resources - apitest.MockDockerStop(utils.Docker) - // Run test - err := Run(context.Background(), "", fsys) - // Check error - assert.ErrorContains(t, err, "network error") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestSetupDatabase(t *testing.T) { - utils.Config.Db.MajorVersion = 15 - - t.Run("revokes default data api privileges when auto_expose_new_tables is unset", func(t *testing.T) { - utils.Config.Db.MajorVersion = 14 - utils.Config.Api.AutoExposeNewTables = nil - defer func() { - utils.Config.Db.MajorVersion = 15 - }() - utils.Config.Db.Port = 5432 - utils.GlobalsSql = "create schema public" - utils.InitialSchemaPg14Sql = "create schema private" - // Setup in-memory fs - fsys := afero.NewMemMapFs() - roles := "create role postgres" - require.NoError(t, afero.WriteFile(fsys, utils.CustomRolesPath, []byte(roles), 0644)) - // Setup mock postgres: with the flag unset, the implicit default now revokes the - // default Data API GRANTs (the May 30 2026 flip) between initial schema and roles.sql - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(utils.GlobalsSql). - Reply("CREATE SCHEMA"). - Query(utils.InitialSchemaPg14Sql). - Reply("CREATE SCHEMA") - helper.MockApiPrivilegesRevoke(conn). - Query(roles). - Reply("CREATE ROLE") - // Run test - err := SetupLocalDatabase(context.Background(), "", fsys, io.Discard, conn.Intercept) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("keeps default data api privileges when auto_expose_new_tables is true", func(t *testing.T) { - utils.Config.Db.MajorVersion = 14 - flag := true - utils.Config.Api.AutoExposeNewTables = &flag - defer func() { - utils.Config.Db.MajorVersion = 15 - utils.Config.Api.AutoExposeNewTables = nil - }() - utils.Config.Db.Port = 5432 - utils.GlobalsSql = "create schema public" - utils.InitialSchemaPg14Sql = "create schema private" - // Setup in-memory fs - fsys := afero.NewMemMapFs() - roles := "create role postgres" - require.NoError(t, afero.WriteFile(fsys, utils.CustomRolesPath, []byte(roles), 0644)) - // Setup mock postgres: explicit true opts into the legacy behaviour, so no revoke SQL - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(utils.GlobalsSql). - Reply("CREATE SCHEMA"). - Query(utils.InitialSchemaPg14Sql). - Reply("CREATE SCHEMA"). - Query(roles). - Reply("CREATE ROLE") - // Run test - err := SetupLocalDatabase(context.Background(), "", fsys, io.Discard, conn.Intercept) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("revokes default data api privileges when auto_expose_new_tables is false", func(t *testing.T) { - utils.Config.Db.MajorVersion = 14 - flag := false - utils.Config.Api.AutoExposeNewTables = &flag - defer func() { - utils.Config.Db.MajorVersion = 15 - utils.Config.Api.AutoExposeNewTables = nil - }() - utils.Config.Db.Port = 5432 - utils.GlobalsSql = "create schema public" - utils.InitialSchemaPg14Sql = "create schema private" - // Setup in-memory fs - fsys := afero.NewMemMapFs() - roles := "create role postgres" - require.NoError(t, afero.WriteFile(fsys, utils.CustomRolesPath, []byte(roles), 0644)) - // Setup mock postgres: the revoke SQL must execute between the initial schema and roles.sql - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(utils.GlobalsSql). - Reply("CREATE SCHEMA"). - Query(utils.InitialSchemaPg14Sql). - Reply("CREATE SCHEMA") - helper.MockApiPrivilegesRevoke(conn). - Query(roles). - Reply("CREATE ROLE") - // Run test - err := SetupLocalDatabase(context.Background(), "", fsys, io.Discard, conn.Intercept) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on connect failure", func(t *testing.T) { - utils.Config.Db.Port = 0 - // Run test - err := SetupLocalDatabase(context.Background(), "", nil, io.Discard) - // Check error - assert.ErrorContains(t, err, "invalid port (outside range)") - }) - - t.Run("throws error on init failure", func(t *testing.T) { - utils.Config.Realtime.Enabled = true - utils.Config.Db.Port = 5432 - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/images/" + utils.GetRegistryImageUrl(utils.Config.Realtime.Image) + "/json"). - ReplyError(errors.New("network error")) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - // Run test - err := SetupLocalDatabase(context.Background(), "", nil, io.Discard, conn.Intercept) - // Check error - assert.ErrorContains(t, err, "network error") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on read failure", func(t *testing.T) { - utils.Config.Db.Port = 5432 - // Setup in-memory fs - fsys := &fstest.OpenErrorFs{DenyPath: utils.CustomRolesPath} - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Realtime.Image), "test-realtime") - require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-realtime", "")) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Storage.Image), "test-storage") - require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-storage", "")) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Auth.Image), "test-auth") - require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-auth", "")) - // Setup mock postgres: the default-privilege revoke runs before roles.sql is read - conn := pgtest.NewConn() - defer conn.Close(t) - helper.MockApiPrivilegesRevoke(conn) - // Run test - err := SetupLocalDatabase(context.Background(), "", fsys, io.Discard, conn.Intercept) - // Check error - assert.ErrorIs(t, err, os.ErrPermission) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} -func TestStartDatabaseWithCustomSettings(t *testing.T) { - t.Run("starts database with custom MaxConnections", func(t *testing.T) { - // Setup - utils.Config.Db.MajorVersion = 15 - utils.DbId = "supabase_db_test" - utils.Config.Db.Port = 5432 - utils.Config.Db.Settings.MaxConnections = cast.Ptr(uint(50)) - - // Setup in-memory fs - fsys := afero.NewMemMapFs() - - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/volumes/" + utils.DbId). - Reply(http.StatusNotFound). - JSON(volume.Volume{}) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Db.Image), utils.DbId) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - Health: &container.Health{Status: types.Healthy}, - }, - }}) - - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Realtime.Image), "test-realtime") - require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-realtime", "")) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Storage.Image), "test-storage") - require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-storage", "")) - apitest.MockDockerStart(utils.Docker, utils.GetRegistryImageUrl(utils.Config.Auth.Image), "test-auth") - require.NoError(t, apitest.MockDockerLogs(utils.Docker, "test-auth", "")) - // Setup mock postgres: with auto_expose_new_tables unset, the default Data API GRANTs - // are revoked by default. - conn := pgtest.NewConn() - defer conn.Close(t) - helper.MockApiPrivilegesRevoke(conn) - - // Run test - err := StartDatabase(context.Background(), "", fsys, io.Discard, conn.Intercept) - - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - - // Check if the custom MaxConnections setting was applied - config := NewContainerConfig() - assert.Contains(t, config.Entrypoint[2], "max_connections = 50") - }) -} diff --git a/apps/cli-go/internal/db/start/templates/_supabase.sql b/apps/cli-go/internal/db/start/templates/_supabase.sql deleted file mode 100644 index 6e5d8487ba..0000000000 --- a/apps/cli-go/internal/db/start/templates/_supabase.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE DATABASE _supabase WITH OWNER postgres; - --- Switch to the newly created _supabase database -\c _supabase --- Create schemas in _supabase database for --- internals tools and reports to not overload user database --- with non-user activity -CREATE SCHEMA IF NOT EXISTS _analytics; -ALTER SCHEMA _analytics OWNER TO postgres; - -CREATE SCHEMA IF NOT EXISTS _supavisor; -ALTER SCHEMA _supavisor OWNER TO postgres; -\c postgres diff --git a/apps/cli-go/internal/db/start/templates/restore.sh b/apps/cli-go/internal/db/start/templates/restore.sh deleted file mode 100755 index 3a1729ad97..0000000000 --- a/apps/cli-go/internal/db/start/templates/restore.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh -set -eu - -####################################### -# Used by both ami and docker builds to initialise database schema. -# Env vars: -# POSTGRES_DB defaults to postgres -# POSTGRES_HOST defaults to localhost -# POSTGRES_PORT defaults to 5432 -# POSTGRES_PASSWORD defaults to "" -# USE_DBMATE defaults to "" -# Exit code: -# 0 if migration succeeds, non-zero on error. -####################################### - -export PGDATABASE="${POSTGRES_DB:-postgres}" -export PGHOST="${POSTGRES_HOST:-localhost}" -export PGPORT="${POSTGRES_PORT:-5432}" -export PGPASSWORD="${POSTGRES_PASSWORD:-}" - -echo "$0: restoring roles" -cat "/etc/backup.sql" \ -| grep 'CREATE ROLE' \ -| grep -v 'supabase_admin' \ -| sed -E 's/^(CREATE ROLE postgres);/\1 WITH SUPERUSER;/' \ -| psql -v ON_ERROR_STOP=1 --no-password --no-psqlrc -U supabase_admin - -echo "$0: restoring schema" -cat "/etc/backup.sql" \ -| sed -E 's/^\\(un)?restrict .*$/-- &/' \ -| sed -E 's/^CREATE VIEW /CREATE OR REPLACE VIEW /' \ -| sed -E 's/^CREATE FUNCTION /CREATE OR REPLACE FUNCTION /' \ -| sed -E 's/^CREATE TRIGGER /CREATE OR REPLACE TRIGGER /' \ -| sed -E 's/^GRANT ALL ON FUNCTION graphql_public\./-- &/' \ -| sed -E 's/^CREATE ROLE /-- &/' \ -| sed -e '/ALTER ROLE postgres WITH / { h; $p; d; }' -e '$G' \ -| psql -v ON_ERROR_STOP=1 --no-password --no-psqlrc -U supabase_admin - -# run any post migration script to update role passwords -postinit="/etc/postgresql.schema.sql" -if [ -e "$postinit" ]; then - echo "$0: running $postinit" - psql -v ON_ERROR_STOP=1 --no-password --no-psqlrc -U supabase_admin -f "$postinit" -fi diff --git a/apps/cli-go/internal/db/start/templates/schema.sql b/apps/cli-go/internal/db/start/templates/schema.sql deleted file mode 100644 index dee166ae58..0000000000 --- a/apps/cli-go/internal/db/start/templates/schema.sql +++ /dev/null @@ -1,17 +0,0 @@ -\set pgpass `echo "$PGPASSWORD"` -\set jwt_secret `echo "$JWT_SECRET"` -\set jwt_exp `echo "$JWT_EXP"` - -ALTER DATABASE postgres SET "app.settings.jwt_secret" TO :'jwt_secret'; -ALTER DATABASE postgres SET "app.settings.jwt_exp" TO :'jwt_exp'; - -ALTER USER postgres WITH PASSWORD :'pgpass'; -ALTER USER authenticator WITH PASSWORD :'pgpass'; -ALTER USER pgbouncer WITH PASSWORD :'pgpass'; -ALTER USER supabase_auth_admin WITH PASSWORD :'pgpass'; -ALTER USER supabase_storage_admin WITH PASSWORD :'pgpass'; -ALTER USER supabase_replication_admin WITH PASSWORD :'pgpass'; -ALTER USER supabase_read_only_user WITH PASSWORD :'pgpass'; - -create schema if not exists _realtime; -alter schema _realtime owner to postgres; diff --git a/apps/cli-go/internal/db/start/templates/webhook.sql b/apps/cli-go/internal/db/start/templates/webhook.sql deleted file mode 100644 index 52cd097473..0000000000 --- a/apps/cli-go/internal/db/start/templates/webhook.sql +++ /dev/null @@ -1,232 +0,0 @@ -BEGIN; - --- Create pg_net extension -CREATE EXTENSION IF NOT EXISTS pg_net SCHEMA extensions; - --- Create supabase_functions schema -CREATE SCHEMA supabase_functions AUTHORIZATION supabase_admin; - -GRANT USAGE ON SCHEMA supabase_functions TO postgres, anon, authenticated, service_role; -ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON TABLES TO postgres, anon, authenticated, service_role; -ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO postgres, anon, authenticated, service_role; -ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO postgres, anon, authenticated, service_role; - --- supabase_functions.migrations definition -CREATE TABLE supabase_functions.migrations ( - version text PRIMARY KEY, - inserted_at timestamptz NOT NULL DEFAULT NOW() -); - --- Initial supabase_functions migration -INSERT INTO supabase_functions.migrations (version) VALUES ('initial'); - --- supabase_functions.hooks definition -CREATE TABLE supabase_functions.hooks ( - id bigserial PRIMARY KEY, - hook_table_id integer NOT NULL, - hook_name text NOT NULL, - created_at timestamptz NOT NULL DEFAULT NOW(), - request_id bigint -); -CREATE INDEX supabase_functions_hooks_request_id_idx ON supabase_functions.hooks USING btree (request_id); -CREATE INDEX supabase_functions_hooks_h_table_id_h_name_idx ON supabase_functions.hooks USING btree (hook_table_id, hook_name); -COMMENT ON TABLE supabase_functions.hooks IS 'Supabase Functions Hooks: Audit trail for triggered hooks.'; - -CREATE FUNCTION supabase_functions.http_request() - RETURNS trigger - LANGUAGE plpgsql - AS $function$ - DECLARE - request_id bigint; - payload jsonb; - url text := TG_ARGV[0]::text; - method text := TG_ARGV[1]::text; - headers jsonb DEFAULT '{}'::jsonb; - params jsonb DEFAULT '{}'::jsonb; - timeout_ms integer DEFAULT 1000; - BEGIN - IF url IS NULL OR url = 'null' THEN - RAISE EXCEPTION 'url argument is missing'; - END IF; - - IF method IS NULL OR method = 'null' THEN - RAISE EXCEPTION 'method argument is missing'; - END IF; - - IF TG_ARGV[2] IS NULL OR TG_ARGV[2] = 'null' THEN - headers = '{"Content-Type": "application/json"}'::jsonb; - ELSE - headers = TG_ARGV[2]::jsonb; - END IF; - - IF TG_ARGV[3] IS NULL OR TG_ARGV[3] = 'null' THEN - params = '{}'::jsonb; - ELSE - params = TG_ARGV[3]::jsonb; - END IF; - - IF TG_ARGV[4] IS NULL OR TG_ARGV[4] = 'null' THEN - timeout_ms = 1000; - ELSE - timeout_ms = TG_ARGV[4]::integer; - END IF; - - CASE - WHEN method = 'GET' THEN - SELECT http_get INTO request_id FROM net.http_get( - url, - params, - headers, - timeout_ms - ); - WHEN method = 'POST' THEN - payload = jsonb_build_object( - 'old_record', OLD, - 'record', NEW, - 'type', TG_OP, - 'table', TG_TABLE_NAME, - 'schema', TG_TABLE_SCHEMA - ); - - SELECT http_post INTO request_id FROM net.http_post( - url, - payload, - params, - headers, - timeout_ms - ); - ELSE - RAISE EXCEPTION 'method argument % is invalid', method; - END CASE; - - INSERT INTO supabase_functions.hooks - (hook_table_id, hook_name, request_id) - VALUES - (TG_RELID, TG_NAME, request_id); - - RETURN NEW; - END -$function$; - --- Supabase super admin -DO -$$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_roles - WHERE rolname = 'supabase_functions_admin' - ) - THEN - CREATE USER supabase_functions_admin NOINHERIT CREATEROLE LOGIN NOREPLICATION; - END IF; -END -$$; - -GRANT ALL PRIVILEGES ON SCHEMA supabase_functions TO supabase_functions_admin; -GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA supabase_functions TO supabase_functions_admin; -GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA supabase_functions TO supabase_functions_admin; -ALTER USER supabase_functions_admin SET search_path = "supabase_functions"; -ALTER table "supabase_functions".migrations OWNER TO supabase_functions_admin; -ALTER table "supabase_functions".hooks OWNER TO supabase_functions_admin; -ALTER function "supabase_functions".http_request() OWNER TO supabase_functions_admin; -GRANT supabase_functions_admin TO postgres; - --- Remove unused supabase_pg_net_admin role -DO -$$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM pg_roles - WHERE rolname = 'supabase_pg_net_admin' - ) - THEN - REASSIGN OWNED BY supabase_pg_net_admin TO supabase_admin; - DROP OWNED BY supabase_pg_net_admin; - DROP ROLE supabase_pg_net_admin; - END IF; -END -$$; - --- pg_net grants when extension is already enabled -DO -$$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM pg_extension - WHERE extname = 'pg_net' - ) - THEN - GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; - - ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; - ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; - - ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; - ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; - - REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; - REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; - - GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; - GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; - END IF; -END -$$; - --- Event trigger for pg_net -CREATE OR REPLACE FUNCTION extensions.grant_pg_net_access() -RETURNS event_trigger -LANGUAGE plpgsql -AS $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM pg_event_trigger_ddl_commands() AS ev - JOIN pg_extension AS ext - ON ev.objid = ext.oid - WHERE ext.extname = 'pg_net' - ) - THEN - GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; - - ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; - ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; - - ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; - ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; - - REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; - REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; - - GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; - GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; - END IF; -END; -$$; -COMMENT ON FUNCTION extensions.grant_pg_net_access IS 'Grants access to pg_net'; - -DO -$$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_event_trigger - WHERE evtname = 'issue_pg_net_access' - ) THEN - CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end WHEN TAG IN ('CREATE EXTENSION') - EXECUTE PROCEDURE extensions.grant_pg_net_access(); - END IF; -END -$$; - -INSERT INTO supabase_functions.migrations (version) VALUES ('20210809183423_update_grants'); - -ALTER function supabase_functions.http_request() SECURITY DEFINER; -ALTER function supabase_functions.http_request() SET search_path = supabase_functions; -REVOKE ALL ON FUNCTION supabase_functions.http_request() FROM PUBLIC; -GRANT EXECUTE ON FUNCTION supabase_functions.http_request() TO postgres, anon, authenticated, service_role; - -COMMIT; diff --git a/apps/cli-go/internal/debug/http.go b/apps/cli-go/internal/debug/http.go deleted file mode 100644 index b22f6bbc8a..0000000000 --- a/apps/cli-go/internal/debug/http.go +++ /dev/null @@ -1,24 +0,0 @@ -package debug - -import ( - "log" - "net/http" - "os" -) - -type debugTransport struct { - http.RoundTripper - logger *log.Logger -} - -func (t *debugTransport) RoundTrip(req *http.Request) (*http.Response, error) { - t.logger.Printf("%s: %s\n", req.Method, req.URL) - return t.RoundTripper.RoundTrip(req) -} - -func NewTransport() http.RoundTripper { - return &debugTransport{ - http.DefaultTransport, - log.New(os.Stderr, "HTTP ", log.LstdFlags|log.Lmsgprefix), - } -} diff --git a/apps/cli-go/internal/debug/postgres.go b/apps/cli-go/internal/debug/postgres.go deleted file mode 100644 index 3d774cb309..0000000000 --- a/apps/cli-go/internal/debug/postgres.go +++ /dev/null @@ -1,262 +0,0 @@ -package debug - -import ( - "context" - "crypto/tls" - "encoding/binary" - "encoding/json" - "errors" - "io" - "log" - "net" - "os" - "time" - - "github.com/jackc/pgproto3/v2" - "github.com/jackc/pgx/v4" - "google.golang.org/grpc/test/bufconn" -) - -type Proxy struct { - dialContext func(ctx context.Context, network, addr string) (net.Conn, error) - // tlsPlan is pgconn's TLS config per attempt, primary first. DialFunc walks it - // so sslmode=allow keeps its plaintext-then-TLS retry, and completes the - // handshake itself, leaving logged frames plaintext in memory but not on the wire. - tlsPlan []*tls.Config - attempts map[string]int - errChan chan error -} - -func NewProxy() Proxy { - dialer := net.Dialer{} - return Proxy{ - dialContext: dialer.DialContext, - attempts: map[string]int{}, - errChan: make(chan error, 1), - } -} - -func SetupPGX(config *pgx.ConnConfig) { - tlsPlan := []*tls.Config{config.TLSConfig} - for _, fallback := range config.Fallbacks { - if fallback.Host != config.Host { - break - } - // ConnectByUrl drops plaintext fallbacks once the primary is TLS, and it - // runs after this, so apply the same rule here or --debug reinstates them. - if config.TLSConfig != nil && fallback.TLSConfig == nil { - continue - } - tlsPlan = append(tlsPlan, fallback.TLSConfig) - } - proxy := Proxy{ - dialContext: config.DialFunc, - tlsPlan: tlsPlan, - attempts: map[string]int{}, - errChan: make(chan error, 1), - } - config.DialFunc = proxy.DialFunc - // pgx must not negotiate TLS again over the in-memory pipe; clearing these - // *without* DialFunc's handshake is what downgraded sslmode=require (#5872). - config.TLSConfig = nil - // pgconn reads Fallbacks independently of TLSConfig, so a restored sslmode=allow - // would still retry TLS through this plaintext proxy. - for _, fallback := range config.Fallbacks { - fallback.TLSConfig = nil - } -} - -// nextTLSConfig returns the TLS config for this attempt against addr. pgconn dials -// each plan entry once per resolved address, in plan order, so a per-address -// counter replays the same TLS/plaintext sequence it would have used itself. -func (p *Proxy) nextTLSConfig(addr string) *tls.Config { - if p.attempts == nil { - p.attempts = map[string]int{} - } - if len(p.tlsPlan) == 0 { - return nil - } - i := p.attempts[addr] - p.attempts[addr]++ - // pgconn redials an addr for the target_session_attrs retry, so clamp rather - // than fall off the plan into plaintext. - if i >= len(p.tlsPlan) { - i = len(p.tlsPlan) - 1 - } - return p.tlsPlan[i] -} - -func (p *Proxy) DialFunc(ctx context.Context, network, addr string) (net.Conn, error) { - tlsConfig := p.nextTLSConfig(addr) - serverConn, err := p.dialContext(ctx, network, addr) - if err != nil { - return nil, err - } - if tlsConfig != nil { - if serverConn, err = startTLS(ctx, serverConn, tlsConfig); err != nil { - return nil, err - } - } - - const bufSize = 1024 * 1024 - ln := bufconn.Listen(bufSize) - go func() { - defer serverConn.Close() - clientConn, err := ln.Accept() - if err != nil { - // Unreachable code as bufconn never throws, but just in case - panic(err) - } - defer clientConn.Close() - - backend := NewBackend(clientConn) - frontend := NewFrontend(serverConn) - go backend.forward(frontend, p.errChan) - go frontend.forward(backend, p.errChan) - - for { - if err := <-p.errChan; err != nil && - // Since pgx closes connection first, every EOF is seen as unexpected - !errors.Is(err, io.ErrUnexpectedEOF) && - // Frontend might receive a reply after pgx closes the connection, in - // which case the backend will write to a closed pipe. So ignore. - !errors.Is(err, io.ErrClosedPipe) { - panic(err) - } - } - }() - - return ln.DialContext(ctx) -} - -// libpq's SSLRequest message code, PG_PROTOCOL(1234,5679). -const sslRequestCode = 80877103 - -// startTLS mirrors pgconn's own startTLS (pgconn@v1.14.3/pgconn.go:406-419), but -// handshakes eagerly so a refusal or bad certificate fails the dial rather than -// surfacing as a parse error inside the forwarding goroutines. -func startTLS(ctx context.Context, conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) { - // pgconn only watches ctx once DialFunc returns, so bound the negotiation here - // or an endpoint that accepts TCP and never replies hangs the read forever. - if deadline, ok := ctx.Deadline(); ok { - if err := conn.SetDeadline(deadline); err != nil { - conn.Close() - return nil, err - } - defer func() { _ = conn.SetDeadline(time.Time{}) }() - } - if err := binary.Write(conn, binary.BigEndian, []int32{8, sslRequestCode}); err != nil { - conn.Close() - return nil, err - } - response := make([]byte, 1) - if _, err := io.ReadFull(conn, response); err != nil { - conn.Close() - return nil, err - } - if response[0] != 'S' { - conn.Close() - return nil, errors.New("server refused TLS connection") - } - tlsConn := tls.Client(conn, tlsConfig) - if err := tlsConn.HandshakeContext(ctx); err != nil { - conn.Close() - return nil, err - } - return tlsConn, nil -} - -type Backend struct { - *pgproto3.Backend - logger *log.Logger -} - -func NewBackend(clientConn net.Conn) Backend { - return Backend{ - pgproto3.NewBackend(pgproto3.NewChunkReader(clientConn), clientConn), - log.New(os.Stderr, "PG Recv: ", log.LstdFlags|log.Lmsgprefix), - } -} - -func (b *Backend) forward(frontend Frontend, errChan chan error) { - startupMessage, err := b.ReceiveStartupMessage() - if err != nil { - errChan <- err - return - } - - buf, err := json.Marshal(startupMessage) - if err != nil { - errChan <- err - return - } - frontend.logger.Println(string(buf)) - - if err = frontend.Send(startupMessage); err != nil { - errChan <- err - return - } - - for { - msg, err := b.Receive() - if err != nil { - errChan <- err - return - } - - buf, err := json.Marshal(msg) - if err != nil { - errChan <- err - return - } - frontend.logger.Println(string(buf)) - - if err = frontend.Send(msg); err != nil { - errChan <- err - return - } - } -} - -type Frontend struct { - *pgproto3.Frontend - logger *log.Logger -} - -func NewFrontend(serverConn net.Conn) Frontend { - return Frontend{ - pgproto3.NewFrontend(pgproto3.NewChunkReader(serverConn), serverConn), - log.New(os.Stderr, "PG Send: ", log.LstdFlags|log.Lmsgprefix), - } -} - -func (f *Frontend) forward(backend Backend, errChan chan error) { - for { - msg, err := f.Receive() - if err != nil { - errChan <- err - return - } - - buf, err := json.Marshal(msg) - if err != nil { - errChan <- err - return - } - backend.logger.Println(string(buf)) - - if _, ok := msg.(pgproto3.AuthenticationResponseMessage); ok { - // Set the authentication type so the next backend.Receive() will - // properly decode the appropriate 'p' message. - if err := backend.SetAuthType(f.GetAuthType()); err != nil { - errChan <- err - return - } - } - - if err := backend.Send(msg); err != nil { - errChan <- err - return - } - } -} diff --git a/apps/cli-go/internal/debug/postgres_test.go b/apps/cli-go/internal/debug/postgres_test.go deleted file mode 100644 index 87a29c3dbe..0000000000 --- a/apps/cli-go/internal/debug/postgres_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package debug - -import ( - "context" - "encoding/binary" - "io" - "net" - "testing" - - "github.com/jackc/pgx/v4" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/pkg/pgtest" -) - -func TestPostgresProxy(t *testing.T) { - const postgresUrl = "postgresql://postgres:password@127.0.0.1:5432/postgres" - - t.Run("forwards messages between frontend and backend", func(t *testing.T) { - // Parse connection url - config, err := pgx.ParseConfig(postgresUrl) - require.NoError(t, err) - // Setup postgres mock - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Intercept(config) - // Run test - SetupPGX(config) - ctx := context.Background() - proxy, err := pgx.ConnectConfig(ctx, config) - assert.NoError(t, err) - assert.NoError(t, proxy.Close(ctx)) - }) - - t.Run("negotiates TLS itself instead of downgrading", func(t *testing.T) { - config, err := pgx.ParseConfig(postgresUrl + "?sslmode=require") - require.NoError(t, err) - require.NotNil(t, config.TLSConfig) - // Run test - SetupPGX(config) - assert.Nil(t, config.TLSConfig) - for _, fallback := range config.Fallbacks { - assert.Nil(t, fallback.TLSConfig) - } - // A server refusing TLS must fail the dial, not continue in plaintext. - ln, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - defer ln.Close() - go func() { - conn, err := ln.Accept() - if err != nil { - return - } - defer conn.Close() - request := make([]byte, 8) - if _, err := io.ReadFull(conn, request); err != nil { - return - } - assert.Equal(t, uint32(sslRequestCode), binary.BigEndian.Uint32(request[4:])) - _, _ = conn.Write([]byte("N")) - }() - _, err = config.DialFunc(context.Background(), "tcp", ln.Addr().String()) - assert.ErrorContains(t, err, "server refused TLS connection") - }) -} diff --git a/apps/cli-go/internal/functions/download/download.go b/apps/cli-go/internal/functions/download/download.go deleted file mode 100644 index 0f497ee699..0000000000 --- a/apps/cli-go/internal/functions/download/download.go +++ /dev/null @@ -1,650 +0,0 @@ -package download - -import ( - "bufio" - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "mime" - "mime/multipart" - "net/http" - "net/textproto" - "net/url" - "os" - "os/exec" - "path" - "path/filepath" - "strings" - - "github.com/andybalholm/brotli" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" - "github.com/docker/go-units" - "github.com/go-errors/errors" - "github.com/google/uuid" - "github.com/spf13/afero" - "github.com/spf13/viper" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/flags" - "github.com/supabase/cli/pkg/api" -) - -var ( - legacyEntrypointPath = "file:///src/index.ts" - legacyImportMapPath = "file:///src/import_map.json" -) - -// ErrUnsafeDownloadPath is returned when a downloaded Function file's path, -// as reported by the server via the Supabase-Path header or the -// Content-Disposition filename, would resolve outside utils.FunctionsDir -// once joined and cleaned, or would require following an existing symlink -// to get there. The server response is not trusted input, so any path that -// looks like it's trying to escape the functions directory is rejected -// rather than sanitized. -var ErrUnsafeDownloadPath = errors.New("invalid path in server response") - -func RunLegacy(ctx context.Context, slug string, projectRef string, fsys afero.Fs) error { - // 1. Sanity checks. - { - if err := utils.ValidateFunctionSlug(slug); err != nil { - return err - } - } - if err := utils.InstallOrUpgradeDeno(ctx, fsys); err != nil { - return err - } - - scriptDir, err := utils.CopyDenoScripts(ctx, fsys) - if err != nil { - return err - } - - // 2. Download Function. - if err := downloadFunction(ctx, projectRef, slug, scriptDir.ExtractPath); err != nil { - return err - } - - fmt.Fprintf(os.Stderr, "Downloaded Function %s from project %s.\n", utils.Aqua(slug), utils.Aqua(projectRef)) - return nil -} - -func getFunctionMetadata(ctx context.Context, projectRef, slug string) (*api.FunctionSlugResponseOutput, error) { - resp, err := utils.GetSupabase().V1GetAFunctionWithResponse(ctx, projectRef, slug) - if err != nil { - return nil, errors.Errorf("failed to get function metadata: %w", err) - } - - switch resp.StatusCode() { - case http.StatusNotFound: - return nil, errors.Errorf("Function %s does not exist on the Supabase project.", utils.Aqua(slug)) - case http.StatusOK: - break - default: - return nil, errors.Errorf("Failed to download Function %s on the Supabase project: %s", utils.Aqua(slug), string(resp.Body)) - } - - if resp.JSON200.EntrypointPath == nil { - resp.JSON200.EntrypointPath = &legacyEntrypointPath - } - if resp.JSON200.ImportMapPath == nil { - resp.JSON200.ImportMapPath = &legacyImportMapPath - } - return resp.JSON200, nil -} - -func downloadFunction(ctx context.Context, projectRef, slug, extractScriptPath string) error { - fmt.Fprintln(os.Stderr, "Downloading function:", utils.Bold(slug)) - denoPath, err := utils.GetDenoPath() - if err != nil { - return err - } - - meta, err := getFunctionMetadata(ctx, projectRef, slug) - if err != nil { - return err - } - - resp, err := utils.GetSupabase().V1GetAFunctionBodyWithResponse(ctx, projectRef, slug) - if err != nil { - return errors.Errorf("failed to get function body: %w", err) - } - if resp.StatusCode() != http.StatusOK { - return errors.New("Unexpected error downloading Function: " + string(resp.Body)) - } - - resBuf := bytes.NewReader(resp.Body) - funcDir := filepath.Join(utils.FunctionsDir, slug) - args := []string{"run", "-A", extractScriptPath, funcDir, *meta.EntrypointPath, utils.FunctionsDir} - cmd := exec.CommandContext(ctx, denoPath, args...) - var errBuf bytes.Buffer - cmd.Stdin = resBuf - cmd.Stdout = os.Stdout - cmd.Stderr = &errBuf - if err := cmd.Run(); err != nil { - return errors.Errorf("Error downloading function: %w\n%v", err, errBuf.String()) - } - return nil -} - -func Run(ctx context.Context, slug, projectRef string, useLegacyBundle, useDocker bool, fsys afero.Fs) error { - // Sanity check - if err := flags.LoadConfig(fsys); err != nil { - return err - } - - // Defaults to server-side unbundling with multipart/form-data - downloader := downloadWithServerSideUnbundle - if useLegacyBundle { - downloader = RunLegacy - } else if useDocker { - if utils.IsDockerRunning(ctx) { - // Download eszip file for client-side unbundling with edge-runtime - downloader = downloadWithDockerUnbundle - } else { - fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), "Docker is not running") - } - } - - if len(slug) > 0 { - return downloader(ctx, slug, projectRef, fsys) - } - return downloadAll(ctx, projectRef, fsys, downloader) -} - -func downloadAll(ctx context.Context, projectRef string, fsys afero.Fs, downloader func(context.Context, string, string, afero.Fs) error) error { - resp, err := utils.GetSupabase().V1ListAllFunctionsWithResponse(ctx, projectRef) - if err != nil { - return errors.Errorf("failed to list functions: %w", err) - } - if resp.JSON200 == nil { - return errors.Errorf("unexpected list functions status %d: %s", resp.StatusCode(), string(resp.Body)) - } - - functions := *resp.JSON200 - if len(functions) == 0 { - fmt.Fprintln(os.Stderr, "No functions found in project ", utils.Aqua(projectRef)) - return nil - } - - fmt.Fprintf(os.Stderr, "Found %d function(s) to download\n", len(functions)) - for _, f := range functions { - // f.Slug comes straight from the Management API response, which - // this threat model treats as untrusted: a malicious or corrupted - // response (or a MITM) could return a slug containing ".." or "/" - // segments. Every downloader below joins this value into a - // filesystem path (utils.TempDir for downloadOne, utils.FunctionsDir - // for downloadWithServerSideUnbundle) before any validation of its - // own, so it must be rejected here -- the single point where it - // enters this dispatch logic -- rather than relying on each - // downstream path-construction site to defend itself. - if err := utils.ValidateFunctionSlug(f.Slug); err != nil { - utils.CmdSuggestion = fmt.Sprintf( - "The Supabase API returned an unexpected function slug (%s). Retry the command, and if this keeps happening, verify your network connection is not being intercepted before contacting Supabase support.", - utils.Aqua(f.Slug), - ) - return errors.Errorf("failed to download function %s: %w", f.Slug, err) - } - if err := downloader(ctx, f.Slug, projectRef, fsys); err != nil { - return err - } - } - - fmt.Fprintln(os.Stderr, "Successfully downloaded all functions from project", utils.Aqua(projectRef)) - return nil -} - -func downloadWithDockerUnbundle(ctx context.Context, slug string, projectRef string, fsys afero.Fs) error { - eszipPath, err := downloadOne(ctx, slug, projectRef, fsys) - if err != nil { - return err - } - if !viper.GetBool("DEBUG") { - defer func() { - if err := fsys.Remove(eszipPath); err != nil { - fmt.Fprintln(os.Stderr, err) - } - }() - } - // Extract eszip to functions directory - err = extractOne(ctx, slug, eszipPath) - if err != nil { - utils.CmdSuggestion += suggestLegacyBundle(slug) - } - return err -} - -func downloadOne(ctx context.Context, slug, projectRef string, fsys afero.Fs) (string, error) { - fmt.Fprintln(os.Stderr, "Downloading function:", utils.Bold(slug)) - resp, err := utils.GetSupabase().V1GetAFunctionBody(ctx, projectRef, slug) - if err != nil { - return "", errors.Errorf("failed to get function body: %w", err) - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - body, err := io.ReadAll(resp.Body) - if err != nil { - return "", errors.Errorf("Error status %d: unexpected error downloading Function", resp.StatusCode) - } - return "", errors.Errorf("Error status %d: %s", resp.StatusCode, string(body)) - } - r := io.Reader(resp.Body) - if strings.EqualFold(resp.Header.Get("Content-Encoding"), "br") { - r = brotli.NewReader(resp.Body) - } - // Create temp file to store downloaded eszip - eszipPath := filepath.Join(utils.TempDir, fmt.Sprintf("output_%s.eszip", slug)) - if err := utils.MkdirIfNotExistFS(fsys, utils.TempDir); err != nil { - return "", err - } - if err := afero.WriteReader(fsys, eszipPath, r); err != nil { - return "", errors.Errorf("failed to download file: %w", err) - } - return eszipPath, nil -} - -func extractOne(ctx context.Context, slug, eszipPath string) error { - hostFuncDirPath, err := filepath.Abs(utils.FunctionsDir) - if err != nil { - return errors.Errorf("failed to resolve functions path: %w", err) - } - - hostEszipPath, err := filepath.Abs(eszipPath) - if err != nil { - return errors.Errorf("failed to resolve eszip path: %w", err) - } - dockerEszipPath := path.Join(utils.DockerEszipDir, filepath.Base(hostEszipPath)) - dockerOutputPath := path.Join(utils.DockerDenoDir, slug) - - binds := []string{ - // Reuse deno cache directory, ie. DENO_DIR, between container restarts - // https://denolib.gitbook.io/guide/advanced/deno_dir-code-fetch-and-cache - utils.EdgeRuntimeId + ":/root/.cache/deno:rw", - hostEszipPath + ":" + dockerEszipPath + ":ro", - hostFuncDirPath + ":" + utils.DockerDenoDir + ":rw", - } - - return utils.DockerRunOnceWithConfig( - ctx, - container.Config{ - Image: utils.Config.EdgeRuntime.Image, - Cmd: []string{"unbundle", "--eszip", dockerEszipPath, "--output", dockerOutputPath}, - }, - container.HostConfig{ - Binds: binds, - }, - network.NetworkingConfig{}, - "", - os.Stdout, - getErrorLogger(), - ) -} - -func getErrorLogger() io.Writer { - if utils.Config.EdgeRuntime.DenoVersion > 1 { - return os.Stderr - } - // Additional error handling for deno v1 - r, w := io.Pipe() - go func() { - logs := bufio.NewScanner(r) - for logs.Scan() { - line := logs.Text() - fmt.Fprintln(os.Stderr, line) - if strings.EqualFold(line, "invalid eszip v2") { - utils.CmdSuggestion = suggestDenoV2() - } - } - if err := logs.Err(); err != nil { - fmt.Fprintln(os.Stderr, err) - } - }() - return w -} - -func suggestDenoV2() string { - return fmt.Sprintf(`Please use deno v2 in %s to download this Function: - -[edge_runtime] -deno_version = 2 -`, utils.Bold(utils.ConfigPath)) -} - -func suggestLegacyBundle(slug string) string { - return fmt.Sprintf("\nIf your function is deployed using CLI < 1.120.0, trying running %s instead.", utils.Aqua("supabase functions download --legacy-bundle "+slug)) -} - -func suggestUnsafeDownloadPath() string { - return "This usually indicates a malformed or unexpected API response. If you're using a self-hosted instance, verify your API URL is correct." -} - -type bundleMetadata struct { - EntrypointPath string `json:"deno2_entrypoint_path,omitempty"` -} - -// New server-side unbundle implementation that mirrors Studio's entrypoint-based -// base-dir + relative path behaviour. -func downloadWithServerSideUnbundle(ctx context.Context, slug, projectRef string, fsys afero.Fs) error { - fmt.Fprintln(os.Stderr, "Downloading Function:", utils.Bold(slug)) - - form, err := readForm(ctx, projectRef, slug) - if err != nil { - return err - } - defer func() { - if err := form.RemoveAll(); err != nil { - fmt.Fprintln(os.Stderr, err) - } - }() - - // Read entrypoint path from deno2 bundles - metadata := bundleMetadata{} - if data, ok := form.Value["metadata"]; ok { - for _, part := range data { - if err := json.Unmarshal([]byte(part), &metadata); err != nil { - return errors.Errorf("failed to unmarshal metadata: %w", err) - } - } - } - - // Fallback to function metadata from upstash - if len(metadata.EntrypointPath) == 0 { - upstash, err := getFunctionMetadata(ctx, projectRef, slug) - if err != nil { - return errors.Errorf("failed to get function metadata: %w", err) - } - entrypointUrl, err := url.Parse(*upstash.EntrypointPath) - if err != nil { - return errors.Errorf("failed to parse entrypoint URL: %w", err) - } - metadata.EntrypointPath = entrypointUrl.Path - } - fmt.Fprintln(utils.GetDebugLogger(), "Using entrypoint path:", metadata.EntrypointPath) - - // Root directory on disk: supabase/functions/ - funcDir := filepath.Join(utils.FunctionsDir, slug) - for _, data := range form.File { - for _, file := range data { - if err := saveFile(file, metadata.EntrypointPath, funcDir, fsys); err != nil { - return err - } - } - } - - fmt.Fprintf(os.Stderr, "Downloaded Function %s from project %s.\n", utils.Aqua(slug), utils.Aqua(projectRef)) - return nil -} - -func readForm(ctx context.Context, projectRef, slug string) (*multipart.Form, error) { - // Request multipart/form-data response using RequestEditorFn - resp, err := utils.GetSupabase().V1GetAFunctionBody(ctx, projectRef, slug, func(ctx context.Context, req *http.Request) error { - req.Header.Set("Accept", "multipart/form-data") - return nil - }) - if err != nil { - return nil, errors.Errorf("failed to download function: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, errors.Errorf("Error status %d: %w", resp.StatusCode, err) - } - return nil, errors.Errorf("Error status %d: %s", resp.StatusCode, string(body)) - } - - // Parse the multipart response - mediaType, params, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) - if err != nil { - return nil, errors.Errorf("failed to parse content type: %w", err) - } - if !strings.HasPrefix(mediaType, "multipart/") { - return nil, errors.Errorf("expected multipart response, got %s", mediaType) - } - - // Read entire response with caching to disk - mr := multipart.NewReader(resp.Body, params["boundary"]) - form, err := mr.ReadForm(units.MiB) - if err != nil { - return nil, errors.Errorf("failed to read form: %w", err) - } - - return form, nil -} - -func saveFile(file *multipart.FileHeader, entrypointPath, funcDir string, fsys afero.Fs) error { - part, err := file.Open() - if err != nil { - return errors.Errorf("failed to open file: %w", err) - } - defer part.Close() - - logger := utils.GetDebugLogger() - partPath, err := getPartPath(file.Header) - if len(partPath) == 0 { - fmt.Fprintln(logger, "Skipping file with empty path:", file.Filename) - return err - } - fmt.Fprintln(logger, "Resolving file path:", partPath) - - relPath, err := filepath.Rel(filepath.FromSlash(entrypointPath), filepath.FromSlash(partPath)) - if err != nil { - // Continue extracting without entrypoint. Go's Rel error embeds - // both paths verbatim ("Rel: can't make relative to - // "), and partPath is server-controlled, so this is gated - // behind --debug like the "Resolving file path" log above rather - // than printed unconditionally to stderr. - fmt.Fprintln(logger, "WARNING:", err) - relPath = filepath.FromSlash(path.Join("..", partPath)) - } - - // partPath (and therefore relPath and dstPath) is derived from - // server-controlled multipart metadata (Supabase-Path header or - // Content-Disposition filename), so it must be validated before it - // touches the filesystem below. - dstPath := filepath.Join(funcDir, path.Base(entrypointPath), relPath) - - // Containment is enforced against the shared functions directory - // rather than funcDir: the entrypoint-mismatch fallback above can - // legitimately resolve one level above funcDir (into a sibling - // function's directory), but must never resolve outside - // utils.FunctionsDir entirely, e.g. via a "../../../etc/passwd" style - // payload. - root := utils.FunctionsDir - if err := validateDownloadPath(root, dstPath); err != nil { - utils.CmdSuggestion = suggestUnsafeDownloadPath() - return err - } - - // A previous part could have planted a symlink at an intermediate - // directory component, which would otherwise let MkdirAll/OpenFile - // silently follow it out of root even though dstPath itself looks - // clean. Check before creating the directory, and again after the - // write lands in case a symlink was swapped in between the two checks. - dstDir := filepath.Dir(dstPath) - if err := ensureNoSymlinkInPath(fsys, root, dstDir); err != nil { - utils.CmdSuggestion = suggestUnsafeDownloadPath() - return err - } - if err := utils.MkdirIfNotExistFS(fsys, dstDir); err != nil { - return err - } - - fmt.Fprintln(os.Stderr, "Extracting file:", dstPath) - if err := writeFileNoFollowSymlink(fsys, dstPath, part); err != nil { - return err - } - - if err := ensureNoSymlinkInPath(fsys, root, dstPath); err != nil { - utils.CmdSuggestion = suggestUnsafeDownloadPath() - return err - } - return nil -} - -// validateDownloadPath rejects dstPath if, once lexically cleaned, it does -// not resolve inside root. This is the primary defense against a hostile -// "../../etc/passwd" style Supabase-Path/filename escaping the functions -// directory: filepath.Join already cleans ".." segments away, so this -// check must run against the cleaned result rather than by scanning the -// raw, attacker-controlled path for "..". -func validateDownloadPath(root, dstPath string) error { - rel, err := filepath.Rel(root, filepath.Clean(dstPath)) - if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) || filepath.IsAbs(rel) { - return errors.Errorf("failed to save file: %w", ErrUnsafeDownloadPath) - } - return nil -} - -// ensureNoSymlinkInPath rejects dir if resolving the symlinks along it would -// land outside root. -// -// Earlier versions of this check rejected the mere presence of a symlink -// anywhere under root, which also broke legitimate setups such as a -// monorepo symlinking a shared directory into place inside the functions -// tree. Instead, this resolves both root and dir the same way -- walking up -// to the deepest ancestor that already exists on disk, following any chain -// of symlinks there via filepath.EvalSymlinks (this also naturally covers -// root itself being a symlink, since dir's walk passes through root), and -// re-joining the still-nonexistent remainder back on -- and re-validates -// the two resolved paths against each other with validateDownloadPath. -// Resolving root the same way dir is resolved, rather than comparing a -// resolved dir against a literal root, matters in practice: it is what -// keeps an OS-level symlink that sits above both of them (e.g. macOS's -// /var -> /private/var, which every path under a t.TempDir() passes -// through) from producing a spurious mismatch. Only a resolved dir that -// escapes the resolved root is rejected; a symlink whose target still -// lands inside root, however deeply nested, is now allowed. -// -// This only has an effect on filesystems that expose real symlink -// semantics through afero.LinkReader, i.e. afero.NewOsFs in production -// (afero.Lstater is not a reliable enough signal on its own: afero.MemMapFs -// also implements it, just by delegating straight to Stat). The in-memory -// filesystem used in tests implements neither, so this check is -// effectively a no-op there -- there is nothing to protect against on a -// filesystem that cannot contain symlinks in the first place. That same -// guard is also why it is safe for filepath.EvalSymlinks below to hit the -// real OS filesystem directly instead of going through fsys: the only -// afero.Fs implementation used in production, afero.NewOsFs, delegates to -// the real OS filesystem for every operation, so the two agree. -func ensureNoSymlinkInPath(fsys afero.Fs, root, dir string) error { - if _, ok := fsys.(afero.LinkReader); !ok { - return nil - } - - // filepath.EvalSymlinks returns an absolute result as soon as it - // crosses one absolute symlink, but stays relative otherwise (per its - // doc comment), so root and dir must both start out absolute here -- - // otherwise root (no symlink crossed) and dir (crossing one) could - // resolve to a relative and an absolute path respectively, which - // filepath.Rel below cannot meaningfully compare. - absRoot, err := filepath.Abs(root) - if err != nil { - return errors.Errorf("failed to inspect extraction path: %w", err) - } - absDir, err := filepath.Abs(dir) - if err != nil { - return errors.Errorf("failed to inspect extraction path: %w", err) - } - - resolvedRoot, err := resolveExistingPath(fsys, absRoot) - if err != nil { - return errors.Errorf("failed to inspect extraction path: %w", err) - } - resolvedDir, err := resolveExistingPath(fsys, absDir) - if err != nil { - return errors.Errorf("failed to inspect extraction path: %w", err) - } - - return validateDownloadPath(resolvedRoot, resolvedDir) -} - -// resolveExistingPath resolves p by walking up to the deepest ancestor of it -// that already exists -- a path component that does not exist yet cannot -// itself be a symlink, so there is nothing there for EvalSymlinks to -// resolve -- following any chain of symlinks in that ancestor to its real -// target, then re-joining the still-nonexistent remainder of p back onto -// the result. -func resolveExistingPath(fsys afero.Fs, p string) (string, error) { - existing := p - var suffix []string - for { - if _, err := fsys.Stat(existing); err == nil { - break - } else if !os.IsNotExist(err) { - return "", err - } - parent := filepath.Dir(existing) - if parent == existing { - // Reached the filesystem root without finding anything that - // exists yet, so there is nothing left to resolve. - return filepath.Join(append([]string{existing}, suffix...)...), nil - } - suffix = append([]string{filepath.Base(existing)}, suffix...) - existing = parent - } - - resolved, err := filepath.EvalSymlinks(existing) - if err != nil { - return "", err - } - return filepath.Join(append([]string{resolved}, suffix...)...), nil -} - -// writeFileNoFollowSymlink writes r to dstPath without following a symlink -// that might already occupy that path. It creates a randomly named -// temporary file next to dstPath with O_EXCL, refusing to write through -// anything already there, then atomically renames it onto dstPath. -// Rename replaces whatever directory entry currently exists at dstPath -- -// including a symlink -- rather than dereferencing it, which is exactly -// what a plain fs.Create/afero.WriteReader would otherwise do. -func writeFileNoFollowSymlink(fsys afero.Fs, dstPath string, r io.Reader) error { - tmpPath := filepath.Join(filepath.Dir(dstPath), fmt.Sprintf(".supabase-download-%s.tmp", uuid.NewString())) - tmp, err := fsys.OpenFile(tmpPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o644) - if err != nil { - return errors.Errorf("failed to save file: %w", err) - } - - if _, err := io.Copy(tmp, r); err != nil { - _ = tmp.Close() - _ = fsys.Remove(tmpPath) - return errors.Errorf("failed to save file: %w", err) - } - if err := tmp.Close(); err != nil { - _ = fsys.Remove(tmpPath) - return errors.Errorf("failed to save file: %w", err) - } - - if err := fsys.Rename(tmpPath, dstPath); err != nil { - _ = fsys.Remove(tmpPath) - return errors.Errorf("failed to save file: %w", err) - } - return nil -} - -// getPartPath extracts the filename for a multipart part, allowing for -// relative paths via the custom Supabase-Path header. -func getPartPath(header textproto.MIMEHeader) (string, error) { - // dedicated header to specify relative path, not expected to be used - if relPath := header.Get("Supabase-Path"); relPath != "" { - return relPath, nil - } - - // part.FileName() does not allow us to handle relative paths, so we parse Content-Disposition manually - cd := header.Get("Content-Disposition") - if cd == "" { - return "", nil - } - - _, params, err := mime.ParseMediaType(cd) - if err != nil { - return "", errors.Errorf("failed to parse content disposition: %w", err) - } - - if filename := params["filename"]; filename != "" { - return filename, nil - } - return "", nil -} diff --git a/apps/cli-go/internal/functions/download/download_test.go b/apps/cli-go/internal/functions/download/download_test.go deleted file mode 100644 index 8267dc7eeb..0000000000 --- a/apps/cli-go/internal/functions/download/download_test.go +++ /dev/null @@ -1,914 +0,0 @@ -package download - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "log" - "mime/multipart" - "net/http" - "net/textproto" - "os" - "path" - "path/filepath" - "strings" - "testing" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/image" - "github.com/docker/docker/api/types/network" - "github.com/docker/docker/api/types/volume" - "github.com/h2non/gock" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/flags" - "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" -) - -func TestMain(m *testing.M) { - // Setup fake deno binary - if len(os.Args) > 1 && (os.Args[1] == "bundle" || os.Args[1] == "upgrade" || os.Args[1] == "run") { - msg := os.Getenv("TEST_DENO_ERROR") - if msg != "" { - fmt.Fprintln(os.Stderr, msg) - os.Exit(1) - } - os.Exit(0) - } - denoPath, err := os.Executable() - if err != nil { - log.Fatalln(err) - } - utils.DenoPathOverride = denoPath - // Run test suite - os.Exit(m.Run()) -} - -type multipartPart struct { - filename string - supabasePath string - contents string -} - -func mockMultipartBody(t *testing.T, projectRef, slug string, metadata bundleMetadata, parts []multipartPart) { - t.Helper() - var buf bytes.Buffer - writer := multipart.NewWriter(&buf) - // Write metadata - headers := textproto.MIMEHeader{} - headers.Set("Content-Disposition", `form-data; name="metadata"`) - headers.Set("Content-Type", "application/json") - pw, err := writer.CreatePart(headers) - require.NoError(t, err) - enc := json.NewEncoder(pw) - require.NoError(t, enc.Encode(metadata)) - // Write files - for _, part := range parts { - headers := textproto.MIMEHeader{} - headers.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, part.filename)) - if part.supabasePath != "" { - headers.Set("Supabase-Path", part.supabasePath) - } - pw, err := writer.CreatePart(headers) - require.NoError(t, err) - _, err = pw.Write([]byte(part.contents)) - require.NoError(t, err) - } - require.NoError(t, writer.Close()) - - gock.New(utils.DefaultApiHost). - Get(fmt.Sprintf("/v1/projects/%s/functions/%s/body", projectRef, slug)). - Reply(http.StatusOK). - SetHeader("Content-Type", writer.FormDataContentType()). - Body(&buf) -} - -func TestRunLegacyUnbundle(t *testing.T) { - const slug = "test-func" - - t.Run("downloads eszip bundle", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup valid project ref - project := apitest.RandomProjectRef() - // Setup valid access token - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - // Setup valid deno path - _, err := fsys.Create(utils.DenoPathOverride) - require.NoError(t, err) - // Setup mock api - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions/" + slug). - Reply(http.StatusOK). - JSON(api.FunctionResponseOutput{Id: "1"}) - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions/" + slug + "/body"). - Reply(http.StatusOK) - // Run test - err = Run(context.Background(), slug, project, true, false, fsys) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on malformed slug", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup valid project ref - project := apitest.RandomProjectRef() - // Run test - err := Run(context.Background(), "@", project, true, false, fsys) - // Check error - assert.ErrorContains(t, err, "Invalid Function name.") - }) - - t.Run("throws error on failure to install deno", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) - // Setup valid project ref - project := apitest.RandomProjectRef() - // Run test - err := Run(context.Background(), slug, project, true, false, fsys) - // Check error - assert.ErrorContains(t, err, "operation not permitted") - }) - - t.Run("throws error on copy failure", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup valid project ref - project := apitest.RandomProjectRef() - // Setup valid deno path - _, err := fsys.Create(utils.DenoPathOverride) - require.NoError(t, err) - // Run test - err = Run(context.Background(), slug, project, true, false, afero.NewReadOnlyFs(fsys)) - // Check error - assert.ErrorContains(t, err, "operation not permitted") - }) - - t.Run("throws error on missing function", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup valid project ref - project := apitest.RandomProjectRef() - // Setup valid access token - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - // Setup valid deno path - _, err := fsys.Create(utils.DenoPathOverride) - require.NoError(t, err) - // Setup mock api - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions/" + slug). - Reply(http.StatusNotFound). - JSON(map[string]string{"message": "Function not found"}) - // Run test - err = Run(context.Background(), slug, project, true, false, fsys) - // Check error - assert.ErrorContains(t, err, "Function test-func does not exist on the Supabase project.") - }) -} - -func TestRunDockerUnbundle(t *testing.T) { - t.Run("downloads bundle with docker when available", func(t *testing.T) { - const slugDocker = "demo" - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - project := apitest.RandomProjectRef() - require.NoError(t, flags.LoadConfig(fsys)) - - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - - require.NoError(t, apitest.MockDocker(utils.Docker)) - dockerHost := utils.Docker.DaemonHost() - - // Setup mock api - defer gock.OffAll() - - gock.New(dockerHost). - Head("/_ping"). - Reply(http.StatusOK) - - imageURL := utils.GetRegistryImageUrl(utils.Config.EdgeRuntime.Image) - containerID := "docker-unbundle-test" - var createRequest struct { - Cmd []string `json:"Cmd"` - HostConfig struct { - Binds []string `json:"Binds"` - } `json:"HostConfig"` - } - gock.New(dockerHost). - Get("/v" + utils.Docker.ClientVersion() + "/images/" + imageURL + "/json"). - Reply(http.StatusOK). - JSON(image.InspectResponse{}) - gock.New(dockerHost). - Post("/v" + utils.Docker.ClientVersion() + "/networks/create"). - Reply(http.StatusCreated). - JSON(network.CreateResponse{}) - gock.New(dockerHost). - Post("/v" + utils.Docker.ClientVersion() + "/volumes/create"). - Persist(). - Reply(http.StatusCreated). - JSON(volume.Volume{}) - gock.New(dockerHost). - Post("/v" + utils.Docker.ClientVersion() + "/containers/create"). - AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { - body, err := io.ReadAll(req.Body) - if err != nil { - return false, err - } - return true, json.Unmarshal(body, &createRequest) - }). - Reply(http.StatusOK). - JSON(container.CreateResponse{ID: containerID}) - gock.New(dockerHost). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + containerID + "/start"). - Reply(http.StatusAccepted) - require.NoError(t, apitest.MockDockerLogs(utils.Docker, containerID, "unbundle ok")) - - gock.New(utils.DefaultApiHost). - Get(fmt.Sprintf("/v1/projects/%s/functions/%s/body", project, slugDocker)). - Reply(http.StatusOK). - BodyString("fake eszip payload") - - err := Run(context.Background(), slugDocker, project, false, true, fsys) - require.NoError(t, err) - - eszipPath := filepath.Join(utils.TempDir, fmt.Sprintf("output_%s.eszip", slugDocker)) - exists, err := afero.Exists(fsys, eszipPath) - require.NoError(t, err) - assert.False(t, exists, "temporary eszip file should be removed after extraction") - - hostFunctionsDirPath, err := filepath.Abs(utils.FunctionsDir) - require.NoError(t, err) - hostEszipPath, err := filepath.Abs(eszipPath) - require.NoError(t, err) - assert.EqualValues(t, []string{ - "unbundle", - "--eszip", - path.Join(utils.DockerEszipDir, filepath.Base(hostEszipPath)), - "--output", - path.Join(utils.DockerDenoDir, slugDocker), - }, createRequest.Cmd) - assert.Contains(t, createRequest.HostConfig.Binds, utils.EdgeRuntimeId+":/root/.cache/deno:rw") - assert.Contains(t, createRequest.HostConfig.Binds, hostEszipPath+":"+path.Join(utils.DockerEszipDir, filepath.Base(hostEszipPath))+":ro") - assert.Contains(t, createRequest.HostConfig.Binds, hostFunctionsDirPath+":"+utils.DockerDenoDir+":rw") - for _, bind := range createRequest.HostConfig.Binds { - assert.False(t, strings.Contains(bind, filepath.Join(hostFunctionsDirPath, slugDocker)+":"+utils.DockerDenoDir), - "docker output should mount supabase/functions, not the slug directory") - } - - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("falls back to server-side unbundle when docker unavailable", func(t *testing.T) { - const slugDocker = "demo-fallback" - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - project := apitest.RandomProjectRef() - require.NoError(t, flags.LoadConfig(fsys)) - - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - - require.NoError(t, apitest.MockDocker(utils.Docker)) - dockerHost := utils.Docker.DaemonHost() - - // Setup mock api - defer gock.OffAll() - - gock.New(dockerHost). - Head("/_ping"). - ReplyError(errors.New("docker unavailable")) - - mockMultipartBody(t, project, slugDocker, bundleMetadata{"/source/index.ts"}, []multipartPart{ - {filename: "/source/index.ts", contents: "console.log('hello')"}, - }) - - err := Run(context.Background(), slugDocker, project, false, true, fsys) - require.NoError(t, err) - - data, err := afero.ReadFile(fsys, filepath.Join(utils.FunctionsDir, slugDocker, "index.ts")) - require.NoError(t, err) - assert.Equal(t, "console.log('hello')", string(data)) - - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -// TestDownloadAllRejectsMaliciousSlug is a regression test for CLI-1891: the -// per-function loop in downloadAll must reject a path-traversal payload in -// a function's Slug -- as returned by V1ListAllFunctionsWithResponse, which -// this threat model treats as untrusted (a malicious/compromised Management -// API response, or a MITM) -- before handing it to any downloader that -// joins it into a filesystem path. -// -// This mirrors an exploit independently confirmed against downloadOne: with -// slug = "../../../../../poc-escaped-outside-project", downloadOne's -// eszipPath := filepath.Join(utils.TempDir, fmt.Sprintf("output_%s.eszip", -// slug)) resolves (after filepath.Clean) to "../poc-escaped-outside-project.eszip", -// i.e. one directory level above the project root, and -// afero.WriteReader happily MkdirAll's its way there and writes the -// server-controlled response body outside the sandbox. -func TestDownloadAllRejectsMaliciousSlug(t *testing.T) { - const maliciousSlug = "../../../../../poc-escaped-outside-project" - - // Use a real OS filesystem rooted at an isolated temp directory so an - // escape can actually be observed landing outside the project root, - // exactly as in the reviewer's PoC. - tmpDir := t.TempDir() - t.Chdir(tmpDir) - fsys := afero.NewOsFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - - project := apitest.RandomProjectRef() - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - - utils.CmdSuggestion = "" - t.Cleanup(func() { utils.CmdSuggestion = "" }) - - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions"). - Reply(http.StatusOK). - JSON([]api.FunctionResponseOutput{{ - Id: "poc-id", - Name: "poc", - Slug: maliciousSlug, - }}) - // Mocked so that, if the fix is removed, the malicious slug's request - // still succeeds and downloadOne proceeds all the way to writing the - // escaped file -- proving the escape, rather than masking it behind an - // unrelated network error. With the fix in place this mock is never hit. - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions/.*/body"). - Reply(http.StatusOK). - BodyString("fake eszip payload") - - // downloadOne is the exact sink the reviewer's PoC targeted directly; - // wrap it as a downloader so downloadAll's dispatch is exercised against - // the real vulnerable code, without also pulling in downloadWithDockerUnbundle's - // unrelated Docker extraction step (and its defer-cleanup of the eszip - // file, which would otherwise remove the escaped file before this test - // can observe it). - downloaderCalled := false - downloader := func(ctx context.Context, slug, projectRef string, fsys afero.Fs) error { - downloaderCalled = true - _, err := downloadOne(ctx, slug, projectRef, fsys) - return err - } - - err := downloadAll(context.Background(), project, fsys, downloader) - - // Check for the escape before any assertion below that could halt the - // test on failure (e.g. require.Error), so this is verified regardless - // of whether downloadAll happened to return an error for some other - // reason. t.TempDir()'s own cleanup RemoveAll's tmpDir's parent, which - // would otherwise sweep away the evidence once the test returns. - // - // The exploit resolves to "../poc-escaped-outside-project.eszip" - // relative to utils.TempDir, i.e. one level above the project root. - escapedPath := filepath.Join(tmpDir, "..", "poc-escaped-outside-project.eszip") - t.Cleanup(func() { _ = os.Remove(escapedPath) }) - exists, existsErr := afero.Exists(fsys, escapedPath) - require.NoError(t, existsErr) - assert.False(t, exists, "malicious slug must not be able to write outside the project directory") - - require.Error(t, err) - assert.ErrorIs(t, err, utils.ErrInvalidSlug) - assert.Contains(t, utils.CmdSuggestion, "unexpected function slug") - assert.False(t, downloaderCalled, "downloader must not be invoked with an unvalidated slug") - - assert.Empty(t, apitest.ListUnmatchedRequests()) -} - -func TestRunServerSideUnbundle(t *testing.T) { - const slug = "test-func" - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - project := apitest.RandomProjectRef() - - t.Run("writes files using inferred base directory", func(t *testing.T) { - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - - defer gock.OffAll() - mockMultipartBody(t, project, slug, bundleMetadata{EntrypointPath: "source/index.ts"}, []multipartPart{ - {filename: "source/index.ts", contents: "console.log('hello')"}, - {filename: "source/utils.ts", contents: "export const value = 1;"}, - }) - - err := Run(context.Background(), slug, project, false, false, fsys) - require.NoError(t, err) - - data, err := afero.ReadFile(fsys, filepath.Join(utils.FunctionsDir, slug, "index.ts")) - require.NoError(t, err) - assert.Equal(t, "console.log('hello')", string(data)) - - data, err = afero.ReadFile(fsys, filepath.Join(utils.FunctionsDir, slug, "utils.ts")) - require.NoError(t, err) - assert.Equal(t, "export const value = 1;", string(data)) - - entries, err := afero.ReadDir(fsys, utils.TempDir) - if err == nil { - assert.Len(t, entries, 0, "expected temporary directory to be cleaned up") - } else { - assert.ErrorIs(t, err, os.ErrNotExist) - } - - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("derives base directory from absolute filenames", func(t *testing.T) { - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - - defer gock.OffAll() - indexPath := "/tmp/functions-download-abs/source/index.ts" - utilsPath := path.Join(path.Dir(indexPath), "lib", "utils.ts") - mockMultipartBody(t, project, slug, bundleMetadata{}, []multipartPart{ - {filename: indexPath, contents: "console.log('abs')"}, - {filename: utilsPath, contents: "export const util = 2;"}, - }) - - gock.New(utils.DefaultApiHost). - Get(fmt.Sprintf("/v1/projects/%s/functions/%s", project, slug)). - Reply(http.StatusOK). - JSON(api.FunctionSlugResponseOutput{ - Id: "1", - Name: slug, - Slug: slug, - Status: api.FunctionSlugResponseOutputStatus("ACTIVE"), - Version: 1, - CreatedAt: 0, - UpdatedAt: 0, - EntrypointPath: cast.Ptr("file://" + indexPath), - }) - - err := Run(context.Background(), slug, project, false, false, fsys) - require.NoError(t, err) - - root := filepath.Join(utils.FunctionsDir, slug) - data, err := afero.ReadFile(fsys, filepath.Join(root, "index.ts")) - require.NoError(t, err) - assert.Equal(t, "console.log('abs')", string(data)) - - data, err = afero.ReadFile(fsys, filepath.Join(root, "lib", "utils.ts")) - require.NoError(t, err) - assert.Equal(t, "export const util = 2;", string(data)) - - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("fails when response not multipart", func(t *testing.T) { - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get(fmt.Sprintf("/v1/projects/%s/functions/%s", project, slug)). - Reply(http.StatusOK). - JSON(api.FunctionSlugResponseOutput{ - Id: "1", - Name: slug, - Slug: slug, - Status: api.FunctionSlugResponseOutputStatus("ACTIVE"), - Version: 1, - CreatedAt: 0, - UpdatedAt: 0, - EntrypointPath: cast.Ptr(legacyEntrypointPath), - }) - - gock.New(utils.DefaultApiHost). - Get(fmt.Sprintf("/v1/projects/%s/functions/%s/body", project, slug)). - Reply(http.StatusOK). - SetHeader("Content-Type", "application/json"). - BodyString(`{"error":"no multipart"}`) - - err := Run(context.Background(), slug, project, false, false, fsys) - assert.ErrorContains(t, err, "expected multipart response") - }) - - t.Run("ignores unresolvable entrypoint path", func(t *testing.T) { - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - - defer gock.OffAll() - mockMultipartBody(t, project, slug, bundleMetadata{}, []multipartPart{ - {filename: "source/index.ts", contents: "console.log('hello')"}, - {filename: "source/secret.env", supabasePath: "../secret.env", contents: "SECRET=1"}, - }) - - gock.New(utils.DefaultApiHost). - Get(fmt.Sprintf("/v1/projects/%s/functions/%s", project, slug)). - Reply(http.StatusOK). - JSON(api.FunctionSlugResponseOutput{ - Id: "1", - Name: slug, - Slug: slug, - Status: api.FunctionSlugResponseOutputStatus("ACTIVE"), - Version: 1, - CreatedAt: 0, - UpdatedAt: 0, - EntrypointPath: cast.Ptr("file:///source/index.ts"), - }) - - err := Run(context.Background(), slug, project, false, false, fsys) - assert.NoError(t, err) - - root := filepath.Join(utils.FunctionsDir, slug) - data, err := afero.ReadFile(fsys, filepath.Join(root, "source", "index.ts")) - require.NoError(t, err) - assert.Equal(t, "console.log('hello')", string(data)) - - data, err = afero.ReadFile(fsys, filepath.Join(utils.FunctionsDir, "secret.env")) - require.NoError(t, err) - assert.Equal(t, "SECRET=1", string(data)) - - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("rejects a path traversal payload in Supabase-Path", func(t *testing.T) { - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - - utils.CmdSuggestion = "" - t.Cleanup(func() { utils.CmdSuggestion = "" }) - - defer gock.OffAll() - mockMultipartBody(t, project, slug, bundleMetadata{EntrypointPath: "source/index.ts"}, []multipartPart{ - {filename: "source/index.ts", contents: "console.log('hello')"}, - {filename: "leftover.ts", supabasePath: "../../../../../../etc/passwd", contents: "root:x:0:0::/root:/bin/bash"}, - }) - - err := Run(context.Background(), slug, project, false, false, fsys) - require.Error(t, err) - assert.ErrorIs(t, err, ErrUnsafeDownloadPath) - - // The generic "invalid path in server response" message on its own - // gives users nothing actionable, so this must carry a specific - // suggestion (DX finding on CLI-1891) rather than falling through - // to the generic --debug suggestion. - assert.Contains(t, utils.CmdSuggestion, "malformed or unexpected API response") - - // Nothing should have escaped the functions directory. - exists, err := afero.Exists(fsys, "/etc/passwd") - require.NoError(t, err) - assert.False(t, exists, "path traversal payload must not be written outside the functions directory") - - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("writes deeply nested legitimate paths", func(t *testing.T) { - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - - defer gock.OffAll() - mockMultipartBody(t, project, slug, bundleMetadata{EntrypointPath: "source/index.ts"}, []multipartPart{ - {filename: "source/index.ts", contents: "console.log('hello')"}, - {filename: "source/a/b/c/d/deep.ts", contents: "export const deep = true;"}, - }) - - err := Run(context.Background(), slug, project, false, false, fsys) - require.NoError(t, err) - - root := filepath.Join(utils.FunctionsDir, slug) - data, err := afero.ReadFile(fsys, filepath.Join(root, "index.ts")) - require.NoError(t, err) - assert.Equal(t, "console.log('hello')", string(data)) - - data, err = afero.ReadFile(fsys, filepath.Join(root, "a", "b", "c", "d", "deep.ts")) - require.NoError(t, err) - assert.Equal(t, "export const deep = true;", string(data)) - - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("does not write through a pre-existing symlink at the destination", func(t *testing.T) { - // Symlink semantics only exist on a real filesystem, so this test - // exercises afero.NewOsFs against an isolated temp directory rather - // than the in-memory fs used elsewhere in this file. - tmpDir := t.TempDir() - t.Chdir(tmpDir) - fsys := afero.NewOsFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - - // A file living outside the sandboxed functions directory that must - // never be touched by the download. - outsideDir := filepath.Join(tmpDir, "outside") - require.NoError(t, os.MkdirAll(outsideDir, 0o755)) - secretPath := filepath.Join(outsideDir, "secret.txt") - require.NoError(t, os.WriteFile(secretPath, []byte("original"), 0o600)) - - // Plant a symlink inside the function directory, before the - // download runs, pointing at the file outside the sandbox. A - // server response that resolves to this exact path must not be - // allowed to write through it. - funcDir := filepath.Join(utils.FunctionsDir, slug) - require.NoError(t, os.MkdirAll(funcDir, 0o755)) - linkPath := filepath.Join(funcDir, "evil.ts") - require.NoError(t, os.Symlink(secretPath, linkPath)) - - defer gock.OffAll() - mockMultipartBody(t, project, slug, bundleMetadata{EntrypointPath: "source/index.ts"}, []multipartPart{ - {filename: "source/index.ts", contents: "console.log('hello')"}, - {filename: "source/evil.ts", contents: "overwritten"}, - }) - - err := Run(context.Background(), slug, project, false, false, fsys) - require.NoError(t, err) - - // The symlink target outside the sandbox must be untouched. - data, err := os.ReadFile(secretPath) - require.NoError(t, err) - assert.Equal(t, "original", string(data), "file outside the functions directory must not be modified") - - // The destination itself should now be a plain file with the - // downloaded contents: the atomic rename replaces the symlink - // directory entry rather than following it. - info, err := os.Lstat(linkPath) - require.NoError(t, err) - assert.Zero(t, info.Mode()&os.ModeSymlink, "planted symlink should have been replaced, not written through") - - data, err = os.ReadFile(linkPath) - require.NoError(t, err) - assert.Equal(t, "overwritten", string(data)) - - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - // Regression test for the DX finding on CLI-1891: ensureNoSymlinkInPath - // used to reject the mere presence of a symlink anywhere under - // utils.FunctionsDir, which would also have broken a legitimate - // monorepo pattern like this one, where a function directory - // symlinks in a shared directory that itself lives inside the - // functions tree. That symlink's target still resolves inside root, - // so it must be allowed end-to-end. - t.Run("writes through a symlink pointing to a legitimate location inside the functions directory", func(t *testing.T) { - tmpDir := t.TempDir() - t.Chdir(tmpDir) - fsys := afero.NewOsFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - - sharedDir := filepath.Join(utils.FunctionsDir, "_shared") - require.NoError(t, os.MkdirAll(sharedDir, 0o755)) - // os.Symlink resolves a relative target against the symlink's own - // containing directory, not the process cwd, so the target must be - // made absolute here for the link to actually point at sharedDir. - absSharedDir, err := filepath.Abs(sharedDir) - require.NoError(t, err) - - funcDir := filepath.Join(utils.FunctionsDir, slug) - require.NoError(t, os.MkdirAll(funcDir, 0o755)) - require.NoError(t, os.Symlink(absSharedDir, filepath.Join(funcDir, "_shared"))) - - defer gock.OffAll() - mockMultipartBody(t, project, slug, bundleMetadata{EntrypointPath: "source/index.ts"}, []multipartPart{ - {filename: "source/index.ts", contents: "console.log('hello')"}, - {filename: "source/_shared/util.ts", contents: "export const util = 2;"}, - }) - - err = Run(context.Background(), slug, project, false, false, fsys) - require.NoError(t, err) - - data, err := afero.ReadFile(fsys, filepath.Join(funcDir, "index.ts")) - require.NoError(t, err) - assert.Equal(t, "console.log('hello')", string(data)) - - // The write landed through the symlink, in the real shared - // directory rather than being rejected. - data, err = os.ReadFile(filepath.Join(sharedDir, "util.ts")) - require.NoError(t, err) - assert.Equal(t, "export const util = 2;", string(data)) - - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -// TestEnsureNoSymlinkInPath exercises ensureNoSymlinkInPath's resolve-then-check -// policy directly: it must still reject a symlink whose target escapes root, -// but must now allow one whose target resolves to a legitimate, even deeply -// nested, location that is still inside root. -func TestEnsureNoSymlinkInPath(t *testing.T) { - t.Run("rejects a symlink whose target resolves outside root", func(t *testing.T) { - tmpDir := t.TempDir() - fsys := afero.NewOsFs() - - root := filepath.Join(tmpDir, "supabase", "functions") - require.NoError(t, os.MkdirAll(root, 0o755)) - - outsideDir := filepath.Join(tmpDir, "outside") - require.NoError(t, os.MkdirAll(outsideDir, 0o755)) - - // Plant a symlink inside root whose target lives outside it - // entirely -- the actual attack this check defends against. - linkDir := filepath.Join(root, "escape") - require.NoError(t, os.Symlink(outsideDir, linkDir)) - - dir := filepath.Join(linkDir, "nested") - err := ensureNoSymlinkInPath(fsys, root, dir) - require.Error(t, err) - assert.ErrorIs(t, err, ErrUnsafeDownloadPath) - }) - - t.Run("allows a symlink whose target resolves to a nested location inside root", func(t *testing.T) { - tmpDir := t.TempDir() - fsys := afero.NewOsFs() - - root := filepath.Join(tmpDir, "supabase", "functions") - require.NoError(t, os.MkdirAll(root, 0o755)) - - // A legitimate monorepo-style symlink: a shared directory that - // lives elsewhere inside the functions tree, symlinked into place - // from a function's own directory. - sharedDir := filepath.Join(root, "_shared") - require.NoError(t, os.MkdirAll(sharedDir, 0o755)) - - funcDir := filepath.Join(root, "my-func") - require.NoError(t, os.MkdirAll(funcDir, 0o755)) - linkDir := filepath.Join(funcDir, "_shared") - require.NoError(t, os.Symlink(sharedDir, linkDir)) - - // dir itself does not exist yet -- ensureNoSymlinkInPath is called - // before MkdirIfNotExistFS creates it -- so this also proves the - // still-nonexistent remainder is correctly re-joined onto the - // resolved ancestor. - dir := filepath.Join(linkDir, "nested", "deeper") - err := ensureNoSymlinkInPath(fsys, root, dir) - assert.NoError(t, err, "a symlink resolving to a legitimate location inside root must be allowed") - }) -} - -func TestDownloadFunction(t *testing.T) { - const slug = "test-func" - // Setup valid project ref - project := apitest.RandomProjectRef() - // Setup valid access token - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - - t.Run("throws error on network error", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions/" + slug). - Reply(http.StatusOK). - JSON(api.FunctionResponseOutput{Id: "1"}) - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions/" + slug + "/body"). - ReplyError(errors.New("network error")) - // Run test - err := downloadFunction(context.Background(), project, slug, "") - // Check error - assert.ErrorContains(t, err, "network error") - }) - - t.Run("throws error on service unavailable", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions/" + slug). - Reply(http.StatusOK). - JSON(api.FunctionResponseOutput{Id: "1"}) - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions/" + slug + "/body"). - Reply(http.StatusServiceUnavailable) - // Run test - err := downloadFunction(context.Background(), project, slug, "") - // Check error - assert.ErrorContains(t, err, "Unexpected error downloading Function:") - }) - - t.Run("throws error on extract failure", func(t *testing.T) { - // Setup deno error - t.Setenv("TEST_DENO_ERROR", "extract failed") - // Setup mock api - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions/" + slug). - Reply(http.StatusOK). - JSON(api.FunctionResponseOutput{Id: "1"}) - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions/" + slug + "/body"). - Reply(http.StatusOK) - // Run test - err := downloadFunction(context.Background(), project, slug, "") - // Check error - assert.ErrorContains(t, err, "Error downloading function: exit status 1\nextract failed\n") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestGetMetadata(t *testing.T) { - const slug = "test-func" - project := apitest.RandomProjectRef() - // Setup valid access token - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - - t.Run("fallback to default paths", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions/" + slug). - Reply(http.StatusOK). - JSON(api.FunctionResponseOutput{Id: "1"}) - // Run test - meta, err := getFunctionMetadata(context.Background(), project, slug) - // Check error - assert.NoError(t, err) - assert.Equal(t, legacyEntrypointPath, *meta.EntrypointPath) - assert.Equal(t, legacyImportMapPath, *meta.ImportMapPath) - }) - - t.Run("throws error on network error", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions/" + slug). - ReplyError(errors.New("network error")) - // Run test - meta, err := getFunctionMetadata(context.Background(), project, slug) - // Check error - assert.ErrorContains(t, err, "network error") - assert.Nil(t, meta) - }) - - t.Run("throws error on service unavailable", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + project + "/functions/" + slug). - Reply(http.StatusServiceUnavailable) - // Run test - meta, err := getFunctionMetadata(context.Background(), project, slug) - // Check error - assert.ErrorContains(t, err, "Failed to download Function test-func on the Supabase project:") - assert.Nil(t, meta) - }) -} - -func TestGetPartPath(t *testing.T) { - t.Parallel() - - t.Run("returns path from Supabase header", func(t *testing.T) { - header := textproto.MIMEHeader{} - header.Set("Supabase-Path", "dir/file.ts") - got, err := getPartPath(header) - require.NoError(t, err) - assert.Equal(t, "dir/file.ts", got) - }) - - t.Run("returns filename from content disposition", func(t *testing.T) { - header := textproto.MIMEHeader{} - header.Set("Content-Disposition", `form-data; name="file"; filename="test-func/index.ts"`) - got, err := getPartPath(header) - require.NoError(t, err) - assert.Equal(t, "test-func/index.ts", got) - }) - - t.Run("returns filename from editor-originated content disposition", func(t *testing.T) { - header := textproto.MIMEHeader{} - header.Set("Content-Disposition", `form-data; name="file"; filename="source/index.ts"`) - got, err := getPartPath(header) - require.NoError(t, err) - assert.Equal(t, "source/index.ts", got) - }) - - t.Run("writes file of arbitrary depth", func(t *testing.T) { - header := textproto.MIMEHeader{} - header.Set("Content-Disposition", `form-data; name="file"; filename="test-func/dir/subdir/file.ts"`) - got, err := getPartPath(header) - require.NoError(t, err) - assert.Equal(t, "test-func/dir/subdir/file.ts", got) - }) - - t.Run("returns empty when no filename provided", func(t *testing.T) { - header := textproto.MIMEHeader{} - header.Set("Content-Disposition", `form-data; name="file"`) - got, err := getPartPath(header) - require.NoError(t, err) - assert.Equal(t, "", got) - }) - - t.Run("returns error on invalid content disposition", func(t *testing.T) { - header := textproto.MIMEHeader{} - header.Set("Content-Disposition", `form-data; filename="unterminated`) - got, err := getPartPath(header) - require.ErrorContains(t, err, "failed to parse content disposition") - assert.Equal(t, "", got) - }) -} diff --git a/apps/cli-go/internal/gen/types/templates/prod-ca-2021.crt b/apps/cli-go/internal/gen/types/templates/prod-ca-2021.crt deleted file mode 100644 index 3d693669b2..0000000000 --- a/apps/cli-go/internal/gen/types/templates/prod-ca-2021.crt +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDxDCCAqygAwIBAgIUbLxMod62P2ktCiAkxnKJwtE9VPYwDQYJKoZIhvcNAQEL -BQAwazELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB0RlbHdhcmUxEzARBgNVBAcMCk5l -dyBDYXN0bGUxFTATBgNVBAoMDFN1cGFiYXNlIEluYzEeMBwGA1UEAwwVU3VwYWJh -c2UgUm9vdCAyMDIxIENBMB4XDTIxMDQyODEwNTY1M1oXDTMxMDQyNjEwNTY1M1ow -azELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB0RlbHdhcmUxEzARBgNVBAcMCk5ldyBD -YXN0bGUxFTATBgNVBAoMDFN1cGFiYXNlIEluYzEeMBwGA1UEAwwVU3VwYWJhc2Ug -Um9vdCAyMDIxIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQXW -QyHOB+qR2GJobCq/CBmQ40G0oDmCC3mzVnn8sv4XNeWtE5XcEL0uVih7Jo4Dkx1Q -DmGHBH1zDfgs2qXiLb6xpw/CKQPypZW1JssOTMIfQppNQ87K75Ya0p25Y3ePS2t2 -GtvHxNjUV6kjOZjEn2yWEcBdpOVCUYBVFBNMB4YBHkNRDa/+S4uywAoaTWnCJLUi -cvTlHmMw6xSQQn1UfRQHk50DMCEJ7Cy1RxrZJrkXXRP3LqQL2ijJ6F4yMfh+Gyb4 -O4XajoVj/+R4GwywKYrrS8PrSNtwxr5StlQO8zIQUSMiq26wM8mgELFlS/32Uclt -NaQ1xBRizkzpZct9DwIDAQABo2AwXjALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFKjX -uXY32CztkhImng4yJNUtaUYsMB8GA1UdIwQYMBaAFKjXuXY32CztkhImng4yJNUt -aUYsMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAB8spzNn+4VU -tVxbdMaX+39Z50sc7uATmus16jmmHjhIHz+l/9GlJ5KqAMOx26mPZgfzG7oneL2b -VW+WgYUkTT3XEPFWnTp2RJwQao8/tYPXWEJDc0WVQHrpmnWOFKU/d3MqBgBm5y+6 -jB81TU/RG2rVerPDWP+1MMcNNy0491CTL5XQZ7JfDJJ9CCmXSdtTl4uUQnSuv/Qx -Cea13BX2ZgJc7Au30vihLhub52De4P/4gonKsNHYdbWjg7OWKwNv/zitGDVDB9Y2 -CMTyZKG3XEu5Ghl1LEnI3QmEKsqaCLv12BnVjbkSeZsMnevJPs1Ye6TjjJwdik5P -o/bKiIz+Fq8= ------END CERTIFICATE----- diff --git a/apps/cli-go/internal/gen/types/templates/prod-ca-2025.crt b/apps/cli-go/internal/gen/types/templates/prod-ca-2025.crt deleted file mode 100644 index da35f1f84a..0000000000 --- a/apps/cli-go/internal/gen/types/templates/prod-ca-2025.crt +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDxzCCAq+gAwIBAgIUeX+gpfmsRW9asFkRvjyXjHxbfgcwDQYJKoZIhvcNAQEL -BQAwazELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB0RlbHdhcmUxEzARBgNVBAcMCk5l -dyBDYXN0bGUxFTATBgNVBAoMDFN1cGFiYXNlIEluYzEeMBwGA1UEAwwVU3VwYWJh -c2UgUm9vdCAyMDIxIENBMB4XDTI1MDkwMzA4MDEyNVoXDTM1MDkwMTA4MDEyNVow -azELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB0RlbHdhcmUxEzARBgNVBAcMCk5ldyBD -YXN0bGUxFTATBgNVBAoMDFN1cGFiYXNlIEluYzEeMBwGA1UEAwwVU3VwYWJhc2Ug -Um9vdCAyMDIxIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5Ve7 -i9UAmc7luUilELPtqzEk8nGHxg7nY0aCStr625M7+K4OPO6RUllTsHh47k1jWyzm -LXLlyYwCsYCjQp+3vn06H+F/HRUxBt6CK2B7bNng230exTunk0xFvfkX6YgHR7B3 -1B7L25Rq3PhuRFPV4hnGYRam2XBZC4UNPqoAgrhV0HOYzXXAVoTr2yaBTMnB331Z -RwOmINh7eqTCk/JRZbb6vfZOhZRAVAe9AoRLoG8aKwmeoLGwlu0UuFx6z3E+6bmA -fSNa8Lx02GEoCdPLw9IRKUFq/SgBpQUKm44H1fDwTjH2CMM0N4p0mL/6wXnNeHvt -C40MmKZ0RcVmHE5wBwIDAQABo2MwYTAdBgNVHQ4EFgQUjvEE541toZcwtXQlZlcB -YOBRTnowHwYDVR0jBBgwFoAUjvEE541toZcwtXQlZlcBYOBRTnowDwYDVR0TAQH/ -BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggEBACD5IcGP -XKvS9qg0CgEQPFqYavt5c7P+0xxFgiZe+xoG8fUw58yNeK2APtgGPRpxEOGfAlNx -z9HDt4gcyHEE00B3qAVDm49pqNxioFWzNqU2LGfM/HL1QmN6urR7hCOkVCJddvOc -FhFX4nZDuRfaBboDvS5HlK3Pzxddp9hvrJi2bemr8HLqYc3HzmVckgPGSLML6t+h -4LRCXSlQsDgQ1LZ4KHsl4cq7K51N6FOXQBLB5q4lMKhs0VUhCT8Pdsj12+84laCV -c22q6p2mdT9SaernCSRnWazXWisgpjv3H7Ex4S1DCYjJIwn3PUToGFv1r8YRN2/S -O19yVSxxCIf64Sg= ------END CERTIFICATE----- diff --git a/apps/cli-go/internal/gen/types/templates/staging-ca-2021.crt b/apps/cli-go/internal/gen/types/templates/staging-ca-2021.crt deleted file mode 100644 index 925f9f5af0..0000000000 --- a/apps/cli-go/internal/gen/types/templates/staging-ca-2021.crt +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID1DCCArygAwIBAgIUbYRdq/8/uNq8G9stMCdOFSBgA2MwDQYJKoZIhvcNAQEL -BQAwczELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB0RlbHdhcmUxEzARBgNVBAcMCk5l -dyBDYXN0bGUxFTATBgNVBAoMDFN1cGFiYXNlIEluYzEmMCQGA1UEAwwdU3VwYWJh -c2UgU3RhZ2luZyBSb290IDIwMjEgQ0EwHhcNMjEwNDI4MTAzNjEzWhcNMzEwNDI2 -MTAzNjEzWjBzMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHRGVsd2FyZTETMBEGA1UE -BwwKTmV3IENhc3RsZTEVMBMGA1UECgwMU3VwYWJhc2UgSW5jMSYwJAYDVQQDDB1T -dXBhYmFzZSBTdGFnaW5nIFJvb3QgMjAyMSBDQTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAN0AKRE8a56O8LaZxiOAcHFUFnwiKUvPoXPq26Ifw+Nv+7zg -N2V5WnMZbbw24q61Os60ZUn0XmbVtuIeJ+stPHsO7qxxuL+bmPR+qU5tkDrIOyEe -YD/2u8/q6ssVv42k4XcXbhM6RVz7CkCDY0TiBm1bMtRZso3xB6E9wAjxDf43XfV5 -PAGs3JI+Zo/vyqCDlN0hHOrB/aBl01JXqQWI84Gia5ooucq4SjA1CyawBcQ2IAvG -rXuy1BouY+xM3zRuNvtfFP6rb5Mta+jCYEMh1AZ8yP8sYUWAyhxX6k9EbOb009wQ -aZljbUCh/UglGWuBxdzePavx+zPjzWXB1NyVkpkCAwEAAaNgMF4wCwYDVR0PBAQD -AgEGMB0GA1UdDgQWBBQFx+PHLf27iIo/PMfIfGqXF7Zb+DAfBgNVHSMEGDAWgBQF -x+PHLf27iIo/PMfIfGqXF7Zb+DAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB -CwUAA4IBAQB/xIiz5dDqzGXjqYqXZYx4iSfSxsVayeOPDMfmaiCfSMJEUG4cUiwG -OvMPGztaUEYeip5SCvSKuAAjVkXyP7ahKR7t7lZ9mErVXyxSZoVLbOd578CuYiZk -OgT17UjPv66WMzEKEr8wGpomTYWWfEkuqt8ENdiM1Z4LNFahdKj36+jm6/a+9R8K -25VIL68DTaQpBxFWG6ixC1HRMHJ12lDhKsshIi099BVpkGibESlxPrQOdKKqBB/J -vIX+/Hb+mS4H5zYMeK2wX0onp+GBcD6X9L1UJuXMVd+BRan8RFidXL5s3++xXjQq -Nzbc6lnA69urKffvcT07YwMsY/OmHzVa ------END CERTIFICATE----- diff --git a/apps/cli-go/internal/gen/types/types.go b/apps/cli-go/internal/gen/types/types.go deleted file mode 100644 index 688ab6302f..0000000000 --- a/apps/cli-go/internal/gen/types/types.go +++ /dev/null @@ -1,253 +0,0 @@ -package types - -import ( - "context" - _ "embed" - "fmt" - "net/url" - "os" - "runtime" - "strings" - "time" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/api" -) - -const ( - LangTypescript = "typescript" - LangGo = "go" - LangSwift = "swift" - LangPython = "python" -) - -const ( - SwiftPublicAccessControl = "public" - SwiftInternalAccessControl = "internal" -) - -func Run(ctx context.Context, projectId string, dbConfig pgconn.Config, lang string, schemas []string, postgrestV9Compat bool, swiftAccessControl string, queryTimeout time.Duration, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - originalURL := utils.ToPostgresURL(dbConfig) - // Add default schemas if --schema flag is not specified - if len(schemas) == 0 { - schemas = utils.RemoveDuplicates(append([]string{"public"}, utils.Config.Api.Schemas...)) - } - included := strings.Join(schemas, ",") - - if projectId != "" { - if lang != LangTypescript { - return errors.Errorf("Unable to generate %s types for selected project. Try using --db-url flag instead.", lang) - } - resp, err := utils.GetSupabase().V1GenerateTypescriptTypesWithResponse(ctx, projectId, &api.V1GenerateTypescriptTypesParams{ - IncludedSchemas: &included, - }) - if err != nil { - return errors.Errorf("failed to get typescript types: %w", err) - } - - if resp.JSON200 == nil { - return errors.New("failed to retrieve generated types: " + string(resp.Body)) - } - - fmt.Print(resp.JSON200.Types) - return nil - } - - hostConfig := container.HostConfig{} - if utils.IsLocalDatabase(dbConfig) { - if err := utils.AssertSupabaseDbIsRunning(); err != nil { - return err - } - - if strings.Contains(utils.Config.Api.Image, "v9") { - postgrestV9Compat = true - } - - // Use custom network when connecting to local database - dbConfig.Host = utils.DbAliases[0] - dbConfig.Port = 5432 - } else { - hostConfig.NetworkMode = network.NetworkHost - } - // pg-meta does not set username as the default database, ie. postgres - if len(dbConfig.Database) == 0 { - dbConfig.Database = "postgres" - } - - fmt.Fprintln(os.Stderr, "Connecting to", dbConfig.Host, dbConfig.Port) - env := []string{ - "PG_META_DB_URL=" + utils.ToPostgresURL(dbConfig), - fmt.Sprintf("PG_CONN_TIMEOUT_SECS=%.0f", queryTimeout.Seconds()), - fmt.Sprintf("PG_QUERY_TIMEOUT_SECS=%.0f", queryTimeout.Seconds()), - "PG_META_GENERATE_TYPES=" + lang, - "PG_META_GENERATE_TYPES_INCLUDED_SCHEMAS=" + included, - "PG_META_GENERATE_TYPES_SWIFT_ACCESS_CONTROL=" + swiftAccessControl, - fmt.Sprintf("PG_META_GENERATE_TYPES_DETECT_ONE_TO_ONE_RELATIONSHIPS=%v", !postgrestV9Compat), - } - if ca, err := GetRootCA(ctx, originalURL, options...); err != nil { - return err - } else if len(ca) > 0 { - env = append(env, "PG_META_DB_SSL_ROOT_CERT="+ca) - } - - return utils.DockerRunOnceWithConfig( - ctx, - container.Config{ - Image: utils.Config.Studio.PgmetaImage, - Env: env, - Cmd: []string{"node", "dist/server/server.js"}, - }, - hostConfig, - network.NetworkingConfig{}, - "", - os.Stdout, - os.Stderr, - ) -} - -var ( - //go:embed templates/staging-ca-2021.crt - caStaging string - //go:embed templates/prod-ca-2021.crt - caProd string - //go:embed templates/prod-ca-2025.crt - caSnap string -) - -func GetRootCA(ctx context.Context, dbURL string, options ...func(*pgx.ConnConfig)) (string, error) { - debugf := func(string, ...any) {} - if IsSSLDebugEnabled() { - debugf = LogSSLDebugf - } - debugf("GetRootCA start db_url=%s", redactPostgresURL(dbURL)) - debugf("env SUPABASE_CA_SKIP_VERIFY=%q SUPABASE_SSL_DEBUG=%q PGSSLROOTCERT=%q SSL_CERT_FILE=%q SSL_CERT_DIR=%q", - os.Getenv("SUPABASE_CA_SKIP_VERIFY"), - os.Getenv("SUPABASE_SSL_DEBUG"), - os.Getenv("PGSSLROOTCERT"), - os.Getenv("SSL_CERT_FILE"), - os.Getenv("SSL_CERT_DIR"), - ) - debugf("runtime goos=%s goarch=%s go=%s", runtime.GOOS, runtime.GOARCH, runtime.Version()) - // node-postgres does not support sslmode=prefer - require, err := isRequireSSL(ctx, dbURL, options...) - debugf("GetRootCA probe_result require_ssl=%t err=%v", require, err) - if !require { - return "", err - } - // Merge all certs to support --db-url flag - ca := caStaging + caProd + caSnap - debugf("GetRootCA return ca_bundle_len=%d", len(ca)) - return ca, nil -} - -func isRequireSSL(ctx context.Context, dbUrl string, options ...func(*pgx.ConnConfig)) (bool, error) { - debugf := func(string, ...any) {} - if IsSSLDebugEnabled() { - debugf = LogSSLDebugf - } - // pgx v4's sslmode=require verifies the server certificate against system CAs, - // unlike libpq where require skips verification. When SUPABASE_CA_SKIP_VERIFY=true, - // skip verification for this probe only (detects whether the server speaks TLS). - // pgconn may still install VerifyPeerCertificate callback when sslrootcert is set, - // so we also clear custom verification callbacks on all TLS configs. - // Cert validation happens downstream in the migra/pgdelta Deno scripts using GetRootCA. - opts := append([]func(*pgx.ConnConfig){}, options...) - if os.Getenv("SUPABASE_CA_SKIP_VERIFY") == "true" { - fmt.Fprintln(os.Stderr, "WARNING: TLS certificate verification disabled for SSL probe (SUPABASE_CA_SKIP_VERIFY=true)") - opts = append(opts, func(cc *pgx.ConnConfig) { - // #nosec G402 -- Intentionally skipped for this TLS capability probe only. - // Downstream migra/pgdelta flows still validate certificates using GetRootCA. - if cc.TLSConfig != nil { - cc.TLSConfig.InsecureSkipVerify = true - cc.TLSConfig.VerifyPeerCertificate = nil - cc.TLSConfig.VerifyConnection = nil - } - for _, fc := range cc.Fallbacks { - if fc.TLSConfig == nil { - continue - } - fc.TLSConfig.InsecureSkipVerify = true - fc.TLSConfig.VerifyPeerCertificate = nil - fc.TLSConfig.VerifyConnection = nil - } - }) - } - debugf("isRequireSSL probe db_url=%s skip_verify=%t", redactPostgresURL(dbUrl), os.Getenv("SUPABASE_CA_SKIP_VERIFY") == "true") - if IsSSLDebugEnabled() { - opts = append(opts, logTLSConfigState("isRequireSSL", dbUrl)) - } - conn, err := utils.ConnectByUrl(ctx, dbUrl+"&sslmode=require", opts...) - if err != nil { - debugf("isRequireSSL probe_error err=%v", err) - if strings.HasSuffix(err.Error(), "(server refused TLS connection)") { - debugf("isRequireSSL result require_ssl=false reason=server_refused_tls") - return false, nil - } - return false, err - } - debugf("isRequireSSL result require_ssl=true") - return true, conn.Close(ctx) -} - -func IsSSLDebugEnabled() bool { - return strings.EqualFold(os.Getenv("SUPABASE_SSL_DEBUG"), "true") -} - -func LogSSLDebugf(format string, args ...any) { - fmt.Fprintf(os.Stderr, "[ssl-debug] "+format+"\n", args...) -} - -func redactPostgresURL(raw string) string { - parsed, err := url.Parse(raw) - if err != nil { - return "" - } - if parsed.User != nil { - username := parsed.User.Username() - if username == "" { - parsed.User = url.UserPassword("redacted", "xxxxx") - } else { - parsed.User = url.UserPassword(username, "xxxxx") - } - } - return parsed.String() -} - -func logTLSConfigState(scope, dbUrl string) func(*pgx.ConnConfig) { - return func(cc *pgx.ConnConfig) { - if cc.TLSConfig == nil { - LogSSLDebugf("%s tls_config=nil db_url=%s fallbacks=%d", scope, redactPostgresURL(dbUrl), len(cc.Fallbacks)) - return - } - LogSSLDebugf("%s tls_config skip_verify=%t verify_peer_cb=%t verify_conn_cb=%t root_cas=%t server_name=%q fallbacks=%d", - scope, - cc.TLSConfig.InsecureSkipVerify, - cc.TLSConfig.VerifyPeerCertificate != nil, - cc.TLSConfig.VerifyConnection != nil, - cc.TLSConfig.RootCAs != nil, - cc.TLSConfig.ServerName, - len(cc.Fallbacks), - ) - for i, fc := range cc.Fallbacks { - if fc == nil || fc.TLSConfig == nil { - LogSSLDebugf("%s fallback[%d] tls_config=nil", scope, i) - continue - } - LogSSLDebugf("%s fallback[%d] skip_verify=%t verify_peer_cb=%t verify_conn_cb=%t root_cas=%t server_name=%q", - scope, - i, - fc.TLSConfig.InsecureSkipVerify, - fc.TLSConfig.VerifyPeerCertificate != nil, - fc.TLSConfig.VerifyConnection != nil, - fc.TLSConfig.RootCAs != nil, - fc.TLSConfig.ServerName, - ) - } - } -} diff --git a/apps/cli-go/internal/gen/types/types_test.go b/apps/cli-go/internal/gen/types/types_test.go deleted file mode 100644 index 915f85ad84..0000000000 --- a/apps/cli-go/internal/gen/types/types_test.go +++ /dev/null @@ -1,192 +0,0 @@ -package types - -import ( - "context" - "errors" - "net/http" - "testing" - "time" - - "github.com/docker/docker/api/types/container" - "github.com/h2non/gock" - "github.com/jackc/pgconn" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/pgtest" -) - -func TestGenLocalCommand(t *testing.T) { - utils.DbId = "test-db" - utils.Config.Hostname = "localhost" - utils.Config.Db.Port = 5432 - - dbConfig := pgconn.Config{ - Host: utils.Config.Hostname, - Port: utils.Config.Db.Port, - User: "admin", - Password: "password", - } - - t.Run("generates typescript types", func(t *testing.T) { - const containerId = "test-pgmeta" - imageUrl := utils.GetRegistryImageUrl(utils.Config.Studio.PgmetaImage) - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId). - Reply(http.StatusOK). - JSON(container.InspectResponse{}) - apitest.MockDockerStart(utils.Docker, imageUrl, containerId) - require.NoError(t, apitest.MockDockerLogs(utils.Docker, containerId, "hello world\n")) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - // Run test - assert.NoError(t, Run(context.Background(), "", dbConfig, LangTypescript, []string{}, true, "", time.Second, fsys, conn.Intercept)) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error when db is not started", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId). - Reply(http.StatusServiceUnavailable) - // Run test - assert.Error(t, Run(context.Background(), "", dbConfig, LangTypescript, []string{}, true, "", time.Second, fsys)) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on image fetch failure", func(t *testing.T) { - utils.Config.Api.Image = "v9" - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId). - Reply(http.StatusOK). - JSON(container.InspectResponse{}) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/images"). - Reply(http.StatusServiceUnavailable) - // Run test - assert.Error(t, Run(context.Background(), "", dbConfig, LangTypescript, []string{}, true, "", time.Second, fsys)) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("generates swift types", func(t *testing.T) { - const containerId = "test-pgmeta" - imageUrl := utils.GetRegistryImageUrl(utils.Config.Studio.PgmetaImage) - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId). - Reply(http.StatusOK). - JSON(container.InspectResponse{}) - apitest.MockDockerStart(utils.Docker, imageUrl, containerId) - require.NoError(t, apitest.MockDockerLogs(utils.Docker, containerId, "hello world\n")) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - // Run test - assert.NoError(t, Run(context.Background(), "", dbConfig, LangSwift, []string{}, true, SwiftInternalAccessControl, time.Second, fsys, conn.Intercept)) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestGenLinkedCommand(t *testing.T) { - // Setup valid projectId id - projectId := apitest.RandomProjectRef() - // Setup valid access token - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - - t.Run("generates typescript types", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Flush pending mocks after test execution - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + projectId + "/types/typescript"). - Reply(200). - JSON(api.TypescriptResponseOutput{Types: ""}) - // Run test - assert.NoError(t, Run(context.Background(), projectId, pgconn.Config{}, LangTypescript, []string{}, true, "", time.Second, fsys)) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on network failure", func(t *testing.T) { - errNetwork := errors.New("network error") - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Flush pending mocks after test execution - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + projectId + "/types/typescript"). - ReplyError(errNetwork) - // Run test - err := Run(context.Background(), projectId, pgconn.Config{}, LangTypescript, []string{}, true, "", time.Second, fsys) - // Validate api - assert.ErrorIs(t, err, errNetwork) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on service unavailable", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Flush pending mocks after test execution - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + projectId + "/types/typescript"). - Reply(http.StatusServiceUnavailable) - // Run test - assert.Error(t, Run(context.Background(), projectId, pgconn.Config{}, LangTypescript, []string{}, true, "", time.Second, fsys)) - }) -} - -func TestGenRemoteCommand(t *testing.T) { - dbConfig := pgconn.Config{ - Host: "db.supabase.co", - Port: 5432, - User: "admin", - Password: "password", - Database: "postgres", - } - - t.Run("generates type from remote db", func(t *testing.T) { - const containerId = "test-pgmeta" - imageUrl := utils.GetRegistryImageUrl(utils.Config.Studio.PgmetaImage) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - apitest.MockDockerStart(utils.Docker, imageUrl, containerId) - require.NoError(t, apitest.MockDockerLogs(utils.Docker, containerId, "hello world\n")) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - // Run test - assert.NoError(t, Run(context.Background(), "", dbConfig, LangTypescript, []string{"public"}, true, "", time.Second, afero.NewMemMapFs(), conn.Intercept)) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} diff --git a/apps/cli-go/internal/migration/apply/apply.go b/apps/cli-go/internal/migration/apply/apply.go deleted file mode 100644 index 18bd330ea6..0000000000 --- a/apps/cli-go/internal/migration/apply/apply.go +++ /dev/null @@ -1,68 +0,0 @@ -package apply - -import ( - "context" - "fmt" - "io/fs" - - "github.com/jackc/pgx/v4" - "github.com/spf13/afero" - "github.com/spf13/viper" - "github.com/supabase/cli/internal/migration/list" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/migration" -) - -func MigrateAndSeed(ctx context.Context, version string, conn *pgx.Conn, fsys afero.Fs) error { - // If pg-delta is enabled, the source of truth for migrations is always the migrations only or the declarative files. - // Declarative files must be used with `db schema declarative sync` commands always. - if viper.GetBool("EXPERIMENTAL") && len(version) == 0 && !utils.IsPgDeltaEnabled() { - if err := applySchemaFiles(ctx, conn, afero.NewIOFS(fsys)); err != nil { - return err - } - } else if err := applyMigrationFiles(ctx, version, conn, fsys); err != nil { - return err - } - return applySeedFiles(ctx, conn, fsys) -} - -func applyMigrationFiles(ctx context.Context, version string, conn *pgx.Conn, fsys afero.Fs) error { - if !utils.Config.Db.Migrations.Enabled { - return nil - } - migrations, err := list.LoadPartialMigrations(version, fsys) - if err != nil { - return err - } - return migration.ApplyMigrations(ctx, migrations, conn, afero.NewIOFS(fsys)) -} - -func applySeedFiles(ctx context.Context, conn *pgx.Conn, fsys afero.Fs) error { - if !utils.Config.Db.Seed.Enabled { - return nil - } - seeds, err := migration.GetPendingSeeds(ctx, utils.Config.Db.Seed.SqlPaths, conn, afero.NewIOFS(fsys)) - if err != nil { - return err - } - return migration.SeedData(ctx, seeds, conn, afero.NewIOFS(fsys)) -} - -func applySchemaFiles(ctx context.Context, conn *pgx.Conn, fsys fs.FS) error { - declared, err := utils.Config.Db.Migrations.SchemaPaths.SQLFiles(fsys) - if len(declared) == 0 { - return err - } - for _, fp := range declared { - schema, err := migration.NewMigrationFromFile(fp, fsys) - if err != nil { - return err - } - schema.Version = "" - if err := schema.ExecBatch(ctx, conn); err != nil { - utils.CmdSuggestion = fmt.Sprintf("See schema file: %s", utils.Bold(fp)) - return err - } - } - return nil -} diff --git a/apps/cli-go/internal/migration/apply/apply_test.go b/apps/cli-go/internal/migration/apply/apply_test.go deleted file mode 100644 index f45891cfb4..0000000000 --- a/apps/cli-go/internal/migration/apply/apply_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package apply - -import ( - "context" - "os" - "path/filepath" - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/fstest" - "github.com/supabase/cli/internal/testing/helper" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/migration" - "github.com/supabase/cli/pkg/pgtest" -) - -func TestMigrateDatabase(t *testing.T) { - t.Run("applies local migration", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := filepath.Join(utils.MigrationsDir, "0_test.sql") - sql := "create schema public" - require.NoError(t, afero.WriteFile(fsys, path, []byte(sql), 0644)) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - helper.MockMigrationHistory(conn). - Query("RESET ALL"). - Reply("RESET"). - Query(sql). - Reply("CREATE SCHEMA"). - Query(migration.INSERT_MIGRATION_VERSION, "0", "test", []string{sql}). - Reply("INSERT 0 1") - // Run test - err := MigrateAndSeed(context.Background(), "", conn.MockClient(t), fsys) - // Check error - assert.NoError(t, err) - }) - - t.Run("skip seeding when seed config is disabled", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := filepath.Join(utils.MigrationsDir, "0_test.sql") - sql := "create schema public" - require.NoError(t, afero.WriteFile(fsys, path, []byte(sql), 0644)) - seedPath := filepath.Join(utils.SupabaseDirPath, "seed.sql") - // This will raise an error when seeding - require.NoError(t, afero.WriteFile(fsys, seedPath, []byte("INSERT INTO test_table;"), 0644)) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - helper.MockMigrationHistory(conn). - Query("RESET ALL"). - Reply("RESET"). - Query(sql). - Reply("CREATE SCHEMA"). - Query(migration.INSERT_MIGRATION_VERSION, "0", "test", []string{sql}). - Reply("INSERT 0 1") - utils.Config.Db.Seed.Enabled = false - // Run test - err := MigrateAndSeed(context.Background(), "", conn.MockClient(t), fsys) - // No error should be returned since seeding is skipped - assert.NoError(t, err) - }) - - t.Run("ignores empty local directory", func(t *testing.T) { - assert.NoError(t, MigrateAndSeed(context.Background(), "", nil, afero.NewMemMapFs())) - }) - - t.Run("throws error on open failure", func(t *testing.T) { - // Setup in-memory fs - fsys := &fstest.OpenErrorFs{DenyPath: utils.MigrationsDir} - // Run test - err := MigrateAndSeed(context.Background(), "", nil, fsys) - // Check error - assert.ErrorIs(t, err, os.ErrPermission) - }) -} diff --git a/apps/cli-go/internal/migration/down/down.go b/apps/cli-go/internal/migration/down/down.go deleted file mode 100644 index 35a9fa7e88..0000000000 --- a/apps/cli-go/internal/migration/down/down.go +++ /dev/null @@ -1,64 +0,0 @@ -package down - -import ( - "context" - "fmt" - "os" - - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/migration/apply" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/migration" - "github.com/supabase/cli/pkg/vault" -) - -func Run(ctx context.Context, last uint, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - if last == 0 { - return errors.Errorf("--last must be greater than 0") - } - conn, err := utils.ConnectByConfig(ctx, config, options...) - if err != nil { - return err - } - defer conn.Close(context.Background()) - remoteMigrations, err := migration.ListRemoteMigrations(ctx, conn) - if err != nil { - return err - } - total := uint(len(remoteMigrations)) - if total <= last { - utils.CmdSuggestion = fmt.Sprintf("Try %s if you want to revert all migrations.", utils.Aqua("supabase db reset")) - return errors.Errorf("--last must be smaller than total applied migrations: %d", total) - } - msg := confirmResetAll(remoteMigrations[total-last:]) - if shouldReset, err := utils.NewConsole().PromptYesNo(ctx, msg, false); err != nil { - return err - } else if !shouldReset { - return errors.New(context.Canceled) - } - version := remoteMigrations[total-last-1] - fmt.Fprintln(os.Stderr, "Resetting database to version:", version) - return ResetAll(ctx, version, conn, fsys) -} - -func ResetAll(ctx context.Context, version string, conn *pgx.Conn, fsys afero.Fs) error { - if err := migration.DropUserSchemas(ctx, conn); err != nil { - return err - } - if err := vault.UpsertVaultSecrets(ctx, utils.Config.Db.Vault, conn); err != nil { - return err - } - return apply.MigrateAndSeed(ctx, version, conn, fsys) -} - -func confirmResetAll(pending []string) string { - msg := fmt.Sprintln("Do you want to revert the following migrations?") - for _, v := range pending { - msg += fmt.Sprintf(" • %s\n", utils.Bold(v)) - } - msg += fmt.Sprintf("%s you will lose all data in this database.", utils.Yellow("WARNING:")) - return msg -} diff --git a/apps/cli-go/internal/migration/down/down_test.go b/apps/cli-go/internal/migration/down/down_test.go deleted file mode 100644 index 89d9ff66d4..0000000000 --- a/apps/cli-go/internal/migration/down/down_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package down - -import ( - "context" - "path/filepath" - "testing" - - "github.com/jackc/pgconn" - "github.com/jackc/pgerrcode" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/helper" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/migration" - "github.com/supabase/cli/pkg/pgtest" -) - -var dbConfig = pgconn.Config{ - Host: "127.0.0.1", - Port: 5432, - User: "admin", - Password: "password", - Database: "postgres", -} - -func TestMigrationsDown(t *testing.T) { - t.Run("resets last n migrations", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - files := []string{ - filepath.Join(utils.MigrationsDir, "20221201000000_test.sql"), - filepath.Join(utils.MigrationsDir, "20221201000001_test.sql"), - filepath.Join(utils.MigrationsDir, "20221201000002_test.sql"), - } - for _, path := range files { - require.NoError(t, afero.WriteFile(fsys, path, []byte(""), 0644)) - } - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.LIST_MIGRATION_VERSION). - Reply("SELECT 2", []any{"20221201000000"}, []any{"20221201000001"}) - // Run test - err := Run(context.Background(), 1, dbConfig, fsys, conn.Intercept) - // Check error - assert.ErrorIs(t, err, context.Canceled) - }) - - t.Run("throws error on out of range", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.LIST_MIGRATION_VERSION). - Reply("SELECT 2", []any{"20221201000000"}, []any{"20221201000001"}) - // Run test - err := Run(context.Background(), 2, dbConfig, fsys, conn.Intercept) - // Check error - assert.ErrorContains(t, err, "--last must be smaller than total applied migrations: 2") - }) - - t.Run("throws error on insufficient privilege", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.LIST_MIGRATION_VERSION). - ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for relation supabase_migrations") - // Run test - err := Run(context.Background(), 1, dbConfig, fsys, conn.Intercept) - // Check error - assert.ErrorContains(t, err, "ERROR: permission denied for relation supabase_migrations (SQLSTATE 42501)") - }) -} - -func TestResetRemote(t *testing.T) { - t.Run("resets remote database", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := filepath.Join(utils.MigrationsDir, "0_schema.sql") - require.NoError(t, afero.WriteFile(fsys, path, nil, 0644)) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.DropObjects). - Reply("INSERT 0") - helper.MockMigrationHistory(conn). - Query("RESET ALL"). - Reply("RESET"). - Query(migration.INSERT_MIGRATION_VERSION, "0", "schema", nil). - Reply("INSERT 0 1") - // Run test - err := ResetAll(context.Background(), "", conn.MockClient(t), fsys) - // Check error - assert.NoError(t, err) - }) - - t.Run("resets remote database with seed config disabled", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := filepath.Join(utils.MigrationsDir, "0_schema.sql") - require.NoError(t, afero.WriteFile(fsys, path, nil, 0644)) - seedPath := filepath.Join(utils.SupabaseDirPath, "seed.sql") - // Will raise an error when seeding - require.NoError(t, afero.WriteFile(fsys, seedPath, []byte("INSERT INTO test_table;"), 0644)) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.DropObjects). - Reply("INSERT 0") - helper.MockMigrationHistory(conn). - Query("RESET ALL"). - Reply("RESET"). - Query(migration.INSERT_MIGRATION_VERSION, "0", "schema", nil). - Reply("INSERT 0 1") - utils.Config.Db.Seed.Enabled = false - // Run test - err := ResetAll(context.Background(), "", conn.MockClient(t), fsys) - // No error should be raised since we're skipping the seed - assert.NoError(t, err) - }) - - t.Run("throws error on drop schema failure", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.DropObjects). - ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for relation supabase_migrations") - // Run test - err := ResetAll(context.Background(), "", conn.MockClient(t), fsys) - // Check error - assert.ErrorContains(t, err, "ERROR: permission denied for relation supabase_migrations (SQLSTATE 42501)") - }) -} diff --git a/apps/cli-go/internal/migration/list/list.go b/apps/cli-go/internal/migration/list/list.go deleted file mode 100644 index ad4a062c06..0000000000 --- a/apps/cli-go/internal/migration/list/list.go +++ /dev/null @@ -1,87 +0,0 @@ -package list - -import ( - "context" - "fmt" - "math" - "strconv" - - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/migration" -) - -func Run(ctx context.Context, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - remoteVersions, err := loadRemoteVersions(ctx, config, options...) - if err != nil { - return err - } - localVersions, err := LoadLocalVersions(fsys) - if err != nil { - return err - } - table := makeTable(remoteVersions, localVersions) - return utils.RenderTable(table) -} - -func loadRemoteVersions(ctx context.Context, config pgconn.Config, options ...func(*pgx.ConnConfig)) ([]string, error) { - conn, err := utils.ConnectByConfig(ctx, config, options...) - if err != nil { - return nil, err - } - defer conn.Close(context.Background()) - return migration.ListRemoteMigrations(ctx, conn) -} - -func makeTable(remoteMigrations, localMigrations []string) string { - var err error - table := "|Local|Remote|Time (UTC)|\n|-|-|-|\n" - for i, j := 0, 0; i < len(remoteMigrations) || j < len(localMigrations); { - remoteTimestamp := math.MaxInt - if i < len(remoteMigrations) { - if remoteTimestamp, err = strconv.Atoi(remoteMigrations[i]); err != nil { - i++ - continue - } - } - localTimestamp := math.MaxInt - if j < len(localMigrations) { - if localTimestamp, err = strconv.Atoi(localMigrations[j]); err != nil { - j++ - continue - } - } - // Top to bottom chronological order - if localTimestamp < remoteTimestamp { - table += fmt.Sprintf("|`%s`|` `|`%s`|\n", localMigrations[j], utils.FormatTimestampVersion(localMigrations[j])) - j++ - } else if remoteTimestamp < localTimestamp { - table += fmt.Sprintf("|` `|`%s`|`%s`|\n", remoteMigrations[i], utils.FormatTimestampVersion(remoteMigrations[i])) - i++ - } else { - table += fmt.Sprintf("|`%s`|`%s`|`%s`|\n", localMigrations[j], remoteMigrations[i], utils.FormatTimestampVersion(remoteMigrations[i])) - i++ - j++ - } - } - return table -} - -func LoadLocalVersions(fsys afero.Fs) ([]string, error) { - var versions []string - filter := func(v string) bool { - versions = append(versions, v) - return true - } - _, err := migration.ListLocalMigrations(utils.MigrationsDir, afero.NewIOFS(fsys), filter) - return versions, err -} - -func LoadPartialMigrations(version string, fsys afero.Fs) ([]string, error) { - filter := func(v string) bool { - return version == "" || v <= version - } - return migration.ListLocalMigrations(utils.MigrationsDir, afero.NewIOFS(fsys), filter) -} diff --git a/apps/cli-go/internal/migration/list/list_test.go b/apps/cli-go/internal/migration/list/list_test.go deleted file mode 100644 index 9744174df9..0000000000 --- a/apps/cli-go/internal/migration/list/list_test.go +++ /dev/null @@ -1,193 +0,0 @@ -package list - -import ( - "context" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/jackc/pgconn" - "github.com/jackc/pgerrcode" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/fstest" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/migration" - "github.com/supabase/cli/pkg/pgtest" -) - -var dbConfig = pgconn.Config{ - Host: "127.0.0.1", - Port: 5432, - User: "admin", - Password: "password", - Database: "postgres", -} - -func TestMigrationList(t *testing.T) { - t.Run("lists remote migrations", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.LIST_MIGRATION_VERSION). - Reply("SELECT 0") - // Run test - err := Run(context.Background(), dbConfig, fsys, conn.Intercept) - // Check error - assert.NoError(t, err) - }) - - t.Run("throws error on remote failure", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - err := Run(context.Background(), pgconn.Config{}, fsys) - // Check error - assert.ErrorContains(t, err, "invalid port (outside range)") - }) - - t.Run("throws error on open failure", func(t *testing.T) { - // Setup in-memory fs - fsys := &fstest.OpenErrorFs{DenyPath: utils.MigrationsDir} - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.LIST_MIGRATION_VERSION). - Reply("SELECT 0") - // Run test - err := Run(context.Background(), dbConfig, fsys, conn.Intercept) - // Check error - assert.ErrorIs(t, err, os.ErrPermission) - }) -} - -func TestRemoteMigrations(t *testing.T) { - t.Run("loads migration versions", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.LIST_MIGRATION_VERSION). - Reply("SELECT 1", []any{"20220727064247"}) - // Run test - versions, err := loadRemoteVersions(context.Background(), dbConfig, conn.Intercept) - // Check error - assert.NoError(t, err) - assert.ElementsMatch(t, []string{"20220727064247"}, versions) - }) - - t.Run("throws error on connect failure", func(t *testing.T) { - // Run test - _, err := loadRemoteVersions(context.Background(), pgconn.Config{}) - // Check error - assert.ErrorContains(t, err, "invalid port (outside range)") - }) - - t.Run("loads empty migrations on missing table", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.LIST_MIGRATION_VERSION). - ReplyError(pgerrcode.UndefinedTable, "relation \"supabase_migrations.schema_migrations\" does not exist") - // Run test - versions, err := loadRemoteVersions(context.Background(), dbConfig, conn.Intercept) - // Check error - assert.NoError(t, err) - assert.Empty(t, versions) - }) - - t.Run("throws error on invalid row", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.LIST_MIGRATION_VERSION). - Reply("SELECT 1", []any{}) - // Run test - _, err := loadRemoteVersions(context.Background(), dbConfig, conn.Intercept) - // Check error - assert.ErrorContains(t, err, "number of field descriptions must equal number of destinations, got 0 and 1") - }) -} - -func TestLocalMigrations(t *testing.T) { - t.Run("loads migration versions", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := filepath.Join(utils.MigrationsDir, "20220727064246_test.sql") - require.NoError(t, afero.WriteFile(fsys, path, []byte{}, 0644)) - path = filepath.Join(utils.MigrationsDir, "20220727064248_test.sql") - require.NoError(t, afero.WriteFile(fsys, path, []byte{}, 0644)) - // Run test - versions, err := LoadLocalVersions(fsys) - // Check error - assert.NoError(t, err) - assert.ElementsMatch(t, []string{"20220727064246", "20220727064248"}, versions) - }) - - t.Run("ignores outdated and invalid files", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := filepath.Join(utils.MigrationsDir, "20211208000000_init.sql") - require.NoError(t, afero.WriteFile(fsys, path, []byte{}, 0644)) - path = filepath.Join(utils.MigrationsDir, "20211208000001_invalid.ts") - require.NoError(t, afero.WriteFile(fsys, path, []byte{}, 0644)) - // Run test - versions, err := LoadLocalVersions(fsys) - // Check error - assert.NoError(t, err) - assert.Empty(t, versions) - }) - - t.Run("throws error on open failure", func(t *testing.T) { - // Setup in-memory fs - fsys := &fstest.OpenErrorFs{DenyPath: utils.MigrationsDir} - // Run test - _, err := LoadLocalVersions(fsys) - // Check error - assert.ErrorIs(t, err, os.ErrPermission) - }) -} - -func TestMakeTable(t *testing.T) { - t.Run("tabulate version", func(t *testing.T) { - // Run test - table := makeTable([]string{"0", "2"}, []string{"0", "1"}) - // Check error - lines := strings.Split(strings.TrimSpace(table), "\n") - assert.ElementsMatch(t, []string{ - "|Local|Remote|Time (UTC)|", - "|-|-|-|", - "|`0`|`0`|`0`|", - "|`1`|` `|`1`|", - "|` `|`2`|`2`|", - }, lines) - }) - - t.Run("tabulate timestamp", func(t *testing.T) { - // Run test - table := makeTable([]string{"20220727064246", "20220727064248"}, []string{"20220727064246", "20220727064247"}) - // Check error - lines := strings.Split(strings.TrimSpace(table), "\n") - assert.ElementsMatch(t, []string{ - "|Local|Remote|Time (UTC)|", - "|-|-|-|", - "|`20220727064246`|`20220727064246`|`2022-07-27 06:42:46`|", - "|`20220727064247`|` `|`2022-07-27 06:42:47`|", - "|` `|`20220727064248`|`2022-07-27 06:42:48`|", - }, lines) - }) - - t.Run("ignores string values", func(t *testing.T) { - // Run test - table := makeTable([]string{"a", "c"}, []string{"a", "b"}) - // Check error - lines := strings.Split(strings.TrimSpace(table), "\n") - assert.ElementsMatch(t, []string{ - "|Local|Remote|Time (UTC)|", - "|-|-|-|", - }, lines) - }) -} diff --git a/apps/cli-go/internal/migration/new/new.go b/apps/cli-go/internal/migration/new/new.go deleted file mode 100644 index be232b5913..0000000000 --- a/apps/cli-go/internal/migration/new/new.go +++ /dev/null @@ -1,46 +0,0 @@ -package new - -import ( - "fmt" - "io" - "os" - "path/filepath" - - "github.com/go-errors/errors" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils" -) - -func Run(migrationName string, stdin afero.File, fsys afero.Fs) error { - path := GetMigrationPath(utils.GetCurrentTimestamp(), migrationName) - if err := utils.MkdirIfNotExistFS(fsys, filepath.Dir(path)); err != nil { - return err - } - f, err := fsys.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) - if err != nil { - return errors.Errorf("failed to open migration file: %w", err) - } - defer func() { - fmt.Println("Created new migration at " + utils.Bold(path)) - // File descriptor will always be closed when process quits - _ = f.Close() - }() - return CopyStdinIfExists(stdin, f) -} - -func GetMigrationPath(timestamp, name string) string { - fullName := fmt.Sprintf("%s_%s.sql", timestamp, name) - return filepath.Join(utils.MigrationsDir, fullName) -} - -func CopyStdinIfExists(stdin afero.File, dst io.Writer) error { - if fi, err := stdin.Stat(); err != nil { - return errors.Errorf("failed to initialise stdin: %w", err) - } else if (fi.Mode() & os.ModeCharDevice) == 0 { - // Ref: https://stackoverflow.com/a/26567513 - if _, err := io.Copy(dst, stdin); err != nil { - return errors.Errorf("failed to copy from stdin: %w", err) - } - } - return nil -} diff --git a/apps/cli-go/internal/migration/new/new_test.go b/apps/cli-go/internal/migration/new/new_test.go deleted file mode 100644 index 39e2fe1156..0000000000 --- a/apps/cli-go/internal/migration/new/new_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package new - -import ( - "os" - "path/filepath" - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/utils" -) - -func TestNewCommand(t *testing.T) { - t.Run("creates new migration file", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup empty stdin - stdin, err := fsys.Create("/dev/stdin") - require.NoError(t, err) - // Run test - assert.NoError(t, Run("test_migrate", stdin, fsys)) - // Validate output - files, err := afero.ReadDir(fsys, utils.MigrationsDir) - assert.NoError(t, err) - assert.Equal(t, 1, len(files)) - assert.Regexp(t, `([0-9]{14})_test_migrate\.sql`, files[0].Name()) - }) - - t.Run("streams content from pipe", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup stdin - r, w, err := os.Pipe() - require.NoError(t, err) - script := "create table pet;\ndrop table pet;\n" - _, err = w.WriteString(script) - require.NoError(t, err) - require.NoError(t, w.Close()) - // Run test - assert.NoError(t, Run("test_migrate", r, fsys)) - // Validate output - files, err := afero.ReadDir(fsys, utils.MigrationsDir) - assert.NoError(t, err) - assert.Equal(t, 1, len(files)) - path := filepath.Join(utils.MigrationsDir, files[0].Name()) - contents, err := afero.ReadFile(fsys, path) - assert.NoError(t, err) - assert.Equal(t, []byte(script), contents) - }) - - t.Run("throws error on failure to create directory", func(t *testing.T) { - // Setup read-only fs - fsys := afero.NewMemMapFs() - // Setup empty stdin - stdin, err := fsys.Create("/dev/stdin") - require.NoError(t, err) - // Run test - assert.Error(t, Run("test_migrate", stdin, afero.NewReadOnlyFs(fsys))) - }) - - t.Run("throws error on closed pipe", func(t *testing.T) { - // Setup read-only fs - fsys := afero.NewMemMapFs() - // Setup empty stdin - r, _, err := os.Pipe() - require.NoError(t, err) - require.NoError(t, r.Close()) - // Run test - assert.Error(t, Run("test_migrate", r, fsys)) - }) -} diff --git a/apps/cli-go/internal/migration/repair/repair.go b/apps/cli-go/internal/migration/repair/repair.go deleted file mode 100644 index a7dfee274e..0000000000 --- a/apps/cli-go/internal/migration/repair/repair.go +++ /dev/null @@ -1,108 +0,0 @@ -package repair - -import ( - "context" - "fmt" - "os" - "path/filepath" - "strconv" - - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/migration/list" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/migration" -) - -const ( - Applied = "applied" - Reverted = "reverted" -) - -var ErrInvalidVersion = errors.New("invalid version number") - -func Run(ctx context.Context, config pgconn.Config, version []string, status string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - for _, v := range version { - if _, err := strconv.Atoi(v); err != nil { - return errors.Errorf("failed to parse %s: %w", v, ErrInvalidVersion) - } - } - repairAll := len(version) == 0 - if repairAll { - msg := "Do you want to repair the entire migration history table to match local migration files?" - if shouldRepair, err := utils.NewConsole().PromptYesNo(ctx, msg, false); err != nil { - return err - } else if !shouldRepair { - return errors.New(context.Canceled) - } - local, err := list.LoadLocalVersions(fsys) - if err != nil { - return err - } - version = append(version, local...) - } - conn, err := utils.ConnectByConfig(ctx, config, options...) - if err != nil { - return err - } - defer conn.Close(context.Background()) - // Update migration history - if err = UpdateMigrationTable(ctx, conn, version, status, repairAll, fsys); err == nil { - utils.CmdSuggestion = fmt.Sprintf("Run %s to show the updated migration history.", utils.Aqua("supabase migration list")) - } - return err -} - -func UpdateMigrationTable(ctx context.Context, conn *pgx.Conn, version []string, status string, repairAll bool, fsys afero.Fs) error { - if err := migration.CreateMigrationTable(ctx, conn); err != nil { - return err - } - // Data statements don't mutate schemas, safe to use statement cache - batch := &pgx.Batch{} - if repairAll { - batch.Queue(migration.TRUNCATE_VERSION_TABLE) - } - switch status { - case Applied: - for _, v := range version { - f, err := NewMigrationFromVersion(v, fsys) - if err != nil { - return err - } - batch.Queue(migration.UPSERT_MIGRATION_VERSION, f.Version, f.Name, f.Statements) - } - case Reverted: - if !repairAll { - batch.Queue(migration.DELETE_MIGRATION_VERSION, version) - } - } - if err := conn.SendBatch(ctx, batch).Close(); err != nil { - return errors.Errorf("failed to update migration table: %w", err) - } - if !repairAll { - fmt.Fprintf(os.Stderr, "Repaired migration history: %v => %s\n", version, status) - } - return nil -} - -func GetMigrationFile(version string, fsys afero.Fs) (string, error) { - path := filepath.Join(utils.MigrationsDir, version+"_*.sql") - matches, err := afero.Glob(fsys, path) - if err != nil { - return "", errors.Errorf("failed to glob migration files: %w", err) - } - if len(matches) == 0 { - return "", errors.Errorf("glob %s: %w", path, os.ErrNotExist) - } - return matches[0], nil -} - -func NewMigrationFromVersion(version string, fsys afero.Fs) (*migration.MigrationFile, error) { - name, err := GetMigrationFile(version, fsys) - if err != nil { - return nil, err - } - return migration.NewMigrationFromFile(name, afero.NewIOFS(fsys)) -} diff --git a/apps/cli-go/internal/migration/repair/repair_test.go b/apps/cli-go/internal/migration/repair/repair_test.go deleted file mode 100644 index 4c0bdb7cf3..0000000000 --- a/apps/cli-go/internal/migration/repair/repair_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package repair - -import ( - "context" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/jackc/pgconn" - "github.com/jackc/pgerrcode" - "github.com/jackc/pgx/v4" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/fstest" - "github.com/supabase/cli/internal/testing/helper" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/migration" - "github.com/supabase/cli/pkg/pgtest" -) - -var dbConfig = pgconn.Config{ - Host: "db.supabase.com", - Port: 5432, - User: "admin", - Password: "password", - Database: "postgres", -} - -func TestRepairCommand(t *testing.T) { - t.Run("applies new version", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := filepath.Join(utils.MigrationsDir, "0_test.sql") - require.NoError(t, afero.WriteFile(fsys, path, []byte("select 1"), 0644)) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - helper.MockMigrationHistory(conn). - Query(migration.UPSERT_MIGRATION_VERSION, "0", "test", []string{"select 1"}). - Reply("INSERT 0 1") - // Run test - err := Run(context.Background(), dbConfig, []string{"0"}, Applied, fsys, conn.Intercept) - // Check error - assert.NoError(t, err) - }) - - t.Run("reverts old version", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - helper.MockMigrationHistory(conn). - Query(migration.DELETE_MIGRATION_VERSION, []string{"0"}). - Reply("DELETE 1") - // Run test - err := Run(context.Background(), dbConfig, []string{"0"}, Reverted, fsys, conn.Intercept) - // Check error - assert.NoError(t, err) - }) - - t.Run("throws error on invalid version", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - err := Run(context.Background(), pgconn.Config{}, []string{"invalid"}, Applied, fsys) - // Check error - assert.ErrorIs(t, err, ErrInvalidVersion) - }) - - t.Run("throws error on connect failure", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - err := Run(context.Background(), pgconn.Config{}, []string{"0"}, Applied, fsys) - // Check error - assert.ErrorContains(t, err, "invalid port (outside range)") - }) - - t.Run("throws error on insert failure", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := filepath.Join(utils.MigrationsDir, "0_test.sql") - require.NoError(t, afero.WriteFile(fsys, path, []byte(""), 0644)) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - helper.MockMigrationHistory(conn). - Query(migration.UPSERT_MIGRATION_VERSION, "0", "test", nil). - ReplyError(pgerrcode.DuplicateObject, `relation "supabase_migrations.schema_migrations" does not exist`) - // Run test - err := Run(context.Background(), dbConfig, []string{"0"}, Applied, fsys, conn.Intercept) - // Check error - assert.ErrorContains(t, err, `ERROR: relation "supabase_migrations.schema_migrations" does not exist (SQLSTATE 42710)`) - }) -} - -func TestRepairAll(t *testing.T) { - t.Run("repairs whole history", func(t *testing.T) { - t.Cleanup(fstest.MockStdin(t, "y")) - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := filepath.Join(utils.MigrationsDir, "0_test.sql") - require.NoError(t, afero.WriteFile(fsys, path, []byte("select 1"), 0644)) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - helper.MockMigrationHistory(conn). - Query(strings.Join([]string{ - migration.TRUNCATE_VERSION_TABLE, - strings.ReplaceAll(migration.UPSERT_MIGRATION_VERSION, "$1, $2, $3", " '0' , 'test' , '{select 1}' "), - }, ";")). - Reply("TRUNCATE TABLE"). - Reply("INSERT 0 1") - // Run test - err := Run(context.Background(), dbConfig, nil, Applied, fsys, conn.Intercept, func(cc *pgx.ConnConfig) { - cc.PreferSimpleProtocol = true - }) - // Check error - assert.NoError(t, err) - }) - - t.Run("reverts whole history", func(t *testing.T) { - t.Cleanup(fstest.MockStdin(t, "y")) - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - helper.MockMigrationHistory(conn). - Query(migration.TRUNCATE_VERSION_TABLE). - Reply("TRUNCATE TABLE") - // Run test - err := Run(context.Background(), dbConfig, nil, Reverted, fsys, conn.Intercept) - // Check error - assert.NoError(t, err) - }) - - t.Run("throws error on cancel", func(t *testing.T) { - t.Cleanup(fstest.MockStdin(t, "n")) - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - err := Run(context.Background(), dbConfig, nil, Applied, fsys) - // Check error - assert.ErrorIs(t, err, context.Canceled) - }) - - t.Run("throws error on permission denied", func(t *testing.T) { - t.Cleanup(fstest.MockStdin(t, "y")) - // Setup in-memory fs - fsys := &fstest.OpenErrorFs{DenyPath: utils.MigrationsDir} - // Run test - err := Run(context.Background(), dbConfig, nil, Applied, fsys) - // Check error - assert.ErrorIs(t, err, os.ErrPermission) - }) -} diff --git a/apps/cli-go/internal/seed/buckets/buckets.go b/apps/cli-go/internal/seed/buckets/buckets.go deleted file mode 100644 index e077f5e716..0000000000 --- a/apps/cli-go/internal/seed/buckets/buckets.go +++ /dev/null @@ -1,84 +0,0 @@ -package buckets - -import ( - "context" - "fmt" - "os" - "strings" - - "github.com/spf13/afero" - "github.com/supabase/cli/internal/storage/client" - "github.com/supabase/cli/internal/utils" -) - -func Run(ctx context.Context, projectRef string, interactive bool, fsys afero.Fs) error { - hasVectorBuckets := len(utils.Config.Storage.VectorBuckets.Buckets) > 0 - if len(projectRef) == 0 && - len(utils.Config.Storage.Buckets) == 0 && - !hasVectorBuckets { - return nil - } - api, err := client.NewStorageAPI(ctx, projectRef) - if err != nil { - return err - } - console := utils.NewConsole() - if !interactive { - console.IsTTY = false - } - filter := func(bucketId string) bool { - label := fmt.Sprintf("Bucket %s already exists. Do you want to overwrite its properties?", utils.Bold(bucketId)) - shouldOverwrite, err := console.PromptYesNo(ctx, label, true) - if err != nil { - fmt.Fprintln(utils.GetDebugLogger(), err) - } - return shouldOverwrite - } - if err := api.UpsertBuckets(ctx, utils.Config.Storage.Buckets, filter); err != nil { - return err - } - prune := func(name string) bool { - label := fmt.Sprintf("Bucket %s not found in %s. Do you want to prune it?", utils.Bold(name), utils.Bold(utils.ConfigPath)) - shouldPrune, err := console.PromptYesNo(ctx, label, false) - if err != nil { - fmt.Fprintln(utils.GetDebugLogger(), err) - } - return shouldPrune - } - if utils.Config.Storage.AnalyticsBuckets.Enabled && len(projectRef) > 0 { - fmt.Fprintln(os.Stderr, "Updating analytics buckets...") - if err := api.UpsertAnalyticsBuckets(ctx, utils.Config.Storage.AnalyticsBuckets.Buckets, prune); err != nil { - return err - } - } - if utils.Config.Storage.VectorBuckets.Enabled && hasVectorBuckets { - fmt.Fprintln(os.Stderr, "Updating vector buckets...") - if err := api.UpsertVectorBuckets(ctx, utils.Config.Storage.VectorBuckets.Buckets, prune); err != nil { - if isVectorBucketsFeatureNotEnabled(err) { - fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), "Vector buckets are not available in this project's region yet. Skipping vector bucket seeding.") - return api.UpsertObjects(ctx, utils.Config.Storage.Buckets, utils.NewRootFS(fsys)) - } - if isLocalVectorBucketsUnavailable(err) { - fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), "Vector buckets are not available in the local storage service. If this project is linked, run `supabase link` to update service versions, then restart the local stack. Skipping vector bucket seeding.") - return api.UpsertObjects(ctx, utils.Config.Storage.Buckets, utils.NewRootFS(fsys)) - } - return err - } - } - return api.UpsertObjects(ctx, utils.Config.Storage.Buckets, utils.NewRootFS(fsys)) -} - -func isVectorBucketsFeatureNotEnabled(err error) bool { - return err != nil && strings.Contains(err.Error(), "FeatureNotEnabled") -} - -func isLocalVectorBucketsUnavailable(err error) bool { - if err == nil { - return false - } - message := err.Error() - return strings.Contains(message, "Vector service not configured") || - (strings.Contains(message, "Error status 404:") && - strings.Contains(message, "Route POST:") && - strings.Contains(message, "ListVectorBuckets")) -} diff --git a/apps/cli-go/internal/seed/buckets/buckets_test.go b/apps/cli-go/internal/seed/buckets/buckets_test.go deleted file mode 100644 index 9527d3f554..0000000000 --- a/apps/cli-go/internal/seed/buckets/buckets_test.go +++ /dev/null @@ -1,297 +0,0 @@ -package buckets - -import ( - "context" - "io" - "net/http" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/BurntSushi/toml" - "github.com/h2non/gock" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/storage" -) - -func TestSeedBuckets(t *testing.T) { - t.Run("seeds buckets", func(t *testing.T) { - t.Cleanup(func() { clear(utils.Config.Storage.Buckets) }) - config := ` -[test] -public = true -[private] -public = false` - require.NoError(t, toml.Unmarshal([]byte(config), &utils.Config.Storage.Buckets)) - // Setup in-memory fs - fsys := afero.NewMemMapFs() - bucketPath := filepath.Join(utils.SupabaseDirPath, "images") - require.NoError(t, fsys.Mkdir(bucketPath, 0755)) - // Setup mock api - defer gock.OffAll() - gock.New(utils.Config.Api.ExternalUrl). - Get("/storage/v1/bucket"). - Reply(http.StatusOK). - JSON([]storage.BucketResponse{{ - Name: "test", - Id: "test", - }}) - gock.New(utils.Config.Api.ExternalUrl). - Put("/storage/v1/bucket/test"). - Reply(http.StatusOK). - JSON(storage.UpdateBucketResponse{}) - gock.New(utils.Config.Api.ExternalUrl). - Post("/storage/v1/bucket"). - Reply(http.StatusOK). - JSON(storage.CreateBucketResponse{Name: "private"}) - // Run test - err := Run(context.Background(), "", false, fsys) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("ignores unconfigured buckets", func(t *testing.T) { - t.Cleanup(func() { - utils.Config.Storage.TargetMigration = "" - gock.OffAll() - }) - utils.Config.Storage.TargetMigration = "custom-metadata" - gock.New(utils.Config.Api.ExternalUrl). - Get("/storage/v1/bucket"). - Reply(http.StatusBadRequest). - JSON(map[string]string{ - "statusCode": "403", - "error": "Unauthorized", - "message": "new row violates row-level security policy", - }) - // Run test - err := Run(context.Background(), "", false, afero.NewMemMapFs()) - // Check error - assert.NoError(t, err) - assert.Len(t, gock.Pending(), 1) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("does not call vector API when no vector buckets are configured", func(t *testing.T) { - t.Cleanup(func() { - clear(utils.Config.Storage.Buckets) - utils.Config.Storage.VectorBuckets.Enabled = false - clear(utils.Config.Storage.VectorBuckets.Buckets) - gock.OffAll() - }) - config := ` -[images] -public = true` - require.NoError(t, toml.Unmarshal([]byte(config), &utils.Config.Storage.Buckets)) - utils.Config.Storage.VectorBuckets.Enabled = true - utils.Config.Storage.VectorBuckets.Buckets = map[string]struct{}{} - - gock.New(utils.Config.Api.ExternalUrl). - Get("/storage/v1/bucket"). - Reply(http.StatusOK). - JSON([]storage.BucketResponse{}) - gock.New(utils.Config.Api.ExternalUrl). - Post("/storage/v1/bucket"). - Reply(http.StatusOK). - JSON(storage.CreateBucketResponse{Name: "images"}) - - err := Run(context.Background(), "", false, afero.NewMemMapFs()) - - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("does not call storage API when no buckets are configured", func(t *testing.T) { - t.Cleanup(func() { - utils.Config.Storage.VectorBuckets.Enabled = false - clear(utils.Config.Storage.VectorBuckets.Buckets) - gock.OffAll() - }) - utils.Config.Storage.VectorBuckets.Enabled = true - utils.Config.Storage.VectorBuckets.Buckets = map[string]struct{}{} - - err := Run(context.Background(), "", false, afero.NewMemMapFs()) - - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("seeds vector buckets locally", func(t *testing.T) { - t.Cleanup(func() { - utils.Config.Storage.VectorBuckets.Enabled = false - clear(utils.Config.Storage.VectorBuckets.Buckets) - gock.OffAll() - }) - utils.Config.Storage.VectorBuckets.Enabled = true - utils.Config.Storage.VectorBuckets.Buckets = map[string]struct{}{ - "documents-openai": {}, - "existing-vec": {}, - } - // Setup mock api: regular buckets list is empty, vector list has one - // configured bucket plus one stale bucket that should be left alone - // because non-interactive prune defaults to false. - gock.New(utils.Config.Api.ExternalUrl). - Get("/storage/v1/bucket"). - Reply(http.StatusOK). - JSON([]storage.BucketResponse{}) - gock.New(utils.Config.Api.ExternalUrl). - Post("/storage/v1/vector/ListVectorBuckets"). - Reply(http.StatusOK). - JSON(storage.ListVectorBucketsResponse{ - VectorBuckets: []storage.VectorBucket{ - {VectorBucketName: "existing-vec"}, - {VectorBucketName: "stale-vec"}, - }, - }) - gock.New(utils.Config.Api.ExternalUrl). - Post("/storage/v1/vector/CreateVectorBucket"). - Reply(http.StatusOK). - JSON(map[string]string{}) - gock.New(utils.Config.Api.ExternalUrl). - Post("/storage/v1/vector/DeleteVectorBucket"). - Reply(http.StatusOK). - JSON(map[string]string{}) - // Run test - err := Run(context.Background(), "", false, afero.NewMemMapFs()) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - // The DeleteVectorBucket mock should remain pending because non-interactive - // prune returns the default (false) and skips the delete. - pending := gock.Pending() - require.Len(t, pending, 1) - assert.Contains(t, pending[0].Request().URLStruct.Path, "DeleteVectorBucket") - }) - - t.Run("warns and continues when vector buckets are unavailable in the region", func(t *testing.T) { - t.Cleanup(func() { - utils.Config.Storage.VectorBuckets.Enabled = false - clear(utils.Config.Storage.VectorBuckets.Buckets) - gock.OffAll() - }) - utils.Config.Storage.VectorBuckets.Enabled = true - utils.Config.Storage.VectorBuckets.Buckets = map[string]struct{}{ - "documents-openai": {}, - } - - gock.New(utils.Config.Api.ExternalUrl). - Get("/storage/v1/bucket"). - Reply(http.StatusOK). - JSON([]storage.BucketResponse{}) - gock.New(utils.Config.Api.ExternalUrl). - Post("/storage/v1/vector/ListVectorBuckets"). - Reply(http.StatusBadRequest). - JSON(map[string]string{ - "code": "FeatureNotEnabled", - "message": "Feature is not enabled", - }) - - stderr := captureStderr(t, func() { - err := Run(context.Background(), "", false, afero.NewMemMapFs()) - assert.NoError(t, err) - }) - - assert.Contains(t, stderr, "WARNING:") - assert.Contains(t, stderr, "Vector buckets are not available in this project's region yet") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("warns and continues when local vector storage is not configured", func(t *testing.T) { - t.Cleanup(func() { - utils.Config.Storage.VectorBuckets.Enabled = false - clear(utils.Config.Storage.VectorBuckets.Buckets) - gock.OffAll() - }) - utils.Config.Storage.VectorBuckets.Enabled = true - utils.Config.Storage.VectorBuckets.Buckets = map[string]struct{}{ - "documents-openai": {}, - } - - gock.New(utils.Config.Api.ExternalUrl). - Get("/storage/v1/bucket"). - Reply(http.StatusOK). - JSON([]storage.BucketResponse{}) - gock.New(utils.Config.Api.ExternalUrl). - Post("/storage/v1/vector/ListVectorBuckets"). - Reply(http.StatusConflict). - JSON(map[string]any{ - "statusCode": http.StatusConflict, - "code": "InvalidRequest", - "error": "InvalidRequest", - "message": "The feature Vector service not configured is not enabled for this resource", - }) - - stderr := captureStderr(t, func() { - err := Run(context.Background(), "", false, afero.NewMemMapFs()) - assert.NoError(t, err) - }) - - assert.Contains(t, stderr, "WARNING:") - assert.Contains(t, stderr, "Vector buckets are not available in the local storage service") - assert.Contains(t, stderr, "supabase link") - assert.Contains(t, stderr, "restart the local stack") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("warns and continues when local vector routes are not registered", func(t *testing.T) { - t.Cleanup(func() { - utils.Config.Storage.VectorBuckets.Enabled = false - clear(utils.Config.Storage.VectorBuckets.Buckets) - gock.OffAll() - }) - utils.Config.Storage.VectorBuckets.Enabled = true - utils.Config.Storage.VectorBuckets.Buckets = map[string]struct{}{ - "documents-openai": {}, - } - - gock.New(utils.Config.Api.ExternalUrl). - Get("/storage/v1/bucket"). - Reply(http.StatusOK). - JSON([]storage.BucketResponse{}) - gock.New(utils.Config.Api.ExternalUrl). - Post("/storage/v1/vector/ListVectorBuckets"). - Reply(http.StatusNotFound). - JSON(map[string]any{ - "statusCode": http.StatusNotFound, - "error": "Not Found", - "message": "Route POST:/vector/ListVectorBuckets not found", - }) - - stderr := captureStderr(t, func() { - err := Run(context.Background(), "", false, afero.NewMemMapFs()) - assert.NoError(t, err) - }) - - assert.Contains(t, stderr, "WARNING:") - assert.Contains(t, stderr, "Vector buckets are not available in the local storage service") - assert.Contains(t, stderr, "supabase link") - assert.Contains(t, stderr, "restart the local stack") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func captureStderr(t *testing.T, run func()) string { - t.Helper() - reader, writer, err := os.Pipe() - require.NoError(t, err) - original := os.Stderr - os.Stderr = writer - t.Cleanup(func() { - os.Stderr = original - reader.Close() - }) - - run() - - require.NoError(t, writer.Close()) - os.Stderr = original - out, err := io.ReadAll(reader) - require.NoError(t, err) - return strings.TrimSpace(string(out)) -} diff --git a/apps/cli-go/internal/status/status.go b/apps/cli-go/internal/status/status.go deleted file mode 100644 index 6596319813..0000000000 --- a/apps/cli-go/internal/status/status.go +++ /dev/null @@ -1,392 +0,0 @@ -package status - -import ( - "context" - "crypto/tls" - "crypto/x509" - _ "embed" - "fmt" - "io" - "net/http" - "net/url" - "os" - "slices" - "sync" - "time" - - "github.com/Netflix/go-env" - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/go-errors/errors" - "github.com/olekukonko/tablewriter" - "github.com/olekukonko/tablewriter/tw" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/flags" - "github.com/supabase/cli/pkg/fetcher" -) - -type CustomName struct { - ApiURL string `env:"api.url,default=API_URL"` - RestURL string `env:"api.rest_url,default=REST_URL"` - GraphqlURL string `env:"api.graphql_url,default=GRAPHQL_URL"` - StorageS3URL string `env:"api.storage_s3_url,default=STORAGE_S3_URL"` - McpURL string `env:"api.mcp_url,default=MCP_URL"` - FunctionsURL string `env:"api.functions_url,default=FUNCTIONS_URL"` - DbURL string `env:"db.url,default=DB_URL"` - StudioURL string `env:"studio.url,default=STUDIO_URL"` - InbucketURL string `env:"inbucket.url,default=INBUCKET_URL,deprecated"` - MailpitURL string `env:"mailpit.url,default=MAILPIT_URL"` - PublishableKey string `env:"auth.publishable_key,default=PUBLISHABLE_KEY"` - SecretKey string `env:"auth.secret_key,default=SECRET_KEY"` - JWTSecret string `env:"auth.jwt_secret,default=JWT_SECRET,deprecated"` - AnonKey string `env:"auth.anon_key,default=ANON_KEY,deprecated"` - ServiceRoleKey string `env:"auth.service_role_key,default=SERVICE_ROLE_KEY,deprecated"` - StorageS3AccessKeyId string `env:"storage.s3_access_key_id,default=S3_PROTOCOL_ACCESS_KEY_ID"` - StorageS3SecretAccessKey string `env:"storage.s3_secret_access_key,default=S3_PROTOCOL_ACCESS_KEY_SECRET"` - StorageS3Region string `env:"storage.s3_region,default=S3_PROTOCOL_REGION"` -} - -func (c *CustomName) toValues(exclude ...string) map[string]string { - values := map[string]string{ - c.DbURL: fmt.Sprintf("postgresql://%s@%s:%d/postgres", url.UserPassword("postgres", utils.Config.Db.Password), utils.Config.Hostname, utils.Config.Db.Port), - } - - kongEnabled := utils.Config.Api.Enabled && !slices.Contains(exclude, utils.KongId) && !slices.Contains(exclude, utils.ShortContainerImageName(utils.Config.Api.KongImage)) - postgrestEnabled := kongEnabled && !slices.Contains(exclude, utils.RestId) && !slices.Contains(exclude, utils.ShortContainerImageName(utils.Config.Api.Image)) - studioEnabled := utils.Config.Studio.Enabled && !slices.Contains(exclude, utils.StudioId) && !slices.Contains(exclude, utils.ShortContainerImageName(utils.Config.Studio.Image)) - authEnabled := utils.Config.Auth.Enabled && !slices.Contains(exclude, utils.GotrueId) && !slices.Contains(exclude, utils.ShortContainerImageName(utils.Config.Auth.Image)) - inbucketEnabled := utils.Config.Inbucket.Enabled && !slices.Contains(exclude, utils.InbucketId) && !slices.Contains(exclude, utils.ShortContainerImageName(utils.Config.Inbucket.Image)) - storageEnabled := utils.Config.Storage.Enabled && !slices.Contains(exclude, utils.StorageId) && !slices.Contains(exclude, utils.ShortContainerImageName(utils.Config.Storage.Image)) - functionsEnabled := utils.Config.EdgeRuntime.Enabled && !slices.Contains(exclude, utils.EdgeRuntimeId) && !slices.Contains(exclude, utils.ShortContainerImageName(utils.Config.EdgeRuntime.Image)) - - if kongEnabled { - values[c.ApiURL] = utils.Config.Api.ExternalUrl - if postgrestEnabled { - values[c.RestURL] = utils.GetApiUrl("/rest/v1") - values[c.GraphqlURL] = utils.GetApiUrl("/graphql/v1") - } - if functionsEnabled { - values[c.FunctionsURL] = utils.GetApiUrl("/functions/v1") - } - if studioEnabled { - values[c.McpURL] = utils.GetApiUrl("/mcp") - } - } - if studioEnabled { - values[c.StudioURL] = fmt.Sprintf("http://%s:%d", utils.Config.Hostname, utils.Config.Studio.Port) - } - if authEnabled { - values[c.PublishableKey] = utils.Config.Auth.PublishableKey.Value - values[c.SecretKey] = utils.Config.Auth.SecretKey.Value - values[c.JWTSecret] = utils.Config.Auth.JwtSecret.Value - values[c.AnonKey] = utils.Config.Auth.AnonKey.Value - values[c.ServiceRoleKey] = utils.Config.Auth.ServiceRoleKey.Value - } - if inbucketEnabled { - values[c.MailpitURL] = fmt.Sprintf("http://%s:%d", utils.Config.Hostname, utils.Config.Inbucket.Port) - values[c.InbucketURL] = fmt.Sprintf("http://%s:%d", utils.Config.Hostname, utils.Config.Inbucket.Port) - } - if storageEnabled && utils.Config.Storage.S3Protocol != nil && utils.Config.Storage.S3Protocol.Enabled { - values[c.StorageS3URL] = utils.GetApiUrl("/storage/v1/s3") - values[c.StorageS3AccessKeyId] = utils.Config.Storage.S3Credentials.AccessKeyId - values[c.StorageS3SecretAccessKey] = utils.Config.Storage.S3Credentials.SecretAccessKey - values[c.StorageS3Region] = utils.Config.Storage.S3Credentials.Region - } - return values -} - -func Run(ctx context.Context, names CustomName, format string, fsys afero.Fs, ignoreHealthCheck bool, exclude ...string) error { - // Sanity checks. - if err := flags.LoadConfig(fsys); err != nil { - return err - } - if !ignoreHealthCheck { - if err := assertContainerHealthy(ctx, utils.DbId); err != nil { - return err - } - } - stopped, err := checkServiceHealth(ctx) - if err != nil { - return err - } - if len(stopped) > 0 { - fmt.Fprintln(os.Stderr, "Stopped services:", stopped) - } - excluded := append(stopped, exclude...) - if format == utils.OutputPretty { - fmt.Fprintf(os.Stderr, "%s local development setup is running.\n\n", utils.Aqua("supabase")) - PrettyPrint(os.Stdout, excluded...) - return nil - } - return printStatus(names, format, os.Stdout, excluded...) -} - -func checkServiceHealth(ctx context.Context) ([]string, error) { - resp, err := utils.Docker.ContainerList(ctx, container.ListOptions{ - Filters: utils.CliProjectFilter(utils.Config.ProjectId), - }) - if err != nil { - return nil, errors.Errorf("failed to list running containers: %w", err) - } - running := make(map[string]struct{}, len(resp)) - for _, c := range resp { - for _, n := range c.Names { - running[n] = struct{}{} - } - } - var stopped []string - for _, containerId := range utils.GetDockerIds() { - if _, ok := running["/"+containerId]; !ok { - stopped = append(stopped, containerId) - } - } - return stopped, nil -} - -func assertContainerHealthy(ctx context.Context, container string) error { - if resp, err := utils.Docker.ContainerInspect(ctx, container); err != nil { - return errors.Errorf("failed to inspect container health: %w", err) - } else if !resp.State.Running { - return errors.Errorf("%s container is not running: %s", container, resp.State.Status) - } else if resp.State.Health != nil && resp.State.Health.Status != types.Healthy { - return errors.Errorf("%s container is not ready: %s", container, resp.State.Health.Status) - } - return nil -} - -func IsServiceReady(ctx context.Context, container string) error { - if container == utils.RestId { - // PostgREST does not support native health checks - return checkHTTPHead(ctx, "/rest-admin/v1/ready") - } - if container == utils.EdgeRuntimeId { - // Native health check logs too much hyper::Error(IncompleteMessage) - return checkHTTPHead(ctx, "/functions/v1/_internal/health") - } - return assertContainerHealthy(ctx, container) -} - -// NewKongClient returns an HTTP client configured for the local Kong gateway. -// It deliberately omits http.Client.Timeout: this client is shared with -// streaming storage uploads, where a full-request deadline would truncate -// large transfers under load. Short-lived callers (e.g. health probes) should -// bound themselves with a per-call context deadline instead. -// -// To regenerate local certificate pair: -// -// openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 \ -// -nodes -keyout kong.local.key -out kong.local.crt -subj "/CN=localhost" \ -// -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" -func NewKongClient() *http.Client { - client := &http.Client{} - if t, ok := http.DefaultTransport.(*http.Transport); ok { - pool, err := x509.SystemCertPool() - if err != nil { - fmt.Fprintln(utils.GetDebugLogger(), err) - pool = x509.NewCertPool() - } - // No need to replace TLS config if we fail to append cert - if pool.AppendCertsFromPEM(utils.Config.Api.Tls.CertContent) { - rt := t.Clone() - rt.TLSClientConfig = &tls.Config{ - MinVersion: tls.VersionTLS12, - RootCAs: pool, - } - client.Transport = rt - } - } - return client -} - -var ( - healthClient *fetcher.Fetcher - healthOnce sync.Once -) - -// healthProbeTimeout caps a single readiness probe so a hung response cannot -// stall the surrounding retry loop in WaitForHealthyService. -const healthProbeTimeout = 10 * time.Second - -func checkHTTPHead(ctx context.Context, path string) error { - healthOnce.Do(func() { - healthClient = fetcher.NewServiceGateway( - utils.Config.Api.ExternalUrl, - utils.Config.Auth.SecretKey.Value, - fetcher.WithHTTPClient(NewKongClient()), - fetcher.WithUserAgent("SupabaseCLI/"+utils.Version), - ) - }) - ctx, cancel := context.WithTimeout(ctx, healthProbeTimeout) - defer cancel() - // HEAD method does not return response body - resp, err := healthClient.Send(ctx, http.MethodHead, path, nil) - if err != nil { - return err - } - defer resp.Body.Close() - return nil -} - -func printStatus(names CustomName, format string, w io.Writer, exclude ...string) (err error) { - values := names.toValues(exclude...) - return utils.EncodeOutput(format, w, values) -} - -func PrettyPrint(w io.Writer, exclude ...string) { - logger := utils.GetDebugLogger() - - names := CustomName{} - if err := env.Unmarshal(env.EnvSet{}, &names); err != nil { - fmt.Fprintln(logger, err) - } - values := names.toValues(exclude...) - - groups := []OutputGroup{ - { - Name: "🔧 Development Tools", - Items: []OutputItem{ - {Label: "Studio", Value: values[names.StudioURL], Type: Link}, - {Label: "Mailpit", Value: values[names.MailpitURL], Type: Link}, - {Label: "MCP", Value: values[names.McpURL], Type: Link}, - }, - }, - { - Name: "🌐 APIs", - Items: []OutputItem{ - {Label: "Project URL", Value: values[names.ApiURL], Type: Link}, - {Label: "REST", Value: values[names.RestURL], Type: Link}, - {Label: "GraphQL", Value: values[names.GraphqlURL], Type: Link}, - {Label: "Edge Functions", Value: values[names.FunctionsURL], Type: Link}, - }, - }, - { - Name: "⛁ Database", - Items: []OutputItem{ - {Label: "URL", Value: values[names.DbURL], Type: Link}, - }, - }, - { - Name: "🔑 Authentication Keys", - Items: []OutputItem{ - {Label: "Publishable", Value: values[names.PublishableKey], Type: Key}, - {Label: "Secret", Value: values[names.SecretKey], Type: Key}, - }, - }, - { - Name: "📦 Storage (S3)", - Items: []OutputItem{ - {Label: "URL", Value: values[names.StorageS3URL], Type: Link}, - {Label: "Access Key", Value: values[names.StorageS3AccessKeyId], Type: Key}, - {Label: "Secret Key", Value: values[names.StorageS3SecretAccessKey], Type: Key}, - {Label: "Region", Value: values[names.StorageS3Region], Type: Text}, - }, - }, - } - - for _, group := range groups { - if err := group.printTable(w); err != nil { - fmt.Fprintln(logger, err) - } else { - fmt.Fprintln(w) - } - } -} - -type OutputType string - -const ( - Text OutputType = "text" - Link OutputType = "link" - Key OutputType = "key" -) - -type OutputItem struct { - Label string - Value string - Type OutputType -} - -type OutputGroup struct { - Name string - Items []OutputItem -} - -func (g *OutputGroup) printTable(w io.Writer) error { - table := tablewriter.NewTable(w, - // Rounded corners - tablewriter.WithSymbols(tw.NewSymbols(tw.StyleRounded)), - - // Table content formatting - tablewriter.WithConfig(tablewriter.Config{ - Header: tw.CellConfig{ - Formatting: tw.CellFormatting{ - AutoFormat: tw.Off, - MergeMode: tw.MergeHorizontal, - }, - Alignment: tw.CellAlignment{ - Global: tw.AlignLeft, - }, - Filter: tw.CellFilter{ - Global: func(s []string) []string { - for i := range s { - s[i] = utils.Bold(s[i]) - } - return s - }, - }, - }, - Row: tw.CellConfig{ - Alignment: tw.CellAlignment{ - Global: tw.AlignLeft, - }, - ColMaxWidths: tw.CellWidth{ - PerColumn: map[int]int{0: 16}, - }, - Filter: tw.CellFilter{ - PerColumn: []func(string) string{ - func(s string) string { - return utils.Green(s) - }, - }, - }, - }, - Behavior: tw.Behavior{ - Compact: tw.Compact{ - Merge: tw.On, - }, - }, - }), - - // Set title as header (merged across all columns) - tablewriter.WithHeader([]string{g.Name, g.Name}), - ) - - // Add data rows with values colored based on type - shouldRender := false - for _, row := range g.Items { - if row.Value == "" { - continue - } - value := row.Value - switch row.Type { - case Link: - value = utils.Aqua(row.Value) - case Key: - value = utils.Yellow(row.Value) - } - if err := table.Append(row.Label, value); err != nil { - return errors.Errorf("failed to append row: %w", err) - } - shouldRender = true - } - - // Ensure at least one item in the group is non-empty - if shouldRender { - if err := table.Render(); err != nil { - return errors.Errorf("failed to render table: %w", err) - } - } - - return nil -} diff --git a/apps/cli-go/internal/status/status_test.go b/apps/cli-go/internal/status/status_test.go deleted file mode 100644 index 37b309b7b1..0000000000 --- a/apps/cli-go/internal/status/status_test.go +++ /dev/null @@ -1,214 +0,0 @@ -package status - -import ( - "bytes" - "context" - "errors" - "net/http" - "testing" - - "github.com/docker/docker/api/types/container" - "github.com/h2non/gock" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/utils" -) - -func TestStatusCommand(t *testing.T) { - t.Run("shows service status", func(t *testing.T) { - var running []container.Summary - for _, name := range utils.GetDockerIds() { - running = append(running, container.Summary{ - Names: []string{name + "_test"}, - }) - } - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, utils.InitConfig(utils.InitParams{ProjectId: "test"}, fsys)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/supabase_db_test/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - Running: true, - }, - }}) - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/json"). - Reply(http.StatusOK). - JSON(running) - // Run test - assert.NoError(t, Run(context.Background(), CustomName{}, utils.OutputPretty, fsys, false)) - // Check error - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("skips database health check when ignored", func(t *testing.T) { - var running []container.Summary - for _, name := range utils.GetDockerIds() { - running = append(running, container.Summary{ - Names: []string{name + "_test"}, - }) - } - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, utils.InitConfig(utils.InitParams{ProjectId: "test"}, fsys)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/json"). - Reply(http.StatusOK). - JSON(running) - // Run test - assert.NoError(t, Run(context.Background(), CustomName{}, utils.OutputPretty, fsys, true)) - // Check error - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on malformed config", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, afero.WriteFile(fsys, utils.ConfigPath, []byte("malformed"), 0644)) - // Run test - err := Run(context.Background(), CustomName{}, utils.OutputPretty, fsys, false) - // Check error - assert.ErrorContains(t, err, "toml: expected = after a key, but the document ends there") - }) - - t.Run("throws error on missing docker", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/supabase_db_"). - ReplyError(errors.New("network error")) - // Run test - err := Run(context.Background(), CustomName{}, utils.OutputPretty, fsys, false) - // Check error - assert.ErrorContains(t, err, "network error") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestServiceHealth(t *testing.T) { - t.Run("checks all services", func(t *testing.T) { - var running []container.Summary - for _, name := range utils.GetDockerIds() { - running = append(running, container.Summary{ - Names: []string{"/" + name}, - }) - } - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/json"). - Reply(http.StatusOK). - JSON(running) - // Run test - stopped, err := checkServiceHealth(context.Background()) - // Check error - assert.NoError(t, err) - assert.Empty(t, stopped) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("shows stopped container", func(t *testing.T) { - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/json"). - Reply(http.StatusOK). - JSON([]container.Summary{}) - // Run test - stopped, err := checkServiceHealth(context.Background()) - // Check error - assert.NoError(t, err) - assert.ElementsMatch(t, utils.GetDockerIds(), stopped) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on network error", func(t *testing.T) { - errNetwork := errors.New("network error") - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers/json"). - ReplyError(errNetwork) - // Run test - stopped, err := checkServiceHealth(context.Background()) - // Check error - assert.ErrorIs(t, err, errNetwork) - assert.Empty(t, stopped) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestPrintStatus(t *testing.T) { - utils.Config.Db.Port = 0 - utils.Config.Hostname = "127.0.0.1" - utils.Config.Api.Enabled = false - utils.Config.Auth.Enabled = false - utils.Config.Storage.Enabled = false - utils.Config.Realtime.Enabled = false - utils.Config.Studio.Enabled = false - utils.Config.Analytics.Enabled = false - utils.Config.Inbucket.Enabled = false - - t.Run("outputs env var", func(t *testing.T) { - utils.Config.Hostname = "127.0.0.1" - // Run test - var stdout bytes.Buffer - assert.NoError(t, printStatus(CustomName{DbURL: "DB_URL"}, utils.OutputEnv, &stdout)) - // Check error - assert.Equal(t, "DB_URL=\"postgresql://postgres:postgres@127.0.0.1:0/postgres\"\n", stdout.String()) - }) - - t.Run("outputs json object", func(t *testing.T) { - // Run test - var stdout bytes.Buffer - assert.NoError(t, printStatus(CustomName{DbURL: "DB_URL"}, utils.OutputJson, &stdout)) - // Check error - assert.Equal(t, "{\n \"DB_URL\": \"postgresql://postgres:postgres@127.0.0.1:0/postgres\"\n}\n", stdout.String()) - }) - - t.Run("omits excluded services from json object", func(t *testing.T) { - utils.Config.Studio.Enabled = true - utils.Config.Studio.Port = 54323 - t.Cleanup(func() { - utils.Config.Studio.Enabled = false - }) - // Run test - var stdout bytes.Buffer - assert.NoError(t, printStatus(CustomName{DbURL: "DB_URL", StudioURL: "STUDIO_URL"}, utils.OutputJson, &stdout, utils.StudioId)) - // Check error - assert.Equal(t, "{\n \"DB_URL\": \"postgresql://postgres:postgres@127.0.0.1:0/postgres\"\n}\n", stdout.String()) - }) - - t.Run("outputs yaml properties", func(t *testing.T) { - // Run test - var stdout bytes.Buffer - assert.NoError(t, printStatus(CustomName{DbURL: "DB_URL"}, utils.OutputYaml, &stdout)) - // Check error - assert.Equal(t, "DB_URL: postgresql://postgres:postgres@127.0.0.1:0/postgres\n", stdout.String()) - }) - - t.Run("outputs toml fields", func(t *testing.T) { - // Run test - var stdout bytes.Buffer - assert.NoError(t, printStatus(CustomName{DbURL: "DB_URL"}, utils.OutputToml, &stdout)) - // Check error - assert.Equal(t, "DB_URL = \"postgresql://postgres:postgres@127.0.0.1:0/postgres\"\n", stdout.String()) - }) -} diff --git a/apps/cli-go/internal/storage/client/api.go b/apps/cli-go/internal/storage/client/api.go deleted file mode 100644 index 33b5d6711f..0000000000 --- a/apps/cli-go/internal/storage/client/api.go +++ /dev/null @@ -1,46 +0,0 @@ -package client - -import ( - "context" - "net/http" - - "github.com/spf13/viper" - "github.com/supabase/cli/internal/status" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/tenant" - "github.com/supabase/cli/pkg/fetcher" - "github.com/supabase/cli/pkg/storage" -) - -func NewStorageAPI(ctx context.Context, projectRef string) (storage.StorageAPI, error) { - client := storage.StorageAPI{} - if len(projectRef) == 0 { - client.Fetcher = newLocalClient() - } else if viper.IsSet("AUTH_SERVICE_ROLE_KEY") { - // Special case for calling storage API without personal access token - client.Fetcher = newRemoteClient(projectRef, utils.Config.Auth.ServiceRoleKey.Value) - } else if apiKey, err := tenant.GetApiKeys(ctx, projectRef); err == nil { - client.Fetcher = newRemoteClient(projectRef, apiKey.ServiceRole) - } else { - return client, err - } - return client, nil -} - -func newLocalClient() *fetcher.Fetcher { - return fetcher.NewServiceGateway( - utils.Config.Api.ExternalUrl, - utils.Config.Auth.ServiceRoleKey.Value, - fetcher.WithHTTPClient(status.NewKongClient()), - fetcher.WithUserAgent("SupabaseCLI/"+utils.Version), - ) -} - -func newRemoteClient(projectRef, token string) *fetcher.Fetcher { - return fetcher.NewServiceGateway( - "https://"+utils.GetSupabaseHost(projectRef), - token, - fetcher.WithHTTPClient(http.DefaultClient), - fetcher.WithUserAgent("SupabaseCLI/"+utils.Version), - ) -} diff --git a/apps/cli-go/internal/storage/client/scheme.go b/apps/cli-go/internal/storage/client/scheme.go deleted file mode 100644 index bb3254f5ba..0000000000 --- a/apps/cli-go/internal/storage/client/scheme.go +++ /dev/null @@ -1,38 +0,0 @@ -package client - -import ( - "net/url" - "strings" - - "github.com/go-errors/errors" -) - -const STORAGE_SCHEME = "ss" - -var ErrInvalidURL = errors.New("URL must match pattern ss:///bucket/[prefix]") - -func ParseStorageURL(objectURL string) (string, error) { - parsed, err := url.Parse(objectURL) - if err != nil { - return "", errors.Errorf("failed to parse storage url: %w", err) - } - if !strings.EqualFold(parsed.Scheme, STORAGE_SCHEME) || len(parsed.Path) == 0 || len(parsed.Host) > 0 { - return "", errors.New(ErrInvalidURL) - } - return parsed.Path, nil -} - -func SplitBucketPrefix(objectPath string) (string, string) { - if objectPath == "" || objectPath == "/" { - return "", "" - } - start := 0 - if objectPath[0] == '/' { - start = 1 - } - sep := strings.IndexByte(objectPath[start:], '/') - if sep < 0 { - return objectPath[start:], "" - } - return objectPath[start : sep+start], objectPath[sep+start+1:] -} diff --git a/apps/cli-go/internal/storage/client/scheme_test.go b/apps/cli-go/internal/storage/client/scheme_test.go deleted file mode 100644 index 5da6191a02..0000000000 --- a/apps/cli-go/internal/storage/client/scheme_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package client - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestParseStorageURL(t *testing.T) { - t.Run("parses valid url", func(t *testing.T) { - path, err := ParseStorageURL("ss:///bucket/folder/name.png") - assert.NoError(t, err) - assert.Equal(t, path, "/bucket/folder/name.png") - }) - - t.Run("throws error on invalid host", func(t *testing.T) { - path, err := ParseStorageURL("ss://bucket") - assert.ErrorIs(t, err, ErrInvalidURL) - assert.Empty(t, path) - }) - - t.Run("throws error on missing path", func(t *testing.T) { - path, err := ParseStorageURL("ss:") - assert.ErrorIs(t, err, ErrInvalidURL) - assert.Empty(t, path) - }) - - t.Run("throws error on invalid scheme", func(t *testing.T) { - path, err := ParseStorageURL(".") - assert.ErrorIs(t, err, ErrInvalidURL) - assert.Empty(t, path) - }) - - t.Run("throws error on invalid url", func(t *testing.T) { - path, err := ParseStorageURL(":") - assert.ErrorContains(t, err, "missing protocol scheme") - assert.Empty(t, path) - }) -} - -func TestSplitBucketPrefix(t *testing.T) { - t.Run("splits empty path", func(t *testing.T) { - bucket, prefix := SplitBucketPrefix("") - assert.Equal(t, bucket, "") - assert.Equal(t, prefix, "") - }) - - t.Run("splits root path", func(t *testing.T) { - bucket, prefix := SplitBucketPrefix("/") - assert.Equal(t, bucket, "") - assert.Equal(t, prefix, "") - }) - - t.Run("splits no slash", func(t *testing.T) { - bucket, prefix := SplitBucketPrefix("bucket") - assert.Equal(t, bucket, "bucket") - assert.Equal(t, prefix, "") - }) - - t.Run("splits prefix slash", func(t *testing.T) { - bucket, prefix := SplitBucketPrefix("/bucket") - assert.Equal(t, bucket, "bucket") - assert.Equal(t, prefix, "") - }) - - t.Run("splits suffix slash", func(t *testing.T) { - bucket, prefix := SplitBucketPrefix("bucket/") - assert.Equal(t, bucket, "bucket") - assert.Equal(t, prefix, "") - }) - - t.Run("splits file path", func(t *testing.T) { - bucket, prefix := SplitBucketPrefix("/bucket/folder/name.png") - assert.Equal(t, bucket, "bucket") - assert.Equal(t, prefix, "folder/name.png") - }) - - t.Run("splits dir path", func(t *testing.T) { - bucket, prefix := SplitBucketPrefix("/bucket/folder/") - assert.Equal(t, bucket, "bucket") - assert.Equal(t, prefix, "folder/") - }) -} diff --git a/apps/cli-go/internal/telemetry/client.go b/apps/cli-go/internal/telemetry/client.go deleted file mode 100644 index 717e4af181..0000000000 --- a/apps/cli-go/internal/telemetry/client.go +++ /dev/null @@ -1,149 +0,0 @@ -package telemetry - -import ( - "log" - "net/http" - "strings" - "time" - - "github.com/go-errors/errors" - "github.com/posthog/posthog-go" - "github.com/supabase/cli/internal/utils" -) - -type Analytics interface { - Enabled() bool - Capture(distinctID string, event string, properties map[string]any, groups map[string]string) error - Identify(distinctID string, properties map[string]any) error - Alias(distinctID string, alias string) error - GroupIdentify(groupType string, groupKey string, properties map[string]any) error - Close() error -} - -type queueClient interface { - Enqueue(posthog.Message) error - Close() error -} - -type constructor func(apiKey string, config posthog.Config) (queueClient, error) - -const shutdownTimeout = 2 * time.Second - -type Client struct { - client queueClient - baseProperties posthog.Properties -} - -func NewClient(apiKey string, endpoint string, baseProperties map[string]any, factory constructor) (*Client, error) { - if strings.TrimSpace(apiKey) == "" { - return &Client{baseProperties: makeProperties(baseProperties)}, nil - } - if factory == nil { - factory = func(apiKey string, config posthog.Config) (queueClient, error) { - return posthog.NewWithConfig(apiKey, config) - } - } - // Without a positive ShutdownTimeout, Close blocks indefinitely when the - // endpoint is unreachable, hanging every command on networks that block - // PostHog; keep it tight because blackholed networks pay the whole timeout - // at exit. The default logger prints delivery failures to stderr even for - // commands that succeeded, so route them to the --debug logger instead. - config := posthog.Config{ - ShutdownTimeout: shutdownTimeout, - Logger: posthog.StdLogger(log.New(utils.GetDebugLogger(), "posthog ", log.LstdFlags), true), - } - if endpoint != "" { - config.Endpoint = endpoint - } - // Preserve the active process-wide transport, which may be wrapped by debug.NewTransport() - // instead of assuming http.DefaultTransport is always a *http.Transport. - config.Transport = http.DefaultTransport - client, err := factory(apiKey, config) - if err != nil { - return nil, errors.Errorf("failed to initialize posthog client: %w", err) - } - return &Client{ - client: client, - baseProperties: makeProperties(baseProperties), - }, nil -} - -func (c *Client) Enabled() bool { - return c != nil && c.client != nil -} - -func (c *Client) Capture(distinctID string, event string, properties map[string]any, groups map[string]string) error { - if !c.Enabled() { - return nil - } - msg := posthog.Capture{ - DistinctId: distinctID, - Event: event, - Properties: c.properties(properties), - } - if len(groups) > 0 { - msg.Groups = makeGroups(groups) - } - return c.client.Enqueue(msg) -} - -func (c *Client) Identify(distinctID string, properties map[string]any) error { - if !c.Enabled() { - return nil - } - return c.client.Enqueue(posthog.Identify{ - DistinctId: distinctID, - Properties: c.properties(properties), - }) -} - -func (c *Client) Alias(distinctID string, alias string) error { - if !c.Enabled() { - return nil - } - return c.client.Enqueue(posthog.Alias{ - DistinctId: distinctID, - Alias: alias, - }) -} - -func (c *Client) GroupIdentify(groupType string, groupKey string, properties map[string]any) error { - if !c.Enabled() { - return nil - } - return c.client.Enqueue(posthog.GroupIdentify{ - Type: groupType, - Key: groupKey, - Properties: c.properties(properties), - }) -} - -func (c *Client) Close() error { - if !c.Enabled() { - return nil - } - return c.client.Close() -} - -func (c *Client) properties(properties map[string]any) posthog.Properties { - merged := posthog.NewProperties() - merged.Merge(c.baseProperties) - merged.Merge(makeProperties(properties)) - return merged -} - -func makeProperties(values map[string]any) posthog.Properties { - props := posthog.NewProperties() - for key, value := range values { - props.Set(key, value) - } - return props -} - -func makeGroups(values map[string]string) posthog.Groups { - groups := posthog.NewGroups() - for key, value := range values { - groups.Set(key, value) - } - return groups -} diff --git a/apps/cli-go/internal/telemetry/client_test.go b/apps/cli-go/internal/telemetry/client_test.go deleted file mode 100644 index bcc8db7d24..0000000000 --- a/apps/cli-go/internal/telemetry/client_test.go +++ /dev/null @@ -1,169 +0,0 @@ -package telemetry - -import ( - "io" - "net/http" - "os" - "testing" - "time" - - "github.com/posthog/posthog-go" - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/debug" -) - -type fakeQueue struct { - messages []posthog.Message - closed bool -} - -func (f *fakeQueue) Enqueue(msg posthog.Message) error { - f.messages = append(f.messages, msg) - return nil -} - -func (f *fakeQueue) Close() error { - f.closed = true - return nil -} - -func TestNewClient(t *testing.T) { - t.Run("uses endpoint and enables analytics when key is set", func(t *testing.T) { - var gotKey string - var gotConfig posthog.Config - - client, err := NewClient("phc_test", "https://eu.i.posthog.com", map[string]any{"platform": "cli"}, func(apiKey string, config posthog.Config) (queueClient, error) { - gotKey = apiKey - gotConfig = config - return &fakeQueue{}, nil - }) - - require.NoError(t, err) - assert.True(t, client.Enabled()) - assert.Equal(t, "phc_test", gotKey) - assert.Equal(t, "https://eu.i.posthog.com", gotConfig.Endpoint) - assert.Equal(t, 2*time.Second, gotConfig.ShutdownTimeout) - assert.NotNil(t, gotConfig.Logger) - }) - - t.Run("becomes a no-op when key is empty", func(t *testing.T) { - client, err := NewClient("", "https://eu.i.posthog.com", map[string]any{"platform": "cli"}, func(apiKey string, config posthog.Config) (queueClient, error) { - t.Fatalf("constructor should not be called without an api key") - return nil, nil - }) - - require.NoError(t, err) - assert.False(t, client.Enabled()) - assert.NoError(t, client.Capture("device-1", EventCommandExecuted, map[string]any{"command": "login"}, nil)) - assert.NoError(t, client.Close()) - }) - t.Run("routes posthog logs to stderr only in debug mode", func(t *testing.T) { - originalStderr := os.Stderr - originalDebug := viper.GetBool("DEBUG") - reader, writer, err := os.Pipe() - require.NoError(t, err) - os.Stderr = writer - t.Cleanup(func() { - os.Stderr = originalStderr - viper.Set("DEBUG", originalDebug) - }) - - configFor := func(debugEnabled bool) posthog.Config { - viper.Set("DEBUG", debugEnabled) - var gotConfig posthog.Config - _, err := NewClient("phc_test", "", nil, func(apiKey string, config posthog.Config) (queueClient, error) { - gotConfig = config - return &fakeQueue{}, nil - }) - require.NoError(t, err) - return gotConfig - } - - configFor(false).Logger.Errorf("dropped %d messages", 1) - configFor(true).Logger.Errorf("dropped %d messages", 2) - - require.NoError(t, writer.Close()) - output, err := io.ReadAll(reader) - require.NoError(t, err) - assert.NotContains(t, string(output), "dropped 1 messages") - assert.Contains(t, string(output), "dropped 2 messages") - }) - t.Run("works when debug wraps the default transport", func(t *testing.T) { - original := http.DefaultTransport - http.DefaultTransport = debug.NewTransport() - t.Cleanup(func() { - http.DefaultTransport = original - }) - - client, err := NewClient("phc_test", "https://eu.i.posthog.com", map[string]any{"platform": "cli"}, nil) - - require.NoError(t, err) - require.NotNil(t, client) - assert.True(t, client.Enabled()) - assert.NoError(t, client.Close()) - }) -} - -func TestCaptureMergesBasePropertiesAndGroups(t *testing.T) { - queue := &fakeQueue{} - client, err := NewClient("phc_test", "https://eu.i.posthog.com", map[string]any{ - "platform": "cli", - "os": "darwin", - }, func(apiKey string, config posthog.Config) (queueClient, error) { - return queue, nil - }) - require.NoError(t, err) - - err = client.Capture("device-1", EventCommandExecuted, map[string]any{ - "command": "login", - }, map[string]string{ - GroupProject: "proj_123", - }) - - require.NoError(t, err) - require.Len(t, queue.messages, 1) - msg, ok := queue.messages[0].(posthog.Capture) - require.True(t, ok) - assert.Equal(t, "device-1", msg.DistinctId) - assert.Equal(t, EventCommandExecuted, msg.Event) - assert.Equal(t, "cli", msg.Properties["platform"]) - assert.Equal(t, "darwin", msg.Properties["os"]) - assert.Equal(t, "login", msg.Properties["command"]) - assert.Equal(t, posthog.Groups{GroupProject: "proj_123"}, msg.Groups) -} - -func TestIdentifyAliasAndGroupIdentify(t *testing.T) { - queue := &fakeQueue{} - client, err := NewClient("phc_test", "", map[string]any{"platform": "cli"}, func(apiKey string, config posthog.Config) (queueClient, error) { - return queue, nil - }) - require.NoError(t, err) - - require.NoError(t, client.Identify("user-123", map[string]any{"schema_version": 1})) - require.NoError(t, client.Alias("user-123", "device-123")) - require.NoError(t, client.GroupIdentify(GroupOrganization, "org_123", map[string]any{"slug": "acme"})) - require.NoError(t, client.Close()) - - require.Len(t, queue.messages, 3) - - identify, ok := queue.messages[0].(posthog.Identify) - require.True(t, ok) - assert.Equal(t, "user-123", identify.DistinctId) - assert.Equal(t, "cli", identify.Properties["platform"]) - assert.Equal(t, 1, identify.Properties["schema_version"]) - - alias, ok := queue.messages[1].(posthog.Alias) - require.True(t, ok) - assert.Equal(t, "user-123", alias.DistinctId) - assert.Equal(t, "device-123", alias.Alias) - - groupIdentify, ok := queue.messages[2].(posthog.GroupIdentify) - require.True(t, ok) - assert.Equal(t, GroupOrganization, groupIdentify.Type) - assert.Equal(t, "org_123", groupIdentify.Key) - assert.Equal(t, "cli", groupIdentify.Properties["platform"]) - assert.Equal(t, "acme", groupIdentify.Properties["slug"]) - assert.True(t, queue.closed) -} diff --git a/apps/cli-go/internal/telemetry/events.go b/apps/cli-go/internal/telemetry/events.go deleted file mode 100644 index 2ee362dc17..0000000000 --- a/apps/cli-go/internal/telemetry/events.go +++ /dev/null @@ -1,184 +0,0 @@ -package telemetry - -import "context" - -// CLI telemetry catalog. -// -// This file is the single place to review what analytics events the CLI sends -// and what metadata may be attached to them. Comments live next to the event, -// property, group, or signal definition they describe so the catalog is easy to -// scan without reading the rest of the implementation. -const ( - // - EventCommandExecuted: sent after a CLI command finishes, whether it - // succeeds or fails. This helps measure command usage, failure rates, and - // runtime. Event-specific properties are PropExitCode (process exit code), - // PropDurationMs (command runtime in milliseconds), and PropOutputFormat - // (the resolved output format the command used). Related groups: none - // added directly by this event. - EventCommandExecuted = "cli_command_executed" - // - EventProjectLinked: sent after the local CLI directory is linked to a - // Supabase project. This helps measure project-linking adoption and connect - // future events to the right project and organization. Event-specific - // properties: none. Related groups: GroupOrganization and GroupProject. - // Related group-identify payloads sent during linking are: - // organization group -> organization_slug, and project group -> name, - // organization_slug. - EventProjectLinked = "cli_project_linked" - // - EventLoginCompleted: sent after a login flow completes successfully. This - // helps measure successful login completion and supports identity stitching - // between anonymous and authenticated usage. Event-specific properties: - // none. Related groups: none added directly by this event. - EventLoginCompleted = "cli_login_completed" - // - EventStackStarted: sent after the local development stack starts - // successfully. This helps measure local development usage and successful - // stack startup. Event-specific properties: none. Related groups: none - // added directly by this event, but linked project groups may still be - // attached when available. - EventStackStarted = "cli_stack_started" - // - EventUpgradeSuggested: sent when a CLI command hits a plan-gated - // feature and displays a billing upgrade link. This helps identify - // which plan gates users encounter most often so we can improve - // error messages and documentation. Event-specific properties are - // PropFeatureKey (the entitlement key that was gated) and PropOrgSlug - // (the organization slug, empty if lookup failed). - EventUpgradeSuggested = "cli_upgrade_suggested" -) - -// Properties specific to EventUpgradeSuggested. -const ( - // PropFeatureKey is the entitlement key that triggered the upgrade suggestion. - PropFeatureKey = "feature_key" - // PropOrgSlug is the organization slug associated with the project. - PropOrgSlug = "org_slug" -) - -// TrackUpgradeSuggested fires an EventUpgradeSuggested telemetry event. -// Safe to call with any context; no-ops when telemetry is not configured. -func TrackUpgradeSuggested(ctx context.Context, featureKey, orgSlug string) { - if svc := FromContext(ctx); svc != nil { - _ = svc.Capture(ctx, EventUpgradeSuggested, map[string]any{ - PropFeatureKey: featureKey, - PropOrgSlug: orgSlug, - }, nil) - } -} - -// Shared event properties added to every captured event by Service.Capture. -const ( - // PropPlatform identifies the product source for the event. The CLI always - // sends "cli". - PropPlatform = "platform" - // PropSchemaVersion is the version of the telemetry payload format. This is - // not a database schema version. - PropSchemaVersion = "schema_version" - // PropDeviceID is an anonymous identifier for this CLI installation on this - // machine. - PropDeviceID = "device_id" - // PropSessionID is the PostHog session identifier used to group activity from - // one CLI session together. - PropSessionID = "$session_id" - // PropIsFirstRun is true when the current telemetry state was created during - // this run, which helps distinguish first-time setup from repeat usage. - PropIsFirstRun = "is_first_run" - // PropIsTTY is true when stdout is attached to an interactive terminal. - PropIsTTY = "is_tty" - // PropIsCI is true when the CLI appears to be running in a CI environment. - PropIsCI = "is_ci" - // PropIsAgent is true when the CLI appears to be running under an AI agent or - // automation tool. - PropIsAgent = "is_agent" - // PropOS is the operating system reported by the Go runtime. - PropOS = "os" - // PropArch is the CPU architecture reported by the Go runtime. - PropArch = "arch" - // PropCLIVersion is the version string of the CLI build that sent the event. - PropCLIVersion = "cli_version" - // PropEnvSignals is an optional summary of coarse environment hints. It is - // not a raw dump of environment variables. - PropEnvSignals = "env_signals" - // PropCommandRunID identifies one command invocation and can be used to tie - // together telemetry emitted during a single command run. - PropCommandRunID = "command_run_id" - // PropCommand is the normalized command path, such as "link" or "db push". - PropCommand = "command" - // PropFlags contains changed CLI flags for that command run. Safe flag values - // may be included, while sensitive values are redacted in the command - // telemetry implementation. - PropFlags = "flags" - // PropExitCode is the process exit code for the command that produced the - // event. - PropExitCode = "exit_code" - // PropDurationMs is the command runtime in milliseconds. - PropDurationMs = "duration_ms" - // PropOutputFormat is the resolved output format the command used (for - // example "pretty", "json", "yaml"), after agent auto-detection and any - // command-local override are applied. - PropOutputFormat = "output_format" -) - -// Group identifiers associate events with higher-level entities in PostHog. -const ( - // GroupOrganization identifies the Supabase organization related to the - // event. - GroupOrganization = "organization" - // GroupProject identifies the Supabase project related to the event. - GroupProject = "project" -) - -var ( - // EnvSignalPresenceKeys lists environment variables whose presence is recorded - // as true inside the "env_signals" property. - EnvSignalPresenceKeys = [...]string{ - // AI tools signals - "CURSOR_AGENT", - "CURSOR_TRACE_ID", - "GEMINI_CLI", - "CODEX_SANDBOX", - "CODEX_CI", - "CODEX_THREAD_ID", - "ANTIGRAVITY_AGENT", - "AUGMENT_AGENT", - "OPENCODE_CLIENT", - "CLAUDECODE", - "CLAUDE_CODE", - "REPL_ID", - "COPILOT_MODEL", - "COPILOT_ALLOW_ALL", - "COPILOT_GITHUB_TOKEN", - // CI signals - "CI", - "GITHUB_ACTIONS", - "BUILDKITE", - "TF_BUILD", - "JENKINS_URL", - "GITLAB_CI", - // Extra signals - "GITHUB_TOKEN", - "GITHUB_HEAD_REF", - "BITBUCKET_CLONE_DIR", - // Supabase environment signals - "SUPABASE_ACCESS_TOKEN", - "SUPABASE_HOME", - "SYSTEMROOT", - "SUPABASE_SSL_DEBUG", - "SUPABASE_CA_SKIP_VERIFY", - "SSL_CERT_FILE", - "SSL_CERT_DIR", - "NPM_CONFIG_REGISTRY", - "SUPABASE_SERVICE_ROLE_KEY", - "SUPABASE_PROJECT_ID", - "SUPABASE_POSTGRES_URL", - "SUPABASE_ENV", - } - - // EnvSignalValueKeys lists environment variables whose trimmed values may be - // recorded inside the "env_signals" property. - EnvSignalValueKeys = [...]string{ - "AI_AGENT", - "CURSOR_EXTENSION_HOST_ROLE", - "TERM", - "TERM_PROGRAM", - "TERM_PROGRAM_VERSION", - "TERM_COLOR_MODE", - } -) diff --git a/apps/cli-go/internal/telemetry/project.go b/apps/cli-go/internal/telemetry/project.go deleted file mode 100644 index a8ceafd1a7..0000000000 --- a/apps/cli-go/internal/telemetry/project.go +++ /dev/null @@ -1,109 +0,0 @@ -package telemetry - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - - "github.com/go-errors/errors" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/api" -) - -type LinkedProject struct { - Ref string `json:"ref"` - Name string `json:"name"` - OrganizationID string `json:"organization_id"` - OrganizationSlug string `json:"organization_slug"` -} - -func linkedProjectPath() string { - return filepath.Join(utils.TempDir, "linked-project.json") -} - -func SaveLinkedProject(project api.V1ProjectWithDatabaseResponseOutput, fsys afero.Fs) error { - linked := LinkedProject{ - Ref: project.Ref, - Name: project.Name, - OrganizationID: project.OrganizationId, - OrganizationSlug: project.OrganizationSlug, - } - contents, err := json.Marshal(linked) - if err != nil { - return errors.Errorf("failed to encode linked project: %w", err) - } - return utils.WriteFile(linkedProjectPath(), contents, fsys) -} - -func LoadLinkedProject(fsys afero.Fs) (LinkedProject, error) { - contents, err := afero.ReadFile(fsys, linkedProjectPath()) - if err != nil { - return LinkedProject{}, err - } - var linked LinkedProject - if err := json.Unmarshal(contents, &linked); err != nil { - return LinkedProject{}, errors.Errorf("failed to parse linked project: %w", err) - } - return linked, nil -} - -// HasLinkedProject reports whether a cached linked-project.json exists. -func HasLinkedProject(fsys afero.Fs) bool { - _, err := LoadLinkedProject(fsys) - return err == nil -} - -// CacheProjectAndIdentifyGroups writes project metadata to linked-project.json -// and fires GroupIdentify for the org and project so PostHog has group metadata. -// This matches the behavior of the `supabase link` flow. -// -// The caller is responsible for fetching the project from the API and checking -// auth — this function only handles caching and PostHog group identification. -// -// Best-effort: logs errors to debug output, never returns them. -func CacheProjectAndIdentifyGroups(project api.V1ProjectWithDatabaseResponseOutput, service *Service, fsys afero.Fs) { - if err := SaveLinkedProject(project, fsys); err != nil { - fmt.Fprintln(utils.GetDebugLogger(), err) - } - if service == nil { - return - } - if project.OrganizationId != "" { - if err := service.GroupIdentify(GroupOrganization, project.OrganizationId, map[string]any{ - "organization_slug": project.OrganizationSlug, - }); err != nil { - fmt.Fprintln(utils.GetDebugLogger(), err) - } - } - if project.Ref != "" { - if err := service.GroupIdentify(GroupProject, project.Ref, map[string]any{ - "name": project.Name, - "organization_slug": project.OrganizationSlug, - }); err != nil { - fmt.Fprintln(utils.GetDebugLogger(), err) - } - } -} - -func linkedProjectGroups(fsys afero.Fs) map[string]string { - linked, err := LoadLinkedProject(fsys) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - return nil - } - return nil - } - groups := make(map[string]string, 2) - if linked.OrganizationID != "" { - groups[GroupOrganization] = linked.OrganizationID - } - if linked.Ref != "" { - groups[GroupProject] = linked.Ref - } - if len(groups) == 0 { - return nil - } - return groups -} diff --git a/apps/cli-go/internal/telemetry/project_test.go b/apps/cli-go/internal/telemetry/project_test.go deleted file mode 100644 index 206855518a..0000000000 --- a/apps/cli-go/internal/telemetry/project_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package telemetry - -import ( - "testing" - "time" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/pkg/api" -) - -var testProject = api.V1ProjectWithDatabaseResponseOutput{ - Ref: "proj_abc", - Name: "My Project", - OrganizationId: "org_123", - OrganizationSlug: "acme", -} - -func newTestService(t *testing.T, fsys afero.Fs, analytics *fakeAnalytics) *Service { - t.Helper() - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return time.Date(2026, time.April, 15, 12, 0, 0, 0, time.UTC) }, - }) - require.NoError(t, err) - return service -} - -func TestHasLinkedProject(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - - t.Run("false when no cache", func(t *testing.T) { - fsys := afero.NewMemMapFs() - assert.False(t, HasLinkedProject(fsys)) - }) - - t.Run("true when cache exists", func(t *testing.T) { - fsys := afero.NewMemMapFs() - require.NoError(t, SaveLinkedProject(testProject, fsys)) - assert.True(t, HasLinkedProject(fsys)) - }) -} - -func TestCacheProjectAndIdentifyGroups(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - - t.Run("writes cache file", func(t *testing.T) { - fsys := afero.NewMemMapFs() - CacheProjectAndIdentifyGroups(testProject, nil, fsys) - - linked, err := LoadLinkedProject(fsys) - require.NoError(t, err) - assert.Equal(t, "proj_abc", linked.Ref) - assert.Equal(t, "org_123", linked.OrganizationID) - assert.Equal(t, "acme", linked.OrganizationSlug) - assert.Equal(t, "My Project", linked.Name) - }) - - t.Run("fires GroupIdentify for org and project", func(t *testing.T) { - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - service := newTestService(t, fsys, analytics) - - CacheProjectAndIdentifyGroups(testProject, service, fsys) - - require.Len(t, analytics.groupIdentifies, 2) - - orgCall := analytics.groupIdentifies[0] - assert.Equal(t, GroupOrganization, orgCall.groupType) - assert.Equal(t, "org_123", orgCall.groupKey) - assert.Equal(t, "acme", orgCall.properties["organization_slug"]) - - projCall := analytics.groupIdentifies[1] - assert.Equal(t, GroupProject, projCall.groupType) - assert.Equal(t, "proj_abc", projCall.groupKey) - assert.Equal(t, "My Project", projCall.properties["name"]) - assert.Equal(t, "acme", projCall.properties["organization_slug"]) - }) - - t.Run("skips GroupIdentify when service is nil", func(t *testing.T) { - fsys := afero.NewMemMapFs() - CacheProjectAndIdentifyGroups(testProject, nil, fsys) - - // Cache should still be written - linked, err := LoadLinkedProject(fsys) - require.NoError(t, err) - assert.Equal(t, "proj_abc", linked.Ref) - }) - - t.Run("skips GroupIdentify for empty org ID", func(t *testing.T) { - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - service := newTestService(t, fsys, analytics) - - noOrgProject := api.V1ProjectWithDatabaseResponseOutput{ - Ref: "proj_abc", - Name: "My Project", - } - CacheProjectAndIdentifyGroups(noOrgProject, service, fsys) - - // Only project GroupIdentify, no org - require.Len(t, analytics.groupIdentifies, 1) - assert.Equal(t, GroupProject, analytics.groupIdentifies[0].groupType) - }) -} - -func TestLinkedProjectGroups(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - - t.Run("returns nil when no cache", func(t *testing.T) { - fsys := afero.NewMemMapFs() - groups := linkedProjectGroups(fsys) - assert.Nil(t, groups) - }) - - t.Run("returns groups from cache", func(t *testing.T) { - fsys := afero.NewMemMapFs() - require.NoError(t, SaveLinkedProject(testProject, fsys)) - groups := linkedProjectGroups(fsys) - assert.Equal(t, map[string]string{ - GroupOrganization: "org_123", - GroupProject: "proj_abc", - }, groups) - }) -} diff --git a/apps/cli-go/internal/telemetry/service.go b/apps/cli-go/internal/telemetry/service.go deleted file mode 100644 index ced8a8ee01..0000000000 --- a/apps/cli-go/internal/telemetry/service.go +++ /dev/null @@ -1,294 +0,0 @@ -package telemetry - -import ( - "context" - "os" - "runtime" - "time" - - "github.com/google/uuid" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils" -) - -type commandContextKey struct{} -type serviceContextKey struct{} - -type CommandContext struct { - RunID string - Command string - Flags map[string]any - Groups map[string]string -} - -type Options struct { - Analytics Analytics - Now func() time.Time - IsTTY bool - IsCI bool - IsAgent bool - EnvSignals map[string]any - CLIName string - GOOS string - GOARCH string -} - -type Service struct { - fsys afero.Fs - analytics Analytics - now func() time.Time - state State - userID string - isFirstRun bool - isTTY bool - isCI bool - isAgent bool - envSignals map[string]any - cliVersion string - goos string - goarch string -} - -func NewService(fsys afero.Fs, opts Options) (*Service, error) { - now := opts.Now - if now == nil { - now = time.Now - } - state, created, err := LoadOrCreateState(fsys, now()) - if err != nil { - return nil, err - } - analytics := opts.Analytics - if analytics == nil { - analytics, err = NewClient(utils.PostHogAPIKey, utils.PostHogEndpoint, nil, nil) - if err != nil { - return nil, err - } - } - cliVersion := opts.CLIName - if cliVersion == "" { - cliVersion = utils.Version - } - goos := opts.GOOS - if goos == "" { - goos = runtime.GOOS - } - goarch := opts.GOARCH - if goarch == "" { - goarch = runtime.GOARCH - } - return &Service{ - fsys: fsys, - analytics: analytics, - now: now, - state: state, - isFirstRun: created, - isTTY: opts.IsTTY, - isCI: opts.IsCI, - isAgent: opts.IsAgent, - envSignals: opts.EnvSignals, - cliVersion: cliVersion, - goos: goos, - goarch: goarch, - }, nil -} - -func WithCommandContext(ctx context.Context, cmd CommandContext) context.Context { - return context.WithValue(ctx, commandContextKey{}, cmd) -} - -func WithService(ctx context.Context, service *Service) context.Context { - return context.WithValue(ctx, serviceContextKey{}, service) -} - -func FromContext(ctx context.Context) *Service { - if ctx == nil { - return nil - } - service, _ := ctx.Value(serviceContextKey{}).(*Service) - return service -} - -// Property catalog: see events.go. -func (s *Service) Capture(ctx context.Context, event string, properties map[string]any, groups map[string]string) error { - if !s.canSend() { - return nil - } - mergedProperties := s.baseProperties() - command := commandContextFrom(ctx) - if command.RunID != "" { - mergedProperties[PropCommandRunID] = command.RunID - } - if command.Command != "" { - mergedProperties[PropCommand] = command.Command - } - if command.Flags != nil { - mergedProperties[PropFlags] = command.Flags - } - for key, value := range properties { - mergedProperties[key] = value - } - return s.analytics.Capture(s.distinctID(), event, mergedProperties, mergeGroups(linkedProjectGroups(s.fsys), mergeGroups(command.Groups, groups))) -} - -// StitchLogin records the authenticated user as the identity for all -// subsequent captures in this process. In persistent runtimes it also merges -// the device's pre-login history via $create_alias and persists the identity -// for future runs; ephemeral runtimes get the in-memory stamp only. -// See docs/adr/0013-hybrid-stitch-stamp-identity-attribution.md. -func (s *Service) StitchLogin(distinctID string) error { - if s == nil { - return nil - } - // Alias only the first identity this device ever sees. Re-aliasing on - // re-login (or on the login command's call after the response hook has - // already stitched) would merge a second user into the device's existing - // person graph in PostHog. - firstIdentity := s.state.DistinctID == "" - s.userID = distinctID - if s.isEphemeralIdentityRuntime() { - return nil - } - if firstIdentity && s.canSend() { - if err := s.analytics.Alias(distinctID, s.state.DeviceID); err != nil { - // Leave the identity re-stitchable without dropping the in-memory - // stamp: nothing was enqueued, so a retry (e.g. the login command - // after the response hook errored) must still qualify as the first - // identity, but captures in this process should remain attributed. - return err - } - } - s.state.DistinctID = distinctID - return SaveState(s.state, s.fsys) -} - -// ObserveAuthenticatedUser records who the current process is authenticated -// as. First-time identities get the full stitch; when an identity already -// exists (e.g. telemetry.json holds a previous user but the active token -// belongs to another), only the in-memory stamp is updated — re-aliasing the -// device to a second user would merge unrelated person graphs in PostHog. -func (s *Service) ObserveAuthenticatedUser(distinctID string) error { - if s == nil { - return nil - } - if s.NeedsIdentityStitch() { - return s.StitchLogin(distinctID) - } - s.userID = distinctID - return nil -} - -// ResetIdentity severs the link between this device and the logged-out user: -// the identity is forgotten and the device ID is rotated, so a later login as -// a different account aliases a fresh device instead of one already merged -// into the previous user's person graph. Logout-only — transient failure -// paths use ClearDistinctID, which keeps the device ID. -func (s *Service) ResetIdentity() error { - if s == nil { - return nil - } - s.userID = "" - s.state.DistinctID = "" - s.state.DeviceID = uuid.NewString() - return SaveState(s.state, s.fsys) -} - -func (s *Service) ClearDistinctID() error { - if s == nil { - return nil - } - s.userID = "" - s.state.DistinctID = "" - return SaveState(s.state, s.fsys) -} - -func (s *Service) NeedsIdentityStitch() bool { - return s != nil && s.userID == "" && s.state.DistinctID == "" && s.canSend() -} - -func (s *Service) isEphemeralIdentityRuntime() bool { - return s.isCI || (s.isFirstRun && !s.isTTY) -} - -func (s *Service) GroupIdentify(groupType string, groupKey string, properties map[string]any) error { - if !s.canSend() { - return nil - } - return s.analytics.GroupIdentify(groupType, groupKey, s.basePropertiesWith(properties)) -} - -func (s *Service) Close() error { - if s == nil || s.analytics == nil { - return nil - } - return s.analytics.Close() -} - -func (s *Service) baseProperties() map[string]any { - properties := map[string]any{ - PropPlatform: "cli", - PropSchemaVersion: s.state.SchemaVersion, - PropDeviceID: s.state.DeviceID, - PropSessionID: s.state.SessionID, - PropIsFirstRun: s.isFirstRun, - PropIsTTY: s.isTTY, - PropIsCI: s.isCI, - PropIsAgent: s.isAgent, - PropOS: s.goos, - PropArch: s.goarch, - PropCLIVersion: s.cliVersion, - } - if len(s.envSignals) > 0 { - properties[PropEnvSignals] = s.envSignals - } - return properties -} - -func (s *Service) basePropertiesWith(properties map[string]any) map[string]any { - merged := s.baseProperties() - for key, value := range properties { - merged[key] = value - } - return merged -} - -func (s *Service) distinctID() string { - if s.userID != "" { - return s.userID - } - if s.state.DistinctID != "" { - return s.state.DistinctID - } - return s.state.DeviceID -} - -func commandContextFrom(ctx context.Context) CommandContext { - if ctx == nil { - return CommandContext{} - } - cmd, _ := ctx.Value(commandContextKey{}).(CommandContext) - return cmd -} - -func mergeGroups(existing map[string]string, extra map[string]string) map[string]string { - if len(existing) == 0 && len(extra) == 0 { - return nil - } - merged := make(map[string]string, len(existing)+len(extra)) - for key, value := range existing { - merged[key] = value - } - for key, value := range extra { - merged[key] = value - } - return merged -} - -func (s *Service) canSend() bool { - return s != nil && - s.analytics != nil && - s.analytics.Enabled() && - s.state.Enabled && - os.Getenv("DO_NOT_TRACK") != "1" && - os.Getenv("SUPABASE_TELEMETRY_DISABLED") != "1" -} diff --git a/apps/cli-go/internal/telemetry/service_test.go b/apps/cli-go/internal/telemetry/service_test.go deleted file mode 100644 index e2fe7b2fda..0000000000 --- a/apps/cli-go/internal/telemetry/service_test.go +++ /dev/null @@ -1,570 +0,0 @@ -package telemetry - -import ( - "context" - "testing" - "time" - - "github.com/google/uuid" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/pkg/api" -) - -type captureCall struct { - distinctID string - event string - properties map[string]any - groups map[string]string -} - -type identifyCall struct { - distinctID string - properties map[string]any -} - -type aliasCall struct { - distinctID string - alias string -} - -type groupIdentifyCall struct { - groupType string - groupKey string - properties map[string]any -} - -type fakeAnalytics struct { - enabled bool - captures []captureCall - identifies []identifyCall - aliases []aliasCall - groupIdentifies []groupIdentifyCall - aliasErr error - closed bool -} - -func (f *fakeAnalytics) Enabled() bool { return f.enabled } - -func (f *fakeAnalytics) Capture(distinctID string, event string, properties map[string]any, groups map[string]string) error { - f.captures = append(f.captures, captureCall{distinctID: distinctID, event: event, properties: properties, groups: groups}) - return nil -} - -func (f *fakeAnalytics) Identify(distinctID string, properties map[string]any) error { - f.identifies = append(f.identifies, identifyCall{distinctID: distinctID, properties: properties}) - return nil -} - -func (f *fakeAnalytics) Alias(distinctID string, alias string) error { - if f.aliasErr != nil { - err := f.aliasErr - f.aliasErr = nil - return err - } - f.aliases = append(f.aliases, aliasCall{distinctID: distinctID, alias: alias}) - return nil -} - -func (f *fakeAnalytics) GroupIdentify(groupType string, groupKey string, properties map[string]any) error { - f.groupIdentifies = append(f.groupIdentifies, groupIdentifyCall{groupType: groupType, groupKey: groupKey, properties: properties}) - return nil -} - -func (f *fakeAnalytics) Close() error { - f.closed = true - return nil -} - -func TestServiceCaptureIncludesBasePropertiesAndCommandContext(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - IsTTY: true, - IsCI: true, - IsAgent: true, - EnvSignals: map[string]any{ - "CLAUDE_CODE": true, - "TERM_PROGRAM": "iTerm.app", - }, - CLIName: "1.2.3", - GOOS: "darwin", - GOARCH: "arm64", - }) - require.NoError(t, err) - - ctx := WithCommandContext(context.Background(), CommandContext{ - RunID: "run-123", - Command: "login", - Flags: map[string]any{ - "token": "", - }, - }) - - require.NoError(t, service.Capture(ctx, EventCommandExecuted, map[string]any{ - PropDurationMs: 42, - }, nil)) - - require.Len(t, analytics.captures, 1) - call := analytics.captures[0] - assert.NoError(t, uuid.Validate(call.distinctID)) - assert.Equal(t, EventCommandExecuted, call.event) - assert.Equal(t, "cli", call.properties[PropPlatform]) - assert.Equal(t, SchemaVersion, call.properties[PropSchemaVersion]) - assert.Equal(t, true, call.properties[PropIsFirstRun]) - assert.Equal(t, true, call.properties[PropIsTTY]) - assert.Equal(t, true, call.properties[PropIsCI]) - assert.Equal(t, true, call.properties[PropIsAgent]) - assert.Equal(t, map[string]any{ - "CLAUDE_CODE": true, - "TERM_PROGRAM": "iTerm.app", - }, call.properties[PropEnvSignals]) - assert.Equal(t, "darwin", call.properties[PropOS]) - assert.Equal(t, "arm64", call.properties[PropArch]) - assert.Equal(t, "1.2.3", call.properties[PropCLIVersion]) - assert.Equal(t, "run-123", call.properties[PropCommandRunID]) - assert.Equal(t, "login", call.properties[PropCommand]) - assert.Equal(t, map[string]any{"token": ""}, call.properties[PropFlags]) - _, hasFlagsUsed := call.properties["flags_used"] - assert.False(t, hasFlagsUsed) - _, hasFlagValues := call.properties["flag_values"] - assert.False(t, hasFlagValues) - assert.Equal(t, 42, call.properties[PropDurationMs]) -} - -func TestServiceStitchLoginPersistsDistinctID(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - IsTTY: true, - }) - require.NoError(t, err) - deviceID := service.state.DeviceID - - require.NoError(t, service.StitchLogin("user-123")) - require.NoError(t, service.Capture(context.Background(), EventLoginCompleted, nil, nil)) - - require.Len(t, analytics.aliases, 1) - assert.Equal(t, "user-123", analytics.aliases[0].distinctID) - assert.Equal(t, deviceID, analytics.aliases[0].alias) - assert.Empty(t, analytics.identifies) - require.Len(t, analytics.captures, 1) - assert.Equal(t, "user-123", analytics.captures[0].distinctID) - - state, err := LoadState(fsys) - require.NoError(t, err) - assert.Equal(t, "user-123", state.DistinctID) -} - -func TestServiceStitchLoginInEphemeralRuntimeStampsWithoutPersisting(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - IsCI: true, - }) - require.NoError(t, err) - - require.NoError(t, service.StitchLogin("user-123")) - require.NoError(t, service.Capture(context.Background(), EventCommandExecuted, nil, nil)) - - require.Len(t, analytics.captures, 1) - assert.Equal(t, "user-123", analytics.captures[0].distinctID) - assert.Empty(t, analytics.aliases) - assert.Empty(t, analytics.identifies) - - state, err := LoadState(fsys) - require.NoError(t, err) - assert.Empty(t, state.DistinctID) -} - -func TestServiceObserveAuthenticatedUser(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - - t.Run("stamps over a stale persisted identity without alias or state write", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - require.NoError(t, SaveState(State{ - Enabled: true, - DeviceID: uuid.NewString(), - SessionID: uuid.NewString(), - SessionLastActive: now, - SchemaVersion: SchemaVersion, - DistinctID: "old-user", - }, fsys)) - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - IsTTY: true, - }) - require.NoError(t, err) - - require.NoError(t, service.ObserveAuthenticatedUser("new-user")) - require.NoError(t, service.Capture(context.Background(), EventCommandExecuted, nil, nil)) - - assert.Empty(t, analytics.aliases) - assert.Empty(t, analytics.identifies) - require.Len(t, analytics.captures, 1) - assert.Equal(t, "new-user", analytics.captures[0].distinctID) - - state, err := LoadState(fsys) - require.NoError(t, err) - assert.Equal(t, "old-user", state.DistinctID) - }) - - t.Run("performs the full stitch when no identity exists yet", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - IsTTY: true, - }) - require.NoError(t, err) - - require.NoError(t, service.ObserveAuthenticatedUser("user-123")) - - require.Len(t, analytics.aliases, 1) - assert.Equal(t, "user-123", analytics.aliases[0].distinctID) - state, err := LoadState(fsys) - require.NoError(t, err) - assert.Equal(t, "user-123", state.DistinctID) - }) -} - -func TestServiceStitchLoginReloginDoesNotRealias(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - require.NoError(t, SaveState(State{ - Enabled: true, - DeviceID: uuid.NewString(), - SessionID: uuid.NewString(), - SessionLastActive: now, - SchemaVersion: SchemaVersion, - DistinctID: "user-a", - }, fsys)) - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - IsTTY: true, - }) - require.NoError(t, err) - - require.NoError(t, service.StitchLogin("user-b")) - - assert.Empty(t, analytics.aliases) - state, err := LoadState(fsys) - require.NoError(t, err) - assert.Equal(t, "user-b", state.DistinctID) - - require.NoError(t, service.Capture(context.Background(), EventLoginCompleted, nil, nil)) - require.Len(t, analytics.captures, 1) - assert.Equal(t, "user-b", analytics.captures[0].distinctID) -} - -func TestServiceStitchLoginIsIdempotentWithinProcess(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - IsTTY: true, - }) - require.NoError(t, err) - - // The response hook stitches first; the login command then calls - // StitchLogin directly with the same id. One alias total. - require.NoError(t, service.ObserveAuthenticatedUser("user-123")) - require.NoError(t, service.StitchLogin("user-123")) - - require.Len(t, analytics.aliases, 1) -} - -func TestServiceResetIdentityRotatesDeviceID(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - IsTTY: true, - }) - require.NoError(t, err) - require.NoError(t, service.StitchLogin("user-a")) - require.Len(t, analytics.aliases, 1) - oldDeviceID := analytics.aliases[0].alias - - require.NoError(t, service.ResetIdentity()) - - state, err := LoadState(fsys) - require.NoError(t, err) - assert.Empty(t, state.DistinctID) - assert.NotEqual(t, oldDeviceID, state.DeviceID) - assert.NoError(t, uuid.Validate(state.DeviceID)) - - // A later login as another user aliases the fresh device id, so the old - // user's person graph is never touched. - require.NoError(t, service.StitchLogin("user-b")) - require.Len(t, analytics.aliases, 2) - assert.Equal(t, state.DeviceID, analytics.aliases[1].alias) - - require.NoError(t, service.Capture(context.Background(), EventCommandExecuted, nil, nil)) - assert.Equal(t, "user-b", analytics.captures[len(analytics.captures)-1].distinctID) -} - -func TestServiceStitchLoginRetriesAliasAfterEnqueueFailure(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true, aliasErr: assert.AnError} - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - IsTTY: true, - }) - require.NoError(t, err) - - require.Error(t, service.StitchLogin("user-123")) - require.NoError(t, service.Capture(context.Background(), EventLoginCompleted, nil, nil)) - require.Len(t, analytics.captures, 1) - assert.Equal(t, "user-123", analytics.captures[0].distinctID) - state, err := LoadState(fsys) - require.NoError(t, err) - assert.Empty(t, state.DistinctID) - - // The failed attempt must not poison the first-identity gate: a retry - // (e.g. the login command after the response hook errored) still aliases. - require.NoError(t, service.StitchLogin("user-123")) - require.Len(t, analytics.aliases, 1) - state, err = LoadState(fsys) - require.NoError(t, err) - assert.Equal(t, "user-123", state.DistinctID) -} - -func TestServiceCapturePrefersInMemoryUserIDOverPersistedDistinctID(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - require.NoError(t, SaveState(State{ - Enabled: true, - DeviceID: uuid.NewString(), - SessionID: uuid.NewString(), - SessionLastActive: now, - SchemaVersion: SchemaVersion, - DistinctID: "old-user", - }, fsys)) - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - IsCI: true, - }) - require.NoError(t, err) - - require.NoError(t, service.StitchLogin("new-user")) - require.NoError(t, service.Capture(context.Background(), EventLoginCompleted, nil, nil)) - - require.Len(t, analytics.captures, 1) - assert.Equal(t, "new-user", analytics.captures[0].distinctID) - - state, err := LoadState(fsys) - require.NoError(t, err) - assert.Equal(t, "old-user", state.DistinctID) -} - -func TestServiceClearDistinctIDFallsBackToDeviceID(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - }) - require.NoError(t, err) - deviceID := service.state.DeviceID - require.NoError(t, service.StitchLogin("user-123")) - - require.NoError(t, service.ClearDistinctID()) - require.NoError(t, service.Capture(context.Background(), EventLoginCompleted, nil, nil)) - - require.Len(t, analytics.captures, 1) - assert.Equal(t, deviceID, analytics.captures[0].distinctID) - - state, err := LoadState(fsys) - require.NoError(t, err) - assert.Empty(t, state.DistinctID) -} - -func TestServiceCaptureIncludesLinkedProjectGroups(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - require.NoError(t, SaveLinkedProject(api.V1ProjectWithDatabaseResponseOutput{ - Ref: "proj_123", - Name: "My Project", - OrganizationId: "org_123", - OrganizationSlug: "acme", - }, fsys)) - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - }) - require.NoError(t, err) - - require.NoError(t, service.Capture(context.Background(), EventStackStarted, nil, nil)) - - require.Len(t, analytics.captures, 1) - assert.Equal(t, map[string]string{ - GroupOrganization: "org_123", - GroupProject: "proj_123", - }, analytics.captures[0].groups) -} - -func TestServiceNeedsIdentityStitch(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - IsTTY: true, - }) - require.NoError(t, err) - - t.Run("true when DistinctID is empty", func(t *testing.T) { - assert.True(t, service.NeedsIdentityStitch()) - }) - - t.Run("false after StitchLogin", func(t *testing.T) { - require.NoError(t, service.StitchLogin("user-123")) - assert.False(t, service.NeedsIdentityStitch()) - }) - - t.Run("true in CI with empty DistinctID so capture stamping can start", func(t *testing.T) { - ciFsys := afero.NewMemMapFs() - ciService, err := NewService(ciFsys, Options{ - Analytics: &fakeAnalytics{enabled: true}, - Now: func() time.Time { return now }, - IsCI: true, - }) - require.NoError(t, err) - assert.True(t, ciService.NeedsIdentityStitch()) - }) - - t.Run("false after StitchLogin in ephemeral runtime despite nothing persisted", func(t *testing.T) { - ephemeralFsys := afero.NewMemMapFs() - ephemeralService, err := NewService(ephemeralFsys, Options{ - Analytics: &fakeAnalytics{enabled: true}, - Now: func() time.Time { return now }, - IsCI: true, - }) - require.NoError(t, err) - require.NoError(t, ephemeralService.StitchLogin("user-123")) - assert.False(t, ephemeralService.NeedsIdentityStitch()) - }) - - t.Run("true in first-run non-TTY runtime", func(t *testing.T) { - ephemeralFsys := afero.NewMemMapFs() - ephemeralService, err := NewService(ephemeralFsys, Options{ - Analytics: &fakeAnalytics{enabled: true}, - Now: func() time.Time { return now }, - }) - require.NoError(t, err) - assert.True(t, ephemeralService.NeedsIdentityStitch()) - }) - - t.Run("true in persisted non-TTY runtime", func(t *testing.T) { - persistedFsys := afero.NewMemMapFs() - require.NoError(t, SaveState(State{ - Enabled: true, - DeviceID: uuid.NewString(), - SessionID: uuid.NewString(), - SessionLastActive: now, - SchemaVersion: SchemaVersion, - }, persistedFsys)) - - persistedService, err := NewService(persistedFsys, Options{ - Analytics: &fakeAnalytics{enabled: true}, - Now: func() time.Time { return now }, - }) - require.NoError(t, err) - assert.True(t, persistedService.NeedsIdentityStitch()) - }) -} - -func TestServiceCaptureHonorsConsentAndEnvOptOut(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - - t.Run("disabled telemetry file suppresses capture", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - require.NoError(t, SaveState(State{ - Enabled: false, - DeviceID: uuid.NewString(), - SessionID: uuid.NewString(), - SessionLastActive: now, - SchemaVersion: SchemaVersion, - }, fsys)) - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - }) - require.NoError(t, err) - - require.NoError(t, service.Capture(context.Background(), EventCommandExecuted, nil, nil)) - assert.Empty(t, analytics.captures) - }) - - t.Run("DO_NOT_TRACK suppresses capture", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - t.Setenv("DO_NOT_TRACK", "1") - fsys := afero.NewMemMapFs() - analytics := &fakeAnalytics{enabled: true} - - service, err := NewService(fsys, Options{ - Analytics: analytics, - Now: func() time.Time { return now }, - }) - require.NoError(t, err) - - require.NoError(t, service.Capture(context.Background(), EventCommandExecuted, nil, nil)) - assert.Empty(t, analytics.captures) - }) -} diff --git a/apps/cli-go/internal/telemetry/state.go b/apps/cli-go/internal/telemetry/state.go deleted file mode 100644 index 3808abb78a..0000000000 --- a/apps/cli-go/internal/telemetry/state.go +++ /dev/null @@ -1,192 +0,0 @@ -package telemetry - -import ( - "encoding/json" - "os" - "path/filepath" - "time" - - "github.com/go-errors/errors" - "github.com/google/uuid" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils" -) - -const SchemaVersion = 1 - -const sessionRotationThreshold = 30 * time.Minute - -// errMalformedState marks any read where the file existed but couldn't be -// decoded into a State — covers JSON syntax errors, unexpected types -// (e.g. session_last_active stored as a number), and field-level unmarshal -// failures from time.Time / uuid. Used to trigger fresh-state creation. -var errMalformedState = errors.New("malformed telemetry state") - -type State struct { - Enabled bool `json:"enabled"` - DeviceID string `json:"device_id"` - SessionID string `json:"session_id"` - SessionLastActive time.Time `json:"session_last_active"` - DistinctID string `json:"distinct_id,omitempty"` - SchemaVersion int `json:"schema_version"` -} - -type rawState struct { - Enabled *bool `json:"enabled"` - Consent *string `json:"consent"` - DeviceID string `json:"device_id"` - SessionID string `json:"session_id"` - SessionLastActive json.RawMessage `json:"session_last_active"` - DistinctID string `json:"distinct_id,omitempty"` - SchemaVersion int `json:"schema_version"` -} - -func telemetryPath() (string, error) { - home, err := utils.SupabaseHomeDir() - if err != nil { - return "", err - } - return filepath.Join(home, "telemetry.json"), nil -} - -func parseConsent(raw rawState) (bool, bool, error) { - if raw.Consent != nil { - switch *raw.Consent { - case "granted": - return true, true, nil - case "denied": - return false, true, nil - default: - return false, false, errors.Errorf("%w: invalid consent", errMalformedState) - } - } - if raw.Enabled == nil { - return false, false, errors.Errorf("%w: missing enabled", errMalformedState) - } - return *raw.Enabled, false, nil -} - -func parseSessionLastActive(raw json.RawMessage, allowUnixMillis bool) (time.Time, error) { - var text string - if err := json.Unmarshal(raw, &text); err == nil { - parsed, err := time.Parse(time.RFC3339Nano, text) - if err != nil { - return time.Time{}, errors.Errorf("%w: invalid session_last_active", errMalformedState) - } - return parsed, nil - } - if allowUnixMillis { - var millis int64 - if err := json.Unmarshal(raw, &millis); err == nil { - return time.UnixMilli(millis).UTC(), nil - } - } - return time.Time{}, errors.Errorf("%w: invalid session_last_active", errMalformedState) -} - -func decodeState(contents []byte) (State, error) { - var raw rawState - if err := json.Unmarshal(contents, &raw); err != nil { - return State{}, errors.Errorf("%w: %v", errMalformedState, err) - } - enabled, allowUnixMillis, err := parseConsent(raw) - if err != nil { - return State{}, err - } - sessionLastActive, err := parseSessionLastActive(raw.SessionLastActive, allowUnixMillis) - if err != nil { - return State{}, err - } - if raw.DeviceID == "" || raw.SessionID == "" { - return State{}, errors.Errorf("%w: missing identity", errMalformedState) - } - schemaVersion := raw.SchemaVersion - if schemaVersion == 0 { - schemaVersion = SchemaVersion - } - return State{ - Enabled: enabled, - DeviceID: raw.DeviceID, - SessionID: raw.SessionID, - SessionLastActive: sessionLastActive, - DistinctID: raw.DistinctID, - SchemaVersion: schemaVersion, - }, nil -} - -func LoadState(fsys afero.Fs) (State, error) { - path, err := telemetryPath() - if err != nil { - return State{}, err - } - contents, err := afero.ReadFile(fsys, path) - if err != nil { - return State{}, err - } - return decodeState(contents) -} - -func SaveState(state State, fsys afero.Fs) error { - path, err := telemetryPath() - if err != nil { - return err - } - contents, err := json.Marshal(state) - if err != nil { - return errors.Errorf("failed to encode telemetry file: %w", err) - } - return utils.WriteFile(path, contents, fsys) -} - -func LoadOrCreateState(fsys afero.Fs, now time.Time) (State, bool, error) { - state, err := LoadState(fsys) - if err == nil { - if now.UTC().Sub(state.SessionLastActive) > sessionRotationThreshold { - state.SessionID = uuid.NewString() - } - state.SessionLastActive = now.UTC() - return state, false, SaveState(state, fsys) - } - // Treat a missing file OR an unparseable file as "no existing state" and - // recreate. Identity fields (device_id, session_id) are not worth - // surfacing an error for — losing them is harmless. We only propagate - // genuine I/O errors (permissions, disk full) so the user can act. - if !errors.Is(err, os.ErrNotExist) && !errors.Is(err, errMalformedState) { - return State{}, false, err - } - state = State{ - Enabled: true, - DeviceID: uuid.NewString(), - SessionID: uuid.NewString(), - SessionLastActive: now.UTC(), - SchemaVersion: SchemaVersion, - } - return state, true, SaveState(state, fsys) -} - -func Disabled(fsys afero.Fs, now time.Time) (bool, error) { - if os.Getenv("DO_NOT_TRACK") == "1" { - return true, nil - } - if os.Getenv("SUPABASE_TELEMETRY_DISABLED") == "1" { - return true, nil - } - state, _, err := LoadOrCreateState(fsys, now) - if err != nil { - return false, err - } - return !state.Enabled, nil -} - -func SetEnabled(fsys afero.Fs, enabled bool, now time.Time) (State, error) { - state, _, err := LoadOrCreateState(fsys, now) - if err != nil { - return State{}, err - } - state.Enabled = enabled - return state, SaveState(state, fsys) -} - -func Status(fsys afero.Fs, now time.Time) (State, bool, error) { - return LoadOrCreateState(fsys, now) -} diff --git a/apps/cli-go/internal/telemetry/state_test.go b/apps/cli-go/internal/telemetry/state_test.go deleted file mode 100644 index 7ca3a178c6..0000000000 --- a/apps/cli-go/internal/telemetry/state_test.go +++ /dev/null @@ -1,289 +0,0 @@ -package telemetry - -import ( - "testing" - "time" - - "github.com/google/uuid" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestTelemetryPath(t *testing.T) { - t.Run("uses SUPABASE_HOME when set", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - t.Setenv("HOME", "/tmp/ignored-home") - - path, err := telemetryPath() - - require.NoError(t, err) - assert.Equal(t, "/tmp/supabase-home/telemetry.json", path) - }) - - t.Run("falls back to HOME/.supabase", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "") - t.Setenv("HOME", "/tmp/home") - - path, err := telemetryPath() - - require.NoError(t, err) - assert.Equal(t, "/tmp/home/.supabase/telemetry.json", path) - }) -} - -func TestLoadOrCreateState(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - - t.Run("creates default state and writes it", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - - state, created, err := LoadOrCreateState(fsys, now) - - require.NoError(t, err) - assert.True(t, created) - assert.True(t, state.Enabled) - assert.Equal(t, SchemaVersion, state.SchemaVersion) - assert.Equal(t, now, state.SessionLastActive) - assert.Empty(t, state.DistinctID) - assert.NoError(t, uuid.Validate(state.DeviceID)) - assert.NoError(t, uuid.Validate(state.SessionID)) - - saved, err := LoadState(fsys) - require.NoError(t, err) - assert.Equal(t, state, saved) - }) - - t.Run("updates last active and preserves existing state", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - initial := State{ - Enabled: false, - DeviceID: uuid.NewString(), - SessionID: uuid.NewString(), - SessionLastActive: now.Add(-10 * time.Minute), - DistinctID: "user-123", - SchemaVersion: SchemaVersion, - } - require.NoError(t, SaveState(initial, fsys)) - - state, created, err := LoadOrCreateState(fsys, now) - - require.NoError(t, err) - assert.False(t, created) - assert.False(t, state.Enabled) - assert.Equal(t, initial.DeviceID, state.DeviceID) - assert.Equal(t, initial.SessionID, state.SessionID) - assert.Equal(t, "user-123", state.DistinctID) - assert.Equal(t, now, state.SessionLastActive) - }) - - t.Run("reads disabled TypeScript telemetry config", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - path, err := telemetryPath() - require.NoError(t, err) - require.NoError(t, fsys.MkdirAll("/tmp/supabase-home", 0755)) - require.NoError(t, afero.WriteFile( - fsys, - path, - []byte(`{"consent":"denied","device_id":"ts-device","session_id":"ts-session","session_last_active":1776770348993,"distinct_id":"user-123"}`), - 0644, - )) - - state, created, err := LoadOrCreateState(fsys, now) - - require.NoError(t, err) - assert.False(t, created) - assert.False(t, state.Enabled) - assert.Equal(t, "ts-device", state.DeviceID) - assert.Equal(t, "ts-session", state.SessionID) - assert.Equal(t, "user-123", state.DistinctID) - assert.Equal(t, SchemaVersion, state.SchemaVersion) - }) - - t.Run("reads enabled TypeScript telemetry config", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - path, err := telemetryPath() - require.NoError(t, err) - require.NoError(t, fsys.MkdirAll("/tmp/supabase-home", 0755)) - require.NoError(t, afero.WriteFile( - fsys, - path, - []byte(`{"consent":"granted","device_id":"ts-device","session_id":"ts-session","session_last_active":1776770348993,"distinct_id":"user-123"}`), - 0644, - )) - - state, created, err := LoadOrCreateState(fsys, now) - - require.NoError(t, err) - assert.False(t, created) - assert.True(t, state.Enabled) - assert.Equal(t, "ts-device", state.DeviceID) - assert.Equal(t, "ts-session", state.SessionID) - assert.Equal(t, "user-123", state.DistinctID) - assert.Equal(t, SchemaVersion, state.SchemaVersion) - }) - - t.Run("recovers from corrupted state file", func(t *testing.T) { - // Each entry simulates a real-world corruption shape we've observed. - corruptions := map[string][]byte{ - "empty file": {}, - "truncated json": []byte(`{"enabled":tru`), - "session_last_active is a number (not a string)": []byte(`{"enabled":true,"device_id":"d","session_id":"s","session_last_active":1776770348993,"schema_version":1}`), - "session_last_active is a malformed string": []byte(`{"enabled":true,"device_id":"d","session_id":"s","session_last_active":"not-a-time","schema_version":1}`), - } - for label, contents := range corruptions { - t.Run(label, func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - path, err := telemetryPath() - require.NoError(t, err) - require.NoError(t, fsys.MkdirAll("/tmp/supabase-home", 0755)) - require.NoError(t, afero.WriteFile(fsys, path, contents, 0644)) - - state, created, err := LoadOrCreateState(fsys, now) - - require.NoError(t, err) - assert.True(t, created) - assert.True(t, state.Enabled) - assert.Equal(t, SchemaVersion, state.SchemaVersion) - assert.NoError(t, uuid.Validate(state.DeviceID)) - assert.NoError(t, uuid.Validate(state.SessionID)) - saved, err := LoadState(fsys) - require.NoError(t, err) - assert.Equal(t, state, saved) - }) - } - }) - - t.Run("rotates stale session after inactivity threshold", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - initial := State{ - Enabled: true, - DeviceID: uuid.NewString(), - SessionID: uuid.NewString(), - SessionLastActive: now.Add(-(sessionRotationThreshold + time.Minute)), - DistinctID: "user-123", - SchemaVersion: SchemaVersion, - } - require.NoError(t, SaveState(initial, fsys)) - - state, created, err := LoadOrCreateState(fsys, now) - - require.NoError(t, err) - assert.False(t, created) - assert.Equal(t, initial.DeviceID, state.DeviceID) - assert.NotEqual(t, initial.SessionID, state.SessionID) - assert.Equal(t, "user-123", state.DistinctID) - assert.Equal(t, now, state.SessionLastActive) - - saved, err := LoadState(fsys) - require.NoError(t, err) - assert.Equal(t, state, saved) - }) -} - -func TestTelemetryDisabled(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - - t.Run("honors DO_NOT_TRACK", func(t *testing.T) { - t.Setenv("DO_NOT_TRACK", "1") - fsys := afero.NewMemMapFs() - - disabled, err := Disabled(fsys, now) - - require.NoError(t, err) - assert.True(t, disabled) - }) - - t.Run("honors SUPABASE_TELEMETRY_DISABLED", func(t *testing.T) { - t.Setenv("SUPABASE_TELEMETRY_DISABLED", "1") - fsys := afero.NewMemMapFs() - - disabled, err := Disabled(fsys, now) - - require.NoError(t, err) - assert.True(t, disabled) - }) - - t.Run("honors disabled state file", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - require.NoError(t, SaveState(State{ - Enabled: false, - DeviceID: uuid.NewString(), - SessionID: uuid.NewString(), - SessionLastActive: now, - SchemaVersion: SchemaVersion, - }, fsys)) - - disabled, err := Disabled(fsys, now) - - require.NoError(t, err) - assert.True(t, disabled) - }) - - t.Run("creates enabled state when missing", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - - disabled, err := Disabled(fsys, now) - - require.NoError(t, err) - assert.False(t, disabled) - }) -} - -func TestSetEnabledAndStatus(t *testing.T) { - now := time.Date(2026, time.April, 1, 12, 0, 0, 0, time.UTC) - - t.Run("disable preserves identity fields", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - initial, _, err := LoadOrCreateState(fsys, now) - require.NoError(t, err) - initial.DistinctID = "user-123" - require.NoError(t, SaveState(initial, fsys)) - - state, err := SetEnabled(fsys, false, now.Add(time.Minute)) - - require.NoError(t, err) - assert.False(t, state.Enabled) - assert.Equal(t, initial.DeviceID, state.DeviceID) - assert.Equal(t, initial.SessionID, state.SessionID) - assert.Equal(t, "user-123", state.DistinctID) - }) - - t.Run("enable flips disabled state back on", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - require.NoError(t, SaveState(State{ - Enabled: false, - DeviceID: uuid.NewString(), - SessionID: uuid.NewString(), - SessionLastActive: now, - SchemaVersion: SchemaVersion, - }, fsys)) - - state, err := SetEnabled(fsys, true, now.Add(time.Minute)) - - require.NoError(t, err) - assert.True(t, state.Enabled) - }) - - t.Run("status creates default state when missing", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/tmp/supabase-home") - fsys := afero.NewMemMapFs() - - state, created, err := Status(fsys, now) - - require.NoError(t, err) - assert.True(t, created) - assert.True(t, state.Enabled) - assert.NoError(t, uuid.Validate(state.DeviceID)) - }) -} diff --git a/apps/cli-go/internal/testing/apitest/docker.go b/apps/cli-go/internal/testing/apitest/docker.go deleted file mode 100644 index b3c80f4b40..0000000000 --- a/apps/cli-go/internal/testing/apitest/docker.go +++ /dev/null @@ -1,156 +0,0 @@ -package apitest - -import ( - "bytes" - "fmt" - "io" - "net/http" - "strings" - - "github.com/docker/docker/api" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/image" - "github.com/docker/docker/api/types/network" - "github.com/docker/docker/api/types/versions" - "github.com/docker/docker/api/types/volume" - "github.com/docker/docker/client" - "github.com/docker/docker/pkg/stdcopy" - "github.com/go-errors/errors" - "github.com/h2non/gock" -) - -const mockHost = "http://127.0.0.1" - -func MockDocker(docker *client.Client) error { - // Skip setup if docker is already mocked - if docker.DaemonHost() == mockHost { - return nil - } - if err := client.WithVersion(api.DefaultVersion)(docker); err != nil { - return err - } - // Safe to ignore errors as transport will be replaced by gock - _ = client.WithHost(mockHost)(docker) - return client.WithHTTPClient(http.DefaultClient)(docker) -} - -// Ref: internal/utils/docker.go::DockerStart -func MockDockerStart(docker *client.Client, imageID, containerID string) { - gock.New(docker.DaemonHost()). - Get("/v" + docker.ClientVersion() + "/images/" + imageID + "/json"). - Reply(http.StatusOK). - JSON(image.InspectResponse{}) - gock.New(docker.DaemonHost()). - Post("/v" + docker.ClientVersion() + "/networks/create"). - Reply(http.StatusCreated). - JSON(network.CreateResponse{}) - gock.New(docker.DaemonHost()). - Post("/v" + docker.ClientVersion() + "/volumes/create"). - Persist(). - Reply(http.StatusCreated). - JSON(volume.Volume{}) - gock.New(docker.DaemonHost()). - Post("/v" + docker.ClientVersion() + "/containers/create"). - Reply(http.StatusOK). - JSON(container.CreateResponse{ID: containerID}) - gock.New(docker.DaemonHost()). - Post("/v" + docker.ClientVersion() + "/containers/" + containerID + "/start"). - Reply(http.StatusAccepted) -} - -// Ref: internal/utils/docker.go::DockerRemoveAll -func MockDockerStop(docker *client.Client) { - gock.New(docker.DaemonHost()). - Get("/v" + docker.ClientVersion() + "/containers/json"). - Reply(http.StatusOK). - JSON([]container.Summary{}) - gock.New(docker.DaemonHost()). - Post("/v" + docker.ClientVersion() + "/containers/prune"). - Reply(http.StatusOK). - JSON(container.PruneReport{}) - if !versions.GreaterThanOrEqualTo(docker.ClientVersion(), "1.42") { - gock.New(docker.DaemonHost()). - Post("/v"+docker.ClientVersion()+"/volumes/prune"). - MatchParam("filters", `"all":{"true":true}`). - ReplyError(errors.New(`failed to parse filters for all=true&label=com.supabase.cli.project%3Dtest: "all" is an invalid volume filter`)) - } - gock.New(docker.DaemonHost()). - Post("/v" + docker.ClientVersion() + "/volumes/prune"). - Reply(http.StatusOK). - JSON(volume.PruneReport{}) - gock.New(docker.DaemonHost()). - Post("/v" + docker.ClientVersion() + "/networks/prune"). - Reply(http.StatusOK). - JSON(network.PruneReport{}) -} - -// Ref: internal/utils/docker.go::DockerRunOnce -func setupDockerLogs(docker *client.Client, containerID, stdout string, exitCode int) error { - err := MockDockerLogsStream(docker, containerID, exitCode, strings.NewReader(stdout)) - gock.New(docker.DaemonHost()). - Delete("/v" + docker.ClientVersion() + "/containers/" + containerID). - Reply(http.StatusOK) - return err -} - -func MockDockerLogsStream(docker *client.Client, containerID string, exitCode int, r io.Reader) error { - var body bytes.Buffer - writer := stdcopy.NewStdWriter(&body, stdcopy.Stdout) - _, err := io.Copy(writer, r) - gock.New(docker.DaemonHost()). - Get("/v"+docker.ClientVersion()+"/containers/"+containerID+"/logs"). - Reply(http.StatusOK). - SetHeader("Content-Type", "application/vnd.docker.raw-stream"). - Body(&body) - gock.New(docker.DaemonHost()). - Get("/v" + docker.ClientVersion() + "/containers/" + containerID + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - ExitCode: exitCode, - }}}) - return err -} - -func MockDockerLogs(docker *client.Client, containerID, stdout string) error { - return setupDockerLogs(docker, containerID, stdout, 0) -} - -func MockDockerLogsExitCode(docker *client.Client, containerID string, exitCode int) error { - return setupDockerLogs(docker, containerID, "", exitCode) -} - -// MockDockerErrorLogs streams stderr output alongside a non-zero exit code, -// mirroring a container whose command failed (e.g. pg_dump unable to reach the -// database). -func MockDockerErrorLogs(docker *client.Client, containerID string, exitCode int, stderr string) error { - var body bytes.Buffer - writer := stdcopy.NewStdWriter(&body, stdcopy.Stderr) - if _, err := io.Copy(writer, strings.NewReader(stderr)); err != nil { - return err - } - gock.New(docker.DaemonHost()). - Get("/v"+docker.ClientVersion()+"/containers/"+containerID+"/logs"). - Reply(http.StatusOK). - SetHeader("Content-Type", "application/vnd.docker.raw-stream"). - Body(&body) - gock.New(docker.DaemonHost()). - Get("/v" + docker.ClientVersion() + "/containers/" + containerID + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - ExitCode: exitCode, - }}}) - gock.New(docker.DaemonHost()). - Delete("/v" + docker.ClientVersion() + "/containers/" + containerID). - Reply(http.StatusOK) - return nil -} - -func ListUnmatchedRequests() []string { - result := make([]string, len(gock.GetUnmatchedRequests())) - for i, r := range gock.GetUnmatchedRequests() { - result[i] = fmt.Sprintln(r.Method, r.URL.Path) - } - return result -} diff --git a/apps/cli-go/internal/testing/apitest/platform.go b/apps/cli-go/internal/testing/apitest/platform.go deleted file mode 100644 index 6ae3d50ded..0000000000 --- a/apps/cli-go/internal/testing/apitest/platform.go +++ /dev/null @@ -1,60 +0,0 @@ -package apitest - -import ( - "crypto/rand" - "encoding/hex" - "fmt" - "os" - "testing" - - "github.com/h2non/gock" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func RandomAccessToken(t *testing.T) []byte { - data := make([]byte, 20) - _, err := rand.Read(data) - require.NoError(t, err) - token := make([]byte, 44) - copy(token, "sbp_") - hex.Encode(token[4:], data) - return token -} - -const letters = "abcdefghijklmnopqrstuvwxyz" - -func RandomProjectRef() string { - data := make([]byte, 20) - _, err := rand.Read(data) - if err != nil { - fmt.Fprintln(os.Stderr, err) - } - for i := range data { - n := int(data[i]) % len(letters) - data[i] = letters[n] - } - return string(data) -} - -func AssertRequestsDone(t *testing.T) { - assert.False(t, gock.HasUnmatchedRequest()) - for _, r := range gock.GetUnmatchedRequests() { - fmt.Fprintln(os.Stderr, "Unmatched:", r.Method, r.URL.Path) - } - assert.True(t, gock.IsDone()) - for _, p := range gock.Pending() { - fmt.Fprintln(os.Stderr, "Pending:", p.Request().Method, p.Request().URLStruct.Path) - } -} - -func MockPlatformAPI(t *testing.T) func() { - // Setup valid access token - token := RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - teardown := func() { - AssertRequestsDone(t) - gock.OffAll() - } - return teardown -} diff --git a/apps/cli-go/internal/testing/fstest/create.go b/apps/cli-go/internal/testing/fstest/create.go deleted file mode 100644 index a8c0734b8d..0000000000 --- a/apps/cli-go/internal/testing/fstest/create.go +++ /dev/null @@ -1,20 +0,0 @@ -package fstest - -import ( - "io/fs" - "strings" - - "github.com/spf13/afero" -) - -type CreateErrorFs struct { - afero.MemMapFs - DenyPath string -} - -func (m *CreateErrorFs) Create(name string) (afero.File, error) { - if strings.HasPrefix(name, m.DenyPath) { - return nil, fs.ErrPermission - } - return m.MemMapFs.Create(name) -} diff --git a/apps/cli-go/internal/testing/fstest/open.go b/apps/cli-go/internal/testing/fstest/open.go deleted file mode 100644 index bc756e3cb8..0000000000 --- a/apps/cli-go/internal/testing/fstest/open.go +++ /dev/null @@ -1,28 +0,0 @@ -package fstest - -import ( - "io/fs" - "os" - "strings" - - "github.com/spf13/afero" -) - -type OpenErrorFs struct { - afero.MemMapFs - DenyPath string -} - -func (m *OpenErrorFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { - if strings.HasPrefix(name, m.DenyPath) { - return nil, fs.ErrPermission - } - return m.MemMapFs.OpenFile(name, flag, perm) -} - -func (m *OpenErrorFs) Open(name string) (afero.File, error) { - if strings.HasPrefix(name, m.DenyPath) { - return nil, fs.ErrPermission - } - return m.MemMapFs.Open(name) -} diff --git a/apps/cli-go/internal/testing/fstest/stat.go b/apps/cli-go/internal/testing/fstest/stat.go deleted file mode 100644 index 3b53f882d6..0000000000 --- a/apps/cli-go/internal/testing/fstest/stat.go +++ /dev/null @@ -1,20 +0,0 @@ -package fstest - -import ( - "io/fs" - "strings" - - "github.com/spf13/afero" -) - -type StatErrorFs struct { - afero.MemMapFs - DenyPath string -} - -func (m *StatErrorFs) Stat(name string) (fs.FileInfo, error) { - if strings.HasPrefix(name, m.DenyPath) { - return nil, fs.ErrPermission - } - return m.MemMapFs.Stat(name) -} diff --git a/apps/cli-go/internal/testing/fstest/stdin.go b/apps/cli-go/internal/testing/fstest/stdin.go deleted file mode 100644 index 63e1a8a259..0000000000 --- a/apps/cli-go/internal/testing/fstest/stdin.go +++ /dev/null @@ -1,28 +0,0 @@ -package fstest - -import ( - "os" - "strings" - "testing" - - "github.com/stretchr/testify/require" -) - -func MockStdin(t *testing.T, input string) func() { - // Setup stdin - r, w, err := os.Pipe() - require.NoError(t, err) - if len(input) > 0 && !strings.HasSuffix(input, "\n") { - input += "\n" - } - _, err = w.WriteString(input) - require.NoError(t, err) - require.NoError(t, w.Close()) - // Replace stdin - oldStdin := os.Stdin - teardown := func() { - os.Stdin = oldStdin - } - os.Stdin = r - return teardown -} diff --git a/apps/cli-go/internal/testing/fstest/stdout.go b/apps/cli-go/internal/testing/fstest/stdout.go deleted file mode 100644 index 06c7bb3dc8..0000000000 --- a/apps/cli-go/internal/testing/fstest/stdout.go +++ /dev/null @@ -1,26 +0,0 @@ -package fstest - -import ( - "io" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func MockStdout(t *testing.T, output string) func() { - r, w, err := os.Pipe() - require.NoError(t, err) - // Replace stdout - oldStdout := os.Stdout - teardown := func() { - os.Stdout = oldStdout - assert.NoError(t, w.Close()) - data, err := io.ReadAll(r) - assert.NoError(t, err) - assert.Equal(t, output, string(data)) - } - os.Stdout = w - return teardown -} diff --git a/apps/cli-go/internal/testing/helper/history.go b/apps/cli-go/internal/testing/helper/history.go deleted file mode 100644 index 95c846b7ad..0000000000 --- a/apps/cli-go/internal/testing/helper/history.go +++ /dev/null @@ -1,28 +0,0 @@ -package helper - -import ( - "github.com/supabase/cli/pkg/migration" - "github.com/supabase/cli/pkg/pgtest" -) - -func MockMigrationHistory(conn *pgtest.MockConn) *pgtest.MockConn { - conn.Query(migration.SET_LOCK_TIMEOUT). - Query(migration.CREATE_VERSION_SCHEMA). - Reply("CREATE SCHEMA"). - Query(migration.CREATE_VERSION_TABLE). - Reply("CREATE TABLE"). - Query(migration.ADD_STATEMENTS_COLUMN). - Reply("ALTER TABLE"). - Query(migration.ADD_NAME_COLUMN). - Reply("ALTER TABLE") - return conn -} - -func MockSeedHistory(conn *pgtest.MockConn) *pgtest.MockConn { - conn.Query(migration.SET_LOCK_TIMEOUT). - Query(migration.CREATE_VERSION_SCHEMA). - Reply("CREATE SCHEMA"). - Query(migration.CREATE_SEED_TABLE). - Reply("CREATE TABLE") - return conn -} diff --git a/apps/cli-go/internal/testing/helper/privileges.go b/apps/cli-go/internal/testing/helper/privileges.go deleted file mode 100644 index 4dcca23f58..0000000000 --- a/apps/cli-go/internal/testing/helper/privileges.go +++ /dev/null @@ -1,19 +0,0 @@ -package helper - -import "github.com/supabase/cli/pkg/pgtest" - -// MockApiPrivilegesRevoke queues the per-statement responses for the default Data API privilege -// revoke that start.ApplyApiPrivileges runs when [api].auto_expose_new_tables is unset or false. -// -// The statements mirror start.RevokeDefaultDataApiPrivilegesSql. They are inlined here rather -// than imported from the start package to avoid an import cycle with that package's own -// internal (package start) tests. -func MockApiPrivilegesRevoke(conn *pgtest.MockConn) *pgtest.MockConn { - conn.Query("alter default privileges for role postgres in schema public\n revoke select, insert, update, delete on tables from anon, authenticated, service_role"). - Reply("ALTER DEFAULT PRIVILEGES"). - Query("alter default privileges for role postgres in schema public\n revoke usage, select on sequences from anon, authenticated, service_role"). - Reply("ALTER DEFAULT PRIVILEGES"). - Query("alter default privileges for role postgres in schema public\n revoke execute on functions from anon, authenticated, service_role"). - Reply("ALTER DEFAULT PRIVILEGES") - return conn -} diff --git a/apps/cli-go/internal/utils/access_token.go b/apps/cli-go/internal/utils/access_token.go deleted file mode 100644 index 79489b564e..0000000000 --- a/apps/cli-go/internal/utils/access_token.go +++ /dev/null @@ -1,138 +0,0 @@ -package utils - -import ( - "fmt" - "os" - "path/filepath" - "regexp" - - "github.com/go-errors/errors" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils/credentials" - "github.com/zalando/go-keyring" -) - -var ( - AccessTokenPattern = regexp.MustCompile(`^sbp_(oauth_|v0_)?[a-f0-9]{40}$`) - ErrInvalidToken = errors.New("Invalid access token format. Must be like `sbp_0102...1920`.") - ErrMissingToken = errors.Errorf("Access token not provided. Supply an access token by running %s or setting the SUPABASE_ACCESS_TOKEN environment variable.", Aqua("supabase login")) - ErrNotLoggedIn = errors.New("You were not logged in, nothing to do.") -) - -const AccessTokenKey = "access-token" - -func LoadAccessTokenFS(fsys afero.Fs) (string, error) { - accessToken, err := loadAccessToken(fsys) - if err != nil { - return "", err - } - if !AccessTokenPattern.MatchString(accessToken) { - return "", errors.New(ErrInvalidToken) - } - return accessToken, nil -} - -func loadAccessToken(fsys afero.Fs) (string, error) { - logger := GetDebugLogger() - // Env takes precedence - if accessToken := os.Getenv("SUPABASE_ACCESS_TOKEN"); accessToken != "" { - fmt.Fprintln(logger, "Using access token from env var...") - return accessToken, nil - } - // Load from current profile - if accessToken, err := credentials.StoreProvider.Get(CurrentProfile.Name); err == nil { - fmt.Fprintln(logger, "Using access token for profile:", CurrentProfile.Name) - return accessToken, nil - } - // Load from legacy key for backwards compatibility - if accessToken, err := credentials.StoreProvider.Get(AccessTokenKey); err == nil { - fmt.Fprintln(logger, "Using access token from credentials store...") - return accessToken, nil - } - // Fallback to token file - return fallbackLoadToken(fsys) -} - -func fallbackLoadToken(fsys afero.Fs) (string, error) { - path, err := getAccessTokenPath() - if err != nil { - return "", err - } - accessToken, err := afero.ReadFile(fsys, path) - if errors.Is(err, os.ErrNotExist) { - return "", errors.New(ErrMissingToken) - } else if err != nil { - return "", errors.Errorf("failed to read access token file: %w", err) - } - fmt.Fprintln(GetDebugLogger(), "Using access token from file:", path) - return string(accessToken), nil -} - -func SaveAccessToken(accessToken string, fsys afero.Fs) error { - // Validate access token - if !AccessTokenPattern.MatchString(accessToken) { - return errors.New(ErrInvalidToken) - } - // Save to current profile - if err := credentials.StoreProvider.Set(CurrentProfile.Name, accessToken); err != nil { - fmt.Fprintln(GetDebugLogger(), err) - } else { - return nil - } - // Fallback to token file - return fallbackSaveToken(accessToken, fsys) -} - -func fallbackSaveToken(accessToken string, fsys afero.Fs) error { - path, err := getAccessTokenPath() - if err != nil { - return err - } - if err := MkdirIfNotExistFS(fsys, filepath.Dir(path)); err != nil { - return err - } - if err := afero.WriteFile(fsys, path, []byte(accessToken), 0600); err != nil { - return errors.Errorf("failed to save access token file: %w", err) - } - return nil -} - -func DeleteAccessToken(fsys afero.Fs) error { - // Always delete the fallback token file to handle legacy CLI - if err := fallbackDeleteToken(fsys); err != nil && !errors.Is(err, os.ErrNotExist) { - return err - } - // Always delete from legacy keyring - if err := credentials.StoreProvider.Delete(AccessTokenKey); err != nil && !errors.Is(err, keyring.ErrNotFound) { - fmt.Fprintln(GetDebugLogger(), err) - } - // Delete profile from native credentials store - if err := credentials.StoreProvider.Delete(CurrentProfile.Name); err != nil { - if errors.Is(err, credentials.ErrNotSupported) || - errors.Is(err, keyring.ErrUnsupportedPlatform) || - errors.Is(err, keyring.ErrNotFound) { - return errors.New(ErrNotLoggedIn) - } - return errors.Errorf("failed to delete access token from keyring: %w", err) - } - return nil -} - -func fallbackDeleteToken(fsys afero.Fs) error { - path, err := getAccessTokenPath() - if err != nil { - return err - } - if err := fsys.Remove(path); err != nil { - return errors.Errorf("failed to remove access token file: %w", err) - } - return nil -} - -func getAccessTokenPath() (string, error) { - home, err := SupabaseHomeDir() - if err != nil { - return "", err - } - return filepath.Join(home, AccessTokenKey), nil -} diff --git a/apps/cli-go/internal/utils/access_token_test.go b/apps/cli-go/internal/utils/access_token_test.go deleted file mode 100644 index 602dec1f6a..0000000000 --- a/apps/cli-go/internal/utils/access_token_test.go +++ /dev/null @@ -1,243 +0,0 @@ -package utils - -import ( - "os" - "path/filepath" - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/testing/fstest" - "github.com/supabase/cli/internal/utils/credentials" - "github.com/zalando/go-keyring" -) - -func TestLoadToken(t *testing.T) { - keyring.MockInit() - token := string(apitest.RandomAccessToken(t)) - - t.Run("loads token from env var", func(t *testing.T) { - t.Setenv("SUPABASE_ACCESS_TOKEN", token) - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - loaded, err := LoadAccessTokenFS(fsys) - // Check error - assert.NoError(t, err) - assert.Equal(t, token, loaded) - }) - - t.Run("loads v0 token from env var", func(t *testing.T) { - v0Token := "sbp_v0_" + token[len("sbp_"):] - t.Setenv("SUPABASE_ACCESS_TOKEN", v0Token) - fsys := afero.NewMemMapFs() - loaded, err := LoadAccessTokenFS(fsys) - assert.NoError(t, err) - assert.Equal(t, v0Token, loaded) - }) - - t.Run("throws error on unknown version prefix", func(t *testing.T) { - t.Setenv("SUPABASE_ACCESS_TOKEN", "sbp_v1_"+token[len("sbp_"):]) - fsys := afero.NewMemMapFs() - loaded, err := LoadAccessTokenFS(fsys) - assert.ErrorIs(t, err, ErrInvalidToken) - assert.Empty(t, loaded) - }) - - t.Run("throws error on invalid token", func(t *testing.T) { - t.Setenv("SUPABASE_ACCESS_TOKEN", "invalid") - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - loaded, err := LoadAccessTokenFS(fsys) - // Check error - assert.ErrorIs(t, err, ErrInvalidToken) - assert.Empty(t, loaded) - }) - - t.Run("throws error on missing token", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - loaded, err := LoadAccessTokenFS(fsys) - // Check error - assert.ErrorIs(t, err, ErrMissingToken) - assert.Empty(t, loaded) - }) -} - -func TestLoadTokenFallback(t *testing.T) { - t.Run("fallback loads from file", func(t *testing.T) { - path, err := getAccessTokenPath() - assert.NoError(t, err) - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, afero.WriteFile(fsys, path, []byte{}, 0600)) - // Run test - token, err := fallbackLoadToken(fsys) - // Check error - assert.NoError(t, err) - assert.Empty(t, token) - }) - - t.Run("throws error on home dir failure", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) - // Setup empty home directory - t.Setenv("HOME", "") - // Run test - token, err := fallbackLoadToken(fsys) - // Check error - assert.ErrorContains(t, err, "$HOME is not defined") - assert.Empty(t, token) - }) - - t.Run("throws error on read failure", func(t *testing.T) { - path, err := getAccessTokenPath() - assert.NoError(t, err) - // Setup in-memory fs - fsys := &fstest.OpenErrorFs{DenyPath: path} - // Run test - token, err := fallbackLoadToken(fsys) - // Check error - assert.ErrorContains(t, err, "permission denied") - assert.Empty(t, token) - }) -} - -func TestSaveToken(t *testing.T) { - keyring.MockInit() - token := string(apitest.RandomAccessToken(t)) - - t.Run("saves token to keyring", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - assert.NoError(t, SaveAccessToken(token, fsys)) - // Validate saved token - saved, err := LoadAccessTokenFS(fsys) - assert.NoError(t, err) - assert.Equal(t, token, saved) - }) - - t.Run("throws error on invalid token", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - err := SaveAccessToken("invalid", fsys) - // Check error - assert.ErrorIs(t, err, ErrInvalidToken) - }) -} - -func TestSaveTokenFallback(t *testing.T) { - token := string(apitest.RandomAccessToken(t)) - - t.Run("fallback saves to file", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - assert.NoError(t, fallbackSaveToken(token, fsys)) - // Validate saved token - path, err := getAccessTokenPath() - assert.NoError(t, err) - contents, err := afero.ReadFile(fsys, path) - assert.NoError(t, err) - assert.Equal(t, []byte(token), contents) - }) - - t.Run("fallback saves to SUPABASE_HOME when configured", func(t *testing.T) { - t.Setenv("HOME", "/home/test") - t.Setenv("SUPABASE_HOME", "/custom/supabase") - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - assert.NoError(t, fallbackSaveToken(token, fsys)) - // Validate saved token - configuredPath := filepath.Join("/custom/supabase", AccessTokenKey) - contents, err := afero.ReadFile(fsys, configuredPath) - assert.NoError(t, err) - assert.Equal(t, []byte(token), contents) - defaultPath := filepath.Join("/home/test", ".supabase", AccessTokenKey) - exists, err := afero.Exists(fsys, defaultPath) - assert.NoError(t, err) - assert.False(t, exists) - }) - - t.Run("throws error on home dir failure", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) - // Setup empty home directory - t.Setenv("HOME", "") - // Run test - err := fallbackSaveToken(token, fsys) - // Check error - assert.ErrorContains(t, err, "$HOME is not defined") - }) - - t.Run("throws error on permission denied", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) - // Run test - err := fallbackSaveToken(token, fsys) - // Check error - assert.ErrorContains(t, err, "operation not permitted") - }) - - t.Run("throws error on write failure", func(t *testing.T) { - home, err := os.UserHomeDir() - assert.NoError(t, err) - // Setup in-memory fs - fsys := &fstest.OpenErrorFs{DenyPath: home} - // Run test - err = fallbackSaveToken(token, fsys) - // Check error - assert.ErrorContains(t, err, "permission denied") - }) -} - -func TestDeleteToken(t *testing.T) { - t.Run("deletes both keyring and fallback", func(t *testing.T) { - token := string(apitest.RandomAccessToken(t)) - require.NoError(t, credentials.StoreProvider.Set(AccessTokenKey, token)) - require.NoError(t, credentials.StoreProvider.Set(CurrentProfile.Name, token)) - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, fallbackSaveToken(token, fsys)) - // Run test - err := DeleteAccessToken(fsys) - // Check error - assert.NoError(t, err) - _, err = credentials.StoreProvider.Get(CurrentProfile.Name) - assert.ErrorIs(t, err, keyring.ErrNotFound) - _, err = credentials.StoreProvider.Get(AccessTokenKey) - assert.ErrorIs(t, err, keyring.ErrNotFound) - path, err := getAccessTokenPath() - assert.NoError(t, err) - exists, err := afero.Exists(fsys, path) - assert.NoError(t, err) - assert.False(t, exists) - }) - - t.Run("throws error if not logged in", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - err := DeleteAccessToken(fsys) - // Check error - assert.ErrorIs(t, err, ErrNotLoggedIn) - }) - - t.Run("throws error on home dir failure", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) - // Setup empty home directory - t.Setenv("HOME", "") - // Run test - err := DeleteAccessToken(fsys) - // Check error - assert.ErrorContains(t, err, "$HOME is not defined") - }) -} diff --git a/apps/cli-go/internal/utils/agent.go b/apps/cli-go/internal/utils/agent.go deleted file mode 100644 index f41ad85cb2..0000000000 --- a/apps/cli-go/internal/utils/agent.go +++ /dev/null @@ -1,24 +0,0 @@ -package utils - -import "github.com/supabase/cli/internal/utils/agent" - -// AgentMode is a global flag for overriding agent detection. -// Allowed values: "auto" (default), "yes", "no". -var AgentMode = EnumFlag{ - Allowed: []string{"auto", "yes", "no"}, - Value: "auto", -} - -// IsAgentMode returns true if the CLI is being used by an AI agent. -// "yes" forces agent mode on, "no" forces it off, and "auto" (default) -// auto-detects based on environment variables. -func IsAgentMode() bool { - switch AgentMode.Value { - case "yes": - return true - case "no": - return false - default: - return agent.IsAgent() - } -} diff --git a/apps/cli-go/internal/utils/agent/agent.go b/apps/cli-go/internal/utils/agent/agent.go deleted file mode 100644 index e3b0d7f88f..0000000000 --- a/apps/cli-go/internal/utils/agent/agent.go +++ /dev/null @@ -1,61 +0,0 @@ -package agent - -import ( - "os" - "strings" -) - -// IsClaudeCode reports whether the CLI is running inside Claude Code. -func IsClaudeCode() bool { - return os.Getenv("CLAUDECODE") != "" || os.Getenv("CLAUDE_CODE") != "" -} - -// IsAgent checks environment variables to detect if the CLI is being invoked -// by an AI coding agent. Based on the detection logic from Vercel's -// @vercel/functions/ai package. -func IsAgent() bool { - if v := strings.TrimSpace(os.Getenv("AI_AGENT")); v != "" { - return true - } - // Cursor - if os.Getenv("CURSOR_AGENT") != "" || os.Getenv("CURSOR_EXTENSION_HOST_ROLE") != "" { - return true - } - // Gemini - if os.Getenv("GEMINI_CLI") != "" { - return true - } - // Codex - if os.Getenv("CODEX_SANDBOX") != "" || os.Getenv("CODEX_CI") != "" || os.Getenv("CODEX_THREAD_ID") != "" { - return true - } - // Antigravity - if os.Getenv("ANTIGRAVITY_AGENT") != "" { - return true - } - // Augment - if os.Getenv("AUGMENT_AGENT") != "" { - return true - } - // OpenCode - if os.Getenv("OPENCODE_CLIENT") != "" { - return true - } - // Claude Code - if IsClaudeCode() { - return true - } - // Replit - if os.Getenv("REPL_ID") != "" { - return true - } - // GitHub Copilot - if os.Getenv("COPILOT_MODEL") != "" || os.Getenv("COPILOT_ALLOW_ALL") != "" || os.Getenv("COPILOT_GITHUB_TOKEN") != "" { - return true - } - // Devin - if _, err := os.Stat("/opt/.devin"); err == nil { - return true - } - return false -} diff --git a/apps/cli-go/internal/utils/agent/agent_test.go b/apps/cli-go/internal/utils/agent/agent_test.go deleted file mode 100644 index 1fc1c8be9b..0000000000 --- a/apps/cli-go/internal/utils/agent/agent_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package agent - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -// clearAgentEnv unsets all known agent environment variables for a clean test. -func clearAgentEnv(t *testing.T) { - t.Helper() - for _, key := range []string{ - "AI_AGENT", - "CURSOR_AGENT", - "CURSOR_EXTENSION_HOST_ROLE", - "GEMINI_CLI", - "CODEX_SANDBOX", "CODEX_CI", "CODEX_THREAD_ID", - "ANTIGRAVITY_AGENT", - "AUGMENT_AGENT", - "OPENCODE_CLIENT", - "CLAUDECODE", "CLAUDE_CODE", - "REPL_ID", - "COPILOT_MODEL", "COPILOT_ALLOW_ALL", "COPILOT_GITHUB_TOKEN", - } { - t.Setenv(key, "") - } -} - -func TestIsAgent(t *testing.T) { - t.Run("returns false with no agent env vars", func(t *testing.T) { - clearAgentEnv(t) - assert.False(t, IsAgent()) - }) - - t.Run("detects AI_AGENT", func(t *testing.T) { - clearAgentEnv(t) - t.Setenv("AI_AGENT", "custom-agent") - assert.True(t, IsAgent()) - }) - - t.Run("ignores empty AI_AGENT", func(t *testing.T) { - clearAgentEnv(t) - t.Setenv("AI_AGENT", " ") - assert.False(t, IsAgent()) - }) - - t.Run("detects Cursor via CURSOR_TRACE_ID", func(t *testing.T) { - t.Setenv("CURSOR_EXTENSION_HOST_ROLE", "agent-exec") - assert.True(t, IsAgent()) - }) - - t.Run("detects Cursor via CURSOR_AGENT", func(t *testing.T) { - clearAgentEnv(t) - t.Setenv("CURSOR_AGENT", "1") - assert.True(t, IsAgent()) - }) - - t.Run("detects Gemini via GEMINI_CLI", func(t *testing.T) { - t.Setenv("GEMINI_CLI", "1") - assert.True(t, IsAgent()) - }) - - t.Run("detects Codex via CODEX_SANDBOX", func(t *testing.T) { - t.Setenv("CODEX_SANDBOX", "1") - assert.True(t, IsAgent()) - }) - - t.Run("detects Claude Code via CLAUDECODE", func(t *testing.T) { - t.Setenv("CLAUDECODE", "1") - assert.True(t, IsAgent()) - }) - - t.Run("detects Claude Code via CLAUDE_CODE", func(t *testing.T) { - t.Setenv("CLAUDE_CODE", "1") - assert.True(t, IsAgent()) - }) - - t.Run("detects GitHub Copilot via COPILOT_MODEL", func(t *testing.T) { - t.Setenv("COPILOT_MODEL", "gpt-4") - assert.True(t, IsAgent()) - }) - - t.Run("detects Replit via REPL_ID", func(t *testing.T) { - t.Setenv("REPL_ID", "abc") - assert.True(t, IsAgent()) - }) - - t.Run("detects Augment via AUGMENT_AGENT", func(t *testing.T) { - t.Setenv("AUGMENT_AGENT", "1") - assert.True(t, IsAgent()) - }) - - t.Run("detects OpenCode via OPENCODE_CLIENT", func(t *testing.T) { - t.Setenv("OPENCODE_CLIENT", "1") - assert.True(t, IsAgent()) - }) - - t.Run("detects Antigravity via ANTIGRAVITY_AGENT", func(t *testing.T) { - t.Setenv("ANTIGRAVITY_AGENT", "1") - assert.True(t, IsAgent()) - }) -} diff --git a/apps/cli-go/internal/utils/api.go b/apps/cli-go/internal/utils/api.go deleted file mode 100644 index 06f37a8a50..0000000000 --- a/apps/cli-go/internal/utils/api.go +++ /dev/null @@ -1,165 +0,0 @@ -package utils - -import ( - "context" - "encoding/json" - "fmt" - "log" - "net" - "net/http" - "sync" - - "github.com/go-errors/errors" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils/cloudflare" - supabase "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" -) - -const ( - DNS_GO_NATIVE = "native" - DNS_OVER_HTTPS = "https" -) - -var OnGotrueID func(string) - -var ( - clientOnce sync.Once - apiClient *supabase.ClientWithResponses - - DNSResolver = EnumFlag{ - Allowed: []string{DNS_GO_NATIVE, DNS_OVER_HTTPS}, - Value: DNS_GO_NATIVE, - } -) - -// Performs DNS lookup via HTTPS, in case firewall blocks native netgo resolver. -func FallbackLookupIP(ctx context.Context, host string) ([]string, error) { - if net.ParseIP(host) != nil { - return []string{host}, nil - } - // Ref: https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/dns-json - cf := cloudflare.NewCloudflareAPI() - data, err := cf.DNSQuery(ctx, cloudflare.DNSParams{Name: host}) - if err != nil { - return nil, err - } - // Look for first valid IP - var resolved []string - for _, answer := range data.Answer { - if answer.Type == cloudflare.TypeA || answer.Type == cloudflare.TypeAAAA { - resolved = append(resolved, answer.Data) - } - } - if len(resolved) == 0 { - return nil, errors.Errorf("failed to locate valid IP for %s; resolves to %#v", host, data.Answer) - } - return resolved, nil -} - -func ResolveCNAME(ctx context.Context, host string) (string, error) { - // Ref: https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/dns-json - cf := cloudflare.NewCloudflareAPI() - data, err := cf.DNSQuery(ctx, cloudflare.DNSParams{Name: host, Type: cast.Ptr(cloudflare.TypeCNAME)}) - if err != nil { - return "", err - } - // Look for first valid IP - for _, answer := range data.Answer { - if answer.Type == cloudflare.TypeCNAME { - return answer.Data, nil - } - } - serialized, err := json.MarshalIndent(data.Answer, "", " ") - if err != nil { - // we ignore the error (not great), and use the underlying struct in our error message - return "", errors.Errorf("failed to locate appropriate CNAME record for %s; resolves to %+v", host, data.Answer) - } - return "", errors.Errorf("failed to locate appropriate CNAME record for %s; resolves to %+v", host, serialized) -} - -type DialContextFunc func(context.Context, string, string) (net.Conn, error) - -// Wraps a DialContext with DNS-over-HTTPS as fallback resolver -func withFallbackDNS(dialContext DialContextFunc) DialContextFunc { - dnsOverHttps := func(ctx context.Context, network, address string) (net.Conn, error) { - host, port, err := net.SplitHostPort(address) - if err != nil { - return nil, errors.Errorf("failed to split host port: %w", err) - } - ip, err := FallbackLookupIP(ctx, host) - if err != nil { - return nil, err - } - conn, err := dialContext(ctx, network, net.JoinHostPort(ip[0], port)) - if err != nil { - return nil, errors.Errorf("failed to dial fallback: %w", err) - } - return conn, nil - } - if DNSResolver.Value == DNS_OVER_HTTPS { - return dnsOverHttps - } - nativeWithFallback := func(ctx context.Context, network, address string) (net.Conn, error) { - conn, err := dialContext(ctx, network, address) - // Workaround when pure Go DNS resolver fails https://github.com/golang/go/issues/12524 - if err, ok := err.(net.Error); ok && err.Timeout() { - if conn, err := dnsOverHttps(ctx, network, address); err == nil { - return conn, nil - } - } - if err != nil { - return nil, errors.Errorf("failed to dial native: %w", err) - } - return conn, nil - } - return nativeWithFallback -} - -func GetSupabase() *supabase.ClientWithResponses { - clientOnce.Do(func() { - token, err := LoadAccessTokenFS(afero.NewOsFs()) - if err != nil { - log.Fatalln(err) - } - if t, ok := http.DefaultTransport.(*http.Transport); ok { - t.DialContext = withFallbackDNS(t.DialContext) - } - transport := &identityTransport{ - RoundTripper: http.DefaultTransport, - onGotrueID: &OnGotrueID, - } - apiClient, err = supabase.NewClientWithResponses( - GetSupabaseAPIHost(), - supabase.WithHTTPClient(&http.Client{Transport: transport}), - supabase.WithRequestEditorFn(func(ctx context.Context, req *http.Request) error { - req.Header.Set("Authorization", "Bearer "+token) - req.Header.Set("User-Agent", "SupabaseCLI/"+Version) - return nil - }), - ) - if err != nil { - log.Fatalln(err) - } - }) - return apiClient -} - -// Used by unit tests -var DefaultApiHost = CurrentProfile.APIURL - -func GetSupabaseAPIHost() string { - return CurrentProfile.APIURL -} - -func GetSupabaseDashboardURL() string { - return CurrentProfile.DashboardURL -} - -func GetSupabaseHost(projectRef string) string { - return fmt.Sprintf("%s.%s", projectRef, CurrentProfile.ProjectHost) -} - -func GetSupabaseDbHost(projectRef string) string { - return "db." + GetSupabaseHost(projectRef) -} diff --git a/apps/cli-go/internal/utils/api_test.go b/apps/cli-go/internal/utils/api_test.go deleted file mode 100644 index 09365897d9..0000000000 --- a/apps/cli-go/internal/utils/api_test.go +++ /dev/null @@ -1,299 +0,0 @@ -package utils - -import ( - "context" - "errors" - "net" - "net/http" - "testing" - - "github.com/h2non/gock" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/utils/cloudflare" -) - -const host = "api.supabase.io" - -func TestLookupIP(t *testing.T) { - t.Run("resolves IPv4 with CloudFlare", func(t *testing.T) { - // Setup http mock - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", host). - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusOK). - JSON(&cloudflare.DNSResponse{Answer: []cloudflare.DNSAnswer{ - {Type: cloudflare.TypeA, Data: "127.0.0.1"}, - }}) - // Run test - ip, err := FallbackLookupIP(context.Background(), host) - // Validate output - assert.NoError(t, err) - assert.ElementsMatch(t, []string{"127.0.0.1"}, ip) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("resolves IPv6 recursively", func(t *testing.T) { - // Setup http mock - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", "api.supabase.com"). - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusOK). - JSON(&cloudflare.DNSResponse{Answer: []cloudflare.DNSAnswer{ - {Type: cloudflare.TypeCNAME, Data: "supabase-api.fly.dev."}, - {Type: cloudflare.TypeAAAA, Data: "2606:2800:220:1:248:1893:25c8:1946"}, - }}) - // Run test - ip, err := FallbackLookupIP(context.Background(), "api.supabase.com") - // Validate output - assert.NoError(t, err) - assert.ElementsMatch(t, []string{"2606:2800:220:1:248:1893:25c8:1946"}, ip) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("returns immediately if already resolved", func(t *testing.T) { - // Run test - ip, err := FallbackLookupIP(context.Background(), "127.0.0.1") - // Validate output - assert.NoError(t, err) - assert.ElementsMatch(t, []string{"127.0.0.1"}, ip) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("empty on network failure", func(t *testing.T) { - // Setup http mock - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", host). - MatchHeader("accept", "application/dns-json"). - ReplyError(errors.New("network error")) - // Run test - ip, err := FallbackLookupIP(context.Background(), host) - // Validate output - assert.ErrorContains(t, err, "network error") - assert.Empty(t, ip) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("empty on service unavailable", func(t *testing.T) { - // Setup http mock - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", host). - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusServiceUnavailable) - // Run test - ip, err := FallbackLookupIP(context.Background(), host) - // Validate output - assert.ErrorContains(t, err, "status 503") - assert.Empty(t, ip) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("empty on malformed json", func(t *testing.T) { - // Setup http mock - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", host). - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusOK). - JSON("malformed") - // Run test - ip, err := FallbackLookupIP(context.Background(), host) - // Validate output - assert.ErrorContains(t, err, "invalid character 'm' looking for beginning of value") - assert.Empty(t, ip) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("empty on no answer", func(t *testing.T) { - // Setup http mock - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", host). - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusOK). - JSON(&cloudflare.DNSResponse{}) - // Run test - ip, err := FallbackLookupIP(context.Background(), host) - // Validate output - assert.ErrorContains(t, err, "failed to locate valid IP for api.supabase.io; resolves to []cloudflare.DNSAnswer(nil)") - assert.Empty(t, ip) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestResolveCNAME(t *testing.T) { - t.Run("resolves CNAMEs with CloudFlare", func(t *testing.T) { - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", host). - MatchParam("type", "5"). - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusOK). - JSON(&cloudflare.DNSResponse{Answer: []cloudflare.DNSAnswer{ - {Type: cloudflare.TypeCNAME, Data: "foobarbaz.supabase.co"}, - }}) - // Run test - cname, err := ResolveCNAME(context.Background(), host) - // Validate output - assert.Equal(t, "foobarbaz.supabase.co", cname) - assert.Nil(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("missing CNAMEs return an error", func(t *testing.T) { - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", host). - MatchParam("type", "5"). - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusOK). - JSON(&cloudflare.DNSResponse{Answer: []cloudflare.DNSAnswer{}}) - // Run test - cname, err := ResolveCNAME(context.Background(), host) - // Validate output - assert.Empty(t, cname) - assert.ErrorContains(t, err, "failed to locate appropriate CNAME record for api.supabase.io") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("missing CNAMEs return an error", func(t *testing.T) { - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", host). - MatchParam("type", "5"). - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusOK). - JSON(&cloudflare.DNSResponse{Answer: []cloudflare.DNSAnswer{ - {Type: cloudflare.TypeA, Data: "127.0.0.1"}, - }}) - // Run test - cname, err := ResolveCNAME(context.Background(), host) - // Validate output - assert.Empty(t, cname) - assert.ErrorContains(t, err, "failed to locate appropriate CNAME record for api.supabase.io") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -type MockDialer struct { - mock.Mock -} - -func (m *MockDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { - args := m.Called(ctx, network, address) - if conn, ok := args.Get(0).(net.Conn); ok { - return conn, args.Error(1) - } - return nil, args.Error(1) -} - -func TestFallbackDNS(t *testing.T) { - errNetwork := errors.New("network error") - errDNS := &net.DNSError{ - IsTimeout: true, - } - - t.Run("overrides DialContext with DoH", func(t *testing.T) { - DNSResolver.Value = DNS_OVER_HTTPS - // Setup mock dialer - dialer := MockDialer{} - dialer.On("DialContext", mock.Anything, mock.Anything, "127.0.0.1:80"). - Return(nil, errNetwork) - wrapped := withFallbackDNS(dialer.DialContext) - // Setup http mock - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", host). - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusOK). - JSON(&cloudflare.DNSResponse{Answer: []cloudflare.DNSAnswer{ - {Type: cloudflare.TypeA, Data: "127.0.0.1"}, - }}) - // Run test - conn, err := wrapped(context.Background(), "udp", host+":80") - // Check error - assert.ErrorIs(t, err, errNetwork) - assert.Nil(t, conn) - dialer.AssertExpectations(t) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("native with DoH fallback", func(t *testing.T) { - DNSResolver.Value = DNS_GO_NATIVE - // Setup mock dialer - dialer := MockDialer{} - dialer.On("DialContext", mock.Anything, mock.Anything, host+":80"). - Return(nil, errDNS) - dialer.On("DialContext", mock.Anything, mock.Anything, "127.0.0.1:80"). - Return(nil, nil) - wrapped := withFallbackDNS(dialer.DialContext) - // Setup http mock - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", host). - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusOK). - JSON(&cloudflare.DNSResponse{Answer: []cloudflare.DNSAnswer{ - {Type: cloudflare.TypeA, Data: "127.0.0.1"}, - }}) - // Run test - conn, err := wrapped(context.Background(), "udp", host+":80") - // Check error - assert.NoError(t, err) - assert.Nil(t, conn) - dialer.AssertExpectations(t) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on malformed address", func(t *testing.T) { - DNSResolver.Value = DNS_OVER_HTTPS - // Setup mock dialer - dialer := MockDialer{} - wrapped := withFallbackDNS(dialer.DialContext) - // Run test - conn, err := wrapped(context.Background(), "udp", "bad?url") - // Check error - assert.ErrorContains(t, err, "missing port in address") - assert.Nil(t, conn) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on fallback failure", func(t *testing.T) { - DNSResolver.Value = DNS_GO_NATIVE - // Setup mock dialer - dialer := MockDialer{} - dialer.On("DialContext", mock.Anything, mock.Anything, host+":80"). - Return(nil, errDNS) - wrapped := withFallbackDNS(dialer.DialContext) - // Setup http mock - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", host). - MatchHeader("accept", "application/dns-json"). - ReplyError(errNetwork) - // Run test - conn, err := wrapped(context.Background(), "udp", host+":80") - // Check error - assert.ErrorIs(t, err, errDNS) - assert.Nil(t, conn) - dialer.AssertExpectations(t) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} diff --git a/apps/cli-go/internal/utils/cloudflare/api.go b/apps/cli-go/internal/utils/cloudflare/api.go deleted file mode 100644 index 7c7cf10a28..0000000000 --- a/apps/cli-go/internal/utils/cloudflare/api.go +++ /dev/null @@ -1,29 +0,0 @@ -package cloudflare - -import ( - "net/http" - "time" - - "github.com/supabase/cli/pkg/fetcher" -) - -type CloudflareAPI struct { - *fetcher.Fetcher -} - -func NewCloudflareAPI() CloudflareAPI { - server := "https://1.1.1.1" - client := &http.Client{ - Timeout: 10 * time.Second, - } - header := func(req *http.Request) { - req.Header.Add("accept", "application/dns-json") - } - api := CloudflareAPI{Fetcher: fetcher.NewFetcher( - server, - fetcher.WithHTTPClient(client), - fetcher.WithRequestEditor(header), - fetcher.WithExpectedStatus(http.StatusOK), - )} - return api -} diff --git a/apps/cli-go/internal/utils/cloudflare/dns.go b/apps/cli-go/internal/utils/cloudflare/dns.go deleted file mode 100644 index f8a06367cf..0000000000 --- a/apps/cli-go/internal/utils/cloudflare/dns.go +++ /dev/null @@ -1,154 +0,0 @@ -package cloudflare - -import ( - "context" - "net/http" - - "github.com/go-errors/errors" - "github.com/google/go-querystring/query" - "github.com/supabase/cli/pkg/fetcher" -) - -type DNSType uint16 - -// Spec: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4 -// Impl: https://github.com/miekg/dns/blob/master/types.go#L23 -const ( - TypeNone DNSType = 0 - TypeA DNSType = 1 // IPv4 - TypeNS DNSType = 2 - TypeMD DNSType = 3 - TypeMF DNSType = 4 - TypeCNAME DNSType = 5 - TypeSOA DNSType = 6 - TypeMB DNSType = 7 - TypeMG DNSType = 8 - TypeMR DNSType = 9 - TypeNULL DNSType = 10 - TypePTR DNSType = 12 - TypeHINFO DNSType = 13 - TypeMINFO DNSType = 14 - TypeMX DNSType = 15 - TypeTXT DNSType = 16 - TypeRP DNSType = 17 - TypeAFSDB DNSType = 18 - TypeX25 DNSType = 19 - TypeISDN DNSType = 20 - TypeRT DNSType = 21 - TypeNSAPPTR DNSType = 23 - TypeSIG DNSType = 24 - TypeKEY DNSType = 25 - TypePX DNSType = 26 - TypeGPOS DNSType = 27 - TypeAAAA DNSType = 28 // IPv6 - TypeLOC DNSType = 29 - TypeNXT DNSType = 30 - TypeEID DNSType = 31 - TypeNIMLOC DNSType = 32 - TypeSRV DNSType = 33 - TypeATMA DNSType = 34 - TypeNAPTR DNSType = 35 - TypeKX DNSType = 36 - TypeCERT DNSType = 37 - TypeDNAME DNSType = 39 - TypeOPT DNSType = 41 // EDNS - TypeAPL DNSType = 42 - TypeDS DNSType = 43 - TypeSSHFP DNSType = 44 - TypeIPSECKEY DNSType = 45 - TypeRRSIG DNSType = 46 - TypeNSEC DNSType = 47 - TypeDNSKEY DNSType = 48 - TypeDHCID DNSType = 49 - TypeNSEC3 DNSType = 50 - TypeNSEC3PARAM DNSType = 51 - TypeTLSA DNSType = 52 - TypeSMIMEA DNSType = 53 - TypeHIP DNSType = 55 - TypeNINFO DNSType = 56 - TypeRKEY DNSType = 57 - TypeTALINK DNSType = 58 - TypeCDS DNSType = 59 - TypeCDNSKEY DNSType = 60 - TypeOPENPGPKEY DNSType = 61 - TypeCSYNC DNSType = 62 - TypeZONEMD DNSType = 63 - TypeSVCB DNSType = 64 - TypeHTTPS DNSType = 65 - TypeSPF DNSType = 99 - TypeUINFO DNSType = 100 - TypeUID DNSType = 101 - TypeGID DNSType = 102 - TypeUNSPEC DNSType = 103 - TypeNID DNSType = 104 - TypeL32 DNSType = 105 - TypeL64 DNSType = 106 - TypeLP DNSType = 107 - TypeEUI48 DNSType = 108 - TypeEUI64 DNSType = 109 - TypeURI DNSType = 256 - TypeCAA DNSType = 257 - TypeAVC DNSType = 258 - TypeAMTRELAY DNSType = 260 - - TypeTKEY DNSType = 249 - TypeTSIG DNSType = 250 - - // valid Question.Qtype only - TypeIXFR DNSType = 251 - TypeAXFR DNSType = 252 - TypeMAILB DNSType = 253 - TypeMAILA DNSType = 254 - TypeANY DNSType = 255 - - TypeTA DNSType = 32768 - TypeDLV DNSType = 32769 - TypeReserved DNSType = 65535 -) - -type DNSQuestion struct { - Name string `json:"name"` - Type DNSType `json:"type"` -} - -type DNSAnswer struct { - Name string `json:"name"` - Type DNSType `json:"type"` - Ttl uint32 `json:"TTL"` - Data string `json:"data"` -} - -// Ref: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6 -type DNSResponse struct { - Status uint16 - TC bool - RD bool - RA bool - AD bool - CD bool - Question []DNSQuestion `json:",omitempty"` - Answer []DNSAnswer `json:",omitempty"` - Authority []DNSAnswer `json:",omitempty"` - Additional []DNSAnswer `json:",omitempty"` -} - -type DNSParams struct { - Name string `url:"name"` - Type *DNSType `url:"type,omitempty"` - Do *bool `url:"do,omitempty"` - Cd *bool `url:"cd,omitempty"` -} - -// Performs DNS lookup via HTTPS, in case firewall blocks native netgo resolver. -// Ref: https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/dns-json -func (c *CloudflareAPI) DNSQuery(ctx context.Context, params DNSParams) (DNSResponse, error) { - values, err := query.Values(params) - if err != nil { - return DNSResponse{}, errors.Errorf("failed to encode query params: %w", err) - } - resp, err := c.Send(ctx, http.MethodGet, "/dns-query?"+values.Encode(), nil) - if err != nil { - return DNSResponse{}, err - } - return fetcher.ParseJSON[DNSResponse](resp.Body) -} diff --git a/apps/cli-go/internal/utils/cloudflare/dns_test.go b/apps/cli-go/internal/utils/cloudflare/dns_test.go deleted file mode 100644 index ee4d00c299..0000000000 --- a/apps/cli-go/internal/utils/cloudflare/dns_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package cloudflare - -import ( - "context" - "net/http" - "testing" - - "github.com/h2non/gock" - "github.com/stretchr/testify/assert" - "github.com/supabase/cli/internal/testing/apitest" -) - -func TestDNSQuery(t *testing.T) { - t.Run("successfully queries A records", func(t *testing.T) { - api := NewCloudflareAPI() - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", "example.com"). - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusOK). - JSON(DNSResponse{ - Answer: []DNSAnswer{ - { - Name: "example.com", - Type: TypeA, - Ttl: 300, - Data: "93.184.216.34", - }, - }, - }) - - resp, err := api.DNSQuery(context.Background(), DNSParams{ - Name: "example.com", - }) - - assert.NoError(t, err) - assert.Len(t, resp.Answer, 1) - assert.Equal(t, "93.184.216.34", resp.Answer[0].Data) - assert.Equal(t, TypeA, resp.Answer[0].Type) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("successfully queries specific DNS type", func(t *testing.T) { - api := NewCloudflareAPI() - dnsType := TypeCNAME - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", "www.example.com"). - MatchParam("type", "5"). // TypeCNAME = 5 - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusOK). - JSON(DNSResponse{ - Answer: []DNSAnswer{ - { - Name: "www.example.com", - Type: TypeCNAME, - Ttl: 3600, - Data: "example.com", - }, - }, - }) - - resp, err := api.DNSQuery(context.Background(), DNSParams{ - Name: "www.example.com", - Type: &dnsType, - }) - - assert.NoError(t, err) - assert.Len(t, resp.Answer, 1) - assert.Equal(t, "example.com", resp.Answer[0].Data) - assert.Equal(t, TypeCNAME, resp.Answer[0].Type) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("handles network error", func(t *testing.T) { - api := NewCloudflareAPI() - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", "example.com"). - ReplyError(gock.ErrCannotMatch) - - resp, err := api.DNSQuery(context.Background(), DNSParams{ - Name: "example.com", - }) - - assert.Error(t, err) - assert.Empty(t, resp.Answer) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("handles service unavailable", func(t *testing.T) { - api := NewCloudflareAPI() - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", "example.com"). - Reply(http.StatusServiceUnavailable) - - resp, err := api.DNSQuery(context.Background(), DNSParams{ - Name: "example.com", - }) - - assert.ErrorContains(t, err, "status 503") - assert.Empty(t, resp.Answer) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("handles malformed response", func(t *testing.T) { - api := NewCloudflareAPI() - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", "example.com"). - Reply(http.StatusOK). - JSON("invalid json") - - resp, err := api.DNSQuery(context.Background(), DNSParams{ - Name: "example.com", - }) - - assert.ErrorContains(t, err, "failed to parse") - assert.Empty(t, resp.Answer) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} diff --git a/apps/cli-go/internal/utils/colors.go b/apps/cli-go/internal/utils/colors.go deleted file mode 100644 index f4f82652c0..0000000000 --- a/apps/cli-go/internal/utils/colors.go +++ /dev/null @@ -1,28 +0,0 @@ -package utils - -import ( - "github.com/charmbracelet/lipgloss" -) - -// For commands & names. -func Aqua(str string) string { - return lipgloss.NewStyle().Foreground(lipgloss.Color("14")).Render(str) -} - -func Yellow(str string) string { - return lipgloss.NewStyle().Foreground(lipgloss.Color("11")).Render(str) -} - -func Green(str string) string { - return lipgloss.NewStyle().Foreground(lipgloss.Color("10")).Render(str) -} - -// For errors. -func Red(str string) string { - return lipgloss.NewStyle().Foreground(lipgloss.Color("9")).Render(str) -} - -// For paths & filenames. -func Bold(str string) string { - return lipgloss.NewStyle().Bold(true).Render(str) -} diff --git a/apps/cli-go/internal/utils/config.go b/apps/cli-go/internal/utils/config.go deleted file mode 100644 index 2ec13b26aa..0000000000 --- a/apps/cli-go/internal/utils/config.go +++ /dev/null @@ -1,268 +0,0 @@ -package utils - -import ( - _ "embed" - "io/fs" - "net" - "net/url" - "os" - "strconv" - - "github.com/compose-spec/compose-go/v2/types" - "github.com/go-errors/errors" - "github.com/spf13/afero" - "github.com/spf13/viper" - "github.com/supabase/cli/pkg/config" -) - -var ( - NetId string - DbId string - KongId string - GotrueId string - InbucketId string - RealtimeId string - RestId string - StorageId string - ImgProxyId string - DifferId string - PgmetaId string - StudioId string - EdgeRuntimeId string - LogflareId string - VectorId string - PoolerId string - - DbAliases = []string{"db", "db.supabase.internal"} - KongAliases = []string{"kong", "api.supabase.internal"} - GotrueAliases = []string{"auth"} - InbucketAliases = []string{"inbucket"} - RealtimeAliases = []string{"realtime", Config.Realtime.TenantId} - RestAliases = []string{"rest"} - StorageAliases = []string{"storage"} - ImgProxyAliases = []string{"imgproxy"} - PgmetaAliases = []string{"pg_meta"} - StudioAliases = []string{"studio"} - EdgeRuntimeAliases = []string{"edge_runtime"} - LogflareAliases = []string{"analytics"} - VectorAliases = []string{"vector"} - PoolerAliases = []string{"pooler"} - - //go:embed templates/initial_schemas/13.sql - InitialSchemaPg13Sql string - //go:embed templates/initial_schemas/14.sql - InitialSchemaPg14Sql string -) - -func GetId(name string) string { - return "supabase_" + name + "_" + Config.ProjectId -} - -func UpdateDockerIds() { - if NetId = viper.GetString("network-id"); len(NetId) == 0 { - NetId = GetId("network") - } - DbId = GetId(DbAliases[0]) - KongId = GetId(KongAliases[0]) - GotrueId = GetId(GotrueAliases[0]) - InbucketId = GetId(InbucketAliases[0]) - RealtimeId = GetId(RealtimeAliases[0]) - RestId = GetId(RestAliases[0]) - StorageId = GetId(StorageAliases[0]) - ImgProxyId = GetId(ImgProxyAliases[0]) - DifferId = GetId("differ") - PgmetaId = GetId(PgmetaAliases[0]) - StudioId = GetId(StudioAliases[0]) - EdgeRuntimeId = GetId(EdgeRuntimeAliases[0]) - LogflareId = GetId(LogflareAliases[0]) - VectorId = GetId(VectorAliases[0]) - PoolerId = GetId(PoolerAliases[0]) -} - -func GetDockerIds() []string { - return []string{ - KongId, - GotrueId, - InbucketId, - RealtimeId, - RestId, - StorageId, - ImgProxyId, - PgmetaId, - StudioId, - EdgeRuntimeId, - LogflareId, - VectorId, - PoolerId, - } -} - -var Config = config.NewConfig(config.WithHostname(GetHostname())) - -func GetServices() types.Services { - services := types.Services{ - "db": { - Name: ShortContainerImageName(Config.Db.Image), - Image: GetRegistryImageUrl(Config.Db.Image), - PullPolicy: types.PullPolicyMissing, - }, - } - if Config.Api.Enabled { - services["gateway"] = types.ServiceConfig{ - Name: ShortContainerImageName(Config.Api.KongImage), - Image: GetRegistryImageUrl(Config.Api.KongImage), - PullPolicy: types.PullPolicyMissing, - } - services["api"] = types.ServiceConfig{ - Name: ShortContainerImageName(Config.Api.Image), - Image: GetRegistryImageUrl(Config.Api.Image), - PullPolicy: types.PullPolicyMissing, - } - } - if Config.Auth.Enabled { - services["auth"] = types.ServiceConfig{ - Name: ShortContainerImageName(Config.Auth.Image), - Image: GetRegistryImageUrl(Config.Auth.Image), - PullPolicy: types.PullPolicyMissing, - } - } - if Config.Inbucket.Enabled { - services["mailpit"] = types.ServiceConfig{ - Name: ShortContainerImageName(Config.Inbucket.Image), - Image: GetRegistryImageUrl(Config.Inbucket.Image), - PullPolicy: types.PullPolicyMissing, - } - } - if Config.Realtime.Enabled { - services["realtime"] = types.ServiceConfig{ - Name: ShortContainerImageName(Config.Realtime.Image), - Image: GetRegistryImageUrl(Config.Realtime.Image), - PullPolicy: types.PullPolicyMissing, - } - } - if Config.Storage.Enabled { - services["storage"] = types.ServiceConfig{ - Name: ShortContainerImageName(Config.Storage.Image), - Image: GetRegistryImageUrl(Config.Storage.Image), - PullPolicy: types.PullPolicyMissing, - } - services["imgProxy"] = types.ServiceConfig{ - Name: ShortContainerImageName(Config.Storage.ImgProxyImage), - Image: GetRegistryImageUrl(Config.Storage.ImgProxyImage), - PullPolicy: types.PullPolicyMissing, - } - } - if Config.EdgeRuntime.Enabled { - services["edgeRuntime"] = types.ServiceConfig{ - Name: ShortContainerImageName(Config.EdgeRuntime.Image), - Image: GetRegistryImageUrl(Config.EdgeRuntime.Image), - PullPolicy: types.PullPolicyMissing, - } - } - if Config.Studio.Enabled { - services["studio"] = types.ServiceConfig{ - Name: ShortContainerImageName(Config.Studio.Image), - Image: GetRegistryImageUrl(Config.Studio.Image), - PullPolicy: types.PullPolicyMissing, - } - services["pgmeta"] = types.ServiceConfig{ - Name: ShortContainerImageName(Config.Studio.PgmetaImage), - Image: GetRegistryImageUrl(Config.Studio.PgmetaImage), - PullPolicy: types.PullPolicyMissing, - } - } - if Config.Analytics.Enabled { - services["analytics"] = types.ServiceConfig{ - Name: ShortContainerImageName(Config.Analytics.Image), - Image: GetRegistryImageUrl(Config.Analytics.Image), - PullPolicy: types.PullPolicyMissing, - } - services["vector"] = types.ServiceConfig{ - Name: ShortContainerImageName(Config.Analytics.VectorImage), - Image: GetRegistryImageUrl(Config.Analytics.VectorImage), - PullPolicy: types.PullPolicyMissing, - } - } - if Config.Db.Pooler.Enabled { - services["pooler"] = types.ServiceConfig{ - Name: ShortContainerImageName(Config.Db.Pooler.Image), - Image: GetRegistryImageUrl(Config.Db.Pooler.Image), - PullPolicy: types.PullPolicyMissing, - } - } - return services -} - -// Adapts fs.FS to support absolute paths -type rootFS struct { - fsys afero.Fs -} - -func (f *rootFS) Open(name string) (fs.File, error) { - return f.fsys.Open(name) -} - -func NewRootFS(fsys afero.Fs) fs.FS { - return &rootFS{fsys: fsys} -} - -func ToRealtimeEnv(addr config.AddressFamily) string { - if addr == config.AddressIPv6 { - return "-proto_dist inet6_tcp" - } - return "-proto_dist inet_tcp" -} - -type InitParams struct { - ProjectId string - UseOrioleDB bool - UsePgDelta bool - Overwrite bool -} - -func InitConfig(params InitParams, fsys afero.Fs) error { - c := config.NewConfig() - c.ProjectId = params.ProjectId - if params.UseOrioleDB { - c.Db.OrioleDBVersion = "17.11.0.002" - } - // The supabase init command opts new projects into pg-delta. Existing configs are - // unaffected because mergeDefaultValues ejects with this flag false (default stays - // migra), and other InitConfig callers leave it disabled. - c.Experimental.PgDeltaInitEnabled = params.UsePgDelta - // Create config file - if err := MkdirIfNotExistFS(fsys, SupabaseDirPath); err != nil { - return err - } - flag := os.O_WRONLY | os.O_CREATE - if params.Overwrite { - flag |= os.O_TRUNC - } else { - flag |= os.O_EXCL - } - f, err := fsys.OpenFile(ConfigPath, flag, 0644) - if err != nil { - return errors.Errorf("failed to create config file: %w", err) - } - defer f.Close() - return c.Eject(f) -} - -func WriteConfig(fsys afero.Fs, _test bool) error { - return InitConfig(InitParams{}, fsys) -} - -func GetApiUrl(path string) string { - if len(Config.Api.ExternalUrl) > 0 { - return Config.Api.ExternalUrl + path - } - hostPort := net.JoinHostPort(Config.Hostname, - strconv.FormatUint(uint64(Config.Api.Port), 10), - ) - apiUrl := url.URL{ - Scheme: "http", - Host: hostPort, - Path: path, - } - return apiUrl.String() -} diff --git a/apps/cli-go/internal/utils/config_test.go b/apps/cli-go/internal/utils/config_test.go deleted file mode 100644 index 98cd4da9e8..0000000000 --- a/apps/cli-go/internal/utils/config_test.go +++ /dev/null @@ -1,197 +0,0 @@ -package utils - -import ( - "os" - "testing" - - "github.com/spf13/afero" - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestGetId(t *testing.T) { - t.Run("generates container id", func(t *testing.T) { - Config.ProjectId = "test-project" - name := "test-service" - - id := GetId(name) - - assert.Equal(t, "supabase_test-service_test-project", id) - }) -} - -func TestUpdateDockerIds(t *testing.T) { - t.Run("updates all container ids", func(t *testing.T) { - Config.ProjectId = "test-project" - viper.Set("network-id", "custom-network") - defer viper.Reset() - - UpdateDockerIds() - - assert.Equal(t, "custom-network", NetId) - assert.Equal(t, "supabase_db_test-project", DbId) - assert.Equal(t, "supabase_kong_test-project", KongId) - assert.Equal(t, "supabase_auth_test-project", GotrueId) - assert.Equal(t, "supabase_inbucket_test-project", InbucketId) - assert.Equal(t, "supabase_realtime_test-project", RealtimeId) - assert.Equal(t, "supabase_rest_test-project", RestId) - assert.Equal(t, "supabase_storage_test-project", StorageId) - assert.Equal(t, "supabase_imgproxy_test-project", ImgProxyId) - assert.Equal(t, "supabase_differ_test-project", DifferId) - assert.Equal(t, "supabase_pg_meta_test-project", PgmetaId) - assert.Equal(t, "supabase_studio_test-project", StudioId) - assert.Equal(t, "supabase_edge_runtime_test-project", EdgeRuntimeId) - assert.Equal(t, "supabase_analytics_test-project", LogflareId) - assert.Equal(t, "supabase_vector_test-project", VectorId) - assert.Equal(t, "supabase_pooler_test-project", PoolerId) - }) - - t.Run("generates network id if not set", func(t *testing.T) { - Config.ProjectId = "test-project" - viper.Reset() - - UpdateDockerIds() - - assert.Equal(t, "supabase_network_test-project", NetId) - }) -} - -func TestInitConfig(t *testing.T) { - t.Run("creates new config file", func(t *testing.T) { - fsys := afero.NewMemMapFs() - params := InitParams{ - ProjectId: "test-project", - } - - err := InitConfig(params, fsys) - - assert.NoError(t, err) - exists, err := afero.Exists(fsys, ConfigPath) - assert.NoError(t, err) - assert.True(t, exists) - }) - - t.Run("generated config enables pgdelta when requested", func(t *testing.T) { - fsys := afero.NewMemMapFs() - params := InitParams{ - ProjectId: "test-project", - UsePgDelta: true, - } - - err := InitConfig(params, fsys) - - require.NoError(t, err) - content, err := afero.ReadFile(fsys, ConfigPath) - require.NoError(t, err) - assert.Contains(t, string(content), "[experimental.pgdelta]\nenabled = true") - }) - - t.Run("generated config leaves pgdelta disabled by default", func(t *testing.T) { - fsys := afero.NewMemMapFs() - params := InitParams{ - ProjectId: "test-project", - } - - err := InitConfig(params, fsys) - - require.NoError(t, err) - content, err := afero.ReadFile(fsys, ConfigPath) - require.NoError(t, err) - assert.Contains(t, string(content), "[experimental.pgdelta]\nenabled = false") - }) - - t.Run("creates config with orioledb", func(t *testing.T) { - fsys := afero.NewMemMapFs() - params := InitParams{ - ProjectId: "test-project", - UseOrioleDB: true, - } - - err := InitConfig(params, fsys) - - assert.NoError(t, err) - content, err := afero.ReadFile(fsys, ConfigPath) - assert.NoError(t, err) - assert.Contains(t, string(content), "17.11.0.002") - }) - - t.Run("fails if config exists and no overwrite", func(t *testing.T) { - fsys := afero.NewMemMapFs() - err := afero.WriteFile(fsys, ConfigPath, []byte("existing"), 0644) - require.NoError(t, err) - params := InitParams{ - ProjectId: "test-project", - } - - err = InitConfig(params, fsys) - - assert.ErrorIs(t, err, os.ErrExist) - }) - - t.Run("overwrites existing config when specified", func(t *testing.T) { - fsys := afero.NewMemMapFs() - err := afero.WriteFile(fsys, ConfigPath, []byte("existing"), 0644) - require.NoError(t, err) - params := InitParams{ - ProjectId: "test-project", - Overwrite: true, - } - - err = InitConfig(params, fsys) - - assert.NoError(t, err) - content, err := afero.ReadFile(fsys, ConfigPath) - assert.NoError(t, err) - assert.NotEqual(t, "existing", string(content)) - }) -} - -func TestGetApiUrl(t *testing.T) { - t.Run("returns external url when configured", func(t *testing.T) { - Config.Api.ExternalUrl = "https://api.example.com" - - url := GetApiUrl("/test") - - assert.Equal(t, "https://api.example.com/test", url) - }) - - t.Run("builds url from hostname and port", func(t *testing.T) { - Config.Hostname = "localhost" - Config.Api.Port = 8000 - Config.Api.ExternalUrl = "" - - url := GetApiUrl("/test") - - assert.Equal(t, "http://localhost:8000/test", url) - }) -} - -func TestRootFS(t *testing.T) { - t.Run("opens file from root fs", func(t *testing.T) { - fsys := afero.NewMemMapFs() - content := []byte("test content") - err := afero.WriteFile(fsys, "/test.txt", content, 0644) - require.NoError(t, err) - - rootfs := NewRootFS(fsys) - f, err := rootfs.Open("/test.txt") - assert.NoError(t, err) - defer f.Close() - - buf := make([]byte, len(content)) - n, err := f.Read(buf) - assert.NoError(t, err) - assert.Equal(t, len(content), n) - assert.Equal(t, content, buf) - }) - - t.Run("returns error for non-existent file", func(t *testing.T) { - fsys := afero.NewMemMapFs() - rootfs := NewRootFS(fsys) - - _, err := rootfs.Open("/non-existent.txt") - - assert.Error(t, err) - }) -} diff --git a/apps/cli-go/internal/utils/connect.go b/apps/cli-go/internal/utils/connect.go deleted file mode 100644 index ec31067d6d..0000000000 --- a/apps/cli-go/internal/utils/connect.go +++ /dev/null @@ -1,386 +0,0 @@ -package utils - -import ( - "context" - "fmt" - "io" - "net" - "net/url" - "os" - "regexp" - "strings" - "time" - - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" - "github.com/spf13/afero" - "github.com/spf13/viper" - "github.com/supabase/cli/internal/debug" - "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/pgxv5" - "golang.org/x/net/publicsuffix" -) - -func ToPostgresURL(config pgconn.Config) string { - return toPostgresURL(config, url.UserPassword(config.User, config.Password)) -} - -func toPostgresURL(config pgconn.Config, userinfo *url.Userinfo) string { - timeoutSecond := int64(config.ConnectTimeout.Seconds()) - if timeoutSecond == 0 { - timeoutSecond = 10 - } - queryParams := fmt.Sprintf("connect_timeout=%d", timeoutSecond) - for k, v := range config.RuntimeParams { - queryParams += fmt.Sprintf("&%s=%s", k, url.QueryEscape(v)) - } - // IPv6 address must be wrapped in square brackets - host := config.Host - if ip := net.ParseIP(host); ip != nil && ip.To4() == nil { - host = fmt.Sprintf("[%s]", host) - } - return fmt.Sprintf( - "postgresql://%s@%s:%d/%s?%s", - userinfo, - host, - config.Port, - url.PathEscape(config.Database), - queryParams, - ) -} - -var ErrPrimaryNotFound = errors.New("primary database not found") - -func GetPoolerConfigPrimary(ctx context.Context, ref string) (api.SupavisorConfigResponseOutput, error) { - var result api.SupavisorConfigResponseOutput - resp, err := GetSupabase().V1GetPoolerConfigWithResponse(ctx, ref) - if err != nil { - return result, errors.Errorf("failed to get pooler: %w", err) - } else if resp.JSON200 == nil { - return result, errors.Errorf("unexpected get pooler status %d: %s", resp.StatusCode(), string(resp.Body)) - } - for _, config := range *resp.JSON200 { - if config.DatabaseType == api.SupavisorConfigResponseOutputDatabaseTypePRIMARY { - return config, nil - } - } - return result, errors.New(ErrPrimaryNotFound) -} - -func GetPoolerConfig(projectRef string) *pgconn.Config { - logger := GetDebugLogger() - if len(Config.Db.Pooler.ConnectionString) == 0 { - fmt.Fprintln(logger, "Pooler URL is not configured") - return nil - } - poolerConfig, err := ParsePoolerURL(Config.Db.Pooler.ConnectionString) - if err != nil { - fmt.Fprintln(logger, err) - return nil - } - if poolerConfig.RuntimeParams == nil { - poolerConfig.RuntimeParams = make(map[string]string) - } - // Verify that the pooler username matches the database host being connected to - if _, ref, found := strings.Cut(poolerConfig.User, "."); !found { - for option := range strings.SplitSeq(poolerConfig.RuntimeParams["options"], ",") { - key, value, found := strings.Cut(option, "=") - if found && key == "reference" && value != projectRef { - fmt.Fprintln(logger, "Pooler options does not match project ref:", projectRef) - return nil - } - } - } else if projectRef != ref { - fmt.Fprintln(logger, "Pooler username does not match project ref:", projectRef) - return nil - } - // There is a risk of MITM attack if we simply trust the hostname specified in pooler URL. - if err := assertDomainInProfile(poolerConfig.Host); err != nil { - fmt.Fprintln(logger, err) - return nil - } - fmt.Fprintln(logger, "Using connection pooler:", Config.Db.Pooler.ConnectionString) - // Supavisor transaction mode does not support prepared statement - poolerConfig.Port = 5432 - return poolerConfig -} - -func ParsePoolerURL(connString string) (*pgconn.Config, error) { - // Remove password from pooler connection string because the placeholder text - // [YOUR-PASSWORD] messes up pgconn.ParseConfig. The password must be percent - // escaped so we cannot simply call strings.Replace with actual password. - poolerUrl := strings.ReplaceAll(connString, "[YOUR-PASSWORD]", "") - poolerConfig, err := pgconn.ParseConfig(poolerUrl) - if err != nil { - return nil, errors.Errorf("failed to parse pooler URL: %w", err) - } - return poolerConfig, nil -} - -func assertDomainInProfile(host string) error { - domain, err := publicsuffix.EffectiveTLDPlusOne(host) - if err != nil { - return errors.Errorf("failed to parse pooler TLD: %w", err) - } - if len(CurrentProfile.PoolerHost) > 0 && !strings.EqualFold(CurrentProfile.PoolerHost, domain) { - return errors.Errorf("Pooler domain does not belong to current profile: %s", domain) - } - return nil -} - -// Connnect to local Postgres with optimised settings. The caller is responsible for closing the connection returned. -func ConnectLocalPostgres(ctx context.Context, config pgconn.Config, options ...func(*pgx.ConnConfig)) (*pgx.Conn, error) { - if len(config.Host) == 0 { - config.Host = Config.Hostname - } - if config.Port == 0 { - config.Port = Config.Db.Port - } - if len(config.User) == 0 { - config.User = "postgres" - } - if len(config.Password) == 0 { - config.Password = Config.Db.Password - } - if len(config.Database) == 0 { - config.Database = "postgres" - } - if config.ConnectTimeout == 0 { - config.ConnectTimeout = 2 * time.Second - } - options = append(options, func(cc *pgx.ConnConfig) { - cc.TLSConfig = nil - }) - return ConnectByUrl(ctx, ToPostgresURL(config), options...) -} - -func ConnectByUrl(ctx context.Context, url string, options ...func(*pgx.ConnConfig)) (*pgx.Conn, error) { - if viper.GetBool("DEBUG") { - options = append(options, debug.SetupPGX) - } - // No fallback from TLS to unsecure connection - options = append(options, func(cc *pgx.ConnConfig) { - if cc.TLSConfig == nil { - return - } - var fallbacks []*pgconn.FallbackConfig - for _, fc := range cc.Fallbacks { - if fc.TLSConfig != nil { - fallbacks = append(fallbacks, fc) - } - } - cc.Fallbacks = fallbacks - }) - conn, err := pgxv5.Connect(ctx, url, options...) - SetConnectSuggestion(err) - return conn, err -} - -const SuggestEnvVar = "Connect to your database by setting the env var correctly: SUPABASE_DB_PASSWORD" - -// ipv6LiteralPattern matches IPv6 addresses in connection errors, e.g. Go dial -// "dial tcp [2406:da18:...]:5432" or libpq -// `connection to server at "host" (2406:da18:...), port 5432 failed`. -var ipv6LiteralPattern = regexp.MustCompile(`(?:\[[0-9a-fA-F:]+\]|\([0-9a-fA-F:]+\))`) - -// isIPv6ConnectivityError reports whether the connection failure stems from the -// host resolving to an IPv6 address that the current network cannot route to. -// Supabase direct database connections (db..supabase.co:5432) are -// IPv6-only unless the IPv4 add-on is enabled, so users on IPv4-only networks -// (or inside a Docker container without an IPv6 stack, e.g. `supabase db dump`) -// hit these failures. -func isIPv6ConnectivityError(msg string) bool { - lower := strings.ToLower(msg) - switch { - case strings.Contains(lower, "address family for hostname not supported"), - strings.Contains(lower, "no address associated with hostname"): - // getaddrinfo inside the pg_dump container when the host is IPv6-only and - // the container has no IPv6 stack, so AI_ADDRCONFIG filters out the AAAA - // record: "could not translate host name ... to address: Address family - // for hostname not supported" / "... No address associated with hostname". - return true - case strings.Contains(lower, "network is unreachable"): - return true - case strings.Contains(lower, "no route to host"), - strings.Contains(lower, "cannot assign requested address"): - // Require an IPv6 literal so genuine project-not-found errors (which the - // branch below maps) keep their existing suggestion. - return ipv6LiteralPattern.MatchString(msg) - } - return false -} - -// IsIPv6ConnectivityError reports whether err is a database connection failure -// caused by an IPv6 address the current network (or container) cannot reach. -func IsIPv6ConnectivityError(err error) bool { - if err == nil { - return false - } - return isIPv6ConnectivityError(err.Error()) -} - -// ipv6Suggestion is the generic, command-agnostic hint shown when a direct -// connection fails because the host is IPv6-only. It points users at the IPv4 -// transaction pooler via --db-url; SuggestIPv6Pooler upgrades it with the -// project's actual connection string when one can be fetched. -func ipv6Suggestion() string { - return fmt.Sprintf( - "Your network does not support IPv6, which is required for direct connections to the database.\n"+ - "Retry with your project's IPv4 transaction pooler connection string via %s.\n"+ - "You can copy it from the dashboard under Connect > Transaction pooler.", - Aqua("--db-url"), - ) -} - -// poolerURLPasswordPattern captures the userinfo password of a postgres -// connection string (the bytes between "user:" and the "@" host separator). -var poolerURLPasswordPattern = regexp.MustCompile(`^(postgres(?:ql)?://[^:@/]+:)[^@]*@`) - -// maskPoolerPassword replaces the password in a pooler connection string with -// the [YOUR-PASSWORD] placeholder. The Management API may return a real password -// in connection_string, and the suggestion is printed to the terminal, so the -// password must never be echoed. The placeholder keeps the hint copy-pasteable. -func maskPoolerPassword(connString string) string { - return poolerURLPasswordPattern.ReplaceAllString(connString, "${1}[YOUR-PASSWORD]@") -} - -// ipv6PoolerSuggestion is the IPv6 hint enriched with the project's transaction -// pooler connection string (password masked), ready to paste into --db-url. -func ipv6PoolerSuggestion(connString string) string { - return fmt.Sprintf( - "Your network does not support IPv6, which is required for direct connections to the database.\n"+ - "Retry through the IPv4 transaction pooler by passing it to %s", - Aqua(fmt.Sprintf(`--db-url "%s"`, maskPoolerPassword(connString))), - ) -} - -// ProjectRefFromDirectDbHost extracts the project ref from a Supabase direct -// database host (db..supabase.co|red). It returns false for any other host, -// including pooler hosts and local databases. -func ProjectRefFromDirectDbHost(host string) (string, bool) { - matches := ProjectHostPattern.FindStringSubmatch(host) - if len(matches) < 3 { - return "", false - } - return matches[2], true -} - -// WarnIPv6PoolerFallback prints a user-visible warning explaining that the direct -// database connection could not be used because the current environment does not -// support IPv6, and that the CLI is retrying through the IPv4 connection pooler. -func WarnIPv6PoolerFallback(directHost string) { - fmt.Fprintln(os.Stderr, Yellow(fmt.Sprintf( - "Warning: Direct connection to %s is unavailable because this environment does not support IPv6.\n"+ - "Retrying via the IPv4 connection pooler.", - directHost, - ))) -} - -// SuggestIPv6Pooler upgrades CmdSuggestion with the project's transaction pooler -// connection string when host is a Supabase direct database host and the pooler -// config can be fetched. Returns true when the suggestion was set. -func SuggestIPv6Pooler(ctx context.Context, host string) bool { - ref, ok := ProjectRefFromDirectDbHost(host) - if !ok { - return false - } - // GetSupabase() fatally exits when no access token is configured, so only - // reach for the API when a token is available (e.g. --db-url without login). - if _, err := LoadAccessTokenFS(afero.NewOsFs()); err != nil { - return false - } - primary, err := GetPoolerConfigPrimary(ctx, ref) - if err != nil || len(primary.ConnectionString) == 0 { - return false - } - CmdSuggestion = ipv6PoolerSuggestion(primary.ConnectionString) - return true -} - -// Sets CmdSuggestion to an actionable hint based on the given pg connection error. -func SetConnectSuggestion(err error) { - if err == nil { - return - } - msg := err.Error() - if strings.Contains(msg, "connect: connection refused") || - strings.Contains(msg, "Address not in tenant allow_list") { - CmdSuggestion = fmt.Sprintf( - "Make sure your local IP is allowed in Network Restrictions and Network Bans.\n%s/project/_/database/settings", - CurrentProfile.DashboardURL, - ) - } else if strings.Contains(msg, "SCRAM exchange: Wrong password") || strings.Contains(msg, "failed SASL auth") { - // password authentication failed for user / invalid SCRAM server-final-message received from server - CmdSuggestion = SuggestEnvVar - } else if isIPv6ConnectivityError(msg) { - CmdSuggestion = ipv6Suggestion() - } else if strings.Contains(msg, "connect: no route to host") || strings.Contains(msg, "Tenant or user not found") { - // Assumes IPv6 check has been performed before this - CmdSuggestion = "Make sure your project exists on profile: " + CurrentProfile.Name - } -} - -const ( - SUPERUSER_ROLE = "supabase_admin" - CLI_LOGIN_PREFIX = "cli_login_" - SET_SESSION_ROLE = "SET SESSION ROLE postgres" -) - -func ConnectByConfigStream(ctx context.Context, config pgconn.Config, w io.Writer, options ...func(*pgx.ConnConfig)) (*pgx.Conn, error) { - if IsLocalDatabase(config) { - fmt.Fprintln(w, "Connecting to local database...") - return ConnectLocalPostgres(ctx, config, options...) - } - fmt.Fprintln(w, "Connecting to remote database...") - // Ahead of the caller's overrides so they still win, e.g. pgtest clearing TLS. - opts := append([]func(*pgx.ConnConfig){preserveTLSConfig(config)}, options...) - opts = append(opts, func(cc *pgx.ConnConfig) { - if DNSResolver.Value == DNS_OVER_HTTPS { - cc.LookupFunc = FallbackLookupIP - } - // Step down from platform provisioned login roles or privileged roles - if user := strings.Split(cc.User, ".")[0]; strings.EqualFold(user, SUPERUSER_ROLE) || - strings.HasPrefix(user, CLI_LOGIN_PREFIX) { - cc.AfterConnect = func(ctx context.Context, pgconn *pgconn.PgConn) error { - return pgconn.Exec(ctx, SET_SESSION_ROLE).Close() - } - } - }) - return ConnectByUrl(ctx, ToPostgresURL(config), opts...) -} - -// preserveTLSConfig replays the TLS state pgconn resolved from sslmode, which -// ToPostgresURL cannot serialize (it lives in TLSConfig/Fallbacks, not -// RuntimeParams) so the rebuilt URL re-resolves it from PGSSLMODE and libpq's -// "prefer" default. Nil Fallbacks, which ParseConfig never produces, marks a -// config assembled in code with no preference to replay. -func preserveTLSConfig(config pgconn.Config) func(*pgx.ConnConfig) { - return func(cc *pgx.ConnConfig) { - if config.Fallbacks == nil { - return - } - cc.TLSConfig = config.TLSConfig - // Rebuild against the URL's endpoint: callers may override the port - // (GetPoolerConfig pins 5432), and extra HA hosts are dropped anyway. - cc.Fallbacks = nil - for _, fb := range config.Fallbacks { - if fb.Host != config.Host { - break - } - cc.Fallbacks = append(cc.Fallbacks, &pgconn.FallbackConfig{ - Host: cc.Host, - Port: cc.Port, - TLSConfig: fb.TLSConfig, - }) - } - } -} - -func ConnectByConfig(ctx context.Context, config pgconn.Config, options ...func(*pgx.ConnConfig)) (*pgx.Conn, error) { - return ConnectByConfigStream(ctx, config, os.Stderr, options...) -} - -func IsLocalDatabase(config pgconn.Config) bool { - return config.Host == Config.Hostname && (config.Port == Config.Db.Port || config.Port == Config.Db.ShadowPort) -} diff --git a/apps/cli-go/internal/utils/connect_test.go b/apps/cli-go/internal/utils/connect_test.go deleted file mode 100644 index b6ea142d91..0000000000 --- a/apps/cli-go/internal/utils/connect_test.go +++ /dev/null @@ -1,440 +0,0 @@ -package utils - -import ( - "context" - "io" - "net" - "net/http" - "os" - "testing" - - "github.com/go-errors/errors" - "github.com/h2non/gock" - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/utils/cloudflare" - "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/pgtest" -) - -var dbConfig = pgconn.Config{ - Host: GetSupabaseDbHost(apitest.RandomProjectRef()), - Port: 6543, - User: "admin", - Password: "password", - Database: "postgres", -} - -var ( - PG13_POOLER_URL = "postgres://postgres:[YOUR-PASSWORD]@aws-0-ap-southeast-1.pooler.supabase.com:6543/postgres?options=reference%3Dzupyfdrjfhbeevcogohz" - PG15_POOLER_URL = "postgres://postgres.zupyfdrjfhbeevcogohz:[YOUR-PASSWORD]@fly-0-sin.pooler.supabase.com:6543/postgres" -) - -func TestConnectByConfig(t *testing.T) { - t.Run("connects to remote postgres with DoH", func(t *testing.T) { - Config.Db.Pooler.ConnectionString = "" - DNSResolver.Value = DNS_OVER_HTTPS - // Setup http mock - defer gock.OffAll() - // pgx makes 2 calls to resolve ip for each connect request - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", dbConfig.Host). - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusOK). - JSON(&cloudflare.DNSResponse{Answer: []cloudflare.DNSAnswer{ - {Type: cloudflare.TypeA, Data: "127.0.0.1"}, - }}) - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", dbConfig.Host). - MatchHeader("accept", "application/dns-json"). - Reply(http.StatusOK). - JSON(&cloudflare.DNSResponse{Answer: []cloudflare.DNSAnswer{ - {Type: cloudflare.TypeA, Data: "127.0.0.1"}, - }}) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - // Run test - c, err := ConnectByConfig(context.Background(), dbConfig, conn.Intercept) - require.NoError(t, err) - defer c.Close(context.Background()) - assert.NoError(t, err) - }) - - t.Run("connects with unescaped db password", func(t *testing.T) { - DNSResolver.Value = DNS_GO_NATIVE - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - // Run test - config := *dbConfig.Copy() - config.Host = "localhost" - config.Password = "pass word" - c, err := ConnectByConfig(context.Background(), config, conn.Intercept) - require.NoError(t, err) - defer c.Close(context.Background()) - assert.Equal(t, config.Password, c.Config().Password) - }) - - t.Run("no retry on connecting successfully with pooler", func(t *testing.T) { - Config.Db.Pooler.ConnectionString = PG15_POOLER_URL - DNSResolver.Value = DNS_GO_NATIVE - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - // Run test - c, err := ConnectByConfig(context.Background(), dbConfig, conn.Intercept) - // Check error - require.NoError(t, err) - defer c.Close(context.Background()) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("fallback to postgres port on dial error", func(t *testing.T) { - Config.Db.Pooler.ConnectionString = PG15_POOLER_URL - DNSResolver.Value = DNS_OVER_HTTPS - netErr := errors.New("network error") - // Setup http mock - defer gock.OffAll() - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", dbConfig.Host). - MatchHeader("accept", "application/dns-json"). - ReplyError(&net.OpError{Op: "dial", Err: netErr}) - gock.New("https://1.1.1.1"). - Get("/dns-query"). - MatchParam("name", "fly-0-sin.pooler.supabase.com"). - MatchHeader("accept", "application/dns-json"). - ReplyError(&net.OpError{Op: "dial", Err: netErr}) - // Run test - _, err := ConnectByConfig(context.Background(), dbConfig) - // Check error - require.ErrorIs(t, err, netErr) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestConnectLocal(t *testing.T) { - t.Run("connects with debug log", func(t *testing.T) { - viper.Set("DEBUG", true) - _, err := ConnectLocalPostgres(context.Background(), pgconn.Config{Host: "0", Port: 6543}) - assert.ErrorContains(t, err, "failed to connect to postgres") - }) - - t.Run("throws error on invalid port", func(t *testing.T) { - Config.Db.Port = 0 - _, err := ConnectLocalPostgres(context.Background(), pgconn.Config{}) - assert.ErrorContains(t, err, "invalid port (outside range)") - }) -} - -func TestPoolerConfig(t *testing.T) { - oldProfile := CurrentProfile - CurrentProfile = allProfiles[0] - defer t.Cleanup(func() { CurrentProfile = oldProfile }) - - t.Run("parses options ref", func(t *testing.T) { - Config.Db.Pooler.ConnectionString = PG13_POOLER_URL - assert.NotNil(t, GetPoolerConfig("zupyfdrjfhbeevcogohz")) - }) - - t.Run("parses username ref", func(t *testing.T) { - Config.Db.Pooler.ConnectionString = PG15_POOLER_URL - assert.NotNil(t, GetPoolerConfig("zupyfdrjfhbeevcogohz")) - }) - - t.Run("returns nil on missing url", func(t *testing.T) { - Config.Db.Pooler.ConnectionString = "" - assert.Nil(t, GetPoolerConfig("zupyfdrjfhbeevcogohz")) - }) - - t.Run("returns nil on malformed url", func(t *testing.T) { - Config.Db.Pooler.ConnectionString = "malformed" - assert.Nil(t, GetPoolerConfig("zupyfdrjfhbeevcogohz")) - }) - - t.Run("returns nil on mismatched project", func(t *testing.T) { - Config.Db.Pooler.ConnectionString = PG13_POOLER_URL - assert.Nil(t, GetPoolerConfig("nlhaskwsizylhnffaqkd")) - Config.Db.Pooler.ConnectionString = PG15_POOLER_URL - assert.Nil(t, GetPoolerConfig("nlhaskwsizylhnffaqkd")) - }) - - t.Run("returns nil on invalid host", func(t *testing.T) { - Config.Db.Pooler.ConnectionString = "postgres://postgres.zupyfdrjfhbeevcogohz:[YOUR-PASSWORD]@localhost:6543/postgres" - assert.Nil(t, GetPoolerConfig("zupyfdrjfhbeevcogohz")) - }) -} - -func TestSetConnectSuggestion(t *testing.T) { - oldProfile := CurrentProfile - CurrentProfile = allProfiles[0] - defer t.Cleanup(func() { CurrentProfile = oldProfile }) - - cases := []struct { - name string - err error - suggestion string - debug bool - }{ - { - name: "no-op on nil error", - err: nil, - suggestion: "", - }, - { - name: "no-op on unrecognised error", - err: errors.New("some unknown error"), - suggestion: "", - }, - { - name: "connection refused", - err: errors.New("connect: connection refused"), - suggestion: "Make sure your local IP is allowed in Network Restrictions and Network Bans", - }, - { - name: "address not in allow list", - err: errors.New("server error (FATAL: Address not in tenant allow_list: {1,2,3} (SQLSTATE XX000))"), - suggestion: "Make sure your local IP is allowed in Network Restrictions and Network Bans", - }, - { - name: "ssl required without debug flag", - err: errors.New("SSL connection is required"), - suggestion: "", - }, - { - // The debug proxy negotiates TLS itself, so --debug is no longer the - // reason a server rejects an unencrypted connection (#5872). - name: "ssl required with debug flag", - err: errors.New("SSL connection is required"), - debug: true, - suggestion: "", - }, - { - name: "wrong password via SCRAM", - err: errors.New("SCRAM exchange: Wrong password"), - suggestion: "Connect to your database by setting the env var correctly: SUPABASE_DB_PASSWORD", - }, - { - name: "failed SASL auth", - err: errors.New("failed SASL auth"), - suggestion: "Connect to your database by setting the env var correctly: SUPABASE_DB_PASSWORD", - }, - { - name: "ipv6 no route to host", - err: errors.New("dial tcp [2406:da18:4fd:9b0d:80ec:9812:3e65:450b]:5432: connect: no route to host"), - suggestion: "Your network does not support IPv6", - }, - { - name: "ipv6 network is unreachable", - err: errors.New("dial tcp [2406:da18:4fd:9b0d:80ec:9812:3e65:450b]:5432: connect: network is unreachable"), - suggestion: "Your network does not support IPv6", - }, - { - name: "libpq unsupported address family", - err: errors.New(`pg_dump: error: connection to server failed: could not translate host name "db.test.supabase.co" to address: Address family for hostname not supported`), - suggestion: "Your network does not support IPv6", - }, - { - name: "libpq no address associated with hostname", - err: errors.New(`pg_dump: error: could not translate host name "db.ngpopfcjxrfmzmhmmpct.supabase.co" to address: No address associated with hostname`), - suggestion: "Your network does not support IPv6", - }, - { - name: "libpq network is unreachable without literal", - err: errors.New(`connection to server at "db.test.supabase.co", port 5432 failed: Network is unreachable`), - suggestion: "Your network does not support IPv6", - }, - { - name: "libpq cannot assign requested address", - err: errors.New(`pg_dump: error: connection to server at "db.test.supabase.co" (2600:1f1c:c19:4901:963f:d22e:683a:381c), port 5432 failed: Cannot assign requested address - Is the server running on that host and accepting TCP/IP connections?`), - suggestion: "Your network does not support IPv6", - }, - { - name: "cannot assign requested address without ipv6 literal", - err: errors.New("connect: cannot assign requested address"), - suggestion: "", - }, - { - name: "no route to host without ipv6 address", - err: errors.New("connect: no route to host"), - suggestion: "Make sure your project exists on profile: " + CurrentProfile.Name, - }, - { - name: "tenant or user not found", - err: errors.New("Tenant or user not found"), - suggestion: "Make sure your project exists on profile: " + CurrentProfile.Name, - }, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - CmdSuggestion = "" - viper.Set("DEBUG", tc.debug) - SetConnectSuggestion(tc.err) - if tc.suggestion == "" { - assert.Empty(t, CmdSuggestion) - } else { - assert.Contains(t, CmdSuggestion, tc.suggestion) - } - }) - } -} - -func TestSuggestIPv6Pooler(t *testing.T) { - ref := apitest.RandomProjectRef() - poolerURL := "postgres://postgres." + ref + ":[YOUR-PASSWORD]@aws-0-us-east-1.pooler.supabase.com:6543/postgres" - - t.Run("enriches suggestion with transaction pooler url", func(t *testing.T) { - CmdSuggestion = "" - t.Cleanup(func() { CmdSuggestion = "" }) - t.Cleanup(apitest.MockPlatformAPI(t)) - gock.New(DefaultApiHost). - Get("/v1/projects/" + ref + "/config/database/pooler"). - Reply(http.StatusOK). - JSON([]api.SupavisorConfigResponseOutput{{ - DatabaseType: api.SupavisorConfigResponseOutputDatabaseTypePRIMARY, - ConnectionString: poolerURL, - }}) - ok := SuggestIPv6Pooler(context.Background(), "db."+ref+".supabase.co") - assert.True(t, ok) - assert.Contains(t, CmdSuggestion, "--db-url") - assert.Contains(t, CmdSuggestion, poolerURL) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("masks a real password returned by the api", func(t *testing.T) { - CmdSuggestion = "" - t.Cleanup(func() { CmdSuggestion = "" }) - t.Cleanup(apitest.MockPlatformAPI(t)) - secretURL := "postgres://postgres." + ref + ":sup3r-s3cret@aws-0-us-east-1.pooler.supabase.com:6543/postgres" - gock.New(DefaultApiHost). - Get("/v1/projects/" + ref + "/config/database/pooler"). - Reply(http.StatusOK). - JSON([]api.SupavisorConfigResponseOutput{{ - DatabaseType: api.SupavisorConfigResponseOutputDatabaseTypePRIMARY, - ConnectionString: secretURL, - }}) - ok := SuggestIPv6Pooler(context.Background(), "db."+ref+".supabase.co") - assert.True(t, ok) - assert.NotContains(t, CmdSuggestion, "sup3r-s3cret") - assert.Contains(t, CmdSuggestion, "[YOUR-PASSWORD]") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("skips non-supabase host without api call", func(t *testing.T) { - CmdSuggestion = "" - assert.False(t, SuggestIPv6Pooler(context.Background(), "localhost")) - assert.Empty(t, CmdSuggestion) - }) - - t.Run("returns false when pooler config is unavailable", func(t *testing.T) { - CmdSuggestion = "" - t.Cleanup(apitest.MockPlatformAPI(t)) - gock.New(DefaultApiHost). - Get("/v1/projects/" + ref + "/config/database/pooler"). - Reply(http.StatusOK). - JSON([]api.SupavisorConfigResponseOutput{}) - assert.False(t, SuggestIPv6Pooler(context.Background(), "db."+ref+".supabase.co")) - assert.Empty(t, CmdSuggestion) - }) -} - -func TestProjectRefFromDirectDbHost(t *testing.T) { - ref := apitest.RandomProjectRef() - - t.Run("extracts ref from direct host", func(t *testing.T) { - got, ok := ProjectRefFromDirectDbHost("db." + ref + ".supabase.co") - assert.True(t, ok) - assert.Equal(t, ref, got) - }) - - t.Run("rejects pooler and local hosts", func(t *testing.T) { - for _, host := range []string{ - "aws-0-us-east-1.pooler.supabase.com", - "localhost", - "127.0.0.1", - "db." + ref + ".supabase.net", - } { - _, ok := ProjectRefFromDirectDbHost(host) - assert.False(t, ok, host) - } - }) -} - -func TestWarnIPv6PoolerFallback(t *testing.T) { - oldStderr := os.Stderr - r, w, err := os.Pipe() - require.NoError(t, err) - os.Stderr = w - t.Cleanup(func() { os.Stderr = oldStderr }) - - WarnIPv6PoolerFallback("db.test.supabase.co") - require.NoError(t, w.Close()) - out, err := io.ReadAll(r) - require.NoError(t, err) - - assert.Contains(t, string(out), "db.test.supabase.co") - assert.Contains(t, string(out), "does not support IPv6") - assert.Contains(t, string(out), "connection pooler") -} - -func TestPostgresURL(t *testing.T) { - url := ToPostgresURL(pgconn.Config{ - Host: "2406:da18:4fd:9b0d:80ec:9812:3e65:450b", - Port: 5432, - User: "postgres", - Password: "!@#$%^&*()", - RuntimeParams: map[string]string{ - "options": "test", - }, - }) - assert.Equal(t, `postgresql://postgres:%21%40%23$%25%5E&%2A%28%29@[2406:da18:4fd:9b0d:80ec:9812:3e65:450b]:5432/?connect_timeout=10&options=test`, url) -} - -func TestPreserveTLSConfig(t *testing.T) { - const dsn = "postgresql://postgres:pw@example.com:5432/postgres" - - t.Run("replays the sslmode resolved from the connection string", func(t *testing.T) { - for _, sslmode := range []string{"disable", "require", "verify-full"} { - parsed, err := pgconn.ParseConfig(dsn + "?sslmode=" + sslmode) - require.NoError(t, err) - rebuilt, err := pgx.ParseConfig(ToPostgresURL(*parsed)) - require.NoError(t, err) - // Run test - preserveTLSConfig(*parsed)(rebuilt) - // Check TLS matches the DSN, not the re-parse default of "prefer" - assert.Equal(t, parsed.TLSConfig, rebuilt.TLSConfig, sslmode) - } - }) - - t.Run("mandates TLS even when PGSSLMODE would disable it", func(t *testing.T) { - t.Setenv("PGSSLMODE", "disable") - parsed, err := pgconn.ParseConfig(dsn + "?sslmode=require") - require.NoError(t, err) - require.NotNil(t, parsed.TLSConfig) - rebuilt, err := pgx.ParseConfig(ToPostgresURL(*parsed)) - require.NoError(t, err) - require.Nil(t, rebuilt.TLSConfig) - // Run test - preserveTLSConfig(*parsed)(rebuilt) - // Check the env var no longer overrides the connection string - assert.NotNil(t, rebuilt.TLSConfig) - }) - - t.Run("leaves configs assembled in code untouched", func(t *testing.T) { - rebuilt, err := pgx.ParseConfig(ToPostgresURL(dbConfig)) - require.NoError(t, err) - before := rebuilt.TLSConfig - // Run test - preserveTLSConfig(dbConfig)(rebuilt) - // Check libpq's default resolution is preserved - assert.Same(t, before, rebuilt.TLSConfig) - }) -} diff --git a/apps/cli-go/internal/utils/console.go b/apps/cli-go/internal/utils/console.go deleted file mode 100644 index 8a34b8ed0e..0000000000 --- a/apps/cli-go/internal/utils/console.go +++ /dev/null @@ -1,107 +0,0 @@ -package utils - -import ( - "bufio" - "context" - "fmt" - "os" - "strings" - "sync" - "time" - - "github.com/go-errors/errors" - "github.com/spf13/viper" - "github.com/supabase/cli/pkg/cast" - "golang.org/x/term" -) - -type Console struct { - IsTTY bool - stdin *bufio.Scanner - token chan string - mu sync.Mutex -} - -func NewConsole() *Console { - return &Console{ - IsTTY: term.IsTerminal(int(os.Stdin.Fd())), - stdin: bufio.NewScanner(os.Stdin), - token: make(chan string), - mu: sync.Mutex{}, - } -} - -// Prevent interactive terminals from hanging more than 10 minutes -const ttyTimeout = time.Minute * 10 -const nonTTYTimeout = time.Millisecond * 100 - -func (c *Console) ReadLine(ctx context.Context) string { - timeout := nonTTYTimeout - if c.IsTTY { - timeout = ttyTimeout - } - timer := time.NewTimer(timeout) - defer timer.Stop() - // Read from stdin in background - go func() { - c.mu.Lock() - defer c.mu.Unlock() - // Scan one line from input or file - if c.stdin.Scan() { - c.token <- strings.TrimSpace(c.stdin.Text()) - } - }() - var input string - select { - case input = <-c.token: - case <-ctx.Done(): - case <-timer.C: - } - return input -} - -// PromptYesNo asks yes/no questions using the label. -func (c *Console) PromptYesNo(ctx context.Context, label string, def bool) (bool, error) { - choices := "Y/n" - if !def { - choices = "y/N" - } - labelWithChoice := fmt.Sprintf("%s [%s] ", label, choices) - if viper.GetBool("YES") { - fmt.Fprintln(os.Stderr, labelWithChoice+"y") - return true, nil - } - // Any error will be handled as default value - input, err := c.PromptText(ctx, labelWithChoice) - if len(input) > 0 { - if answer := parseYesNo(input); answer != nil { - return *answer, nil - } - } - return def, err -} - -func parseYesNo(s string) *bool { - s = strings.ToLower(s) - if s == "y" || s == "yes" { - return cast.Ptr(true) - } - if s == "n" || s == "no" { - return cast.Ptr(false) - } - return nil -} - -// PromptText asks for input using the label. -func (c *Console) PromptText(ctx context.Context, label string) (string, error) { - fmt.Fprint(os.Stderr, label) - input := c.ReadLine(ctx) - // Echo to stderr for non-interactive terminals - if !c.IsTTY { - fmt.Fprintln(os.Stderr, input) - } - if err := ctx.Err(); err != nil { - return "", errors.New(err) - } - return input, nil -} diff --git a/apps/cli-go/internal/utils/console_test.go b/apps/cli-go/internal/utils/console_test.go deleted file mode 100644 index ffc9532e75..0000000000 --- a/apps/cli-go/internal/utils/console_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package utils - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/supabase/cli/internal/testing/fstest" -) - -func TestPromptYesNo(t *testing.T) { - t.Run("defaults on closed stdin", func(t *testing.T) { - c := NewConsole() - c.IsTTY = false - // Run test - val, err := c.PromptYesNo(context.Background(), "test", false) - // Check error - assert.NoError(t, err) - assert.False(t, val) - }) - - t.Run("parses piped stdin", func(t *testing.T) { - t.Cleanup(fstest.MockStdin(t, "y")) - c := NewConsole() - // Run test - val, err := c.PromptYesNo(context.Background(), "test", false) - // Check error - assert.NoError(t, err) - assert.True(t, val) - }) -} - -func TestPromptText(t *testing.T) { - t.Run("defaults on timeout", func(t *testing.T) { - t.Cleanup(fstest.MockStdin(t, "")) - c := NewConsole() - // Run test - val, err := c.PromptText(context.Background(), "test") - // Check error - assert.NoError(t, err) - assert.Empty(t, val) - }) - - t.Run("throws error on cancel", func(t *testing.T) { - c := NewConsole() - // Setup cancelled context - ctx, cancel := context.WithCancel(context.Background()) - cancel() - // Run test - val, err := c.PromptText(ctx, "test") - // Check error - assert.ErrorIs(t, err, context.Canceled) - assert.Empty(t, val) - }) -} diff --git a/apps/cli-go/internal/utils/container_output.go b/apps/cli-go/internal/utils/container_output.go deleted file mode 100644 index 912d071a41..0000000000 --- a/apps/cli-go/internal/utils/container_output.go +++ /dev/null @@ -1,53 +0,0 @@ -package utils - -import ( - "encoding/json" - "io" - "strings" - - "github.com/docker/docker/pkg/jsonmessage" -) - -func ProcessPullOutput(out io.ReadCloser, p Program) error { - dec := json.NewDecoder(out) - - downloads := make(map[string]struct{ current, total int64 }) - - for { - var progress jsonmessage.JSONMessage - - if err := dec.Decode(&progress); err == io.EOF { - break - } else if err != nil { - return err - } - - if strings.HasPrefix(progress.Status, "Pulling from") { - p.Send(StatusMsg(progress.Status + "...")) - } else if progress.Status == "Pulling fs layer" || progress.Status == "Waiting" { - downloads[progress.ID] = struct{ current, total int64 }{ - current: 0, - total: 0, - } - } else if progress.Status == "Downloading" { - downloads[progress.ID] = struct{ current, total int64 }{ - current: progress.Progress.Current, - total: progress.Progress.Total, - } - - var overallProgress float64 - for _, percentage := range downloads { - if percentage.total > 0 { - progress := float64(percentage.current) / float64(percentage.total) - overallProgress += progress / float64(len(downloads)) - } - } - - p.Send(ProgressMsg(&overallProgress)) - } - } - - p.Send(ProgressMsg(nil)) - - return nil -} diff --git a/apps/cli-go/internal/utils/container_output_test.go b/apps/cli-go/internal/utils/container_output_test.go deleted file mode 100644 index 44376e2d34..0000000000 --- a/apps/cli-go/internal/utils/container_output_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package utils - -import ( - "encoding/json" - "io" - "sync" - "testing" - - tea "github.com/charmbracelet/bubbletea" - "github.com/docker/docker/pkg/jsonmessage" - "github.com/stretchr/testify/assert" -) - -func TestProcessPullOutput(t *testing.T) { - t.Run("processes docker pull messages", func(t *testing.T) { - messages := []jsonmessage.JSONMessage{ - {Status: "Pulling from library/postgres"}, - {ID: "layer1", Status: "Pulling fs layer"}, - {ID: "layer1", Status: "Downloading", Progress: &jsonmessage.JSONProgress{Current: 50, Total: 100}}, - {ID: "layer2", Status: "Pulling fs layer"}, - {ID: "layer2", Status: "Downloading", Progress: &jsonmessage.JSONProgress{Current: 75, Total: 100}}, - } - - // Create a pipe to write messages - r, w := io.Pipe() - enc := json.NewEncoder(w) - go func() { - for _, msg := range messages { - err := enc.Encode(msg) - assert.Nil(t, err) - } - w.Close() - }() - - var status string - var progressFirst *float64 - var progress *float64 - p := NewMockProgram(func(msg tea.Msg) { - switch m := msg.(type) { - case StatusMsg: - status = string(m) - case ProgressMsg: - progress = m - if progressFirst == nil { - progressFirst = m - } - } - }) - - err := ProcessPullOutput(r, p) - - assert.NoError(t, err) - assert.Equal(t, "Pulling from library/postgres...", status) - assert.Equal(t, *progressFirst, 0.5) - assert.Nil(t, progress) - }) -} - -type MockProgram struct { - handler func(tea.Msg) - mu sync.Mutex // Add mutex for thread safety -} - -func NewMockProgram(handler func(tea.Msg)) *MockProgram { - return &MockProgram{ - handler: handler, - } -} - -func (m *MockProgram) Send(msg tea.Msg) { - if m.handler != nil { - m.mu.Lock() - m.handler(msg) - m.mu.Unlock() - } -} - -func (m *MockProgram) Start() error { - return nil -} - -func (m *MockProgram) Quit() {} diff --git a/apps/cli-go/internal/utils/credentials/input.go b/apps/cli-go/internal/utils/credentials/input.go deleted file mode 100644 index 290b807aea..0000000000 --- a/apps/cli-go/internal/utils/credentials/input.go +++ /dev/null @@ -1,30 +0,0 @@ -package credentials - -import ( - "bytes" - "fmt" - "io" - "os" - - "golang.org/x/term" -) - -func PromptMasked(stdin *os.File) string { - // Start a new line after reading input - defer fmt.Println() - // Copy if stdin is piped: https://stackoverflow.com/a/26567513 - if !term.IsTerminal(int(stdin.Fd())) { - var buf bytes.Buffer - if _, err := io.Copy(&buf, stdin); err != nil { - return "" - } - return buf.String() - } - // Read with masked tokens - bytepw, err := term.ReadPassword(int(stdin.Fd())) - if err != nil { - fmt.Fprintln(os.Stderr, "Failed to read password:", err) - return "" - } - return string(bytepw) -} diff --git a/apps/cli-go/internal/utils/credentials/input_test.go b/apps/cli-go/internal/utils/credentials/input_test.go deleted file mode 100644 index c83d6d3adc..0000000000 --- a/apps/cli-go/internal/utils/credentials/input_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package credentials - -import ( - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPromptMasked(t *testing.T) { - t.Run("reads from piped stdin", func(t *testing.T) { - // Setup token - r, w, err := os.Pipe() - require.NoError(t, err) - _, err = w.WriteString("token") - require.NoError(t, err) - require.NoError(t, w.Close()) - // Run test - input := PromptMasked(r) - // Check error - assert.Equal(t, "token", input) - }) - - t.Run("empty string on closed pipe", func(t *testing.T) { - // Setup empty stdin - r, _, err := os.Pipe() - require.NoError(t, err) - require.NoError(t, r.Close()) - // Run test - input := PromptMasked(r) - // Check error - assert.Empty(t, input) - }) -} diff --git a/apps/cli-go/internal/utils/credentials/keyring_test.go b/apps/cli-go/internal/utils/credentials/keyring_test.go deleted file mode 100644 index 4f450078f2..0000000000 --- a/apps/cli-go/internal/utils/credentials/keyring_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package credentials - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/zalando/go-keyring" -) - -func TestDeleteAll(t *testing.T) { - service := "test-cli" - // Nothing to delete - err := keyring.DeleteAll(service) - assert.NoError(t, err) - // Setup 2 items - err = keyring.Set(service, "key1", "value") - assert.NoError(t, err) - err = keyring.Set(service, "key2", "value") - assert.NoError(t, err) - // Delete all items - err = keyring.DeleteAll(service) - assert.NoError(t, err) - // Check items are gone - _, err = keyring.Get(service, "key1") - assert.ErrorIs(t, err, keyring.ErrNotFound) - _, err = keyring.Get(service, "key2") - assert.ErrorIs(t, err, keyring.ErrNotFound) -} diff --git a/apps/cli-go/internal/utils/credentials/store.go b/apps/cli-go/internal/utils/credentials/store.go deleted file mode 100644 index d7bf9d2b2d..0000000000 --- a/apps/cli-go/internal/utils/credentials/store.go +++ /dev/null @@ -1,88 +0,0 @@ -package credentials - -import ( - "bytes" - "os" - "os/exec" - - "github.com/go-errors/errors" - "github.com/zalando/go-keyring" -) - -const namespace = "Supabase CLI" - -var ErrNotSupported = errors.New("Keyring is not supported on WSL") - -type Store interface { - Get(key string) (string, error) - Set(key, value string) error - Delete(project string) error - DeleteAll() error -} - -type KeyringStore struct{} - -var StoreProvider Store = &KeyringStore{} - -// Get retrieves the password for a project from the keyring. -func (ks *KeyringStore) Get(project string) (string, error) { - if err := assertKeyringSupported(); err != nil { - return "", err - } - val, err := keyring.Get(namespace, project) - if errors.Is(err, exec.ErrNotFound) { - return "", errors.New(ErrNotSupported) - } else if err != nil { - return "", errors.Errorf("failed to load credentials: %w", err) - } - return val, nil -} - -func (ks *KeyringStore) Set(project, password string) error { - if err := assertKeyringSupported(); err != nil { - return err - } - if err := keyring.Set(namespace, project, password); err != nil { - if errors.Is(err, exec.ErrNotFound) { - return ErrNotSupported - } - return errors.Errorf("failed to set credentials: %w", err) - } - return nil -} - -func (ks *KeyringStore) Delete(project string) error { - if err := assertKeyringSupported(); err != nil { - return err - } - if err := keyring.Delete(namespace, project); err != nil { - if errors.Is(err, exec.ErrNotFound) { - return ErrNotSupported - } - return errors.Errorf("failed to delete credentials: %w", err) - } - return nil -} - -func (ks *KeyringStore) DeleteAll() error { - if err := assertKeyringSupported(); err != nil { - return err - } - if err := keyring.DeleteAll(namespace); err != nil { - if errors.Is(err, exec.ErrNotFound) { - return ErrNotSupported - } - return errors.Errorf("failed to delete all credentials in %s: %w", namespace, err) - } - return nil -} - -func assertKeyringSupported() error { - // Suggested check: https://github.com/microsoft/WSL/issues/423 - if f, err := os.ReadFile("/proc/sys/kernel/osrelease"); err == nil { - if bytes.Contains(f, []byte("WSL")) || bytes.Contains(f, []byte("Microsoft")) { - return errors.New(ErrNotSupported) - } - } - return nil -} diff --git a/apps/cli-go/internal/utils/credentials/store_test.go b/apps/cli-go/internal/utils/credentials/store_test.go deleted file mode 100644 index 4bb62f6581..0000000000 --- a/apps/cli-go/internal/utils/credentials/store_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package credentials - -import ( - "testing" - - "github.com/go-errors/errors" - "github.com/stretchr/testify/assert" - "github.com/zalando/go-keyring" -) - -func TestKeyringStore(t *testing.T) { - t.Run("stores and retrieves password", func(t *testing.T) { - keyring.MockInit() - project := "test-project" - password := "test-password" - - err := StoreProvider.Set(project, password) - assert.NoError(t, err) - - retrieved, err := StoreProvider.Get(project) - assert.NoError(t, err) - assert.Equal(t, password, retrieved) - }) - - t.Run("returns error for non-existent project", func(t *testing.T) { - keyring.MockInit() - project := "non-existent" - - retrieved, err := StoreProvider.Get(project) - assert.ErrorIs(t, err, keyring.ErrNotFound) - assert.Empty(t, retrieved) - }) - - t.Run("deletes specific project password", func(t *testing.T) { - keyring.MockInit() - project := "test-project" - password := "test-password" - - err := StoreProvider.Set(project, password) - assert.NoError(t, err) - err = StoreProvider.Delete(project) - assert.NoError(t, err) - - _, err = StoreProvider.Get(project) - assert.ErrorIs(t, err, keyring.ErrNotFound) - }) - - t.Run("deletes all project passwords", func(t *testing.T) { - keyring.MockInit() - projects := []string{"project1", "project2"} - - for _, project := range projects { - err := StoreProvider.Set(project, "password") - assert.NoError(t, err) - } - - err := StoreProvider.DeleteAll() - assert.NoError(t, err) - - for _, project := range projects { - _, err := StoreProvider.Get(project) - assert.ErrorIs(t, err, keyring.ErrNotFound) - } - }) -} - -func TestKeyringErrors(t *testing.T) { - t.Run("handles Get error", func(t *testing.T) { - mockErr := errors.New("mock error") - keyring.MockInitWithError(mockErr) - - _, err := StoreProvider.Get("test") - assert.ErrorIs(t, err, mockErr) - }) - - t.Run("handles Set error", func(t *testing.T) { - mockErr := errors.New("mock error") - keyring.MockInitWithError(mockErr) - - err := StoreProvider.Set("test", "pass") - assert.ErrorIs(t, err, mockErr) - }) - - t.Run("handles Delete error", func(t *testing.T) { - mockErr := errors.New("mock error") - keyring.MockInitWithError(mockErr) - - err := StoreProvider.Delete("test") - assert.ErrorIs(t, err, mockErr) - }) - - t.Run("handles DeleteAll error", func(t *testing.T) { - mockErr := errors.New("mock error") - keyring.MockInitWithError(mockErr) - - err := StoreProvider.DeleteAll() - assert.ErrorIs(t, err, mockErr) - }) -} diff --git a/apps/cli-go/internal/utils/deno.go b/apps/cli-go/internal/utils/deno.go deleted file mode 100644 index 79786c0b56..0000000000 --- a/apps/cli-go/internal/utils/deno.go +++ /dev/null @@ -1,272 +0,0 @@ -package utils - -import ( - "archive/zip" - "bytes" - "context" - "crypto/sha256" - "embed" - "fmt" - "io" - "io/fs" - "net/http" - "os" - "os/exec" - "path" - "path/filepath" - "runtime" - "strings" - - "github.com/go-errors/errors" - "github.com/spf13/afero" - "github.com/supabase/cli/pkg/function" -) - -var ( - //go:embed denos/* - denoEmbedDir embed.FS - // Used by unit tests - DenoPathOverride string -) - -const ( - // Legacy bundle options - DockerDenoDir = "/home/deno" - DockerEszipDir = "/root/eszips" - DenoVersion = "1.30.3" -) - -func GetDenoPath() (string, error) { - if len(DenoPathOverride) > 0 { - return DenoPathOverride, nil - } - home, err := SupabaseHomeDir() - if err != nil { - return "", err - } - denoBinName := "deno" - if runtime.GOOS == "windows" { - denoBinName = "deno.exe" - } - denoPath := filepath.Join(home, denoBinName) - return denoPath, nil -} - -func InstallOrUpgradeDeno(ctx context.Context, fsys afero.Fs) error { - denoPath, err := GetDenoPath() - if err != nil { - return err - } - - if _, err := fsys.Stat(denoPath); err == nil { - // Upgrade Deno. - cmd := exec.CommandContext(ctx, denoPath, "upgrade", "--version", DenoVersion) - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - return cmd.Run() - } else if !errors.Is(err, os.ErrNotExist) { - return err - } - - // Install Deno. - if err := MkdirIfNotExistFS(fsys, filepath.Dir(denoPath)); err != nil { - return err - } - - // 1. Determine OS triple - assetFilename, err := getDenoAssetFileName() - if err != nil { - return err - } - assetRepo := "denoland/deno" - if runtime.GOOS == "linux" && runtime.GOARCH == "arm64" { - // TODO: version pin to official release once available https://github.com/denoland/deno/issues/1846 - assetRepo = "LukeChannings/deno-arm64" - } - - // 2. Download & install Deno binary. - { - assetUrl := fmt.Sprintf("https://github.com/%s/releases/download/v%s/%s", assetRepo, DenoVersion, assetFilename) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, assetUrl, nil) - if err != nil { - return err - } - resp, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != 200 { - return errors.New("Failed installing Deno binary.") - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - - r, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) - // There should be only 1 file: the deno binary - if len(r.File) != 1 { - return err - } - denoContents, err := r.File[0].Open() - if err != nil { - return err - } - defer denoContents.Close() - - denoBytes, err := io.ReadAll(denoContents) - if err != nil { - return err - } - - if err := afero.WriteFile(fsys, denoPath, denoBytes, 0755); err != nil { - return err - } - } - - return nil -} - -func isScriptModified(fsys afero.Fs, destPath string, src []byte) (bool, error) { - dest, err := afero.ReadFile(fsys, destPath) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - return true, nil - } - return false, err - } - - // compare the md5 checksum of src bytes with user's copy. - // if the checksums doesn't match, script is modified. - return sha256.Sum256(dest) != sha256.Sum256(src), nil -} - -type DenoScriptDir struct { - ExtractPath string - BuildPath string -} - -// Copy Deno scripts needed for function deploy and downloads, returning a DenoScriptDir struct or an error. -func CopyDenoScripts(ctx context.Context, fsys afero.Fs) (*DenoScriptDir, error) { - denoPath, err := GetDenoPath() - if err != nil { - return nil, err - } - - denoDirPath := filepath.Dir(denoPath) - scriptDirPath := filepath.Join(denoDirPath, "denos") - - // make the script directory if not exist - if err := MkdirIfNotExistFS(fsys, scriptDirPath); err != nil { - return nil, err - } - - // copy embed files to script directory - err = fs.WalkDir(denoEmbedDir, "denos", func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - - // skip copying the directory - if d.IsDir() { - return nil - } - - destPath := filepath.Join(denoDirPath, path) - - contents, err := fs.ReadFile(denoEmbedDir, path) - if err != nil { - return err - } - - // check if the script should be copied - modified, err := isScriptModified(fsys, destPath, contents) - if err != nil { - return err - } - if !modified { - return nil - } - - if err := afero.WriteFile(fsys, filepath.Join(denoDirPath, path), contents, 0666); err != nil { - return err - } - - return nil - }) - if err != nil { - return nil, err - } - - sd := DenoScriptDir{ - ExtractPath: filepath.Join(scriptDirPath, "extract.ts"), - BuildPath: filepath.Join(scriptDirPath, "build.ts"), - } - - return &sd, nil -} - -func BindHostModules(cwd, relEntrypointPath, relImportMapPath string, fsys afero.Fs) ([]string, error) { - var modules []string - bindModule := func(srcPath string, r io.Reader) error { - hostPath := filepath.Join(cwd, filepath.FromSlash(srcPath)) - dockerPath := ToDockerPath(hostPath) - modules = append(modules, hostPath+":"+dockerPath+":ro") - return nil - } - importMap := function.ImportMap{} - if imPath := filepath.ToSlash(relImportMapPath); len(imPath) > 0 { - if err := importMap.LoadAsDeno(imPath, afero.NewIOFS(fsys), bindModule); err != nil { - return nil, err - } - } - // Resolving all Import Graph - addModule := func(unixPath string, w io.Writer) error { - hostPath := toHostPath(cwd, unixPath) - f, err := fsys.Open(hostPath) - if err != nil { - return errors.Errorf("failed to read file: %w", err) - } - defer f.Close() - if _, err := io.Copy(w, f); err != nil { - return errors.Errorf("failed to copy file content: %w", err) - } - dockerPath := ToDockerPath(hostPath) - modules = append(modules, hostPath+":"+dockerPath+":ro") - return nil - } - unixPath := filepath.ToSlash(relEntrypointPath) - if err := importMap.WalkImportPaths(unixPath, addModule); err != nil { - return nil, err - } - // Also mount local directories declared in scopes - for _, scope := range importMap.Scopes { - for _, unixPath := range scope { - hostPath := toHostPath(cwd, unixPath) - // Ref: https://docs.deno.com/runtime/fundamentals/modules/#overriding-https-imports - if _, err := fsys.Stat(hostPath); err != nil { - return nil, errors.Errorf("failed to resolve scope: %w", err) - } - dockerPath := ToDockerPath(hostPath) - modules = append(modules, hostPath+":"+dockerPath+":ro") - } - } - return modules, nil -} - -func toHostPath(cwd, unixPath string) string { - hostPath := filepath.FromSlash(unixPath) - if path.IsAbs(unixPath) { - return filepath.VolumeName(cwd) + hostPath - } - return filepath.Join(cwd, hostPath) -} - -func ToDockerPath(absHostPath string) string { - prefix := filepath.VolumeName(absHostPath) - dockerPath := filepath.ToSlash(absHostPath) - return strings.TrimPrefix(dockerPath, prefix) -} diff --git a/apps/cli-go/internal/utils/deno_darwin.go b/apps/cli-go/internal/utils/deno_darwin.go deleted file mode 100644 index fe0b89d10e..0000000000 --- a/apps/cli-go/internal/utils/deno_darwin.go +++ /dev/null @@ -1,29 +0,0 @@ -//go:build darwin - -package utils - -import ( - "syscall" - - "github.com/go-errors/errors" -) - -func getDenoAssetFileName() (string, error) { - // Simple runtime.GOARCH detection doesn't work if the CLI is - // running under Rosetta: - // https://github.com/supabase/cli/issues/1266. So as a workaround - // we use Apple Silicon detection: - // https://www.yellowduck.be/posts/detecting-apple-silicon-via-go. - _, err := syscall.Sysctl("sysctl.proc_translated") - if err != nil { - if err.Error() == "no such file or directory" { - // Running on Intel Mac. - return "deno-x86_64-apple-darwin.zip", nil - } else { - return "", errors.Errorf("failed to determine OS triple: %w", err) - } - } else { - // Running on Apple Silicon. - return "deno-aarch64-apple-darwin.zip", nil - } -} diff --git a/apps/cli-go/internal/utils/deno_generic.go b/apps/cli-go/internal/utils/deno_generic.go deleted file mode 100644 index 2992c7baba..0000000000 --- a/apps/cli-go/internal/utils/deno_generic.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build !darwin - -package utils - -import ( - "runtime" - - "github.com/go-errors/errors" -) - -func getDenoAssetFileName() (string, error) { - if runtime.GOOS == "linux" && runtime.GOARCH == "amd64" { - return "deno-x86_64-unknown-linux-gnu.zip", nil - } else if runtime.GOOS == "linux" && runtime.GOARCH == "arm64" { - // TODO: version pin to official release once available https://github.com/denoland/deno/issues/1846 - return "deno-linux-arm64.zip", nil - } else if runtime.GOOS == "windows" && runtime.GOARCH == "amd64" { - return "deno-x86_64-pc-windows-msvc.zip", nil - } else { - return "", errors.New("Platform " + runtime.GOOS + "/" + runtime.GOARCH + " is currently unsupported for Functions.") - } -} diff --git a/apps/cli-go/internal/utils/deno_test.go b/apps/cli-go/internal/utils/deno_test.go deleted file mode 100644 index 1c104870d7..0000000000 --- a/apps/cli-go/internal/utils/deno_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package utils - -import ( - "context" - "os" - "path/filepath" - "runtime" - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestBindModules(t *testing.T) { - t.Run("binds docker imports", func(t *testing.T) { - fsys := afero.NewMemMapFs() - entrypoint := `import "https://deno.land" -import "/tmp/index.ts" -import "../common/index.ts" -import "../../../supabase/tests/index.ts" -import "./child/index.ts"` - require.NoError(t, WriteFile("/app/supabase/functions/hello/index.ts", []byte(entrypoint), fsys)) - require.NoError(t, WriteFile("/tmp/index.ts", []byte{}, fsys)) - require.NoError(t, WriteFile("/app/supabase/functions/common/index.ts", []byte{}, fsys)) - require.NoError(t, WriteFile("/app/supabase/tests/index.ts", []byte{}, fsys)) - require.NoError(t, WriteFile("/app/supabase/functions/hello/child/index.ts", []byte{}, fsys)) - // Run test - mods, err := BindHostModules("/app", "supabase/functions/hello/index.ts", "", fsys) - // Check error - assert.NoError(t, err) - assert.ElementsMatch(t, mods, []string{ - "/app/supabase/functions/hello/index.ts:/app/supabase/functions/hello/index.ts:ro", - "/tmp/index.ts:/tmp/index.ts:ro", - "/app/supabase/functions/common/index.ts:/app/supabase/functions/common/index.ts:ro", - "/app/supabase/tests/index.ts:/app/supabase/tests/index.ts:ro", - "/app/supabase/functions/hello/child/index.ts:/app/supabase/functions/hello/child/index.ts:ro", - }) - }) -} - -func TestGetDenoPath(t *testing.T) { - t.Run("returns override path when set", func(t *testing.T) { - override := "/custom/path/to/deno" - DenoPathOverride = override - defer func() { DenoPathOverride = "" }() - - path, err := GetDenoPath() - - assert.NoError(t, err) - assert.Equal(t, override, path) - }) - - t.Run("returns default path", func(t *testing.T) { - home, err := os.UserHomeDir() - require.NoError(t, err) - expected := filepath.Join(home, ".supabase", "deno") - if runtime.GOOS == "windows" { - expected += ".exe" - } - - path, err := GetDenoPath() - - assert.NoError(t, err) - assert.Equal(t, expected, path) - }) - - t.Run("returns SUPABASE_HOME path when configured", func(t *testing.T) { - t.Setenv("SUPABASE_HOME", "/custom/supabase") - expected := filepath.Join("/custom/supabase", "deno") - if runtime.GOOS == "windows" { - expected += ".exe" - } - - path, err := GetDenoPath() - - assert.NoError(t, err) - assert.Equal(t, expected, path) - }) -} - -func TestIsScriptModified(t *testing.T) { - t.Run("detects modified script", func(t *testing.T) { - fsys := afero.NewMemMapFs() - destPath := "/path/to/script.ts" - original := []byte("original content") - modified := []byte("modified content") - require.NoError(t, afero.WriteFile(fsys, destPath, modified, 0644)) - - isModified, err := isScriptModified(fsys, destPath, original) - - assert.NoError(t, err) - assert.True(t, isModified) - }) - - t.Run("detects unmodified script", func(t *testing.T) { - fsys := afero.NewMemMapFs() - destPath := "/path/to/script.ts" - content := []byte("test content") - require.NoError(t, afero.WriteFile(fsys, destPath, content, 0644)) - - isModified, err := isScriptModified(fsys, destPath, content) - - assert.NoError(t, err) - assert.False(t, isModified) - }) - - t.Run("handles non-existent script", func(t *testing.T) { - fsys := afero.NewMemMapFs() - destPath := "/path/to/script.ts" - content := []byte("test content") - - isModified, err := isScriptModified(fsys, destPath, content) - - assert.NoError(t, err) - assert.True(t, isModified) - }) -} - -func TestCopyDenoScripts(t *testing.T) { - t.Run("copies deno scripts", func(t *testing.T) { - fsys := afero.NewMemMapFs() - home, err := os.UserHomeDir() - require.NoError(t, err) - denoDir := filepath.Join(home, ".supabase") - require.NoError(t, fsys.MkdirAll(denoDir, 0755)) - - scripts, err := CopyDenoScripts(context.Background(), fsys) - - assert.NoError(t, err) - assert.NotNil(t, scripts) - extractExists, err := afero.Exists(fsys, scripts.ExtractPath) - assert.NoError(t, err) - assert.True(t, extractExists) - }) - - t.Run("skips copying unmodified scripts", func(t *testing.T) { - fsys := afero.NewMemMapFs() - home, err := os.UserHomeDir() - require.NoError(t, err) - scriptDir := filepath.Join(home, ".supabase", "denos") - require.NoError(t, fsys.MkdirAll(scriptDir, 0755)) - - scripts1, err := CopyDenoScripts(context.Background(), fsys) - require.NoError(t, err) - stat1, err := fsys.Stat(scripts1.ExtractPath) - require.NoError(t, err) - modTime1 := stat1.ModTime() - - // Second copy - scripts2, err := CopyDenoScripts(context.Background(), fsys) - require.NoError(t, err) - stat2, err := fsys.Stat(scripts2.ExtractPath) - require.NoError(t, err) - modTime2 := stat2.ModTime() - - // Verify file wasn't rewritten - assert.Equal(t, modTime1, modTime2) - }) -} diff --git a/apps/cli-go/internal/utils/denos/extract.ts b/apps/cli-go/internal/utils/denos/extract.ts deleted file mode 100644 index 1e47399677..0000000000 --- a/apps/cli-go/internal/utils/denos/extract.ts +++ /dev/null @@ -1,71 +0,0 @@ -import * as path from "https://deno.land/std@0.127.0/path/mod.ts"; -import { readAll } from "https://deno.land/std@0.162.0/streams/conversion.ts"; -import { Parser } from "https://deno.land/x/eszip@v0.30.0/mod.ts"; - -async function write(p: string, content: string) { - await Deno.mkdir(path.dirname(p), { recursive: true }); - await Deno.writeTextFile(p, content); -} - -async function loadEszip(bytes: Uint8Array) { - const parser = await Parser.createInstance(); - const specifiers = await parser.parseBytes(bytes); - await parser.load(); - return { parser, specifiers }; -} - -async function extractEszip( - destPath: string, - entrypointUrl: string, - rootPath: string, - parser: Parser, - specifiers: string[], -) { - const entrypointPath = path.fromFileUrl(entrypointUrl); - const basePath = path.dirname(entrypointPath); - // Containment is enforced against rootPath (the shared functions - // directory), not destPath (this function's own directory): a common - // layout imports a sibling like "../_shared/cors.ts", which legitimately - // resolves one level above destPath, but must never resolve outside - // rootPath entirely. - const resolvedRoot = path.resolve(rootPath); - for (const specifier of specifiers) { - // skip remote dependencies - if (!specifier.startsWith("file:")) { - continue; - } - console.error(specifier); - const module = await parser.getModuleSource(specifier); - const absPath = path.fromFileUrl(specifier); - const relPath = path.relative(basePath, absPath); - const dest = path.join(destPath, relPath); - - const resolvedDest = path.resolve(dest); - const relToRoot = path.relative(resolvedRoot, resolvedDest); - if ( - relToRoot === ".." || - relToRoot.startsWith(`..${path.SEP}`) || - path.isAbsolute(relToRoot) - ) { - throw new Error( - `Refusing to extract "${specifier}" outside of ${rootPath}`, - ); - } - - console.info(path.resolve(dest)); - await write(dest, module); - } -} - -async function extractSource( - destPath: string, - entrypointUrl: string, - rootPath: string, -) { - const buf = await readAll(Deno.stdin); - - const { parser, specifiers } = await loadEszip(buf); - await extractEszip(destPath, entrypointUrl, rootPath, parser, specifiers); -} - -extractSource(...Deno.args); diff --git a/apps/cli-go/internal/utils/docker.go b/apps/cli-go/internal/utils/docker.go deleted file mode 100644 index 29d2edd397..0000000000 --- a/apps/cli-go/internal/utils/docker.go +++ /dev/null @@ -1,686 +0,0 @@ -package utils - -import ( - "bytes" - "context" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "log" - "os" - "regexp" - "strings" - "sync" - "time" - - "github.com/containerd/errdefs" - podman "github.com/containers/common/libnetwork/types" - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/compose/loader" - dockerConfig "github.com/docker/cli/cli/config" - dockerFlags "github.com/docker/cli/cli/flags" - "github.com/docker/cli/cli/streams" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/api/types/image" - "github.com/docker/docker/api/types/mount" - "github.com/docker/docker/api/types/network" - "github.com/docker/docker/api/types/versions" - "github.com/docker/docker/api/types/volume" - "github.com/docker/docker/client" - "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/docker/pkg/stdcopy" - "github.com/go-errors/errors" - "github.com/spf13/viper" - "go.opentelemetry.io/otel" -) - -var Docker = NewDocker() - -func NewDocker() *client.Client { - // TODO: refactor to initialize lazily - cli, err := command.NewDockerCli() - if err != nil { - log.Fatalln("Failed to create Docker client:", err) - } - // Silence otel errors as users don't care about docker metrics - // 2024/08/12 23:11:12 1 errors occurred detecting resource: - // * conflicting Schema URL: https://opentelemetry.io/schemas/1.21.0 - otel.SetErrorHandler(otel.ErrorHandlerFunc(func(cause error) {})) - if err := cli.Initialize(&dockerFlags.ClientOptions{}); err != nil { - log.Fatalln("Failed to initialize Docker client:", err) - } - return cli.Client().(*client.Client) -} - -const ( - DinDHost = "host.docker.internal" - CliProjectLabel = "com.supabase.cli.project" - composeProjectLabel = "com.docker.compose.project" -) - -func DockerNetworkCreateIfNotExists(ctx context.Context, mode container.NetworkMode, labels map[string]string) error { - // Non-user defined networks should already exist - if !isUserDefined(mode) { - return nil - } - _, err := Docker.NetworkCreate(ctx, mode.NetworkName(), network.CreateOptions{Labels: labels}) - // if error is network already exists, no need to propagate to user - if errdefs.IsConflict(err) || errors.Is(err, podman.ErrNetworkExists) { - return nil - } - if err != nil { - return errors.Errorf("failed to create docker network: %w", err) - } - return err -} - -func WaitAll[T any](containers []T, exec func(container T) error) []error { - var wg sync.WaitGroup - result := make([]error, len(containers)) - for i, container := range containers { - wg.Add(1) - go func(i int, container T) { - defer wg.Done() - result[i] = exec(container) - }(i, container) - } - wg.Wait() - return result -} - -// NoBackupVolume TODO: encapsulate this state in a class -var NoBackupVolume = false - -func DockerRemoveAll(ctx context.Context, w io.Writer, projectId string) error { - fmt.Fprintln(w, "Stopping containers...") - args := CliProjectFilter(projectId) - containers, err := Docker.ContainerList(ctx, container.ListOptions{ - All: true, - Filters: args, - }) - if err != nil { - return errors.Errorf("failed to list containers: %w", err) - } - // Gracefully shutdown containers - var ids []string - for _, c := range containers { - ids = append(ids, c.ID) - } - result := WaitAll(ids, func(id string) error { - if err := Docker.ContainerStop(ctx, id, container.StopOptions{}); err != nil && !errdefs.IsNotModified(err) { - return errors.Errorf("failed to stop container: %w", err) - } - return nil - }) - if err := errors.Join(result...); err != nil { - return err - } - if report, err := Docker.ContainersPrune(ctx, args); err != nil { - return errors.Errorf("failed to prune containers: %w", err) - } else if viper.GetBool("DEBUG") { - fmt.Fprintln(os.Stderr, "Pruned containers:", report.ContainersDeleted) - } - // Remove named volumes - if NoBackupVolume { - vargs := args.Clone() - if versions.GreaterThanOrEqualTo(Docker.ClientVersion(), "1.42") { - // Since docker engine 25.0.3, all flag is required to include named volumes. - // https://github.com/docker/cli/blob/master/cli/command/volume/prune.go#L76 - vargs.Add("all", "true") - } - if report, err := Docker.VolumesPrune(ctx, vargs); err != nil { - return errors.Errorf("failed to prune volumes: %w", err) - } else if viper.GetBool("DEBUG") { - fmt.Fprintln(os.Stderr, "Pruned volumes:", report.VolumesDeleted) - } - } - // Remove networks. - if report, err := Docker.NetworksPrune(ctx, args); err != nil { - return errors.Errorf("failed to prune networks: %w", err) - } else if viper.GetBool("DEBUG") { - fmt.Fprintln(os.Stderr, "Pruned network:", report.NetworksDeleted) - } - return nil -} - -func CliProjectFilter(projectId string) filters.Args { - if len(projectId) == 0 { - return filters.NewArgs( - filters.Arg("label", CliProjectLabel), - ) - } - return filters.NewArgs( - filters.Arg("label", CliProjectLabel+"="+projectId), - ) -} - -var ( - registryAuth sync.Map -) - -// registryAuthEntry memoises a single registry's encoded auth so that -// loadRegistryAuth runs at most once per registry, even when concurrent image -// pulls request the same registry simultaneously. -type registryAuthEntry struct { - once sync.Once - value string -} - -func GetRegistryAuth() string { - return getRegistryAuth(GetRegistry()) -} - -func GetRegistryAuthForImage(imageTag string) string { - return getRegistryAuth(registryFromImage(imageTag)) -} - -func getRegistryAuth(registry string) string { - // Docker stores Hub credentials under the legacy index server, which - // resolves to the `index.docker.io` hostname; a bare `docker.io` lookup - // never matches them. Normalising here also routes images Docker treats as - // Hub references (a registry override or repository segment without a dot, - // colon, or `localhost`) to the right credentials. - if registry == "docker.io" { - registry = "index.docker.io" - } - // LoadOrStore + a per-key sync.Once guarantees loadRegistryAuth runs exactly - // once per registry, even under the concurrent image pulls `supabase start` - // issues. The result (including the empty string for a missing or unreadable - // credential entry) is then reused, so the Docker config file is read once - // and the credential warning is printed at most once per registry. - cached, _ := registryAuth.LoadOrStore(registry, ®istryAuthEntry{}) - entry := cached.(*registryAuthEntry) - entry.once.Do(func() { - entry.value = loadRegistryAuth(registry) - }) - return entry.value -} - -func loadRegistryAuth(registry string) string { - config := dockerConfig.LoadDefaultConfigFile(os.Stderr) - // Ref: https://docs.docker.com/engine/api/sdk/examples/#pull-an-image-with-authentication - auth, err := config.GetAuthConfig(registry) - if err != nil { - fmt.Fprintln(os.Stderr, "Failed to load registry credentials:", err) - return "" - } - encoded, err := json.Marshal(auth) - if err != nil { - fmt.Fprintln(os.Stderr, "Failed to serialise auth config:", err) - return "" - } - return base64.URLEncoding.EncodeToString(encoded) -} - -// Defaults to Supabase public ECR for faster image pull -const defaultRegistry = "public.ecr.aws" -const ghcrRegistry = "ghcr.io" - -func GetRegistry() string { - registry := viper.GetString("INTERNAL_IMAGE_REGISTRY") - if len(registry) == 0 { - return defaultRegistry - } - return strings.ToLower(registry) -} - -func HasRegistryOverride() bool { - return len(viper.GetString("INTERNAL_IMAGE_REGISTRY")) > 0 -} - -func GetRegistryImageUrl(imageName string) string { - registry := GetRegistry() - if registry == "docker.io" { - return imageName - } - // Configure mirror registry - parts := strings.Split(imageName, "/") - imageName = parts[len(parts)-1] - return registry + "/supabase/" + imageName -} - -func GetRegistryImageUrls(imageName string) []string { - if HasRegistryOverride() { - return []string{GetRegistryImageUrl(imageName)} - } - parts := strings.Split(imageName, "/") - lastPart := parts[len(parts)-1] - return dedupeStrings([]string{ - defaultRegistry + "/supabase/" + lastPart, - ghcrRegistry + "/supabase/" + lastPart, - dockerHubFallbackImage(imageName, lastPart), - }) -} - -func dockerHubFallbackImage(imageName, lastPart string) string { - if strings.HasPrefix(imageName, defaultRegistry+"/supabase/") || strings.HasPrefix(imageName, ghcrRegistry+"/supabase/") { - return "supabase/" + lastPart - } - return imageName -} - -func dedupeStrings(values []string) []string { - seen := make(map[string]struct{}, len(values)) - result := make([]string, 0, len(values)) - for _, value := range values { - if _, ok := seen[value]; ok { - continue - } - seen[value] = struct{}{} - result = append(result, value) - } - return result -} - -func registryFromImage(imageTag string) string { - parts := strings.Split(imageTag, "/") - if len(parts) == 1 { - return "docker.io" - } - first := parts[0] - if strings.Contains(first, ".") || strings.Contains(first, ":") || first == "localhost" { - return first - } - return "docker.io" -} - -func DockerImagePull(ctx context.Context, imageTag string, w io.Writer) error { - out, err := Docker.ImagePull(ctx, imageTag, image.PullOptions{ - RegistryAuth: GetRegistryAuthForImage(imageTag), - }) - if err != nil { - return errors.Errorf("failed to pull docker image: %w", err) - } - defer out.Close() - if err := jsonmessage.DisplayJSONMessagesToStream(out, streams.NewOut(w), nil); err != nil { - return errors.Errorf("failed to display json stream: %w", err) - } - return nil -} - -// Used by unit tests -var timeUnit = time.Second - -func DockerImagePullWithRetry(ctx context.Context, image string, retries int) error { - err := DockerImagePull(ctx, image, os.Stderr) - for i := range retries { - if err == nil || errors.Is(ctx.Err(), context.Canceled) { - break - } - fmt.Fprintln(os.Stderr, err) - period := time.Duration(2<<(i+1)) * timeUnit - fmt.Fprintf(os.Stderr, "Retrying after %v: %s\n", period, image) - time.Sleep(period) - err = DockerImagePull(ctx, image, os.Stderr) - } - return err -} - -func DockerPullImageIfNotCached(ctx context.Context, imageName string) error { - _, err := DockerResolveImageIfNotCached(ctx, imageName) - return err -} - -func DockerResolveImageIfNotCached(ctx context.Context, imageName string) (string, error) { - imageUrls := GetRegistryImageUrls(imageName) - for _, imageUrl := range imageUrls { - if _, err := Docker.ImageInspect(ctx, imageUrl); err == nil { - return imageUrl, nil - } else if !errdefs.IsNotFound(err) { - return "", errors.Errorf("failed to inspect docker image: %w", err) - } - } - var pullErrors []error - for _, imageUrl := range imageUrls { - err := DockerImagePullWithRetry(ctx, imageUrl, 2) - if err == nil { - return imageUrl, nil - } - pullErrors = append(pullErrors, fmt.Errorf("%s: %w", imageUrl, err)) - } - // errors.Join keeps every candidate's cause chain, so DockerStart's - // client.IsErrConnectionFailed unwrap still fires (errors.As traverses the - // Unwrap() []error tree) without flattening causes to a string or - // duplicating a representative error in the message. - return "", errors.Errorf("failed to pull docker image from all registries: %w", errors.Join(pullErrors...)) -} - -var suggestDockerInstall = "Docker Desktop is a prerequisite for local development. Follow the official docs to install: https://docs.docker.com/desktop" - -// SuggestDockerInstallIfConnectionFailed sets the install hint when err is a -// Docker daemon connection failure. Callers that resolve images before -// DockerStart (e.g. the concurrent ensureImagesCached pre-pull) call this once -// on the aggregated error, so the hint is surfaced without racing on the -// CmdSuggestion global from multiple goroutines. -func SuggestDockerInstallIfConnectionFailed(err error) { - if client.IsErrConnectionFailed(err) { - CmdSuggestion = suggestDockerInstall - } -} - -func DockerStart(ctx context.Context, config container.Config, hostConfig container.HostConfig, networkingConfig network.NetworkingConfig, containerName string) (string, error) { - // Pull container image - imageUrl, err := DockerResolveImageIfNotCached(ctx, config.Image) - if err != nil { - SuggestDockerInstallIfConnectionFailed(err) - return "", err - } - // Setup default config - config.Image = imageUrl - if config.Labels == nil { - config.Labels = make(map[string]string, 2) - } - config.Labels[CliProjectLabel] = Config.ProjectId - config.Labels[composeProjectLabel] = Config.ProjectId - // Configure container network - hostConfig.ExtraHosts = append(hostConfig.ExtraHosts, extraHosts...) - if networkId := viper.GetString("network-id"); len(networkId) > 0 { - hostConfig.NetworkMode = container.NetworkMode(networkId) - } else if len(hostConfig.NetworkMode) == 0 { - hostConfig.NetworkMode = container.NetworkMode(NetId) - } - if err := DockerNetworkCreateIfNotExists(ctx, hostConfig.NetworkMode, config.Labels); err != nil { - return "", err - } - // Configure container volumes - var binds, sources []string - for _, bind := range hostConfig.Binds { - spec, err := loader.ParseVolume(bind) - if err != nil { - return "", errors.Errorf("failed to parse docker volume: %w", err) - } - if spec.Type != string(mount.TypeVolume) { - binds = append(binds, bind) - } else if len(spec.Source) > 0 { - sources = append(sources, spec.Source) - } - } - // Skip named volume for BitBucket pipeline - if os.Getenv("BITBUCKET_CLONE_DIR") != "" { - hostConfig.Binds = binds - // Bitbucket doesn't allow for --security-opt option to be set - // https://support.atlassian.com/bitbucket-cloud/docs/run-docker-commands-in-bitbucket-pipelines/#Full-list-of-restricted-commands - hostConfig.SecurityOpt = nil - } else { - // Create named volumes with labels - for _, name := range sources { - if _, err := Docker.VolumeCreate(ctx, volume.CreateOptions{ - Name: name, - Labels: config.Labels, - }); err != nil { - return "", errors.Errorf("failed to create volume: %w", err) - } - } - } - // Create container from image - resp, err := Docker.ContainerCreate(ctx, &config, &hostConfig, &networkingConfig, nil, containerName) - if err != nil { - return "", errors.Errorf("failed to create docker container: %w", err) - } - // Run container in background - err = Docker.ContainerStart(ctx, resp.ID, container.StartOptions{}) - if err != nil { - if hostPort := parsePortBindError(err); len(hostPort) > 0 { - CmdSuggestion = suggestDockerStop(ctx, hostPort) - prefix := "Or configure" - if len(CmdSuggestion) == 0 { - prefix = "Try configuring" - } - name := containerName - if endpoint, ok := networkingConfig.EndpointsConfig[NetId]; ok && len(endpoint.Aliases) > 0 { - name = endpoint.Aliases[0] - } - CmdSuggestion += fmt.Sprintf("\n%s a different %s port in %s", prefix, name, Bold(ConfigPath)) - } - err = errors.Errorf("failed to start docker container %q: %w", containerName, err) - } - return resp.ID, err -} - -func DockerRemove(containerId string) { - if err := Docker.ContainerRemove(context.Background(), containerId, container.RemoveOptions{ - RemoveVolumes: true, - Force: true, - }); err != nil { - fmt.Fprintln(os.Stderr, "Failed to remove container:", containerId, err) - } -} - -type DockerJob struct { - Image string - Env []string - Cmd []string -} - -func DockerRunJob(ctx context.Context, job DockerJob, stdout, stderr io.Writer) error { - return DockerRunOnceWithStream(ctx, job.Image, job.Env, job.Cmd, stdout, stderr) -} - -// Runs a container image exactly once, returning stdout and throwing error on non-zero exit code. -func DockerRunOnce(ctx context.Context, image string, env []string, cmd []string) (string, error) { - stderr := GetDebugLogger() - var out bytes.Buffer - err := DockerRunOnceWithStream(ctx, image, env, cmd, &out, stderr) - return out.String(), err -} - -func DockerRunOnceWithStream(ctx context.Context, image string, env, cmd []string, stdout, stderr io.Writer) error { - return DockerRunOnceWithConfig(ctx, container.Config{ - Image: image, - Env: env, - Cmd: cmd, - }, container.HostConfig{}, network.NetworkingConfig{}, "", stdout, stderr) -} - -func DockerRunOnceWithConfig(ctx context.Context, config container.Config, hostConfig container.HostConfig, networkingConfig network.NetworkingConfig, containerName string, stdout, stderr io.Writer) error { - // Cannot rely on docker's auto remove because - // 1. We must inspect exit code after container stops - // 2. Context cancellation may happen after start - container, err := DockerStart(ctx, config, hostConfig, networkingConfig, containerName) - if err != nil { - return err - } - defer DockerRemove(container) - return DockerStreamLogs(ctx, container, stdout, stderr) -} - -// DockerRunOnceWaitWithConfig is like DockerRunOnceWithConfig but waits for the -// container to exit and then reads its already-buffered logs WITHOUT following -// the stream. -// -// DockerRunOnceWithConfig detects completion by reading a follow=true log stream -// until EOF. That EOF never arrives under podman: its /containers//logs?follow -// endpoint does not close the response when the container stops, so -// stdcopy.StdCopy blocks on the chunked HTTP body forever and the caller hangs at -// 0% CPU (supabase/pg-toolbelt#312). Waiting on /wait for the exit code and then -// reading the log once is reliable on both Docker and podman. -// -// Use this only for short-lived containers with bounded output (e.g. the -// edge-runtime pg-delta scripts): the full log is read after exit rather than -// streamed, so it is not suitable for large streaming output such as db dump. -func DockerRunOnceWaitWithConfig(ctx context.Context, config container.Config, hostConfig container.HostConfig, networkingConfig network.NetworkingConfig, containerName string, stdout, stderr io.Writer) error { - containerId, err := DockerStart(ctx, config, hostConfig, networkingConfig, containerName) - if err != nil { - return err - } - defer DockerRemove(containerId) - exitCode, err := dockerWaitExit(ctx, containerId) - if err != nil { - return err - } - if err := DockerStreamLogsOnce(ctx, containerId, stdout, stderr); err != nil { - return err - } - switch exitCode { - case 0: - return nil - case 137: - err = ErrContainerKilled - default: - err = errors.Errorf("exit %d", exitCode) - } - return errors.Errorf("error running container: %w", err) -} - -// dockerWaitInterval is how often dockerWaitExit polls container state. Kept -// short so bounded one-shot scripts return promptly; it is a package var so -// tests can drop it to zero. -var dockerWaitInterval = 200 * time.Millisecond - -// dockerWaitExit polls container state until it stops and returns its exit code. -// -// It deliberately uses ContainerInspect rather than a followed log stream (or -// /wait) to detect completion: inspect is reliable under podman, whereas -// podman's /logs?follow endpoint does not close when the container stops, which -// is what hangs DockerStreamLogs (see DockerRunOnceWaitWithConfig). Reusing -// inspect also keeps the request surface identical to DockerStreamLogs, so the -// existing test mocks continue to apply. -func dockerWaitExit(ctx context.Context, containerId string) (int64, error) { - for { - resp, err := Docker.ContainerInspect(ctx, containerId) - if err != nil { - return 0, errors.Errorf("failed to inspect docker container: %w", err) - } - if resp.State != nil && !resp.State.Running { - return int64(resp.State.ExitCode), nil - } - select { - case <-ctx.Done(): - return 0, ctx.Err() - case <-time.After(dockerWaitInterval): - } - } -} - -var ErrContainerKilled = errors.New("exit 137") - -func DockerStreamLogs(ctx context.Context, containerId string, stdout, stderr io.Writer, opts ...func(*container.LogsOptions)) error { - logsOptions := container.LogsOptions{ - ShowStdout: true, - ShowStderr: true, - Follow: true, - } - for _, apply := range opts { - apply(&logsOptions) - } - // Stream logs - logs, err := Docker.ContainerLogs(ctx, containerId, logsOptions) - if err != nil { - return errors.Errorf("failed to read docker logs: %w", err) - } - defer logs.Close() - if _, err := stdcopy.StdCopy(stdout, stderr, logs); err != nil { - return errors.Errorf("failed to copy docker logs: %w", err) - } - // Check exit code - resp, err := Docker.ContainerInspect(ctx, containerId) - if err != nil { - return errors.Errorf("failed to inspect docker container: %w", err) - } - switch resp.State.ExitCode { - case 0: - return nil - case 137: - err = ErrContainerKilled - default: - err = errors.Errorf("exit %d", resp.State.ExitCode) - } - return errors.Errorf("error running container: %w", err) -} - -func DockerStreamLogsOnce(ctx context.Context, containerId string, stdout, stderr io.Writer) error { - logs, err := Docker.ContainerLogs(ctx, containerId, container.LogsOptions{ - ShowStdout: true, - ShowStderr: true, - }) - if err != nil { - return errors.Errorf("failed to read docker logs: %w", err) - } - defer logs.Close() - if _, err := stdcopy.StdCopy(stdout, stderr, logs); err != nil { - return errors.Errorf("failed to copy docker logs: %w", err) - } - return nil -} - -// Exec a command once inside a container, returning stdout and throwing error on non-zero exit code. -func DockerExecOnce(ctx context.Context, containerId string, env []string, cmd []string) (string, error) { - stderr := io.Discard - if viper.GetBool("DEBUG") { - stderr = os.Stderr - } - var out bytes.Buffer - err := DockerExecOnceWithStream(ctx, containerId, "", env, cmd, &out, stderr) - return out.String(), err -} - -func DockerExecOnceWithStream(ctx context.Context, containerId, workdir string, env, cmd []string, stdout, stderr io.Writer) error { - // Reset shadow database - exec, err := Docker.ContainerExecCreate(ctx, containerId, container.ExecOptions{ - Env: env, - Cmd: cmd, - WorkingDir: workdir, - AttachStderr: true, - AttachStdout: true, - }) - if err != nil { - return errors.Errorf("failed to exec docker create: %w", err) - } - // Read exec output - resp, err := Docker.ContainerExecAttach(ctx, exec.ID, container.ExecStartOptions{}) - if err != nil { - return errors.Errorf("failed to exec docker attach: %w", err) - } - defer resp.Close() - // Capture error details - if _, err := stdcopy.StdCopy(stdout, stderr, resp.Reader); err != nil { - return errors.Errorf("failed to copy docker logs: %w", err) - } - // Get the exit code - iresp, err := Docker.ContainerExecInspect(ctx, exec.ID) - if err != nil { - return errors.Errorf("failed to exec docker inspect: %w", err) - } - if iresp.ExitCode > 0 { - err = errors.New("error executing command") - } - return err -} - -func IsDockerRunning(ctx context.Context) bool { - _, err := Docker.Ping(ctx) - return !client.IsErrConnectionFailed(err) -} - -var portErrorPattern = regexp.MustCompile("Bind for (.*) failed: port is already allocated") - -func parsePortBindError(err error) string { - matches := portErrorPattern.FindStringSubmatch(err.Error()) - if len(matches) > 1 { - return matches[len(matches)-1] - } - return "" -} - -func suggestDockerStop(ctx context.Context, hostPort string) string { - if containers, err := Docker.ContainerList(ctx, container.ListOptions{}); err == nil { - for _, c := range containers { - for _, p := range c.Ports { - if fmt.Sprintf("%s:%d", p.IP, p.PublicPort) == hostPort { - if project, ok := c.Labels[CliProjectLabel]; ok { - return "\nTry stopping the running project with " + Aqua("supabase stop --project-id "+project) - } else { - name := c.ID - if len(c.Names) > 0 { - name = c.Names[0] - } - return "\nTry stopping the running container with " + Aqua("docker stop "+name) - } - } - } - } - } - return "" -} diff --git a/apps/cli-go/internal/utils/docker_darwin.go b/apps/cli-go/internal/utils/docker_darwin.go deleted file mode 100644 index af183bc31a..0000000000 --- a/apps/cli-go/internal/utils/docker_darwin.go +++ /dev/null @@ -1,11 +0,0 @@ -//go:build darwin - -package utils - -import "github.com/docker/docker/api/types/container" - -var extraHosts []string - -func isUserDefined(mode container.NetworkMode) bool { - return mode.IsUserDefined() -} diff --git a/apps/cli-go/internal/utils/docker_linux.go b/apps/cli-go/internal/utils/docker_linux.go deleted file mode 100644 index b5463d0604..0000000000 --- a/apps/cli-go/internal/utils/docker_linux.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build linux - -package utils - -import "github.com/docker/docker/api/types/container" - -// Allows containers to resolve host network: https://stackoverflow.com/a/62431165 -var extraHosts = []string{DinDHost + ":host-gateway"} - -func isUserDefined(mode container.NetworkMode) bool { - return mode.IsUserDefined() -} diff --git a/apps/cli-go/internal/utils/docker_test.go b/apps/cli-go/internal/utils/docker_test.go deleted file mode 100644 index 5c58d5aea0..0000000000 --- a/apps/cli-go/internal/utils/docker_test.go +++ /dev/null @@ -1,386 +0,0 @@ -package utils - -import ( - "bytes" - "context" - "net/http" - "testing" - "time" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/image" - "github.com/docker/docker/api/types/network" - "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/docker/pkg/stdcopy" - "github.com/h2non/gock" - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" -) - -const ( - containerId = "test-container" - imageId = "test-image" -) - -func TestPullImage(t *testing.T) { - viper.Set("INTERNAL_IMAGE_REGISTRY", "docker.io") - - t.Run("pulls image if missing", func(t *testing.T) { - // Setup mock docker - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - gock.New(Docker.DaemonHost()). - Get("/v" + Docker.ClientVersion() + "/images/" + imageId + "/json"). - Reply(http.StatusNotFound) - gock.New(Docker.DaemonHost()). - Post("/v"+Docker.ClientVersion()+"/images/create"). - MatchParam("fromImage", imageId). - MatchParam("tag", "latest"). - Reply(http.StatusAccepted) - // Run test - assert.NoError(t, DockerPullImageIfNotCached(context.Background(), imageId)) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("does nothing if image exists", func(t *testing.T) { - // Setup mock docker - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - gock.New(Docker.DaemonHost()). - Get("/v" + Docker.ClientVersion() + "/images/" + imageId + "/json"). - Reply(http.StatusOK). - JSON(image.InspectResponse{}) - // Run test - assert.NoError(t, DockerPullImageIfNotCached(context.Background(), imageId)) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error if docker is unavailable", func(t *testing.T) { - // Setup mock docker - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - gock.New(Docker.DaemonHost()). - Get("/v" + Docker.ClientVersion() + "/images/" + imageId + "/json"). - Reply(http.StatusServiceUnavailable) - // Run test - assert.Error(t, DockerPullImageIfNotCached(context.Background(), imageId)) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on failure to pull image", func(t *testing.T) { - timeUnit = time.Duration(0) - // Setup mock docker - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - gock.New(Docker.DaemonHost()). - Get("/v" + Docker.ClientVersion() + "/images/" + imageId + "/json"). - Reply(http.StatusNotFound) - // Total 3 tries - gock.New(Docker.DaemonHost()). - Post("/v"+Docker.ClientVersion()+"/images/create"). - MatchParam("fromImage", imageId). - MatchParam("tag", "latest"). - Reply(http.StatusServiceUnavailable) - gock.New(Docker.DaemonHost()). - Post("/v"+Docker.ClientVersion()+"/images/create"). - MatchParam("fromImage", imageId). - MatchParam("tag", "latest"). - Reply(http.StatusAccepted). - JSON(jsonmessage.JSONMessage{Error: &jsonmessage.JSONError{Message: "toomanyrequests"}}) - gock.New(Docker.DaemonHost()). - Post("/v"+Docker.ClientVersion()+"/images/create"). - MatchParam("fromImage", imageId). - MatchParam("tag", "latest"). - Reply(http.StatusAccepted). - JSON(jsonmessage.JSONMessage{Error: &jsonmessage.JSONError{Message: "no space left on device"}}) - // Run test - err := DockerPullImageIfNotCached(context.Background(), imageId) - // Validate api - assert.ErrorContains(t, err, "no space left on device") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("falls back to GHCR when the default ECR pull fails", func(t *testing.T) { - viper.Set("INTERNAL_IMAGE_REGISTRY", "") - timeUnit = time.Duration(0) - t.Cleanup(func() { - viper.Set("INTERNAL_IMAGE_REGISTRY", "docker.io") - timeUnit = time.Second - }) - // Setup mock docker - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - ecrImage := "public.ecr.aws/supabase/" + imageId - ghcrImage := "ghcr.io/supabase/" + imageId - dockerImage := imageId - for _, image := range []string{ecrImage, ghcrImage, dockerImage} { - gock.New(Docker.DaemonHost()). - Get("/v" + Docker.ClientVersion() + "/images/" + image + "/json"). - Reply(http.StatusNotFound) - } - // ECR gets the initial attempt plus two retries. - for range 3 { - gock.New(Docker.DaemonHost()). - Post("/v"+Docker.ClientVersion()+"/images/create"). - MatchParam("fromImage", ecrImage). - MatchParam("tag", "latest"). - Reply(http.StatusAccepted). - JSON(jsonmessage.JSONMessage{Error: &jsonmessage.JSONError{Message: "toomanyrequests: Rate exceeded"}}) - } - gock.New(Docker.DaemonHost()). - Post("/v"+Docker.ClientVersion()+"/images/create"). - MatchParam("fromImage", ghcrImage). - MatchParam("tag", "latest"). - Reply(http.StatusAccepted) - // Run test - imageURL, err := DockerResolveImageIfNotCached(context.Background(), imageId) - // Validate api - assert.NoError(t, err) - assert.Equal(t, ghcrImage, imageURL) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestRegistryImageUrls(t *testing.T) { - t.Cleanup(func() { - viper.Set("INTERNAL_IMAGE_REGISTRY", "") - }) - - t.Run("returns fallback candidates when the registry is unset", func(t *testing.T) { - viper.Set("INTERNAL_IMAGE_REGISTRY", "") - - assert.Equal(t, []string{ - "public.ecr.aws/supabase/postgres:17.6.1.138", - "ghcr.io/supabase/postgres:17.6.1.138", - "supabase/postgres:17.6.1.138", - }, GetRegistryImageUrls("supabase/postgres:17.6.1.138")) - }) - - t.Run("dedupes an already-defaulted image", func(t *testing.T) { - viper.Set("INTERNAL_IMAGE_REGISTRY", "") - - assert.Equal(t, []string{ - "public.ecr.aws/supabase/postgres:17.6.1.138", - "ghcr.io/supabase/postgres:17.6.1.138", - "supabase/postgres:17.6.1.138", - }, GetRegistryImageUrls("public.ecr.aws/supabase/postgres:17.6.1.138")) - }) - - t.Run("uses a single candidate when the registry is explicitly configured", func(t *testing.T) { - viper.Set("INTERNAL_IMAGE_REGISTRY", "public.ecr.aws") - assert.Equal(t, []string{ - "public.ecr.aws/supabase/postgres:17.6.1.138", - }, GetRegistryImageUrls("supabase/postgres:17.6.1.138")) - - viper.Set("INTERNAL_IMAGE_REGISTRY", "docker.io") - assert.Equal(t, []string{ - "supabase/postgres:17.6.1.138", - }, GetRegistryImageUrls("supabase/postgres:17.6.1.138")) - }) -} - -func TestRunOnce(t *testing.T) { - viper.Set("INTERNAL_IMAGE_REGISTRY", "docker.io") - - t.Run("runs once in container", func(t *testing.T) { - // Setup mock docker - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - apitest.MockDockerStart(Docker, imageId, containerId) - require.NoError(t, apitest.MockDockerLogs(Docker, containerId, "hello world")) - // Run test - out, err := DockerRunOnce(context.Background(), imageId, nil, nil) - assert.NoError(t, err) - // Validate api - assert.Equal(t, "hello world", out) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on container create", func(t *testing.T) { - // Setup mock docker - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - gock.New(Docker.DaemonHost()). - Get("/v" + Docker.ClientVersion() + "/images/" + imageId + "/json"). - Reply(http.StatusOK). - JSON(image.InspectResponse{}) - gock.New(Docker.DaemonHost()). - Post("/v" + Docker.ClientVersion() + "/networks/create"). - Reply(http.StatusCreated). - JSON(network.CreateResponse{}) - gock.New(Docker.DaemonHost()). - Post("/v" + Docker.ClientVersion() + "/containers/create"). - Reply(http.StatusServiceUnavailable) - // Run test - _, err := DockerRunOnce(context.Background(), imageId, nil, nil) - assert.Error(t, err) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on container start", func(t *testing.T) { - // Setup mock docker - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - gock.New(Docker.DaemonHost()). - Get("/v" + Docker.ClientVersion() + "/images/" + imageId + "/json"). - Reply(http.StatusOK). - JSON(image.InspectResponse{}) - gock.New(Docker.DaemonHost()). - Post("/v" + Docker.ClientVersion() + "/networks/create"). - Reply(http.StatusCreated). - JSON(network.CreateResponse{}) - gock.New(Docker.DaemonHost()). - Post("/v" + Docker.ClientVersion() + "/containers/create"). - Reply(http.StatusOK). - JSON(container.CreateResponse{ID: containerId}) - gock.New(Docker.DaemonHost()). - Post("/v" + Docker.ClientVersion() + "/containers/" + containerId + "/start"). - Reply(http.StatusServiceUnavailable) - // Run test - _, err := DockerRunOnce(context.Background(), imageId, nil, nil) - assert.Error(t, err) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("removes container on cancel", func(t *testing.T) { - // Setup mock docker - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - apitest.MockDockerStart(Docker, imageId, containerId) - gock.New(Docker.DaemonHost()). - Get("/v"+Docker.ClientVersion()+"/containers/"+containerId+"/logs"). - Reply(http.StatusOK). - SetHeader("Content-Type", "application/vnd.docker.raw-stream"). - Delay(1 * time.Second) - ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(200*time.Millisecond)) - defer cancel() - gock.New(Docker.DaemonHost()). - Delete("/v" + Docker.ClientVersion() + "/containers/" + containerId). - Reply(http.StatusOK) - // Run test - _, err := DockerRunOnce(ctx, imageId, nil, nil) - assert.Error(t, err) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on failure to parse logs", func(t *testing.T) { - // Setup mock docker - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - apitest.MockDockerStart(Docker, imageId, containerId) - gock.New(Docker.DaemonHost()). - Get("/v"+Docker.ClientVersion()+"/containers/"+containerId+"/logs"). - Reply(http.StatusOK). - SetHeader("Content-Type", "application/vnd.docker.raw-stream"). - BodyString("hello world") - gock.New(Docker.DaemonHost()). - Delete("/v" + Docker.ClientVersion() + "/containers/" + containerId). - Reply(http.StatusOK) - // Run test - _, err := DockerRunOnce(context.Background(), imageId, nil, nil) - assert.Error(t, err) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on failure to inspect", func(t *testing.T) { - // Setup mock docker - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - apitest.MockDockerStart(Docker, imageId, containerId) - // Setup docker style logs - var body bytes.Buffer - writer := stdcopy.NewStdWriter(&body, stdcopy.Stdout) - _, err := writer.Write([]byte("hello world")) - require.NoError(t, err) - gock.New("http:///var/run/docker.sock"). - Get("/v"+Docker.ClientVersion()+"/containers/"+containerId+"/logs"). - Reply(http.StatusOK). - SetHeader("Content-Type", "application/vnd.docker.raw-stream"). - Body(&body) - gock.New("http:///var/run/docker.sock"). - Get("/v" + Docker.ClientVersion() + "/containers/" + containerId + "/json"). - Reply(http.StatusServiceUnavailable) - gock.New(Docker.DaemonHost()). - Delete("/v" + Docker.ClientVersion() + "/containers/" + containerId). - Reply(http.StatusOK) - // Run test - _, err = DockerRunOnce(context.Background(), imageId, nil, nil) - assert.ErrorContains(t, err, "request returned 503 Service Unavailable for API route and version") - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on non-zero exit code", func(t *testing.T) { - // Setup mock docker - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - apitest.MockDockerStart(Docker, imageId, containerId) - // Setup docker style logs - var body bytes.Buffer - writer := stdcopy.NewStdWriter(&body, stdcopy.Stdout) - _, err := writer.Write([]byte("hello world")) - require.NoError(t, err) - gock.New("http:///var/run/docker.sock"). - Get("/v"+Docker.ClientVersion()+"/containers/"+containerId+"/logs"). - Reply(http.StatusOK). - SetHeader("Content-Type", "application/vnd.docker.raw-stream"). - Body(&body) - gock.New("http:///var/run/docker.sock"). - Get("/v" + Docker.ClientVersion() + "/containers/" + containerId + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ - ExitCode: 1, - }}}) - gock.New(Docker.DaemonHost()). - Delete("/v" + Docker.ClientVersion() + "/containers/" + containerId). - Reply(http.StatusOK) - // Run test - _, err = DockerRunOnce(context.Background(), imageId, nil, nil) - assert.ErrorContains(t, err, "error running container: exit 1") - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestExecOnce(t *testing.T) { - t.Run("throws error on failure to exec", func(t *testing.T) { - // Setup mock server - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - gock.New(Docker.DaemonHost()). - Post("/v" + Docker.ClientVersion() + "/containers/" + containerId + "/exec"). - Reply(http.StatusServiceUnavailable) - // Run test - _, err := DockerExecOnce(context.Background(), containerId, nil, nil) - assert.Error(t, err) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on failure to hijack", func(t *testing.T) { - // Setup mock server - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - gock.New(Docker.DaemonHost()). - Post("/v" + Docker.ClientVersion() + "/containers/" + containerId + "/exec"). - Reply(http.StatusAccepted). - JSON(container.ExecCreateResponse{ID: "test-command"}) - // Run test - _, err := DockerExecOnce(context.Background(), containerId, nil, nil) - assert.Error(t, err) - // Validate api - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - // TODO: mock tcp hijack -} diff --git a/apps/cli-go/internal/utils/docker_wait_test.go b/apps/cli-go/internal/utils/docker_wait_test.go deleted file mode 100644 index 22ee32e0ee..0000000000 --- a/apps/cli-go/internal/utils/docker_wait_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package utils - -import ( - "bytes" - "context" - "testing" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" - "github.com/h2non/gock" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" -) - -// DockerRunOnceWaitWithConfig must detect container completion via inspect and -// read logs without following the stream. Following the log stream to detect -// exit hangs forever under podman, whose /logs?follow endpoint never closes when -// the container stops (supabase/pg-toolbelt#312). These tests pin that the runner -// reads the captured output and maps the inspected exit code to an error. -func TestDockerRunOnceWait(t *testing.T) { - imageUrl := GetRegistryImageUrl(imageId) - - t.Run("waits for exit then reads buffered logs", func(t *testing.T) { - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - apitest.MockDockerStart(Docker, imageUrl, containerId) - require.NoError(t, apitest.MockDockerLogs(Docker, containerId, "CATALOG\n")) - // Run test - var stdout, stderr bytes.Buffer - err := DockerRunOnceWaitWithConfig( - context.Background(), - container.Config{Image: imageUrl}, - container.HostConfig{}, - network.NetworkingConfig{}, - containerId, - &stdout, - &stderr, - ) - // Validate - assert.NoError(t, err) - assert.Equal(t, "CATALOG\n", stdout.String()) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("maps non-zero exit code to error", func(t *testing.T) { - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - apitest.MockDockerStart(Docker, imageUrl, containerId) - require.NoError(t, apitest.MockDockerLogsExitCode(Docker, containerId, 1)) - // Run test - var stdout, stderr bytes.Buffer - err := DockerRunOnceWaitWithConfig( - context.Background(), - container.Config{Image: imageUrl}, - container.HostConfig{}, - network.NetworkingConfig{}, - containerId, - &stdout, - &stderr, - ) - // Validate - assert.ErrorContains(t, err, "error running container: exit 1") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} diff --git a/apps/cli-go/internal/utils/docker_windows.go b/apps/cli-go/internal/utils/docker_windows.go deleted file mode 100644 index 99e68d9b69..0000000000 --- a/apps/cli-go/internal/utils/docker_windows.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build windows - -package utils - -import ( - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" -) - -var extraHosts []string - -func isUserDefined(mode container.NetworkMode) bool { - // Host network requires explicit check on windows: https://github.com/supabase/cli/pull/952 - return mode.IsUserDefined() && mode.UserDefined() != network.NetworkHost -} diff --git a/apps/cli-go/internal/utils/edgeruntime.go b/apps/cli-go/internal/utils/edgeruntime.go deleted file mode 100644 index 8e54afa628..0000000000 --- a/apps/cli-go/internal/utils/edgeruntime.go +++ /dev/null @@ -1,164 +0,0 @@ -package utils - -import ( - "bytes" - "context" - "fmt" - "net" - "strings" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/network" - "github.com/go-errors/errors" - "github.com/spf13/viper" -) - -// EdgeRuntimeScriptErrorSentinel is printed to stderr by the pg-delta Deno -// templates when their body throws (see the `catch` blocks in -// internal/db/diff/templates/*.ts). The templates force the edge-runtime worker -// to exit by throwing on both the success and failure paths, and that non-zero -// exit is otherwise suppressed here when stderr contains "main worker has been -// destroyed". Without a distinct marker a crashed script would be -// indistinguishable from a successful empty diff, so `db pull` would report "No -// schema changes found" while the real error (e.g. a permission-denied catalog -// query) was silently swallowed. Templates and tests must reference this exact -// string. See supabase/cli#5826. -const EdgeRuntimeScriptErrorSentinel = "PGDELTA_SCRIPT_ERROR" - -// edgeRuntimeFile is a single file dropped into the edge-runtime container's -// working directory before the configured command is run. -type edgeRuntimeFile struct { - name string - content string -} - -// edgeRuntimeOptions accumulates the optional inputs assembled by -// EdgeRuntimeOption functions and consumed by RunEdgeRuntimeScript. -type edgeRuntimeOptions struct { - extraFiles []edgeRuntimeFile - extraEnv []string -} - -// EdgeRuntimeOption customizes a RunEdgeRuntimeScript invocation. The current -// shape (extra files dropped alongside index.ts, extra container env vars) -// covers the local-pg-delta use case; extend the option struct as new needs -// arrive instead of adding more positional arguments. -type EdgeRuntimeOption func(*edgeRuntimeOptions) - -// WithExtraFile schedules an extra file alongside `index.ts` in the container. -// Useful for project-local config files (e.g. `.npmrc`, `deno.json`) that need -// to live next to the script Deno is asked to run. -func WithExtraFile(name, content string) EdgeRuntimeOption { - return func(o *edgeRuntimeOptions) { - o.extraFiles = append(o.extraFiles, edgeRuntimeFile{name: name, content: content}) - } -} - -// WithExtraEnv appends container env entries in `KEY=value` form. -func WithExtraEnv(entries ...string) EdgeRuntimeOption { - return func(o *edgeRuntimeOptions) { - o.extraEnv = append(o.extraEnv, entries...) - } -} - -// getFreeHostPort asks the OS for an unused TCP port on the host. -func getFreeHostPort() (int, error) { - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - return 0, errors.Errorf("failed to allocate free port: %w", err) - } - defer listener.Close() - return listener.Addr().(*net.TCPAddr).Port, nil -} - -// EdgeRuntimeStartCmd builds the base command for launching a one-shot Edge -// Runtime script. The runtime's HTTP listener is bound to a free host port so -// concurrent or leftover containers (which share the host network namespace -// because diff containers run with NetworkMode=host) don't collide on the -// edge-runtime default port, which surfaces as "Address already in use (os -// error 98)". See https://github.com/supabase/cli/issues/5407. -func EdgeRuntimeStartCmd() []string { - cmd := []string{"edge-runtime", "start", "--main-service=."} - // Skip the flag on the rare allocation failure to preserve prior behavior. - if port, err := getFreeHostPort(); err == nil { - cmd = append(cmd, fmt.Sprintf("--port=%d", port)) - } - return cmd -} - -// RunEdgeRuntimeScript executes a TypeScript program inside the configured Edge -// Runtime container and streams stdout/stderr back to the caller. -func RunEdgeRuntimeScript(ctx context.Context, env []string, script string, binds []string, errPrefix string, stdout, stderr *bytes.Buffer, opts ...EdgeRuntimeOption) error { - state := &edgeRuntimeOptions{} - for _, opt := range opts { - if opt != nil { - opt(state) - } - } - cmd := EdgeRuntimeStartCmd() - if viper.GetBool("DEBUG") { - cmd = append(cmd, "--verbose") - } - cmdString := strings.Join(cmd, " ") - files := append([]edgeRuntimeFile{{name: "index.ts", content: script}}, state.extraFiles...) - entrypoint := []string{"sh", "-c", buildEdgeRuntimeEntrypoint(files, cmdString)} - combinedEnv := env - if len(state.extraEnv) > 0 { - combinedEnv = append(append([]string{}, env...), state.extraEnv...) - } - // Wait for the container to exit and then read its logs, rather than - // following the log stream to detect completion. The edge-runtime worker is - // forced to exit once the script flushes its output, but podman's - // /logs?follow endpoint does not close when the container stops, so a - // followed read (DockerRunOnceWithConfig) hangs the CLI forever - // (supabase/pg-toolbelt#312). pg-delta script output is bounded, so reading - // the log once after exit is safe. - if err := DockerRunOnceWaitWithConfig( - ctx, - container.Config{ - Image: Config.EdgeRuntime.Image, - Env: combinedEnv, - Entrypoint: entrypoint, - }, - container.HostConfig{ - Binds: binds, - NetworkMode: network.NetworkHost, - }, - network.NetworkingConfig{}, - "", - stdout, - stderr, - ); err != nil && !strings.Contains(stderr.String(), "main worker has been destroyed") { - return errors.Errorf("%s: %w:\n%s", errPrefix, err, stderr.String()) - } - // The templates suppress their own non-zero exit (they throw to force the - // worker to exit, which surfaces as "main worker has been destroyed"), so a - // script crash can slip past the check above. Treat the sentinel — printed - // only by the templates' catch blocks — as a hard failure so the real error, - // collected in stderr, reaches the user instead of looking like an empty diff. - if strings.Contains(stderr.String(), EdgeRuntimeScriptErrorSentinel) { - return errors.Errorf("%s: error running script:\n%s", errPrefix, stderr.String()) - } - return nil -} - -// buildEdgeRuntimeEntrypoint emits a `sh -c` body that writes each file via a -// here-document and then runs cmd. All heredoc openers are joined with `&&` -// before the bodies so bash stacks them in declaration order; each body is -// terminated with a unique sentinel so file contents can contain `EOF` safely. -func buildEdgeRuntimeEntrypoint(files []edgeRuntimeFile, cmd string) string { - if len(files) == 0 { - return cmd + "\n" - } - var head strings.Builder - var bodies strings.Builder - for i, f := range files { - sentinel := fmt.Sprintf("__EDGE_RT_FILE_%d__", i) - fmt.Fprintf(&head, "cat <<'%s' > %s && ", sentinel, f.name) - fmt.Fprintf(&bodies, "%s\n%s\n", f.content, sentinel) - } - head.WriteString(cmd) - head.WriteString("\n") - head.WriteString(bodies.String()) - return head.String() -} diff --git a/apps/cli-go/internal/utils/edgeruntime_test.go b/apps/cli-go/internal/utils/edgeruntime_test.go deleted file mode 100644 index 2497630255..0000000000 --- a/apps/cli-go/internal/utils/edgeruntime_test.go +++ /dev/null @@ -1,168 +0,0 @@ -package utils - -import ( - "bytes" - "context" - "io" - "net/http" - "strconv" - "strings" - "testing" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/pkg/stdcopy" - "github.com/h2non/gock" - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" -) - -// mockEdgeRuntimeLogs registers the docker responses RunEdgeRuntimeScript needs: -// a one-shot log read multiplexing stdout+stderr, an inspect reporting the exit -// code, and the container delete. Mirrors apitest.MockDockerErrorLogs but also -// carries stdout so we can assert the success path preserves the script output. -func mockEdgeRuntimeLogs(t *testing.T, containerID, stdout, stderr string, exitCode int) { - t.Helper() - var body bytes.Buffer - if len(stdout) > 0 { - _, err := io.Copy(stdcopy.NewStdWriter(&body, stdcopy.Stdout), strings.NewReader(stdout)) - require.NoError(t, err) - } - if len(stderr) > 0 { - _, err := io.Copy(stdcopy.NewStdWriter(&body, stdcopy.Stderr), strings.NewReader(stderr)) - require.NoError(t, err) - } - gock.New(Docker.DaemonHost()). - Get("/v"+Docker.ClientVersion()+"/containers/"+containerID+"/logs"). - Reply(http.StatusOK). - SetHeader("Content-Type", "application/vnd.docker.raw-stream"). - Body(&body) - gock.New(Docker.DaemonHost()). - Get("/v" + Docker.ClientVersion() + "/containers/" + containerID + "/json"). - Reply(http.StatusOK). - JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{ - State: &container.State{ExitCode: exitCode}, - }}) - gock.New(Docker.DaemonHost()). - Delete("/v" + Docker.ClientVersion() + "/containers/" + containerID). - Reply(http.StatusOK) -} - -func TestRunEdgeRuntimeScript(t *testing.T) { - const containerID = "test-edge-runtime" - imageUrl := GetRegistryImageUrl(Config.EdgeRuntime.Image) - - t.Run("surfaces the real error when the script crashes behind the worker-destroyed message", func(t *testing.T) { - viper.Set("INTERNAL_IMAGE_REGISTRY", "docker.io") - t.Cleanup(func() { viper.Set("INTERNAL_IMAGE_REGISTRY", "") }) - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - apitest.MockDockerStart(Docker, imageUrl, containerID) - // The pg-delta template throws to force the worker to exit (surfacing as a - // non-zero exit + "main worker has been destroyed"), and its catch block - // prints the real error and the sentinel. This must NOT look like an empty diff. - stderr := "error: permission denied for table pg_user_mapping\n" + - EdgeRuntimeScriptErrorSentinel + "\n" + - "worker boot error\nmain worker has been destroyed\n" - mockEdgeRuntimeLogs(t, containerID, "", stderr, 1) - - var stdout, stderrBuf bytes.Buffer - err := RunEdgeRuntimeScript(context.Background(), nil, "console.log('x')", nil, "error diffing schema", &stdout, &stderrBuf) - require.Error(t, err) - assert.Contains(t, err.Error(), "error diffing schema: error running script:") - // The real, actionable error must reach the user, not "No schema changes found". - assert.Contains(t, err.Error(), "permission denied for table pg_user_mapping") - }) - - t.Run("still ignores a worker-destroyed exit when no sentinel is present", func(t *testing.T) { - viper.Set("INTERNAL_IMAGE_REGISTRY", "docker.io") - t.Cleanup(func() { viper.Set("INTERNAL_IMAGE_REGISTRY", "") }) - require.NoError(t, apitest.MockDocker(Docker)) - defer gock.OffAll() - apitest.MockDockerStart(Docker, imageUrl, containerID) - // Success path: the template forces the worker to exit after writing output, - // so the exit is non-zero with "main worker has been destroyed" but no sentinel. - mockEdgeRuntimeLogs(t, containerID, "ALTER TABLE x;\n", "main worker has been destroyed\n", 1) - - var stdout, stderrBuf bytes.Buffer - err := RunEdgeRuntimeScript(context.Background(), nil, "console.log('x')", nil, "error diffing schema", &stdout, &stderrBuf) - require.NoError(t, err) - assert.Equal(t, "ALTER TABLE x;\n", stdout.String()) - }) -} - -func TestBuildEdgeRuntimeEntrypoint(t *testing.T) { - t.Run("emits a single heredoc when only the script is provided", func(t *testing.T) { - got := buildEdgeRuntimeEntrypoint( - []edgeRuntimeFile{{name: "index.ts", content: "console.log('hi')"}}, - "edge-runtime start --main-service=.", - ) - assert.True(t, strings.HasPrefix(got, "cat <<'__EDGE_RT_FILE_0__' > index.ts && edge-runtime start --main-service=.\n")) - assert.Contains(t, got, "console.log('hi')\n__EDGE_RT_FILE_0__\n") - }) - - t.Run("chains heredocs in declaration order so each cat reads the matching body", func(t *testing.T) { - got := buildEdgeRuntimeEntrypoint( - []edgeRuntimeFile{ - {name: "index.ts", content: "TS_CONTENT"}, - {name: ".npmrc", content: "NPMRC_CONTENT"}, - }, - "edge-runtime start --main-service=.", - ) - // Both cat declarations must come before any body, separated by &&. - assert.Contains(t, got, "cat <<'__EDGE_RT_FILE_0__' > index.ts && cat <<'__EDGE_RT_FILE_1__' > .npmrc && edge-runtime start --main-service=.") - // Bodies must follow in the same order as the declarations. - idxScript := strings.Index(got, "TS_CONTENT") - idxNpmrc := strings.Index(got, "NPMRC_CONTENT") - require.Greater(t, idxScript, 0) - require.Greater(t, idxNpmrc, idxScript, ".npmrc body must come after index.ts body") - // Sentinels close each body so user content containing `EOF` cannot - // terminate the heredoc early. - assert.Contains(t, got, "TS_CONTENT\n__EDGE_RT_FILE_0__") - assert.Contains(t, got, "NPMRC_CONTENT\n__EDGE_RT_FILE_1__") - assert.True(t, strings.HasSuffix(got, "\n")) - }) - - t.Run("returns just the command when no files are provided", func(t *testing.T) { - got := buildEdgeRuntimeEntrypoint(nil, "edge-runtime start --main-service=.") - assert.Equal(t, "edge-runtime start --main-service=.\n", got) - }) -} - -func TestEdgeRuntimeStartCmd(t *testing.T) { - t.Run("binds an explicit free port", func(t *testing.T) { - cmd := EdgeRuntimeStartCmd() - // Base command must always be present. - assert.Equal(t, []string{"edge-runtime", "start", "--main-service=."}, cmd[:3]) - // A --port flag avoids collisions on the edge-runtime default port (#5407). - var portFlag string - for _, arg := range cmd { - if strings.HasPrefix(arg, "--port=") { - portFlag = arg - } - } - require.NotEmpty(t, portFlag, "expected a --port flag to be set") - port, err := strconv.Atoi(strings.TrimPrefix(portFlag, "--port=")) - require.NoError(t, err) - assert.Greater(t, port, 0) - assert.LessOrEqual(t, port, 65535) - }) - - t.Run("allocates a distinct port per invocation", func(t *testing.T) { - first := getPortArg(t, EdgeRuntimeStartCmd()) - second := getPortArg(t, EdgeRuntimeStartCmd()) - assert.NotEqual(t, first, second) - }) -} - -func getPortArg(t *testing.T, cmd []string) string { - t.Helper() - for _, arg := range cmd { - if strings.HasPrefix(arg, "--port=") { - return arg - } - } - require.FailNow(t, "missing --port flag") - return "" -} diff --git a/apps/cli-go/internal/utils/enum.go b/apps/cli-go/internal/utils/enum.go deleted file mode 100644 index 06cac719d1..0000000000 --- a/apps/cli-go/internal/utils/enum.go +++ /dev/null @@ -1,35 +0,0 @@ -package utils - -import ( - "fmt" - "slices" - "strings" - - "github.com/go-errors/errors" -) - -// Ref: https://github.com/spf13/pflag/issues/236#issuecomment-931600452 -type EnumFlag struct { - Allowed []string - Value string -} - -func (a EnumFlag) String() string { - return a.Value -} - -func (a *EnumFlag) Set(p string) error { - if !slices.Contains(a.Allowed, p) { - return errors.Errorf("must be one of [ %s ]", strings.Join(a.Allowed, " | ")) - } - a.Value = p - return nil -} - -func (a *EnumFlag) Type() string { - values := strings.Join(a.Allowed, " | ") - if len(values) < 40 { - return fmt.Sprintf("[ %s ]", values) - } - return "string" -} diff --git a/apps/cli-go/internal/utils/flags/config_path.go b/apps/cli-go/internal/utils/flags/config_path.go deleted file mode 100644 index 4df1bff746..0000000000 --- a/apps/cli-go/internal/utils/flags/config_path.go +++ /dev/null @@ -1,26 +0,0 @@ -package flags - -import ( - "strings" - - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils" -) - -func LoadConfig(fsys afero.Fs) error { - utils.Config.ProjectId = ProjectRef - if err := utils.Config.Load("", utils.NewRootFS(fsys)); err != nil { - return err - } - utils.UpdateDockerIds() - // Apply profile specific overrides - if strings.EqualFold(utils.CurrentProfile.Name, "snap") { - ext := utils.Config.Auth.External["snapchat"] - ext.Enabled = true - ext.ClientId = utils.CurrentProfile.AuthClientID - // Any dummy value should work for local dev - ext.Secret.Value = utils.CurrentProfile.AuthClientID - utils.Config.Auth.External["snapchat"] = ext - } - return nil -} diff --git a/apps/cli-go/internal/utils/flags/db_url.go b/apps/cli-go/internal/utils/flags/db_url.go deleted file mode 100644 index bbfda766d6..0000000000 --- a/apps/cli-go/internal/utils/flags/db_url.go +++ /dev/null @@ -1,293 +0,0 @@ -package flags - -import ( - "context" - "crypto/rand" - _ "embed" - "fmt" - "math/big" - "net" - "net/http" - "os" - "strings" - "time" - - "github.com/cenkalti/backoff/v4" - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/spf13/afero" - "github.com/spf13/pflag" - "github.com/spf13/viper" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/credentials" - "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/config" -) - -type connection int - -const ( - unknown connection = iota - direct - local - linked - proxy -) - -var DbConfig pgconn.Config - -// PoolerFallbackEligible reports whether DbConfig was resolved via the linked -// path, where transparently retrying a failed remote operation through the -// project's IPv4 transaction pooler is safe. Explicit --db-url and --local -// targets must never be silently rerouted, so the fallback stays disabled for -// them. -var PoolerFallbackEligible bool - -func ParseDatabaseConfig(ctx context.Context, flagSet *pflag.FlagSet, fsys afero.Fs) error { - // Changed flags take precedence over default values - var connType connection - if flag := flagSet.Lookup("db-url"); flag != nil && flag.Changed { - connType = direct - } else if flag := flagSet.Lookup("local"); flag != nil && flag.Changed { - connType = local - } else if flag := flagSet.Lookup("linked"); flag != nil && flag.Changed { - connType = linked - } else if flag := flagSet.Lookup("proxy"); flag != nil && flag.Changed { - connType = proxy - } else if value, err := flagSet.GetBool("local"); err == nil && value { - connType = local - } else if value, err := flagSet.GetBool("linked"); err == nil && value { - connType = linked - } else if value, err := flagSet.GetBool("proxy"); err == nil && value { - connType = proxy - } - // Update connection config - switch connType { - case direct: - if err := LoadConfig(fsys); err != nil { - return err - } - if flag := flagSet.Lookup("db-url"); flag != nil { - config, err := pgconn.ParseConfig(flag.Value.String()) - if err != nil { - return errors.Errorf("failed to parse connection string: %w", err) - } - DbConfig = *config - } - case local: - if err := LoadConfig(fsys); err != nil { - return err - } - // Ignore other PG settings - DbConfig.Host = utils.Config.Hostname - DbConfig.Port = utils.Config.Db.Port - DbConfig.User = "postgres" - DbConfig.Password = utils.Config.Db.Password - DbConfig.Database = "postgres" - case linked: - if err := LoadProjectRef(fsys); err != nil { - return err - } - if err := LoadConfig(fsys); err != nil { - return err - } - var err error - if DbConfig, err = NewDbConfigWithPassword(ctx, ProjectRef); err != nil { - return err - } - case proxy: - token, err := utils.LoadAccessTokenFS(fsys) - if err != nil { - return err - } - if err := LoadProjectRef(fsys); err != nil { - return err - } - DbConfig.Host = utils.GetSupabaseAPIHost() - DbConfig.Port = 443 - DbConfig.User = "postgres" - DbConfig.Password = token - DbConfig.Database = ProjectRef - } - // Only the linked path may be transparently rerouted to the IPv4 pooler. - PoolerFallbackEligible = connType == linked - return nil -} - -const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - -func RandomString(size int) (string, error) { - data := make([]byte, size) - _, err := rand.Read(data) - if err != nil { - return "", errors.Errorf("failed to read random: %w", err) - } - for i := range data { - n := int(data[i]) % len(letters) - data[i] = letters[n] - } - return string(data), nil -} - -func NewDbConfigWithPassword(ctx context.Context, projectRef string) (pgconn.Config, error) { - config := pgconn.Config{ - Host: utils.GetSupabaseDbHost(projectRef), - Port: 5432, - User: "postgres", - Password: viper.GetString("DB_PASSWORD"), - Database: "postgres", - } - logger := utils.GetDebugLogger() - // Use pooler if host is not reachable directly - d := net.Dialer{Timeout: 5 * time.Second} - addr := fmt.Sprintf("%s:%d", config.Host, config.Port) - if conn, err := d.DialContext(ctx, "tcp", addr); err == nil { - if err := conn.Close(); err != nil { - fmt.Fprintln(logger, err) - } - fmt.Fprintf(logger, "Resolved DNS: %v\n", conn.RemoteAddr()) - } else if poolerConfig := utils.GetPoolerConfig(projectRef); poolerConfig != nil { - if len(config.Password) > 0 { - fmt.Fprintln(logger, "Using database password from env var...") - poolerConfig.Password = config.Password - } else if err := initPoolerLogin(ctx, projectRef, poolerConfig); err != nil { - utils.SetConnectSuggestion(err) - if utils.CmdSuggestion == "" { - utils.CmdSuggestion = utils.SuggestEnvVar - } - return *poolerConfig, err - } - return *poolerConfig, nil - } else { - utils.CmdSuggestion = fmt.Sprintf("Run %s to setup IPv4 connection.", utils.Aqua("supabase link --project-ref "+projectRef)) - return config, errors.Errorf("IPv6 is not supported on your current network: %w", err) - } - // Connect via direct connection - if len(config.Password) > 0 { - fmt.Fprintln(logger, "Using database password from env var...") - } else if err := initLoginRole(ctx, projectRef, &config); err != nil { - // Do not prompt because reading masked input is buggy on windows - utils.CmdSuggestion = utils.SuggestEnvVar - return config, err - } - return config, nil -} - -// ResolvePoolerConfigForFallback returns an authenticated IPv4 transaction -// pooler connection config for the given linked project. It prefers the pooler -// URL persisted at link time and falls back to fetching it from the Management -// API. This is used to transparently retry remote pg operations that failed -// because the direct database host was unreachable over IPv6. -func ResolvePoolerConfigForFallback(ctx context.Context, projectRef string) (pgconn.Config, error) { - poolerConfig := utils.GetPoolerConfig(projectRef) - if poolerConfig == nil { - primary, err := utils.GetPoolerConfigPrimary(ctx, projectRef) - if err != nil { - return pgconn.Config{}, err - } - poolerConfig, err = utils.ParsePoolerURL(primary.ConnectionString) - if err != nil { - return pgconn.Config{}, err - } - // Supavisor transaction mode does not support prepared statements. - poolerConfig.Port = 5432 - } - if password := viper.GetString("DB_PASSWORD"); len(password) > 0 { - poolerConfig.Password = password - } else if err := initPoolerLogin(ctx, projectRef, poolerConfig); err != nil { - return *poolerConfig, err - } - return *poolerConfig, nil -} - -func initLoginRole(ctx context.Context, projectRef string, config *pgconn.Config) error { - fmt.Fprintln(os.Stderr, "Initialising login role...") - body := api.CreateRoleBody{ReadOnly: false} - resp, err := utils.GetSupabase().V1CreateLoginRoleWithResponse(ctx, projectRef, body) - if err != nil { - return errors.Errorf("failed to initialise login role: %w", err) - } else if resp.JSON201 == nil { - return errors.Errorf("unexpected login role status %d: %s", resp.StatusCode(), string(resp.Body)) - } - config.User = resp.JSON201.Role - config.Password = resp.JSON201.Password - return nil -} - -func initPoolerLogin(ctx context.Context, projectRef string, poolerConfig *pgconn.Config) error { - poolerUser := poolerConfig.User - if err := initLoginRole(ctx, projectRef, poolerConfig); err != nil { - return err - } - suffix := "." + projectRef - if strings.HasSuffix(poolerUser, suffix) { - poolerConfig.User += suffix - } - // Wait for pooler to refresh password - login := func() error { - conn, err := pgconn.ConnectConfig(ctx, poolerConfig) - if err != nil { - return errors.Errorf("failed to connect as temp role: %w", err) - } - return conn.Close(ctx) - } - notify := utils.NewErrorCallback(func(attempt uint) error { - if attempt < 3 { - return nil - } - if ips, err := ListNetworkBans(ctx, projectRef); err != nil { - return err - } else if len(ips) > 0 { - return UnbanIP(ctx, projectRef, ips...) - } - return nil - }) - return backoff.RetryNotify(login, utils.NewBackoffPolicy(ctx), notify) -} - -func ListNetworkBans(ctx context.Context, projectRef string) ([]string, error) { - resp, err := utils.GetSupabase().V1ListAllNetworkBansWithResponse(ctx, projectRef) - if err != nil { - return nil, errors.Errorf("failed to list network bans: %w", err) - } else if resp.JSON201 == nil { - return nil, errors.Errorf("unexpected list bans status %d: %s", resp.StatusCode(), string(resp.Body)) - } - return resp.JSON201.BannedIpv4Addresses, nil -} - -func UnbanIP(ctx context.Context, projectRef string, addrs ...string) error { - includeSelf := len(addrs) == 0 - body := api.RemoveNetworkBanRequest{ - Ipv4Addresses: append([]string{}, addrs...), - RequesterIp: &includeSelf, - } - if resp, err := utils.GetSupabase().V1DeleteNetworkBansWithResponse(ctx, projectRef, body); err != nil { - return errors.Errorf("failed to remove network bans: %w", err) - } else if resp.StatusCode() != http.StatusOK { - return errors.Errorf("unexpected unban status %d: %s", resp.StatusCode(), string(resp.Body)) - } - return nil -} - -const PASSWORD_LENGTH = 16 - -func PromptPassword(stdin *os.File) string { - fmt.Fprint(os.Stderr, "Enter your database password (or leave blank to generate one): ") - if input := credentials.PromptMasked(stdin); len(input) > 0 { - return input - } - // Generate a password, see ./Settings/Database/DatabaseSettings/ResetDbPassword.tsx#L83 - var password []byte - charset := string(config.LowerUpperLettersDigits.ToChar()) - charset = strings.ReplaceAll(charset, ":", "") - maxRange := big.NewInt(int64(len(charset))) - for range PASSWORD_LENGTH { - random, err := rand.Int(rand.Reader, maxRange) - if err != nil { - fmt.Fprintln(os.Stderr, "Failed to randomise password:", err) - continue - } - password = append(password, charset[random.Int64()]) - } - return string(password) -} diff --git a/apps/cli-go/internal/utils/flags/db_url_test.go b/apps/cli-go/internal/utils/flags/db_url_test.go deleted file mode 100644 index 7343be6519..0000000000 --- a/apps/cli-go/internal/utils/flags/db_url_test.go +++ /dev/null @@ -1,180 +0,0 @@ -package flags - -import ( - "context" - "fmt" - "net/http" - "os" - "strings" - "testing" - - "github.com/h2non/gock" - "github.com/spf13/afero" - "github.com/spf13/pflag" - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/api" -) - -func TestParseDatabaseConfig(t *testing.T) { - // Setup valid access token - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - - t.Run("parses direct connection from db-url flag", func(t *testing.T) { - flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) - flagSet.String("db-url", "postgres://postgres:password@localhost:5432/postgres", "") - err := flagSet.Set("db-url", "postgres://admin:secret@db.example.com:6432/app") - assert.Nil(t, err) - - fsys := afero.NewMemMapFs() - - err = ParseDatabaseConfig(context.Background(), flagSet, fsys) - - assert.NoError(t, err) - assert.Equal(t, "db.example.com", DbConfig.Host) - assert.Equal(t, uint16(6432), DbConfig.Port) - assert.Equal(t, "admin", DbConfig.User) - assert.Equal(t, "secret", DbConfig.Password) - assert.Equal(t, "app", DbConfig.Database) - }) - - t.Run("parses local connection", func(t *testing.T) { - flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) - flagSet.Bool("local", false, "") - err := flagSet.Set("local", "true") - assert.Nil(t, err) - - fsys := afero.NewMemMapFs() - - utils.Config.Hostname = "localhost" - utils.Config.Db.Port = 54322 - utils.Config.Db.Password = "local-password" - - err = ParseDatabaseConfig(context.Background(), flagSet, fsys) - - assert.NoError(t, err) - assert.Equal(t, "localhost", DbConfig.Host) - assert.Equal(t, uint16(54322), DbConfig.Port) - assert.Equal(t, "postgres", DbConfig.User) - assert.Equal(t, "local-password", DbConfig.Password) - assert.Equal(t, "postgres", DbConfig.Database) - }) - - t.Run("parses linked connection", func(t *testing.T) { - flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError) - flagSet.Bool("linked", false, "") - err := flagSet.Set("linked", "true") - assert.Nil(t, err) - - fsys := afero.NewMemMapFs() - - project := apitest.RandomProjectRef() - err = afero.WriteFile(fsys, utils.ProjectRefPath, []byte(project), 0644) - require.NoError(t, err) - - dbURL := fmt.Sprintf("postgres://postgres:postgres@db.%s.supabase.co:6543/postgres", project) - err = afero.WriteFile(fsys, utils.PoolerUrlPath, []byte(dbURL), 0644) - require.NoError(t, err) - - viper.Set("DB_PASSWORD", "test") - err = ParseDatabaseConfig(context.Background(), flagSet, fsys) - - assert.NoError(t, err) - assert.True(t, strings.HasPrefix(DbConfig.Host, utils.GetSupabaseDbHost(project))) - }) -} - -func TestResolvePoolerConfigForFallback(t *testing.T) { - ref := apitest.RandomProjectRef() - - t.Run("uses linked pooler url with db password", func(t *testing.T) { - utils.Config.Db.Pooler.ConnectionString = "postgres://postgres." + ref + ":[YOUR-PASSWORD]@aws-0-us-east-1.pooler.supabase.com:6543/postgres" - viper.Set("DB_PASSWORD", "secret") - t.Cleanup(func() { - utils.Config.Db.Pooler.ConnectionString = "" - viper.Set("DB_PASSWORD", "") - }) - - config, err := ResolvePoolerConfigForFallback(context.Background(), ref) - - require.NoError(t, err) - assert.Equal(t, "aws-0-us-east-1.pooler.supabase.com", config.Host) - assert.Equal(t, uint16(5432), config.Port) - assert.Equal(t, "postgres."+ref, config.User) - assert.Equal(t, "secret", config.Password) - }) - - t.Run("falls back to api pooler config", func(t *testing.T) { - poolerURL := "postgres://postgres." + ref + ":[YOUR-PASSWORD]@aws-0-eu-west-1.pooler.supabase.com:6543/postgres" - utils.Config.Db.Pooler.ConnectionString = "" - viper.Set("DB_PASSWORD", "secret") - t.Cleanup(func() { viper.Set("DB_PASSWORD", "") }) - t.Cleanup(apitest.MockPlatformAPI(t)) - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + ref + "/config/database/pooler"). - Reply(http.StatusOK). - JSON([]api.SupavisorConfigResponseOutput{{ - DatabaseType: api.SupavisorConfigResponseOutputDatabaseTypePRIMARY, - ConnectionString: poolerURL, - }}) - - config, err := ResolvePoolerConfigForFallback(context.Background(), ref) - - require.NoError(t, err) - assert.Equal(t, "aws-0-eu-west-1.pooler.supabase.com", config.Host) - assert.Equal(t, uint16(5432), config.Port) - assert.Equal(t, "secret", config.Password) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("returns error when api pooler is unavailable", func(t *testing.T) { - utils.Config.Db.Pooler.ConnectionString = "" - t.Cleanup(apitest.MockPlatformAPI(t)) - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + ref + "/config/database/pooler"). - Reply(http.StatusOK). - JSON([]api.SupavisorConfigResponseOutput{}) - - _, err := ResolvePoolerConfigForFallback(context.Background(), ref) - - assert.Error(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestPromptPassword(t *testing.T) { - t.Run("returns user input when provided", func(t *testing.T) { - r, w, err := os.Pipe() - require.NoError(t, err) - defer r.Close() - go func() { - defer w.Close() - _, err := w.Write([]byte("test-password")) - assert.Nil(t, err) - }() - - password := PromptPassword(r) - - assert.Equal(t, "test-password", password) - }) - - t.Run("generates password when input is empty", func(t *testing.T) { - r, w, err := os.Pipe() - require.NoError(t, err) - defer r.Close() - go func() { - defer w.Close() - _, err := w.Write([]byte("")) - assert.Nil(t, err) - }() - - password := PromptPassword(r) - - assert.Len(t, password, PASSWORD_LENGTH) - assert.NotEqual(t, "", password) - }) -} diff --git a/apps/cli-go/internal/utils/flags/project_ref.go b/apps/cli-go/internal/utils/flags/project_ref.go deleted file mode 100644 index fab2920014..0000000000 --- a/apps/cli-go/internal/utils/flags/project_ref.go +++ /dev/null @@ -1,76 +0,0 @@ -package flags - -import ( - "bytes" - "context" - "fmt" - "os" - - tea "github.com/charmbracelet/bubbletea" - "github.com/go-errors/errors" - "github.com/spf13/afero" - "github.com/spf13/viper" - "github.com/supabase/cli/internal/utils" - "golang.org/x/term" -) - -var ProjectRef string - -func ParseProjectRef(ctx context.Context, fsys afero.Fs) error { - if err := LoadProjectRef(fsys); !errors.Is(err, utils.ErrNotLinked) { - return err - } - // Prompt as the last resort - if term.IsTerminal(int(os.Stdin.Fd())) { - return PromptProjectRef(ctx, "Select a project:") - } - return errors.New(utils.ErrNotLinked) -} - -func PromptProjectRef(ctx context.Context, title string, opts ...tea.ProgramOption) error { - resp, err := utils.GetSupabase().V1ListAllProjectsWithResponse(ctx) - if err != nil { - return errors.Errorf("failed to retrieve projects: %w", err) - } - if resp.JSON200 == nil { - return errors.New("Unexpected error retrieving projects: " + string(resp.Body)) - } - items := make([]utils.PromptItem, len(*resp.JSON200)) - for i, project := range *resp.JSON200 { - items[i] = utils.PromptItem{ - Summary: project.Id, - Details: fmt.Sprintf("name: %s, org: %s, region: %s", project.Name, project.OrganizationSlug, project.Region), - } - } - choice, err := utils.PromptChoice(ctx, title, items, opts...) - if err != nil { - return err - } - ProjectRef = choice.Summary - fmt.Fprintln(os.Stderr, "Selected project:", ProjectRef) - return nil -} - -func LoadProjectRef(fsys afero.Fs) error { - debuglogger := utils.GetDebugLogger() - // Flag takes highest precedence - if len(ProjectRef) > 0 { - fmt.Fprintf(debuglogger, "Loading project ref from flag: %s\n", ProjectRef) - return utils.AssertProjectRefIsValid(ProjectRef) - } - // Env var takes precedence over ref file - if ProjectRef = viper.GetString("PROJECT_ID"); len(ProjectRef) > 0 { - fmt.Fprintf(debuglogger, "Loading project ref from env var: %s\n", ProjectRef) - return utils.AssertProjectRefIsValid(ProjectRef) - } - // Load from local file last - projectRefBytes, err := afero.ReadFile(fsys, utils.ProjectRefPath) - fmt.Fprintf(debuglogger, "Loading project ref from file: %s\n", utils.ProjectRefPath) - if errors.Is(err, os.ErrNotExist) { - return errors.New(utils.ErrNotLinked) - } else if err != nil { - return errors.Errorf("failed to load project ref: %w", err) - } - ProjectRef = string(bytes.TrimSpace(projectRefBytes)) - return utils.AssertProjectRefIsValid(ProjectRef) -} diff --git a/apps/cli-go/internal/utils/flags/project_ref_test.go b/apps/cli-go/internal/utils/flags/project_ref_test.go deleted file mode 100644 index 27627a1b08..0000000000 --- a/apps/cli-go/internal/utils/flags/project_ref_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package flags - -import ( - "context" - "net/http" - "os" - "strings" - "testing" - - tea "github.com/charmbracelet/bubbletea" - "github.com/go-errors/errors" - "github.com/h2non/gock" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/testing/fstest" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/api" -) - -func TestProjectRef(t *testing.T) { - t.Run("validates cmd flag", func(t *testing.T) { - ProjectRef = "invalid" - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - err := ParseProjectRef(context.Background(), fsys) - // Check error - assert.Error(t, err, utils.ErrInvalidRef) - }) - - t.Run("loads from linked", func(t *testing.T) { - ProjectRef = "" - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Setup valid project ref - project := apitest.RandomProjectRef() - err := afero.WriteFile(fsys, utils.ProjectRefPath, []byte(project), 0644) - require.NoError(t, err) - // Run test - err = ParseProjectRef(context.Background(), fsys) - // Check error - assert.NoError(t, err) - }) - - t.Run("throws error on read failure", func(t *testing.T) { - ProjectRef = "" - // Setup in-memory fs - fsys := &fstest.OpenErrorFs{DenyPath: utils.ProjectRefPath} - // Run test - err := ParseProjectRef(context.Background(), fsys) - // Check error - assert.ErrorIs(t, err, os.ErrPermission) - }) - - t.Run("throws error if all fail", func(t *testing.T) { - ProjectRef = "" - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - err := ParseProjectRef(context.Background(), fsys) - // Check error - assert.ErrorIs(t, err, utils.ErrNotLinked) - }) -} - -func TestProjectPrompt(t *testing.T) { - // Setup valid access token - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - - t.Run("validates prompt input", func(t *testing.T) { - input := tea.WithInput(strings.NewReader("\r")) - // Setup mock api - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects"). - Reply(http.StatusOK). - JSON([]api.V1ProjectResponseOutput{{ - Id: "test-project", - Name: "My Project", - OrganizationSlug: "test-org", - }}) - // Run test - err := PromptProjectRef(context.Background(), "", input) - // Check error - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on network failure", func(t *testing.T) { - errNetwork := errors.New("network error") - // Setup mock api - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects"). - ReplyError(errNetwork) - // Run test - err := PromptProjectRef(context.Background(), "") - // Check error - assert.ErrorIs(t, err, errNetwork) - }) - - t.Run("throws error on service unavailable", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New(utils.DefaultApiHost). - Get("/v1/projects"). - Reply(http.StatusServiceUnavailable) - // Run test - err := PromptProjectRef(context.Background(), "") - // Check error - assert.ErrorContains(t, err, "Unexpected error retrieving projects:") - }) -} diff --git a/apps/cli-go/internal/utils/git.go b/apps/cli-go/internal/utils/git.go deleted file mode 100644 index 328e7f94ae..0000000000 --- a/apps/cli-go/internal/utils/git.go +++ /dev/null @@ -1,27 +0,0 @@ -package utils - -import ( - "os" - - "github.com/go-git/go-git/v5" - "github.com/spf13/afero" -) - -func GetGitBranch(fsys afero.Fs) string { - return GetGitBranchOrDefault("main", fsys) -} - -func GetGitBranchOrDefault(def string, fsys afero.Fs) string { - head := os.Getenv("GITHUB_HEAD_REF") - if len(head) > 0 { - return head - } - if root, err := findGitRoot("."); err != nil { - return def - } else if repo, err := git.PlainOpen(root); err == nil { - if ref, err := repo.Head(); err == nil { - return ref.Name().Short() - } - } - return def -} diff --git a/apps/cli-go/internal/utils/identity_transport.go b/apps/cli-go/internal/utils/identity_transport.go deleted file mode 100644 index bcf01d97e7..0000000000 --- a/apps/cli-go/internal/utils/identity_transport.go +++ /dev/null @@ -1,21 +0,0 @@ -package utils - -import "net/http" - -const HeaderGotrueID = "X-Gotrue-Id" - -type identityTransport struct { - http.RoundTripper - onGotrueID *func(string) -} - -func (t *identityTransport) RoundTrip(req *http.Request) (*http.Response, error) { - resp, err := t.RoundTripper.RoundTrip(req) - if err != nil { - return resp, err - } - if id := resp.Header.Get(HeaderGotrueID); id != "" && t.onGotrueID != nil && *t.onGotrueID != nil { - (*t.onGotrueID)(id) - } - return resp, err -} diff --git a/apps/cli-go/internal/utils/identity_transport_test.go b/apps/cli-go/internal/utils/identity_transport_test.go deleted file mode 100644 index 314039d0a9..0000000000 --- a/apps/cli-go/internal/utils/identity_transport_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package utils - -import ( - "net/http" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestIdentityTransport_CapturesGotrueIdHeader(t *testing.T) { - var captured string - cb := func(id string) { captured = id } - transport := &identityTransport{ - RoundTripper: roundTripFunc(func(req *http.Request) (*http.Response, error) { - return &http.Response{ - StatusCode: 200, - Header: http.Header{"X-Gotrue-Id": []string{"user-abc-123"}}, - }, nil - }), - onGotrueID: &cb, - } - req, _ := http.NewRequest("GET", "https://api.supabase.io/v1/projects", nil) - resp, err := transport.RoundTrip(req) - assert.NoError(t, err) - assert.Equal(t, 200, resp.StatusCode) - assert.Equal(t, "user-abc-123", captured) -} - -func TestIdentityTransport_IgnoresWhenHeaderMissing(t *testing.T) { - var captured string - cb := func(id string) { captured = id } - transport := &identityTransport{ - RoundTripper: roundTripFunc(func(req *http.Request) (*http.Response, error) { - return &http.Response{ - StatusCode: 200, - Header: http.Header{}, - }, nil - }), - onGotrueID: &cb, - } - req, _ := http.NewRequest("GET", "https://api.supabase.io/v1/projects", nil) - _, err := transport.RoundTrip(req) - assert.NoError(t, err) - assert.Empty(t, captured) -} - -func TestIdentityTransport_NilCallbackDoesNotPanic(t *testing.T) { - transport := &identityTransport{ - RoundTripper: roundTripFunc(func(req *http.Request) (*http.Response, error) { - return &http.Response{ - StatusCode: 200, - Header: http.Header{"X-Gotrue-Id": []string{"user-abc-123"}}, - }, nil - }), - onGotrueID: nil, - } - req, _ := http.NewRequest("GET", "https://api.supabase.io/v1/projects", nil) - resp, err := transport.RoundTrip(req) - assert.NoError(t, err) - assert.Equal(t, 200, resp.StatusCode) -} - -func TestIdentityTransport_NilFuncBehindPointerDoesNotPanic(t *testing.T) { - var cb func(string) // nil func - transport := &identityTransport{ - RoundTripper: roundTripFunc(func(req *http.Request) (*http.Response, error) { - return &http.Response{ - StatusCode: 200, - Header: http.Header{"X-Gotrue-Id": []string{"user-abc-123"}}, - }, nil - }), - onGotrueID: &cb, - } - req, _ := http.NewRequest("GET", "https://api.supabase.io/v1/projects", nil) - resp, err := transport.RoundTrip(req) - assert.NoError(t, err) - assert.Equal(t, 200, resp.StatusCode) -} - -func TestIdentityTransport_InnerTransportError(t *testing.T) { - var captured string - cb := func(id string) { captured = id } - transport := &identityTransport{ - RoundTripper: roundTripFunc(func(req *http.Request) (*http.Response, error) { - return nil, assert.AnError - }), - onGotrueID: &cb, - } - req, _ := http.NewRequest("GET", "https://api.supabase.io/v1/projects", nil) - resp, err := transport.RoundTrip(req) - assert.Error(t, err) - assert.Nil(t, resp) - assert.Empty(t, captured) -} - -// roundTripFunc is a test helper to create inline RoundTrippers. -type roundTripFunc func(*http.Request) (*http.Response, error) - -func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { - return f(req) -} diff --git a/apps/cli-go/internal/utils/logger.go b/apps/cli-go/internal/utils/logger.go deleted file mode 100644 index 0cde1df38d..0000000000 --- a/apps/cli-go/internal/utils/logger.go +++ /dev/null @@ -1,15 +0,0 @@ -package utils - -import ( - "io" - "os" - - "github.com/spf13/viper" -) - -func GetDebugLogger() io.Writer { - if viper.GetBool("DEBUG") { - return os.Stderr - } - return io.Discard -} diff --git a/apps/cli-go/internal/utils/misc.go b/apps/cli-go/internal/utils/misc.go deleted file mode 100644 index 186573146b..0000000000 --- a/apps/cli-go/internal/utils/misc.go +++ /dev/null @@ -1,318 +0,0 @@ -package utils - -import ( - "context" - _ "embed" - "fmt" - "net" - "os" - "path/filepath" - "regexp" - "time" - - "github.com/containerd/errdefs" - "github.com/docker/docker/client" - "github.com/go-errors/errors" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing/format/gitignore" - "github.com/spf13/afero" - "github.com/spf13/viper" - "github.com/supabase/cli/internal/utils/agent" - "github.com/supabase/cli/pkg/migration" - "golang.org/x/term" -) - -// Assigned using `-ldflags` https://stackoverflow.com/q/11354518 -var ( - Version string - SentryDsn string - PostHogAPIKey string - PostHogEndpoint string -) - -func ShortContainerImageName(imageName string) string { - matches := ImageNamePattern.FindStringSubmatch(imageName) - if len(matches) < 2 { - return imageName - } - return matches[1] -} - -const SuggestDebugFlag = "Try rerunning the command with --debug to troubleshoot the error." - -const claudeCodeHint = `` - -func SuggestClaudePlugin() string { - if !agent.IsClaudeCode() { - return "" - } - // Suppress the hint when stdout is non-interactive (redirected to a file - // or piped). Without this guard, captured output could be contaminated by - // the trailer if anything ever leaks the tag onto stdout, and the - // install-prompt UX is only meaningful in an interactive shell anyway. - if !term.IsTerminal(int(os.Stdout.Fd())) { //nolint:gosec // G115: stdout fd is a small int on supported platforms - return "" - } - return claudeCodeHint -} - -var ( - CmdSuggestion string - CurrentDirAbs string - - // pg_dumpall --globals-only --no-role-passwords --dbname $DB_URL \ - // | sed '/^CREATE ROLE postgres;/d' \ - // | sed '/^ALTER ROLE postgres WITH /d' \ - // | sed "/^ALTER ROLE .* WITH .* LOGIN /s/;$/ PASSWORD 'postgres';/" - //go:embed templates/globals.sql - GlobalsSql string - - ProjectRefPattern = regexp.MustCompile(`^[a-z]{20}$`) - UUIDPattern = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`) - ProjectHostPattern = regexp.MustCompile(`^(db\.)([a-z]{20})\.supabase\.(co|red)$`) - BranchNamePattern = regexp.MustCompile(`[[:word:]-]+`) - FuncSlugPattern = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_-]*$`) - ImageNamePattern = regexp.MustCompile(`\/(.*):`) - - // These schemas are ignored from db diff and db dump - PgSchemas = migration.InternalSchemas[:2] - InternalSchemas = migration.InternalSchemas - - SupabaseDirPath = "supabase" - ConfigPath = filepath.Join(SupabaseDirPath, "config.toml") - GitIgnorePath = filepath.Join(SupabaseDirPath, ".gitignore") - TempDir = filepath.Join(SupabaseDirPath, ".temp") - ImportMapsDir = filepath.Join(TempDir, "import_maps") - ProjectRefPath = filepath.Join(TempDir, "project-ref") - PoolerUrlPath = filepath.Join(TempDir, "pooler-url") - PostgresVersionPath = filepath.Join(TempDir, "postgres-version") - GotrueVersionPath = filepath.Join(TempDir, "gotrue-version") - RestVersionPath = filepath.Join(TempDir, "rest-version") - StorageVersionPath = filepath.Join(TempDir, "storage-version") - StorageMigrationPath = filepath.Join(TempDir, "storage-migration") - StudioVersionPath = filepath.Join(TempDir, "studio-version") - PgmetaVersionPath = filepath.Join(TempDir, "pgmeta-version") - PoolerVersionPath = filepath.Join(TempDir, "pooler-version") - RealtimeVersionPath = filepath.Join(TempDir, "realtime-version") - CliVersionPath = filepath.Join(TempDir, "cli-latest") - CurrBranchPath = filepath.Join(SupabaseDirPath, ".branches", "_current_branch") - // DeclarativeDir is the canonical location for pg-delta declarative schema - // files generated or synced by `supabase db schema declarative` commands. - DeclarativeDir = filepath.Join(SupabaseDirPath, "schemas") - ClusterDir = filepath.Join(SupabaseDirPath, "cluster") - SchemasDir = filepath.Join(SupabaseDirPath, "schemas") - MigrationsDir = filepath.Join(SupabaseDirPath, "migrations") - FunctionsDir = filepath.Join(SupabaseDirPath, "functions") - SnippetsDir = filepath.Join(SupabaseDirPath, "snippets") - FallbackImportMapPath = filepath.Join(FunctionsDir, "import_map.json") - FallbackEnvFilePath = filepath.Join(FunctionsDir, ".env") - DbTestsDir = filepath.Join(SupabaseDirPath, "tests") - CustomRolesPath = filepath.Join(SupabaseDirPath, "roles.sql") - - ErrNotLinked = errors.Errorf("Cannot find project ref. Have you run %s?", Aqua("supabase link")) - ErrInvalidRef = errors.New("Invalid project ref format. Must be like `abcdefghijklmnopqrst`.") - ErrInvalidSlug = errors.New("Invalid Function name. Must start with at least one letter, and only include alphanumeric characters, underscores, and hyphens. (^[A-Za-z][A-Za-z0-9_-]*$)") - ErrNotRunning = errors.Errorf("%s is not running.", Aqua("supabase start")) -) - -func GetDeclarativeDir() string { - if Config.Experimental.PgDelta != nil && len(Config.Experimental.PgDelta.DeclarativeSchemaPath) > 0 { - return Config.Experimental.PgDelta.DeclarativeSchemaPath - } - return DeclarativeDir -} - -func IsPgDeltaEnabled() bool { - return Config.Experimental.PgDelta != nil && Config.Experimental.PgDelta.Enabled -} - -func GetCurrentTimestamp() string { - // Magic number: https://stackoverflow.com/q/45160822. - return GetVersionTimestamp(time.Now()) -} - -// GetVersionTimestamp formats t as a migration version (UTC `YYYYMMDDHHMMSS`). -// Callers that write several ordered migration files in one pass add real time -// offsets to a shared base rather than incrementing the formatted string. -func GetVersionTimestamp(t time.Time) string { - return t.UTC().Format(layoutVersion) -} - -func GetCurrentBranchFS(fsys afero.Fs) (string, error) { - branch, err := afero.ReadFile(fsys, CurrBranchPath) - if err != nil { - return "", errors.Errorf("failed to load current branch: %w", err) - } - - return string(branch), nil -} - -func AssertSupabaseDbIsRunning() error { - return AssertServiceIsRunning(context.Background(), DbId) -} - -func AssertServiceIsRunning(ctx context.Context, containerId string) error { - if _, err := Docker.ContainerInspect(ctx, containerId); err != nil { - if errdefs.IsNotFound(err) { - return errors.New(ErrNotRunning) - } - if client.IsErrConnectionFailed(err) { - CmdSuggestion = suggestDockerInstall - } - return errors.Errorf("failed to inspect service: %w", err) - } - return nil -} - -func IsGitRepo() bool { - _, err := findGitRoot(".") - return err == nil -} - -func IsGitIgnored(fp ...string) (bool, error) { - root, err := findGitRoot(".") - if err != nil { - return false, err - } - repo, err := git.PlainOpen(root) - if err != nil { - return false, err - } - wt, err := repo.Worktree() - if err != nil { - return false, err - } - ps, err := gitignore.ReadPatterns(wt.Filesystem, nil) - if err != nil { - return false, err - } - m := gitignore.NewMatcher(ps) - return m.Match(fp, false), nil -} - -func findGitRoot(path string) (string, error) { - cwd, err := filepath.Abs(path) - if err != nil { - return "", err - } - for { - if _, err := os.Stat(filepath.Join(cwd, git.GitDirName)); err == nil { - return cwd, nil - } else if !errors.Is(err, os.ErrNotExist) { - return "", err - } - if parent := filepath.Dir(cwd); parent != cwd { - cwd = parent - } else { - return "", git.ErrRepositoryNotExists - } - } -} - -// If the `os.Getwd()` is within a supabase project, this will return -// the root of the given project as the current working directory. -// Otherwise, the `os.Getwd()` is kept as is. -func getProjectRoot(absPath string, fsys afero.Fs) string { - for cwd := absPath; ; cwd = filepath.Dir(cwd) { - path := filepath.Join(cwd, ConfigPath) - // Treat all errors as file not exists - if isSupaProj, err := afero.Exists(fsys, path); isSupaProj { - return cwd - } else if err != nil && !errors.Is(err, os.ErrNotExist) { - logger := GetDebugLogger() - fmt.Fprintln(logger, err) - } - if isRootDirectory(cwd) { - break - } - } - return absPath -} - -func isRootDirectory(cleanPath string) bool { - // A cleaned path only ends with separator if it is root - return os.IsPathSeparator(cleanPath[len(cleanPath)-1]) -} - -func ChangeWorkDir(fsys afero.Fs) error { - // Track the original workdir before changing to project root - if !filepath.IsAbs(CurrentDirAbs) { - var err error - if CurrentDirAbs, err = os.Getwd(); err != nil { - return errors.Errorf("failed to get current directory: %w", err) - } - } - workdir := viper.GetString("WORKDIR") - if len(workdir) == 0 { - workdir = getProjectRoot(CurrentDirAbs, fsys) - } - if err := os.Chdir(workdir); err != nil { - return errors.Errorf("failed to change workdir: %w", err) - } - if cwd, err := os.Getwd(); err == nil && cwd != CurrentDirAbs { - fmt.Fprintln(os.Stderr, "Using workdir", Bold(workdir)) - } - return nil -} - -func IsBranchNameReserved(branch string) bool { - switch branch { - case "_current_branch", "main", "postgres", "template0", "template1": - return true - default: - return false - } -} - -func MkdirIfNotExist(path string) error { - return MkdirIfNotExistFS(afero.NewOsFs(), path) -} - -func MkdirIfNotExistFS(fsys afero.Fs, path string) error { - if err := fsys.MkdirAll(path, 0755); err != nil && !errors.Is(err, os.ErrExist) { - return errors.Errorf("failed to mkdir: %w", err) - } - - return nil -} - -func WriteFile(path string, contents []byte, fsys afero.Fs) error { - if err := MkdirIfNotExistFS(fsys, filepath.Dir(path)); err != nil { - return err - } - if err := afero.WriteFile(fsys, path, contents, 0644); err != nil { - return errors.Errorf("failed to write file: %w", err) - } - return nil -} - -func AssertProjectRefIsValid(projectRef string) error { - if !ProjectRefPattern.MatchString(projectRef) { - return errors.New(ErrInvalidRef) - } - return nil -} - -func ValidateFunctionSlug(slug string) error { - if !FuncSlugPattern.MatchString(slug) { - return errors.New(ErrInvalidSlug) - } - - return nil -} - -func GetHostname() string { - // Overrides the host for local service connections. Useful when - // running inside a dev container when the Docker host is not - // 127.0.0.1 (container's own loopback). - if h := os.Getenv("SUPABASE_SERVICES_HOSTNAME"); h != "" { - return h - } - host := Docker.DaemonHost() - if parsed, err := client.ParseHostURL(host); err == nil && parsed.Scheme == "tcp" { - if host, _, err := net.SplitHostPort(parsed.Host); err == nil { - return host - } - } - return "127.0.0.1" -} diff --git a/apps/cli-go/internal/utils/misc_test.go b/apps/cli-go/internal/utils/misc_test.go deleted file mode 100644 index e8e075201b..0000000000 --- a/apps/cli-go/internal/utils/misc_test.go +++ /dev/null @@ -1,217 +0,0 @@ -package utils - -import ( - "io/fs" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/pkg/config" -) - -type MockFs struct { - afero.MemMapFs - DenyPath string -} - -func (m *MockFs) Stat(name string) (fs.FileInfo, error) { - if strings.HasPrefix(name, m.DenyPath) { - return nil, fs.ErrPermission - } - return m.MemMapFs.Stat(name) -} - -func TestProjectRoot(t *testing.T) { - root := string(filepath.Separator) - - t.Run("stops at root dir", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - _, err := fsys.Create(filepath.Join(root, ConfigPath)) - require.NoError(t, err) - // Run test - cwd := filepath.Join(root, "home", "user", "project") - path := getProjectRoot(cwd, fsys) - // Check error - assert.Equal(t, root, path) - }) - - t.Run("stops at closest parent", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - _, err := fsys.Create(filepath.Join(root, "supabase", ConfigPath)) - require.NoError(t, err) - // Run test - cwd := filepath.Join(root, "supabase", "supabase", "functions") - path := getProjectRoot(cwd, fsys) - // Check error - assert.Equal(t, filepath.Join(root, "supabase"), path) - }) - - t.Run("ignores error on config not found", func(t *testing.T) { - cwd, err := os.Getwd() - require.NoError(t, err) - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - path := getProjectRoot(cwd, fsys) - // Check error - assert.NoError(t, err) - assert.Equal(t, cwd, path) - }) - - t.Run("ignores error if path is not directory", func(t *testing.T) { - cwd, err := os.Getwd() - require.NoError(t, err) - // Setup in-memory fs - fsys := &MockFs{DenyPath: filepath.Join(cwd, "supabase")} - // Run test - path := getProjectRoot(cwd, fsys) - // Check error - assert.NoError(t, err) - assert.Equal(t, cwd, path) - }) -} - -func TestShortContainerImageName(t *testing.T) { - t.Run("extracts short name from image", func(t *testing.T) { - input := "registry.supabase.com/postgres:15.1.0.99" - expected := "postgres" - - result := ShortContainerImageName(input) - - assert.Equal(t, expected, result) - }) -} - -func TestIsBranchNameReserved(t *testing.T) { - t.Run("identifies reserved names", func(t *testing.T) { - reserved := []string{"_current_branch", "main"} - for _, name := range reserved { - assert.True(t, IsBranchNameReserved(name), "Expected %s to be reserved", name) - } - }) - - t.Run("allows custom names", func(t *testing.T) { - allowed := []string{"my-feature", "test-branch-123"} - for _, name := range allowed { - assert.False(t, IsBranchNameReserved(name), "Expected %s to be allowed", name) - } - }) -} - -func TestValidateFunctionSlug(t *testing.T) { - t.Run("validates correct slugs", func(t *testing.T) { - valid := []string{ - "my-function", - "MyFunction", - "function_1", - "a123", - } - for _, slug := range valid { - err := ValidateFunctionSlug(slug) - assert.NoError(t, err, "Expected %s to be valid", slug) - } - }) - - t.Run("rejects invalid slugs", func(t *testing.T) { - invalid := []string{ - "1function", - "my function", - "function!", - "", - "-function", - "_function", - } - for _, slug := range invalid { - err := ValidateFunctionSlug(slug) - assert.ErrorIs(t, err, ErrInvalidSlug, "Expected %s to be invalid", slug) - } - }) -} - -func TestAssertProjectRefIsValid(t *testing.T) { - t.Run("validates correct refs", func(t *testing.T) { - validRef := "abcdefghijklmnopqrst" - err := AssertProjectRefIsValid(validRef) - assert.NoError(t, err) - }) - - t.Run("rejects invalid refs", func(t *testing.T) { - invalid := []string{ - "tooshort", - "toolongabcdefghijklmnopqrst", - "UPPERCASE", - "special-chars", - "123", - "", - } - for _, ref := range invalid { - err := AssertProjectRefIsValid(ref) - assert.ErrorIs(t, err, ErrInvalidRef, "Expected %s to be invalid", ref) - } - }) -} - -func TestGetHostname(t *testing.T) { - t.Run("returns SUPABASE_SERVICES_HOSTNAME when set", func(t *testing.T) { - t.Setenv("SUPABASE_SERVICES_HOSTNAME", "host.docker.internal") - assert.Equal(t, "host.docker.internal", GetHostname()) - }) - - t.Run("returns 127.0.0.1 when SUPABASE_SERVICES_HOSTNAME is not set", func(t *testing.T) { - t.Setenv("SUPABASE_SERVICES_HOSTNAME", "") - assert.Equal(t, "127.0.0.1", GetHostname()) - }) -} - -func TestWriteFile(t *testing.T) { - t.Run("writes file with directories", func(t *testing.T) { - fsys := afero.NewMemMapFs() - path := filepath.Join("deep", "nested", "dir", "file.txt") - content := []byte("test content") - - err := WriteFile(path, content, fsys) - - assert.NoError(t, err) - - written, err := afero.ReadFile(fsys, path) - assert.NoError(t, err) - assert.Equal(t, content, written) - }) - - t.Run("overwrites existing file", func(t *testing.T) { - fsys := afero.NewMemMapFs() - path := "test.txt" - original := []byte("original") - updated := []byte("updated") - require.NoError(t, afero.WriteFile(fsys, path, original, 0644)) - - err := WriteFile(path, updated, fsys) - - assert.NoError(t, err) - written, err := afero.ReadFile(fsys, path) - assert.NoError(t, err) - assert.Equal(t, updated, written) - }) -} - -func TestGetDeclarativeDir(t *testing.T) { - t.Run("uses configured pgdelta path", func(t *testing.T) { - Config.Experimental.PgDelta = &config.PgDeltaConfig{ - DeclarativeSchemaPath: filepath.Join(SupabaseDirPath, "db", "decl"), - } - - assert.Equal(t, filepath.Join(SupabaseDirPath, "db", "decl"), GetDeclarativeDir()) - }) - - t.Run("falls back to default declarative dir", func(t *testing.T) { - Config.Experimental.PgDelta = nil - - assert.Equal(t, DeclarativeDir, GetDeclarativeDir()) - }) -} diff --git a/apps/cli-go/internal/utils/output.go b/apps/cli-go/internal/utils/output.go deleted file mode 100644 index df879fb427..0000000000 --- a/apps/cli-go/internal/utils/output.go +++ /dev/null @@ -1,123 +0,0 @@ -package utils - -import ( - "encoding/json" - "fmt" - "io" - "strings" - - "github.com/BurntSushi/toml" - "github.com/charmbracelet/glamour" - "github.com/charmbracelet/glamour/styles" - "github.com/go-errors/errors" - "github.com/go-viper/mapstructure/v2" - "github.com/joho/godotenv" - "github.com/spf13/viper" - "gopkg.in/yaml.v3" -) - -const ( - OutputEnv = "env" - OutputJson = "json" - OutputPretty = "pretty" - OutputToml = "toml" - OutputYaml = "yaml" - - // OutputMetadata is used with certain SSO commands only. - OutputMetadata = "metadata" -) - -var OutputFormat = EnumFlag{ - Allowed: []string{ - OutputEnv, - OutputPretty, - OutputJson, - OutputToml, - OutputYaml, - }, - Value: OutputPretty, -} - -var ErrEnvNotSupported = errors.New("--output env flag is not supported") - -func EncodeOutput(format string, w io.Writer, value any) error { - switch format { - case OutputEnv: - mapvalue, err := ToEnvMap(value) - if err != nil { - return err - } - - out, err := godotenv.Marshal(mapvalue) - if err != nil { - return errors.Errorf("failed to marshal env map: %w", err) - } - - if _, err := fmt.Fprintln(w, out); err != nil { - return errors.Errorf("failed to write encoded output: %w", err) - } - - case OutputJson: - enc := json.NewEncoder(w) - enc.SetIndent("", " ") - - if err := enc.Encode(value); err != nil { - return errors.Errorf("failed to output json: %w", err) - } - - case OutputYaml: - enc := yaml.NewEncoder(w) - if err := enc.Encode(value); err != nil { - return errors.Errorf("failed to output yaml: %w", err) - } - - case OutputToml: - enc := toml.NewEncoder(w) - if err := enc.Encode(value); err != nil { - return errors.Errorf("failed to output toml: %w", err) - } - - default: - return errors.Errorf("Unsupported output encoding %q", format) - } - return nil -} - -func ToEnvMap(value any) (map[string]string, error) { - var result map[string]any - if dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - TagName: "json", - Result: &result, - }); err != nil { - return nil, errors.Errorf("failed to init decoder: %w", err) - } else if err := dec.Decode(value); err != nil { - return nil, errors.Errorf("failed to decode env: %w", err) - } - v := viper.New() - if err := v.MergeConfigMap(result); err != nil { - return nil, errors.Errorf("failed to merge env: %w", err) - } - keys := v.AllKeys() - mapvalue := make(map[string]string, len(keys)) - for _, k := range keys { - name := strings.ToUpper(strings.ReplaceAll(k, ".", "_")) - mapvalue[name] = v.GetString(k) - } - return mapvalue, nil -} - -func RenderTable(markdown string) error { - r, err := glamour.NewTermRenderer( - glamour.WithStandardStyle(styles.AsciiStyle), - glamour.WithWordWrap(-1), - ) - if err != nil { - return errors.Errorf("failed to initialise terminal renderer: %w", err) - } - out, err := r.Render(markdown) - if err != nil { - return errors.Errorf("failed to render markdown: %w", err) - } - fmt.Print(out) - return nil -} diff --git a/apps/cli-go/internal/utils/output_test.go b/apps/cli-go/internal/utils/output_test.go deleted file mode 100644 index a04be2536b..0000000000 --- a/apps/cli-go/internal/utils/output_test.go +++ /dev/null @@ -1,146 +0,0 @@ -package utils - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestEncodeOutput(t *testing.T) { - t.Run("encodes flat env", func(t *testing.T) { - input := map[string]string{ - "DATABASE_URL": "postgres://user:pass@host:5432/db", - "API_KEY": "secret-key", - } - var buf bytes.Buffer - err := EncodeOutput(OutputEnv, &buf, input) - assert.NoError(t, err) - expected := "API_KEY=\"secret-key\"\nDATABASE_URL=\"postgres://user:pass@host:5432/db\"\n" - assert.Equal(t, expected, buf.String()) - }) - - t.Run("encodes nested env", func(t *testing.T) { - input := map[string]any{ - "FOO": map[string]any{ - "BAR": 123, - }, - } - var buf bytes.Buffer - err := EncodeOutput(OutputEnv, &buf, input) - assert.NoError(t, err) - assert.Equal(t, "FOO_BAR=123\n", buf.String()) - }) - - t.Run("encodes json format", func(t *testing.T) { - input := map[string]any{ - "name": "test", - "config": map[string]any{ - "port": 5432, - "enabled": true, - }, - } - var buf bytes.Buffer - err := EncodeOutput(OutputJson, &buf, input) - assert.NoError(t, err) - expected := `{ - "config": { - "enabled": true, - "port": 5432 - }, - "name": "test" -} -` - assert.Equal(t, expected, buf.String()) - }) - - t.Run("encodes yaml format", func(t *testing.T) { - input := map[string]any{ - "name": "test", - "config": map[string]any{ - "port": 5432, - "enabled": true, - }, - } - var buf bytes.Buffer - err := EncodeOutput(OutputYaml, &buf, input) - assert.NoError(t, err) - expected := `config: - enabled: true - port: 5432 -name: test -` - assert.Equal(t, expected, buf.String()) - }) - - t.Run("encodes toml format", func(t *testing.T) { - input := map[string]any{ - "name": "test", - "config": map[string]any{ - "port": 5432, - "enabled": true, - }, - } - var buf bytes.Buffer - err := EncodeOutput(OutputToml, &buf, input) - assert.NoError(t, err) - expected := `name = "test" - -[config] - enabled = true - port = 5432 -` - assert.Equal(t, expected, buf.String()) - }) - - t.Run("fails with unsupported format", func(t *testing.T) { - var buf bytes.Buffer - err := EncodeOutput("invalid", &buf, nil) - assert.ErrorContains(t, err, `Unsupported output encoding "invalid"`) - }) - - t.Run("handles complex nested structures", func(t *testing.T) { - input := map[string]any{ - "database": map[string]any{ - "connections": []map[string]any{ - { - "host": "localhost", - "port": 5432, - }, - { - "host": "remote", - "port": 6543, - }, - }, - "settings": map[string]any{ - "max_connections": 100, - "ssl_enabled": true, - }, - }, - } - var buf bytes.Buffer - err := EncodeOutput(OutputJson, &buf, input) - require.NoError(t, err) - expected := `{ - "database": { - "connections": [ - { - "host": "localhost", - "port": 5432 - }, - { - "host": "remote", - "port": 6543 - } - ], - "settings": { - "max_connections": 100, - "ssl_enabled": true - } - } -} -` - assert.Equal(t, expected, buf.String()) - }) -} diff --git a/apps/cli-go/internal/utils/plan_gate.go b/apps/cli-go/internal/utils/plan_gate.go deleted file mode 100644 index c666725557..0000000000 --- a/apps/cli-go/internal/utils/plan_gate.go +++ /dev/null @@ -1,97 +0,0 @@ -package utils - -import ( - "context" - "encoding/json" - "fmt" - "strings" - - "github.com/supabase/cli/pkg/api" -) - -func GetOrgSlugFromProjectRef(ctx context.Context, projectRef string) (string, error) { - resp, err := GetSupabase().V1GetProjectWithResponse(ctx, projectRef) - if err != nil { - return "", fmt.Errorf("failed to get project: %w", err) - } - if resp.JSON200 == nil { - return "", fmt.Errorf("unexpected get project status %d: %s", resp.StatusCode(), string(resp.Body)) - } - return resp.JSON200.OrganizationSlug, nil -} - -func GetOrgBillingURL(orgSlug string) string { - return fmt.Sprintf("%s/org/%s/billing", GetSupabaseDashboardURL(), orgSlug) -} - -func orgSlugFromBillingURL(billingURL string) string { - const marker = "/org/" - start := strings.Index(billingURL, marker) - if start < 0 { - return "" - } - rest := billingURL[start+len(marker):] - if end := strings.Index(rest, "/"); end >= 0 { - return rest[:end] - } - return rest -} - -func parsePlanGateError(body []byte) (feature, upgradeURL string, ok bool) { - var envelope api.PlanGateErrorBody - if err := json.Unmarshal(body, &envelope); err != nil || envelope.Error == nil { - return "", "", false - } - if envelope.Error.Code != api.EntitlementRequired || envelope.Error.Feature == "" { - return "", "", false - } - if envelope.Error.UpgradeUrl == nil || *envelope.Error.UpgradeUrl == "" { - return "", "", false - } - return envelope.Error.Feature, *envelope.Error.UpgradeUrl, true -} - -func setUpgradeSuggestion(billingURL string) { - CmdSuggestion = fmt.Sprintf("Your organization does not have access to this feature. Upgrade your plan: %s", Bold(billingURL)) -} - -// SuggestUpgradeOnError checks whether a failed API response is a plan gate and -// sets CmdSuggestion with the billing URL when it is. The entitlement_required -// envelope on the response body is authoritative and needs no network calls; -// when absent, a non-empty featureKey falls back to the entitlements lookup. -// An empty featureKey disables the fallback (envelope-only sites). Returns the -// effective feature and org slug for telemetry, and whether a suggestion was -// shown. Only fires on 4xx. -func SuggestUpgradeOnError(ctx context.Context, projectRef, featureKey string, statusCode int, body []byte) (gatedFeature, orgSlug string, isGated bool) { - if statusCode < 400 || statusCode >= 500 { - return "", "", false - } - - if feature, upgradeURL, ok := parsePlanGateError(body); ok { - setUpgradeSuggestion(upgradeURL) - return feature, orgSlugFromBillingURL(upgradeURL), true - } - - if featureKey == "" { - return "", "", false - } - - slug, err := GetOrgSlugFromProjectRef(ctx, projectRef) - if err != nil { - return "", "", false - } - - resp, err := GetSupabase().V1GetOrganizationEntitlementsWithResponse(ctx, slug) - if err != nil || resp.JSON200 == nil { - return "", slug, false - } - - for _, e := range resp.JSON200.Entitlements { - if string(e.Feature.Key) == featureKey && !e.HasAccess { - setUpgradeSuggestion(GetOrgBillingURL(slug)) - return featureKey, slug, true - } - } - - return "", slug, false -} diff --git a/apps/cli-go/internal/utils/plan_gate_test.go b/apps/cli-go/internal/utils/plan_gate_test.go deleted file mode 100644 index 5f85d5076f..0000000000 --- a/apps/cli-go/internal/utils/plan_gate_test.go +++ /dev/null @@ -1,251 +0,0 @@ -package utils - -import ( - "context" - "net/http" - "testing" - - "github.com/h2non/gock" - "github.com/stretchr/testify/assert" - "github.com/supabase/cli/internal/testing/apitest" -) - -var planGateProjectJSON = map[string]interface{}{ - "ref": "test-ref", - "organization_slug": "my-org", - "name": "test", - "region": "us-east-1", - "created_at": "2024-01-01T00:00:00Z", - "status": "ACTIVE_HEALTHY", - "database": map[string]interface{}{"host": "db.example.supabase.co", "version": "15.1.0.117"}, -} - -func TestGetOrgSlugFromProjectRef(t *testing.T) { - ref := apitest.RandomProjectRef() - - t.Run("returns org slug on success", func(t *testing.T) { - t.Cleanup(apitest.MockPlatformAPI(t)) - gock.New(DefaultApiHost). - Get("/v1/projects/" + ref). - Reply(http.StatusOK). - JSON(planGateProjectJSON) - slug, err := GetOrgSlugFromProjectRef(context.Background(), ref) - assert.NoError(t, err) - assert.Equal(t, "my-org", slug) - }) - - t.Run("returns error on not found", func(t *testing.T) { - t.Cleanup(apitest.MockPlatformAPI(t)) - gock.New(DefaultApiHost). - Get("/v1/projects/" + ref). - Reply(http.StatusNotFound) - _, err := GetOrgSlugFromProjectRef(context.Background(), ref) - assert.ErrorContains(t, err, "unexpected get project status 404") - }) - - t.Run("returns error on network failure", func(t *testing.T) { - t.Cleanup(apitest.MockPlatformAPI(t)) - gock.New(DefaultApiHost). - Get("/v1/projects/" + ref). - ReplyError(assert.AnError) - _, err := GetOrgSlugFromProjectRef(context.Background(), ref) - assert.ErrorContains(t, err, "failed to get project") - }) -} - -func TestGetOrgBillingURL(t *testing.T) { - url := GetOrgBillingURL("my-org") - assert.Equal(t, GetSupabaseDashboardURL()+"/org/my-org/billing", url) -} - -func entitlementsJSON(featureKey string, hasAccess bool) map[string]interface{} { - return map[string]interface{}{ - "entitlements": []map[string]interface{}{ - { - "feature": map[string]interface{}{"key": featureKey, "type": "numeric"}, - "hasAccess": hasAccess, - "type": "numeric", - "config": map[string]interface{}{"enabled": hasAccess, "value": 0, "unlimited": false, "unit": "count"}, - }, - }, - } -} - -// mockEntitlementsCheck sets up gock mocks for project lookup + entitlements. -func mockEntitlementsCheck(ref string, featureKey string, hasAccess bool) { - gock.New(DefaultApiHost). - Get("/v1/projects/" + ref). - Reply(http.StatusOK). - JSON(planGateProjectJSON) - gock.New(DefaultApiHost). - Get("/v1/organizations/my-org/entitlements"). - Reply(http.StatusOK). - JSON(entitlementsJSON(featureKey, hasAccess)) -} - -func TestSuggestUpgradeOnError(t *testing.T) { - ref := apitest.RandomProjectRef() - - t.Run("sets suggestion on 402 with gated feature", func(t *testing.T) { - t.Cleanup(apitest.MockPlatformAPI(t)) - t.Cleanup(func() { CmdSuggestion = "" }) - mockEntitlementsCheck(ref, "branching_limit", false) - feature, slug, got := SuggestUpgradeOnError(context.Background(), ref, "branching_limit", http.StatusPaymentRequired, nil) - assert.True(t, got) - assert.Equal(t, "branching_limit", feature) - assert.Equal(t, "my-org", slug) - assert.Contains(t, CmdSuggestion, "/org/my-org/billing") - assert.Contains(t, CmdSuggestion, "does not have access") - }) - - t.Run("sets suggestion on 400 with gated feature", func(t *testing.T) { - t.Cleanup(apitest.MockPlatformAPI(t)) - t.Cleanup(func() { CmdSuggestion = "" }) - mockEntitlementsCheck(ref, "vanity_subdomain", false) - feature, slug, got := SuggestUpgradeOnError(context.Background(), ref, "vanity_subdomain", http.StatusBadRequest, nil) - assert.True(t, got) - assert.Equal(t, "vanity_subdomain", feature) - assert.Equal(t, "my-org", slug) - assert.Contains(t, CmdSuggestion, "/org/my-org/billing") - assert.Contains(t, CmdSuggestion, "does not have access") - }) - - t.Run("sets suggestion on 404 with gated feature", func(t *testing.T) { - t.Cleanup(apitest.MockPlatformAPI(t)) - t.Cleanup(func() { CmdSuggestion = "" }) - mockEntitlementsCheck(ref, "auth.saml_2", false) - _, slug, got := SuggestUpgradeOnError(context.Background(), ref, "auth.saml_2", http.StatusNotFound, nil) - assert.True(t, got) - assert.Equal(t, "my-org", slug) - assert.Contains(t, CmdSuggestion, "/org/my-org/billing") - }) - - t.Run("no suggestion when entitlements lookup fails", func(t *testing.T) { - t.Cleanup(apitest.MockPlatformAPI(t)) - t.Cleanup(func() { CmdSuggestion = "" }) - gock.New(DefaultApiHost). - Get("/v1/projects/" + ref). - Reply(http.StatusOK). - JSON(planGateProjectJSON) - gock.New(DefaultApiHost). - Get("/v1/organizations/my-org/entitlements"). - Reply(http.StatusInternalServerError) - _, slug, got := SuggestUpgradeOnError(context.Background(), ref, "branching_limit", http.StatusPaymentRequired, nil) - assert.False(t, got) - assert.Equal(t, "my-org", slug) - assert.Empty(t, CmdSuggestion) - }) - - t.Run("no suggestion when project lookup fails", func(t *testing.T) { - t.Cleanup(apitest.MockPlatformAPI(t)) - t.Cleanup(func() { CmdSuggestion = "" }) - gock.New(DefaultApiHost). - Get("/v1/projects/" + ref). - Reply(http.StatusNotFound) - _, slug, got := SuggestUpgradeOnError(context.Background(), ref, "branching_limit", http.StatusPaymentRequired, nil) - assert.False(t, got) - assert.Empty(t, slug) - assert.Empty(t, CmdSuggestion) - }) - - t.Run("no suggestion when feature has access", func(t *testing.T) { - t.Cleanup(apitest.MockPlatformAPI(t)) - t.Cleanup(func() { CmdSuggestion = "" }) - mockEntitlementsCheck(ref, "branching_limit", true) - _, slug, got := SuggestUpgradeOnError(context.Background(), ref, "branching_limit", http.StatusPaymentRequired, nil) - assert.False(t, got) - assert.Equal(t, "my-org", slug) - assert.Empty(t, CmdSuggestion) - }) - - t.Run("skips on 503 server error", func(t *testing.T) { - CmdSuggestion = "" - _, _, got := SuggestUpgradeOnError(context.Background(), ref, "branching_limit", http.StatusServiceUnavailable, nil) - assert.False(t, got) - assert.Empty(t, CmdSuggestion) - }) - - t.Run("skips on 200", func(t *testing.T) { - CmdSuggestion = "" - _, _, got := SuggestUpgradeOnError(context.Background(), ref, "branching_limit", http.StatusOK, nil) - assert.False(t, got) - assert.Empty(t, CmdSuggestion) - }) - - t.Run("skips on 201", func(t *testing.T) { - CmdSuggestion = "" - _, _, got := SuggestUpgradeOnError(context.Background(), ref, "branching_limit", http.StatusCreated, nil) - assert.False(t, got) - assert.Empty(t, CmdSuggestion) - }) -} - -func TestSuggestUpgradeEnvelope(t *testing.T) { - envelope := `{"message":"x","error":{"code":"entitlement_required","feature":"custom_domain","upgrade_url":"https://supabase.com/dashboard/org/acme/billing"}}` - - t.Run("envelope sets suggestion with zero API calls", func(t *testing.T) { - t.Cleanup(func() { CmdSuggestion = "" }) - feature, slug, gated := SuggestUpgradeOnError(context.Background(), "ref", "", http.StatusBadRequest, []byte(envelope)) - assert.True(t, gated) - assert.Equal(t, "custom_domain", feature) - assert.Equal(t, "acme", slug) - assert.Contains(t, CmdSuggestion, "org/acme/billing") - assert.Contains(t, CmdSuggestion, "does not have access") - }) - - t.Run("envelope feature wins over call-site featureKey", func(t *testing.T) { - t.Cleanup(func() { CmdSuggestion = "" }) - body := `{"message":"x","error":{"code":"entitlement_required","feature":"branching_persistent","upgrade_url":"https://supabase.com/dashboard/org/acme/billing"}}` - feature, _, gated := SuggestUpgradeOnError(context.Background(), "ref", "branching_limit", http.StatusPaymentRequired, []byte(body)) - assert.True(t, gated) - assert.Equal(t, "branching_persistent", feature) - }) - - t.Run("envelope without upgrade_url falls back to entitlements", func(t *testing.T) { - ref := apitest.RandomProjectRef() - t.Cleanup(apitest.MockPlatformAPI(t)) - t.Cleanup(func() { CmdSuggestion = "" }) - mockEntitlementsCheck(ref, "branching_limit", false) - body := `{"message":"x","error":{"code":"entitlement_required","feature":"branching_limit"}}` - feature, slug, gated := SuggestUpgradeOnError(context.Background(), ref, "branching_limit", http.StatusPaymentRequired, []byte(body)) - assert.True(t, gated) - assert.Equal(t, "branching_limit", feature) - assert.Equal(t, "my-org", slug) - assert.Contains(t, CmdSuggestion, "/org/my-org/billing") - }) - - t.Run("malformed body falls back to entitlements", func(t *testing.T) { - ref := apitest.RandomProjectRef() - t.Cleanup(apitest.MockPlatformAPI(t)) - t.Cleanup(func() { CmdSuggestion = "" }) - mockEntitlementsCheck(ref, "branching_limit", false) - _, _, gated := SuggestUpgradeOnError(context.Background(), ref, "branching_limit", http.StatusPaymentRequired, []byte(`not json`)) - assert.True(t, gated) - }) - - t.Run("no envelope and empty featureKey is a no-op with zero API calls", func(t *testing.T) { - t.Cleanup(func() { CmdSuggestion = "" }) - _, _, gated := SuggestUpgradeOnError(context.Background(), "ref", "", http.StatusNotFound, []byte(`{"message":"not found"}`)) - assert.False(t, gated) - assert.Empty(t, CmdSuggestion) - }) - - t.Run("non-gate error code is ignored", func(t *testing.T) { - t.Cleanup(func() { CmdSuggestion = "" }) - body := `{"message":"x","error":{"code":"validation_failed","feature":"custom_domain","upgrade_url":"https://supabase.com/dashboard/org/acme/billing"}}` - _, _, gated := SuggestUpgradeOnError(context.Background(), "ref", "", http.StatusBadRequest, []byte(body)) - assert.False(t, gated) - assert.Empty(t, CmdSuggestion) - }) -} - -func TestOrgSlugFromBillingURL(t *testing.T) { - cases := map[string]string{ - "https://supabase.com/dashboard/org/acme/billing": "acme", - "https://supabase.com/dashboard/org/acme": "acme", - "https://example.com/nope": "", - } - for in, want := range cases { - assert.Equal(t, want, orgSlugFromBillingURL(in), in) - } -} diff --git a/apps/cli-go/internal/utils/profile.go b/apps/cli-go/internal/utils/profile.go deleted file mode 100644 index 4b24870271..0000000000 --- a/apps/cli-go/internal/utils/profile.go +++ /dev/null @@ -1,160 +0,0 @@ -package utils - -import ( - "context" - "fmt" - "path/filepath" - "sort" - "strings" - - "github.com/go-errors/errors" - "github.com/go-playground/validator/v10" - "github.com/spf13/afero" - "github.com/spf13/viper" - "github.com/supabase/cli/pkg/api" -) - -type Profile struct { - Name string `mapstructure:"name" validate:"required"` - APIURL string `mapstructure:"api_url" validate:"required,http_url"` - DashboardURL string `mapstructure:"dashboard_url" validate:"required,http_url"` - DocsURL string `mapstructure:"docs_url" validate:"omitempty,http_url"` - ProjectHost string `mapstructure:"project_host" validate:"required,hostname_rfc1123"` - PoolerHost string `mapstructure:"pooler_host" validate:"omitempty,hostname_rfc1123"` - AuthClientID string `mapstructure:"client_id" validate:"omitempty,uuid4"` - StudioImage string `mapstructure:"studio_image"` - ProjectRegions []api.V1CreateProjectBodyRegion `mapstructure:"regions"` -} - -var allProfiles = []Profile{{ - Name: "supabase", - APIURL: "https://api.supabase.com", - DashboardURL: "https://supabase.com/dashboard", - DocsURL: "https://supabase.com/docs", - ProjectHost: "supabase.co", - PoolerHost: "supabase.com", - ProjectRegions: []api.V1CreateProjectBodyRegion{ - api.V1CreateProjectBodyRegionApEast1, - api.V1CreateProjectBodyRegionApNortheast1, - api.V1CreateProjectBodyRegionApNortheast2, - api.V1CreateProjectBodyRegionApSouth1, - api.V1CreateProjectBodyRegionApSoutheast1, - api.V1CreateProjectBodyRegionApSoutheast2, - api.V1CreateProjectBodyRegionCaCentral1, - api.V1CreateProjectBodyRegionEuCentral1, - api.V1CreateProjectBodyRegionEuCentral2, - api.V1CreateProjectBodyRegionEuNorth1, - api.V1CreateProjectBodyRegionEuWest1, - api.V1CreateProjectBodyRegionEuWest2, - api.V1CreateProjectBodyRegionEuWest3, - api.V1CreateProjectBodyRegionSaEast1, - api.V1CreateProjectBodyRegionUsEast1, - api.V1CreateProjectBodyRegionUsEast2, - api.V1CreateProjectBodyRegionUsWest1, - api.V1CreateProjectBodyRegionUsWest2, - }, -}, { - Name: "supabase-staging", - APIURL: "https://api.supabase.green", - DashboardURL: "https://supabase.green/dashboard", - DocsURL: "https://supabase.com/docs", - ProjectHost: "supabase.red", - PoolerHost: "supabase.green", - ProjectRegions: []api.V1CreateProjectBodyRegion{ - api.V1CreateProjectBodyRegionApSoutheast1, - api.V1CreateProjectBodyRegionUsEast1, - api.V1CreateProjectBodyRegionEuCentral1, - }, -}, { - Name: "supabase-local", - APIURL: "http://localhost:8080", - DashboardURL: "http://localhost:8082", - DocsURL: "https://supabase.com/docs", - ProjectHost: "supabase.red", - ProjectRegions: []api.V1CreateProjectBodyRegion{ - api.V1CreateProjectBodyRegionApSoutheast1, - api.V1CreateProjectBodyRegionUsEast1, - api.V1CreateProjectBodyRegionEuCentral1, - }, -}, { - Name: "snap", - APIURL: "https://cloudapi.snap.com", - DashboardURL: "https://cloud.snap.com/dashboard", - DocsURL: "https://cloud.snap.com/docs", - ProjectHost: "snapcloud.dev", - PoolerHost: "snapcloud.co", - AuthClientID: "f7573b20-df47-48f1-b606-e8db4ec16252", - ProjectRegions: []api.V1CreateProjectBodyRegion{ - api.V1CreateProjectBodyRegionUsEast1, - }, -}} - -var CurrentProfile Profile - -func LoadProfile(ctx context.Context, fsys afero.Fs) error { - prof := getProfileName(fsys) - for _, p := range allProfiles { - if strings.EqualFold(p.Name, prof) { - CurrentProfile = p - return nil - } - } - // Instantiate to avoid leaking profile into global viper state - v := viper.New() - v.SetFs(fsys) - v.SetConfigFile(prof) - if err := v.ReadInConfig(); err != nil { - return errors.Errorf("failed to read profile: %w", err) - } - // Load profile into viper, rejecting keys not defined by config - if err := v.UnmarshalExact(&CurrentProfile); err != nil { - return errors.Errorf("failed to parse profile: %w", err) - } - validate := validator.New(validator.WithRequiredStructEnabled()) - if err := validate.StructCtx(ctx, &CurrentProfile); err != nil { - return errors.Errorf("invalid profile: %w", err) - } - return nil -} - -func getProfileName(fsys afero.Fs) string { - debuglogger := GetDebugLogger() - if prof := viper.GetString("PROFILE"); viper.IsSet("PROFILE") { - fmt.Fprintln(debuglogger, "Loading profile from flag:", prof) - return prof - } else if profilePath, err := getProfilePath(); err != nil { - fmt.Fprintln(debuglogger, err) - return prof - } else if content, err := afero.ReadFile(fsys, profilePath); err == nil { - fmt.Fprintln(debuglogger, "Loading profile from file:", profilePath) - return string(content) - } else { - fmt.Fprintln(debuglogger, err) - return prof - } -} - -func getProfilePath() (string, error) { - home, err := SupabaseHomeDir() - if err != nil { - return "", err - } - return filepath.Join(home, "profile"), nil -} - -func SaveProfileName(prof string, fsys afero.Fs) error { - profilePath, err := getProfilePath() - if err != nil { - return err - } - return WriteFile(profilePath, []byte(prof), fsys) -} - -func AwsRegions() []string { - result := make([]string, len(allProfiles[0].ProjectRegions)) - for i, region := range allProfiles[0].ProjectRegions { - result[i] = string(region) - } - sort.Strings(result) - return result -} diff --git a/apps/cli-go/internal/utils/profile_test.go b/apps/cli-go/internal/utils/profile_test.go deleted file mode 100644 index 4c5925e2d4..0000000000 --- a/apps/cli-go/internal/utils/profile_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package utils - -import ( - "context" - "embed" - "os" - "path/filepath" - "testing" - - "github.com/go-playground/validator/v10" - "github.com/spf13/afero" - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" -) - -//go:embed testdata/*.json -var testdata embed.FS - -func TestLoadProfile(t *testing.T) { - validate := validator.New(validator.WithRequiredStructEnabled()) - for _, p := range allProfiles { - t.Run("loads profile "+p.Name, func(t *testing.T) { - viper.Set("PROFILE", p.Name) - t.Cleanup(viper.Reset) - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - err := LoadProfile(context.Background(), fsys) - // Check error - assert.NoError(t, err) - assert.NoError(t, validate.Struct(&CurrentProfile)) - }) - } - - t.Run("loads from json", func(t *testing.T) { - viper.Set("PROFILE", "testdata/profile.json") - t.Cleanup(viper.Reset) - // Setup in-memory fs - fsys := afero.FromIOFS{FS: testdata} - // Run test - err := LoadProfile(context.Background(), fsys) - // Check error - assert.NoError(t, err) - }) - - t.Run("throws error on invalid profile", func(t *testing.T) { - viper.Set("PROFILE", "testdata/invalid.json") - t.Cleanup(viper.Reset) - // Setup in-memory fs - fsys := afero.FromIOFS{FS: testdata} - // Run test - err := LoadProfile(context.Background(), fsys) - // Check error - assert.ErrorContains(t, err, "Field validation for 'APIURL' failed on the 'http_url' tag") - assert.ErrorContains(t, err, "Field validation for 'ProjectHost' failed on the 'hostname_rfc1123' tag") - }) - - t.Run("throws error on malformed profile", func(t *testing.T) { - viper.Set("PROFILE", "testdata/malformed.json") - t.Cleanup(viper.Reset) - // Setup in-memory fs - fsys := afero.FromIOFS{FS: testdata} - // Run test - err := LoadProfile(context.Background(), fsys) - // Check error - assert.ErrorContains(t, err, "invalid keys: test_url") - }) - - t.Run("throws error on missing profile", func(t *testing.T) { - viper.Set("PROFILE", "testdata/missing.json") - t.Cleanup(viper.Reset) - // Setup in-memory fs - fsys := afero.FromIOFS{FS: testdata} - // Run test - err := LoadProfile(context.Background(), fsys) - // Check error - assert.ErrorIs(t, err, os.ErrNotExist) - }) -} - -func TestSaveProfileName(t *testing.T) { - t.Run("saves to SUPABASE_HOME when configured", func(t *testing.T) { - t.Setenv("HOME", "/home/test") - t.Setenv("SUPABASE_HOME", "/custom/supabase") - // Setup in-memory fs - fsys := afero.NewMemMapFs() - // Run test - err := SaveProfileName("supabase-staging", fsys) - // Check error - assert.NoError(t, err) - contents, err := afero.ReadFile(fsys, filepath.Join("/custom/supabase", "profile")) - assert.NoError(t, err) - assert.Equal(t, "supabase-staging", string(contents)) - exists, err := afero.Exists(fsys, filepath.Join("/home/test", ".supabase", "profile")) - assert.NoError(t, err) - assert.False(t, exists) - }) -} diff --git a/apps/cli-go/internal/utils/prompt.go b/apps/cli-go/internal/utils/prompt.go deleted file mode 100644 index ae2dedf2ae..0000000000 --- a/apps/cli-go/internal/utils/prompt.go +++ /dev/null @@ -1,144 +0,0 @@ -package utils - -import ( - "context" - "fmt" - "io" - "os" - "strings" - - "github.com/charmbracelet/bubbles/list" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/go-errors/errors" -) - -var ( - titleStyle = lipgloss.NewStyle().MarginLeft(2) - itemStyle = lipgloss.NewStyle().PaddingLeft(4) - selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170")) - paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4) - helpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1) -) - -// PromptItem is exposed as prompt input, empty summary + details will be excluded. -type PromptItem struct { - Summary string - Details string - Index int -} - -func (i PromptItem) Title() string { return i.Summary } -func (i PromptItem) Description() string { return i.Details } -func (i PromptItem) FilterValue() string { return i.Summary + " " + i.Details } - -// Item delegate is used to finetune the list item renderer. -type itemDelegate struct{} - -func (d itemDelegate) Height() int { return 1 } -func (d itemDelegate) Spacing() int { return 0 } -func (d itemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil } -func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { - i, ok := listItem.(PromptItem) - if !ok { - return - } - - str := fmt.Sprintf("%d. %s", index+1, i.Summary) - if i.Details != "" { - str += fmt.Sprintf(" [%s]", i.Details) - } - - fn := itemStyle.Render - if index == m.Index() { - fn = func(s ...string) string { - items := append([]string{"> "}, s...) - return selectedItemStyle.Render(items...) - } - } - - fmt.Fprint(w, fn(str)) -} - -// Model is used to store state of user choices. -type model struct { - cancel context.CancelFunc - list list.Model - choice PromptItem -} - -func (m model) Init() tea.Cmd { - return nil -} - -func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.WindowSizeMsg: - m.list.SetWidth(msg.Width) - return m, nil - - case tea.KeyMsg: - switch msg.Type { - case tea.KeyCtrlC: - m.cancel() - return m, tea.Quit - - case tea.KeyEnter: - if choice, ok := m.list.SelectedItem().(PromptItem); ok { - m.choice = choice - } - return m, tea.Quit - } - } - - var cmd tea.Cmd - m.list, cmd = m.list.Update(msg) - return m, cmd -} - -func (m model) View() string { - if m.choice.Summary != "" { - return "" - } - return "\n" + m.list.View() -} - -// Prompt user to choose from a list of items, returns the chosen index. -func PromptChoice(ctx context.Context, title string, items []PromptItem, opts ...tea.ProgramOption) (PromptItem, error) { - // Create list items - var listItems []list.Item - for _, v := range items { - if strings.TrimSpace(v.FilterValue()) == "" { - continue - } - listItems = append(listItems, v) - } - // Create list model - height := min(len(listItems)*4, 14) - l := list.New(listItems, itemDelegate{}, 0, height) - l.Title = title - l.SetShowStatusBar(false) - l.Styles.Title = titleStyle - l.Styles.PaginationStyle = paginationStyle - l.Styles.HelpStyle = helpStyle - // Create our model - ctx, cancel := context.WithCancel(ctx) - initial := model{cancel: cancel, list: l} - // Interactive prompts should always be written to stderr - opts = append(opts, tea.WithOutput(os.Stderr)) - prog := tea.NewProgram(initial, opts...) - state, err := prog.Run() - if err != nil { - return initial.choice, errors.Errorf("failed to prompt choice: %w", err) - } - if ctx.Err() != nil { - return initial.choice, ctx.Err() - } - if m, ok := state.(model); ok { - if m.choice == initial.choice { - return initial.choice, errors.New("user aborted") - } - return m.choice, nil - } - return initial.choice, err -} diff --git a/apps/cli-go/internal/utils/release.go b/apps/cli-go/internal/utils/release.go deleted file mode 100644 index c68430c6aa..0000000000 --- a/apps/cli-go/internal/utils/release.go +++ /dev/null @@ -1,48 +0,0 @@ -package utils - -import ( - "context" - "net/http" - "os" - "sync" - - "github.com/go-errors/errors" - "github.com/google/go-github/v62/github" - "golang.org/x/oauth2" -) - -var ( - githubClient *github.Client - githubOnce sync.Once -) - -func GetGitHubClient(ctx context.Context) *github.Client { - githubOnce.Do(func() { - var client *http.Client - if token := os.Getenv("GITHUB_TOKEN"); len(token) > 0 { - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - ) - client = oauth2.NewClient(ctx, ts) - } - githubClient = github.NewClient(client) - }) - return githubClient -} - -const ( - CLI_OWNER = "supabase" - CLI_REPO = "cli" -) - -func GetLatestRelease(ctx context.Context) (string, error) { - client := GetGitHubClient(ctx) - release, _, err := client.Repositories.GetLatestRelease(ctx, CLI_OWNER, CLI_REPO) - if err != nil { - return "", errors.Errorf("Failed to fetch latest release: %w", err) - } - if release.TagName == nil { - return "", nil - } - return *release.TagName, nil -} diff --git a/apps/cli-go/internal/utils/release_test.go b/apps/cli-go/internal/utils/release_test.go deleted file mode 100644 index 25aa920e8f..0000000000 --- a/apps/cli-go/internal/utils/release_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package utils - -import ( - "context" - "net/http" - "testing" - - "github.com/go-errors/errors" - "github.com/google/go-github/v62/github" - "github.com/h2non/gock" - "github.com/stretchr/testify/assert" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/pkg/cast" -) - -func TestLatestRelease(t *testing.T) { - t.Run("fetches latest release", func(t *testing.T) { - // Setup api mock - defer gock.OffAll() - gock.New("https://api.github.com"). - Get("/repos/supabase/cli/releases/latest"). - Reply(http.StatusOK). - JSON(github.RepositoryRelease{TagName: cast.Ptr("v2")}) - // Run test - version, err := GetLatestRelease(context.Background()) - // Check error - assert.NoError(t, err) - assert.Equal(t, version, "v2") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("ignores missing version", func(t *testing.T) { - // Setup api mock - defer gock.OffAll() - gock.New("https://api.github.com"). - Get("/repos/supabase/cli/releases/latest"). - Reply(http.StatusOK). - JSON(github.RepositoryRelease{}) - // Run test - version, err := GetLatestRelease(context.Background()) - // Check error - assert.NoError(t, err) - assert.Empty(t, version) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on network error", func(t *testing.T) { - errNetwork := errors.New("network error") - // Setup api mock - defer gock.OffAll() - gock.New("https://api.github.com"). - Get("/repos/supabase/cli/releases/latest"). - ReplyError(errNetwork) - // Run test - version, err := GetLatestRelease(context.Background()) - // Check error - assert.ErrorIs(t, err, errNetwork) - assert.Empty(t, version) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} diff --git a/apps/cli-go/internal/utils/render.go b/apps/cli-go/internal/utils/render.go deleted file mode 100644 index ee4a4dc6c5..0000000000 --- a/apps/cli-go/internal/utils/render.go +++ /dev/null @@ -1,60 +0,0 @@ -package utils - -import ( - "fmt" - "time" -) - -const ( - layoutVersion = "20060102150405" - layoutHuman = "2006-01-02 15:04:05" -) - -func FormatTime(t time.Time) string { - return t.UTC().Format(layoutHuman) -} - -func FormatTimestamp(timestamp string) string { - return parse(time.RFC3339, timestamp) -} - -func FormatTimestampVersion(timestamp string) string { - return parse(layoutVersion, timestamp) -} - -func parse(layout, value string) string { - t, err := time.Parse(layout, value) - if err != nil { - fmt.Fprintln(GetDebugLogger(), err) - return value - } - return FormatTime(t) -} - -var regionMap = map[string]string{ - "ap-east-1": "East Asia (Hong Kong)", - "ap-northeast-1": "Northeast Asia (Tokyo)", - "ap-northeast-2": "Northeast Asia (Seoul)", - "ap-south-1": "South Asia (Mumbai)", - "ap-southeast-1": "Southeast Asia (Singapore)", - "ap-southeast-2": "Oceania (Sydney)", - "ca-central-1": "Canada (Central)", - "eu-central-1": "Central EU (Frankfurt)", - "eu-central-2": "Central Europe (Zurich)", - "eu-north-1": "North EU (Stockholm)", - "eu-west-1": "West EU (Ireland)", - "eu-west-2": "West Europe (London)", - "eu-west-3": "West EU (Paris)", - "sa-east-1": "South America (São Paulo)", - "us-east-1": "East US (North Virginia)", - "us-east-2": "East US (Ohio)", - "us-west-1": "West US (North California)", - "us-west-2": "West US (Oregon)", -} - -func FormatRegion(region string) string { - if readable, ok := regionMap[region]; ok { - return readable - } - return region -} diff --git a/apps/cli-go/internal/utils/retry.go b/apps/cli-go/internal/utils/retry.go deleted file mode 100644 index 6f0fb57e1d..0000000000 --- a/apps/cli-go/internal/utils/retry.go +++ /dev/null @@ -1,35 +0,0 @@ -package utils - -import ( - "context" - "fmt" - "os" - "time" - - "github.com/cenkalti/backoff/v4" -) - -const maxRetries = 8 - -func NewBackoffPolicy(ctx context.Context) backoff.BackOffContext { - b := backoff.NewExponentialBackOff(backoff.WithInitialInterval(3 * time.Second)) - return backoff.WithContext(backoff.WithMaxRetries(b, maxRetries), ctx) -} - -func NewErrorCallback(callbacks ...func(attempt uint) error) backoff.Notify { - failureCount := uint(0) - logger := GetDebugLogger() - return func(err error, d time.Duration) { - failureCount += 1 - if failureCount*3 > maxRetries { - logger = os.Stderr - } - for _, cb := range callbacks { - if err := cb(failureCount); err != nil { - fmt.Fprintln(logger, err) - } - } - fmt.Fprintln(logger, err) - fmt.Fprintf(logger, "Retry (%d/%d): ", failureCount, maxRetries) - } -} diff --git a/apps/cli-go/internal/utils/slice.go b/apps/cli-go/internal/utils/slice.go deleted file mode 100644 index 9babf6c59c..0000000000 --- a/apps/cli-go/internal/utils/slice.go +++ /dev/null @@ -1,12 +0,0 @@ -package utils - -func RemoveDuplicates[T comparable](slice []T) (result []T) { - set := make(map[T]struct{}) - for _, item := range slice { - if _, exists := set[item]; !exists { - set[item] = struct{}{} - result = append(result, item) - } - } - return result -} diff --git a/apps/cli-go/internal/utils/slice_test.go b/apps/cli-go/internal/utils/slice_test.go deleted file mode 100644 index 3c2a3e9575..0000000000 --- a/apps/cli-go/internal/utils/slice_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package utils - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestRemoveDuplicates(t *testing.T) { - t.Run("string slice", func(t *testing.T) { - input := []string{"a", "b", "a", "c", "b", "d"} - expected := []string{"a", "b", "c", "d"} - assert.Equal(t, expected, RemoveDuplicates(input)) - }) - - t.Run("int slice", func(t *testing.T) { - input := []int{1, 2, 2, 3, 1, 4, 3, 5} - expected := []int{1, 2, 3, 4, 5} - assert.Equal(t, expected, RemoveDuplicates(input)) - }) - - t.Run("empty slice", func(t *testing.T) { - assert.Empty(t, RemoveDuplicates([]string{})) - }) - - t.Run("no duplicates", func(t *testing.T) { - input := []int{1, 2, 3, 4, 5} - assert.Equal(t, input, RemoveDuplicates(input)) - }) -} diff --git a/apps/cli-go/internal/utils/supabase_home.go b/apps/cli-go/internal/utils/supabase_home.go deleted file mode 100644 index 02dba18b10..0000000000 --- a/apps/cli-go/internal/utils/supabase_home.go +++ /dev/null @@ -1,24 +0,0 @@ -package utils - -import ( - "os" - "path/filepath" - "strings" - - "github.com/go-errors/errors" -) - -// SupabaseHomeDir returns the global Supabase CLI state root. It is overridden -// by the SUPABASE_HOME environment variable when set to a non-empty value (an -// absolute path is expected; the value is used verbatim), otherwise it defaults -// to ~/.supabase. -func SupabaseHomeDir() (string, error) { - if home := strings.TrimSpace(os.Getenv("SUPABASE_HOME")); home != "" { - return home, nil - } - home, err := os.UserHomeDir() - if err != nil { - return "", errors.Errorf("failed to get $HOME directory: %w", err) - } - return filepath.Join(home, ".supabase"), nil -} diff --git a/apps/cli-go/internal/utils/tea.go b/apps/cli-go/internal/utils/tea.go deleted file mode 100644 index b7965eebfc..0000000000 --- a/apps/cli-go/internal/utils/tea.go +++ /dev/null @@ -1,201 +0,0 @@ -package utils - -import ( - "bytes" - "context" - "fmt" - "os" - "strings" - - "github.com/charmbracelet/bubbles/progress" - "github.com/charmbracelet/bubbles/spinner" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/muesli/reflow/wrap" - "golang.org/x/term" -) - -func NewProgram(model tea.Model, opts ...tea.ProgramOption) Program { - var p Program - if term.IsTerminal(int(os.Stdin.Fd())) { - p = tea.NewProgram(model, opts...) - } else { - p = newFakeProgram(model) - } - return p -} - -// An interface describing the parts of BubbleTea's Program that we actually use. -type Program interface { - Start() error - Send(msg tea.Msg) - Quit() -} - -func newFakeProgram(model tea.Model) *fakeProgram { - p := &fakeProgram{ - model: model, - } - return p -} - -// A dumb text implementation of BubbleTea's Program that allows -// for output to be piped to another program. -type fakeProgram struct { - model tea.Model -} - -func (p *fakeProgram) Start() error { - initCmd := p.model.Init() - message := initCmd() - if message != nil { - p.model.Update(message) - } - return nil -} - -func (p *fakeProgram) Send(msg tea.Msg) { - switch msg := msg.(type) { - case StatusMsg: - fmt.Println(msg) - case PsqlMsg: - if msg != nil { - fmt.Println(*msg) - } - } - - _, cmd := p.model.Update(msg) - if cmd != nil { - cmd() - } -} - -func (p *fakeProgram) Quit() { - p.Send(tea.Quit()) -} - -type ( - StatusMsg string - ProgressMsg *float64 - PsqlMsg *string -) - -type StatusWriter struct { - Program -} - -func (t StatusWriter) Write(p []byte) (int, error) { - trimmed := bytes.TrimRight(p, "\n") - t.Send(StatusMsg(trimmed)) - return len(p), nil -} - -func RunProgram(ctx context.Context, f func(p Program, ctx context.Context) error) error { - ctx, cancel := context.WithCancel(ctx) - p := NewProgram(logModel{ - cancel: cancel, - spinner: spinner.New( - spinner.WithSpinner(spinner.Dot), - spinner.WithStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("205"))), - ), - }) - - errCh := make(chan error, 1) - go func() { - errCh <- f(p, ctx) - p.Quit() - }() - - if err := p.Start(); err != nil { - return err - } - return <-errCh -} - -type logModel struct { - cancel context.CancelFunc - - spinner spinner.Model - status string - progress *progress.Model - psqlOutputs []string - - width int -} - -func (m logModel) Init() tea.Cmd { - return m.spinner.Tick -} - -func (m logModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.KeyMsg: - switch msg.Type { - case tea.KeyCtrlC: - if m.cancel != nil { - m.cancel() - } - return m, tea.Quit - default: - return m, nil - } - case tea.WindowSizeMsg: - m.width = msg.Width - return m, nil - case spinner.TickMsg: - spinnerModel, cmd := m.spinner.Update(msg) - m.spinner = spinnerModel - return m, cmd - case progress.FrameMsg: - if m.progress == nil { - return m, nil - } - - tmp, cmd := m.progress.Update(msg) - progressModel := tmp.(progress.Model) - m.progress = &progressModel - return m, cmd - case StatusMsg: - m.status = string(msg) - return m, nil - case ProgressMsg: - if msg == nil { - m.progress = nil - return m, nil - } - - if m.progress == nil { - progressModel := progress.New(progress.WithGradient("#1c1c1c", "#34b27b")) - m.progress = &progressModel - } - - return m, m.progress.SetPercent(*msg) - case PsqlMsg: - if msg == nil { - m.psqlOutputs = []string{} - return m, nil - } - - m.psqlOutputs = append(m.psqlOutputs, *msg) - if len(m.psqlOutputs) > 5 { - m.psqlOutputs = m.psqlOutputs[1:] - } - return m, nil - default: - return m, nil - } -} - -func (m logModel) View() string { - var progress string - if m.progress != nil { - progress = "\n\n" + m.progress.View() - } - - var psqlOutputs string - if len(m.psqlOutputs) > 0 { - psqlOutputs = "\n\n" + strings.Join(m.psqlOutputs, "\n") - } - - return wrap.String(m.spinner.View()+m.status+progress+psqlOutputs, m.width) -} diff --git a/apps/cli-go/internal/utils/templates/globals.sql b/apps/cli-go/internal/utils/templates/globals.sql deleted file mode 100644 index 0b8fbaef01..0000000000 --- a/apps/cli-go/internal/utils/templates/globals.sql +++ /dev/null @@ -1,146 +0,0 @@ --- --- PostgreSQL database cluster dump --- - -SET default_transaction_read_only = off; - -SET client_encoding = 'UTF8'; -SET standard_conforming_strings = on; - --- --- Roles --- - -CREATE ROLE anon; -ALTER ROLE anon WITH NOSUPERUSER NOINHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; -CREATE ROLE authenticated; -ALTER ROLE authenticated WITH NOSUPERUSER NOINHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; -CREATE ROLE authenticator; -ALTER ROLE authenticator WITH NOSUPERUSER NOINHERIT NOCREATEROLE NOCREATEDB LOGIN NOREPLICATION NOBYPASSRLS PASSWORD 'postgres'; -CREATE ROLE dashboard_user; -ALTER ROLE dashboard_user WITH NOSUPERUSER INHERIT CREATEROLE CREATEDB NOLOGIN REPLICATION NOBYPASSRLS; -CREATE ROLE pgbouncer; -ALTER ROLE pgbouncer WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB LOGIN NOREPLICATION NOBYPASSRLS PASSWORD 'postgres'; --- CREATE ROLE pgsodium_keyholder; --- ALTER ROLE pgsodium_keyholder WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; --- CREATE ROLE pgsodium_keyiduser; --- ALTER ROLE pgsodium_keyiduser WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; --- CREATE ROLE pgsodium_keymaker; --- ALTER ROLE pgsodium_keymaker WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; --- CREATE ROLE postgres; --- ALTER ROLE postgres WITH NOSUPERUSER INHERIT CREATEROLE CREATEDB LOGIN REPLICATION BYPASSRLS; -CREATE ROLE service_role; -ALTER ROLE service_role WITH NOSUPERUSER NOINHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION BYPASSRLS; -CREATE ROLE supabase_admin; -ALTER ROLE supabase_admin WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN REPLICATION BYPASSRLS PASSWORD 'postgres'; -CREATE ROLE supabase_auth_admin; -ALTER ROLE supabase_auth_admin WITH NOSUPERUSER NOINHERIT CREATEROLE NOCREATEDB LOGIN NOREPLICATION NOBYPASSRLS PASSWORD 'postgres'; -CREATE ROLE supabase_functions_admin; -ALTER ROLE supabase_functions_admin WITH NOSUPERUSER NOINHERIT CREATEROLE NOCREATEDB LOGIN NOREPLICATION NOBYPASSRLS PASSWORD 'postgres'; -CREATE ROLE supabase_read_only_user; -ALTER ROLE supabase_read_only_user WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB LOGIN NOREPLICATION BYPASSRLS PASSWORD 'postgres'; -CREATE ROLE supabase_replication_admin; -ALTER ROLE supabase_replication_admin WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB LOGIN REPLICATION NOBYPASSRLS PASSWORD 'postgres'; -CREATE ROLE supabase_storage_admin; -ALTER ROLE supabase_storage_admin WITH NOSUPERUSER NOINHERIT CREATEROLE NOCREATEDB LOGIN NOREPLICATION NOBYPASSRLS PASSWORD 'postgres'; - --- --- User Configurations --- - --- --- User Config "anon" --- - -ALTER ROLE anon SET statement_timeout TO '3s'; - --- --- User Config "authenticated" --- - -ALTER ROLE authenticated SET statement_timeout TO '8s'; - --- --- User Config "authenticator" --- - -ALTER ROLE authenticator SET session_preload_libraries TO 'safeupdate'; -ALTER ROLE authenticator SET statement_timeout TO '8s'; - --- --- User Config "postgres" --- - -ALTER ROLE postgres SET search_path TO E'\\$user', 'public', 'extensions'; - --- --- User Config "supabase_admin" --- - -ALTER ROLE supabase_admin SET search_path TO E'\\$user', 'public', 'auth', 'extensions'; - --- --- User Config "supabase_auth_admin" --- - -ALTER ROLE supabase_auth_admin SET search_path TO 'auth'; -ALTER ROLE supabase_auth_admin SET idle_in_transaction_session_timeout TO '60000'; - --- --- User Config "supabase_functions_admin" --- - -ALTER ROLE supabase_functions_admin SET search_path TO 'supabase_functions'; - --- --- User Config "supabase_storage_admin" --- - -ALTER ROLE supabase_storage_admin SET search_path TO 'storage'; - - --- --- Role memberships --- - -GRANT anon TO authenticator GRANTED BY postgres; -GRANT anon TO postgres GRANTED BY supabase_admin; -GRANT anon TO supabase_storage_admin GRANTED BY supabase_admin; -GRANT authenticated TO authenticator GRANTED BY postgres; -GRANT authenticated TO postgres GRANTED BY supabase_admin; -GRANT authenticated TO supabase_storage_admin GRANTED BY supabase_admin; -GRANT pg_monitor TO postgres GRANTED BY supabase_admin; --- GRANT pg_read_all_data TO supabase_read_only_user GRANTED BY postgres; --- GRANT pgsodium_keyholder TO pgsodium_keymaker GRANTED BY postgres; --- GRANT pgsodium_keyholder TO postgres WITH ADMIN OPTION GRANTED BY postgres; --- GRANT pgsodium_keyiduser TO pgsodium_keyholder GRANTED BY postgres; --- GRANT pgsodium_keyiduser TO pgsodium_keymaker GRANTED BY postgres; --- GRANT pgsodium_keyiduser TO postgres WITH ADMIN OPTION GRANTED BY postgres; --- GRANT pgsodium_keymaker TO postgres WITH ADMIN OPTION GRANTED BY postgres; -GRANT service_role TO authenticator GRANTED BY postgres; -GRANT service_role TO postgres GRANTED BY supabase_admin; -GRANT service_role TO supabase_storage_admin GRANTED BY supabase_admin; -GRANT supabase_auth_admin TO postgres GRANTED BY supabase_admin; -GRANT supabase_functions_admin TO postgres GRANTED BY supabase_admin; -GRANT supabase_storage_admin TO postgres GRANTED BY supabase_admin; - - - - --- --- PostgreSQL database cluster dump complete --- - -DO $$ -BEGIN - -- role pg_read_all_data is not available on pg13 - IF EXISTS ( - SELECT FROM pg_catalog.pg_roles - WHERE rolname = 'pg_read_all_data' - ) THEN - GRANT pg_read_all_data TO supabase_read_only_user GRANTED BY postgres; - END IF; -END -$$; - -RESET ALL; diff --git a/apps/cli-go/internal/utils/templates/initial_schemas/13.sql b/apps/cli-go/internal/utils/templates/initial_schemas/13.sql deleted file mode 100644 index 135ce9a516..0000000000 --- a/apps/cli-go/internal/utils/templates/initial_schemas/13.sql +++ /dev/null @@ -1,3094 +0,0 @@ --- --- PostgreSQL database dump --- - --- Dumped from database version 13.3 (Debian 13.3-1.pgdg100+1) --- Dumped by pg_dump version 15.3 - -SET statement_timeout = 0; -SET lock_timeout = 0; -SET idle_in_transaction_session_timeout = 0; -SET client_encoding = 'UTF8'; -SET standard_conforming_strings = on; -SELECT pg_catalog.set_config('search_path', '', false); -SET check_function_bodies = false; -SET xmloption = content; -SET client_min_messages = warning; -SET row_security = off; - --- --- Name: _analytics; Type: SCHEMA; Schema: -; Owner: postgres --- - -CREATE SCHEMA IF NOT EXISTS _analytics; - - -ALTER SCHEMA _analytics OWNER TO postgres; - --- --- Name: _realtime; Type: SCHEMA; Schema: -; Owner: postgres --- - -CREATE SCHEMA IF NOT EXISTS _realtime; - - -ALTER SCHEMA _realtime OWNER TO postgres; - --- --- Name: auth; Type: SCHEMA; Schema: -; Owner: supabase_admin --- - -CREATE SCHEMA IF NOT EXISTS auth; - - -ALTER SCHEMA auth OWNER TO supabase_admin; - --- --- Name: extensions; Type: SCHEMA; Schema: -; Owner: postgres --- - -CREATE SCHEMA IF NOT EXISTS extensions; - - -ALTER SCHEMA extensions OWNER TO postgres; - --- --- Name: graphql_public; Type: SCHEMA; Schema: -; Owner: supabase_admin --- - -CREATE SCHEMA IF NOT EXISTS graphql_public; - - -ALTER SCHEMA graphql_public OWNER TO supabase_admin; - --- --- Name: public; Type: SCHEMA; Schema: -; Owner: postgres --- - --- *not* creating schema, since initdb creates it - - -ALTER SCHEMA public OWNER TO postgres; - --- --- Name: realtime; Type: SCHEMA; Schema: -; Owner: supabase_admin --- - -CREATE SCHEMA IF NOT EXISTS realtime; - - -ALTER SCHEMA realtime OWNER TO supabase_admin; - --- --- Name: storage; Type: SCHEMA; Schema: -; Owner: supabase_admin --- - -CREATE SCHEMA IF NOT EXISTS storage; - - -ALTER SCHEMA storage OWNER TO supabase_admin; - --- --- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA extensions; - - --- --- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions'; - - --- --- Name: pgjwt; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS pgjwt WITH SCHEMA extensions; - - --- --- Name: EXTENSION pgjwt; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION pgjwt IS 'JSON Web Token API for Postgresql'; - - --- --- Name: uuid-ossp; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA extensions; - - --- --- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; - - --- --- Name: aal_level; Type: TYPE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TYPE auth.aal_level AS ENUM ( - 'aal1', - 'aal2', - 'aal3' -); - - -ALTER TYPE auth.aal_level OWNER TO supabase_auth_admin; - --- --- Name: code_challenge_method; Type: TYPE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TYPE auth.code_challenge_method AS ENUM ( - 's256', - 'plain' -); - - -ALTER TYPE auth.code_challenge_method OWNER TO supabase_auth_admin; - --- --- Name: factor_status; Type: TYPE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TYPE auth.factor_status AS ENUM ( - 'unverified', - 'verified' -); - - -ALTER TYPE auth.factor_status OWNER TO supabase_auth_admin; - --- --- Name: factor_type; Type: TYPE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TYPE auth.factor_type AS ENUM ( - 'totp', - 'webauthn' -); - - -ALTER TYPE auth.factor_type OWNER TO supabase_auth_admin; - --- --- Name: email(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin --- - -CREATE OR REPLACE FUNCTION auth.email() RETURNS text - LANGUAGE sql STABLE - AS $$ - select - coalesce( - nullif(current_setting('request.jwt.claim.email', true), ''), - (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'email') - )::text -$$; - - -ALTER FUNCTION auth.email() OWNER TO supabase_auth_admin; - --- --- Name: FUNCTION email(); Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON FUNCTION auth.email() IS 'Deprecated. Use auth.jwt() -> ''email'' instead.'; - - --- --- Name: jwt(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin --- - -CREATE OR REPLACE FUNCTION auth.jwt() RETURNS jsonb - LANGUAGE sql STABLE - AS $$ - select - coalesce( - nullif(current_setting('request.jwt.claim', true), ''), - nullif(current_setting('request.jwt.claims', true), '') - )::jsonb -$$; - - -ALTER FUNCTION auth.jwt() OWNER TO supabase_auth_admin; - --- --- Name: role(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin --- - -CREATE OR REPLACE FUNCTION auth.role() RETURNS text - LANGUAGE sql STABLE - AS $$ - select - coalesce( - nullif(current_setting('request.jwt.claim.role', true), ''), - (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'role') - )::text -$$; - - -ALTER FUNCTION auth.role() OWNER TO supabase_auth_admin; - --- --- Name: FUNCTION role(); Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON FUNCTION auth.role() IS 'Deprecated. Use auth.jwt() -> ''role'' instead.'; - - --- --- Name: uid(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin --- - -CREATE OR REPLACE FUNCTION auth.uid() RETURNS uuid - LANGUAGE sql STABLE - AS $$ - select - coalesce( - nullif(current_setting('request.jwt.claim.sub', true), ''), - (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'sub') - )::uuid -$$; - - -ALTER FUNCTION auth.uid() OWNER TO supabase_auth_admin; - --- --- Name: FUNCTION uid(); Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON FUNCTION auth.uid() IS 'Deprecated. Use auth.jwt() -> ''sub'' instead.'; - - --- --- Name: grant_pg_cron_access(); Type: FUNCTION; Schema: extensions; Owner: postgres --- - -CREATE OR REPLACE FUNCTION extensions.grant_pg_cron_access() RETURNS event_trigger - LANGUAGE plpgsql - AS $$ -DECLARE - schema_is_cron bool; -BEGIN - schema_is_cron = ( - SELECT n.nspname = 'cron' - FROM pg_event_trigger_ddl_commands() AS ev - LEFT JOIN pg_catalog.pg_namespace AS n - ON ev.objid = n.oid - ); - - IF schema_is_cron - THEN - grant usage on schema cron to postgres with grant option; - - alter default privileges in schema cron grant all on tables to postgres with grant option; - alter default privileges in schema cron grant all on functions to postgres with grant option; - alter default privileges in schema cron grant all on sequences to postgres with grant option; - - alter default privileges for user supabase_admin in schema cron grant all - on sequences to postgres with grant option; - alter default privileges for user supabase_admin in schema cron grant all - on tables to postgres with grant option; - alter default privileges for user supabase_admin in schema cron grant all - on functions to postgres with grant option; - - grant all privileges on all tables in schema cron to postgres with grant option; - - END IF; - -END; -$$; - - -ALTER FUNCTION extensions.grant_pg_cron_access() OWNER TO postgres; - --- --- Name: FUNCTION grant_pg_cron_access(); Type: COMMENT; Schema: extensions; Owner: postgres --- - -COMMENT ON FUNCTION extensions.grant_pg_cron_access() IS 'Grants access to pg_cron'; - - --- --- Name: grant_pg_graphql_access(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin --- - -CREATE OR REPLACE FUNCTION extensions.grant_pg_graphql_access() RETURNS event_trigger - LANGUAGE plpgsql - AS $_$ -DECLARE - func_is_graphql_resolve bool; -BEGIN - func_is_graphql_resolve = ( - SELECT n.proname = 'resolve' - FROM pg_event_trigger_ddl_commands() AS ev - LEFT JOIN pg_catalog.pg_proc AS n - ON ev.objid = n.oid - ); - - IF func_is_graphql_resolve - THEN - - -- Update public wrapper to pass all arguments through to the pg_graphql resolve func - create or replace function graphql_public.graphql( - "operationName" text default null, - query text default null, - variables jsonb default null, - extensions jsonb default null - ) - returns jsonb - language sql - as $$ - select graphql.resolve( - query := query, - variables := coalesce(variables, '{}'), - "operationName" := "operationName", - extensions := extensions - ); - $$; - - -- This hook executes when `graphql.resolve` is created. That is not necessarily the last - -- function in the extension so we need to grant permissions on existing entities AND - -- update default permissions to any others that are created after `graphql.resolve` - grant usage on schema graphql to postgres, anon, authenticated, service_role; - grant select on all tables in schema graphql to postgres, anon, authenticated, service_role; - grant execute on all functions in schema graphql to postgres, anon, authenticated, service_role; - grant all on all sequences in schema graphql to postgres, anon, authenticated, service_role; - alter default privileges in schema graphql grant all on tables to postgres, anon, authenticated, service_role; - alter default privileges in schema graphql grant all on functions to postgres, anon, authenticated, service_role; - alter default privileges in schema graphql grant all on sequences to postgres, anon, authenticated, service_role; - END IF; - -END; -$_$; - - -ALTER FUNCTION extensions.grant_pg_graphql_access() OWNER TO supabase_admin; - --- --- Name: FUNCTION grant_pg_graphql_access(); Type: COMMENT; Schema: extensions; Owner: supabase_admin --- - -COMMENT ON FUNCTION extensions.grant_pg_graphql_access() IS 'Grants access to pg_graphql'; - - --- --- Name: grant_pg_net_access(); Type: FUNCTION; Schema: extensions; Owner: postgres --- - -CREATE OR REPLACE FUNCTION extensions.grant_pg_net_access() RETURNS event_trigger - LANGUAGE plpgsql - AS $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM pg_event_trigger_ddl_commands() AS ev - JOIN pg_extension AS ext - ON ev.objid = ext.oid - WHERE ext.extname = 'pg_net' - ) - THEN - IF NOT EXISTS ( - SELECT 1 - FROM pg_roles - WHERE rolname = 'supabase_functions_admin' - ) - THEN - CREATE USER supabase_functions_admin NOINHERIT CREATEROLE LOGIN NOREPLICATION; - END IF; - - GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; - - ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; - ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; - ALTER function net.http_collect_response(request_id bigint, async boolean) SECURITY DEFINER; - - ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; - ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; - ALTER function net.http_collect_response(request_id bigint, async boolean) SET search_path = net; - - REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; - REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; - REVOKE ALL ON FUNCTION net.http_collect_response(request_id bigint, async boolean) FROM PUBLIC; - - GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; - GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; - GRANT EXECUTE ON FUNCTION net.http_collect_response(request_id bigint, async boolean) TO supabase_functions_admin, postgres, anon, authenticated, service_role; - END IF; -END; -$$; - - -ALTER FUNCTION extensions.grant_pg_net_access() OWNER TO postgres; - --- --- Name: FUNCTION grant_pg_net_access(); Type: COMMENT; Schema: extensions; Owner: postgres --- - -COMMENT ON FUNCTION extensions.grant_pg_net_access() IS 'Grants access to pg_net'; - - --- --- Name: pgrst_ddl_watch(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin --- - -CREATE OR REPLACE FUNCTION extensions.pgrst_ddl_watch() RETURNS event_trigger - LANGUAGE plpgsql - AS $$ -DECLARE - cmd record; -BEGIN - FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands() - LOOP - IF cmd.command_tag IN ( - 'CREATE SCHEMA', 'ALTER SCHEMA' - , 'CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO', 'ALTER TABLE' - , 'CREATE FOREIGN TABLE', 'ALTER FOREIGN TABLE' - , 'CREATE VIEW', 'ALTER VIEW' - , 'CREATE MATERIALIZED VIEW', 'ALTER MATERIALIZED VIEW' - , 'CREATE FUNCTION', 'ALTER FUNCTION' - , 'CREATE TRIGGER' - , 'CREATE TYPE', 'ALTER TYPE' - , 'CREATE RULE' - , 'COMMENT' - ) - -- don't notify in case of CREATE TEMP table or other objects created on pg_temp - AND cmd.schema_name is distinct from 'pg_temp' - THEN - NOTIFY pgrst, 'reload schema'; - END IF; - END LOOP; -END; $$; - - -ALTER FUNCTION extensions.pgrst_ddl_watch() OWNER TO supabase_admin; - --- --- Name: pgrst_drop_watch(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin --- - -CREATE OR REPLACE FUNCTION extensions.pgrst_drop_watch() RETURNS event_trigger - LANGUAGE plpgsql - AS $$ -DECLARE - obj record; -BEGIN - FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() - LOOP - IF obj.object_type IN ( - 'schema' - , 'table' - , 'foreign table' - , 'view' - , 'materialized view' - , 'function' - , 'trigger' - , 'type' - , 'rule' - ) - AND obj.is_temporary IS false -- no pg_temp objects - THEN - NOTIFY pgrst, 'reload schema'; - END IF; - END LOOP; -END; $$; - - -ALTER FUNCTION extensions.pgrst_drop_watch() OWNER TO supabase_admin; - --- --- Name: set_graphql_placeholder(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin --- - -CREATE OR REPLACE FUNCTION extensions.set_graphql_placeholder() RETURNS event_trigger - LANGUAGE plpgsql - AS $_$ - DECLARE - graphql_is_dropped bool; - BEGIN - graphql_is_dropped = ( - SELECT ev.schema_name = 'graphql_public' - FROM pg_event_trigger_dropped_objects() AS ev - WHERE ev.schema_name = 'graphql_public' - ); - - IF graphql_is_dropped - THEN - create or replace function graphql_public.graphql( - "operationName" text default null, - query text default null, - variables jsonb default null, - extensions jsonb default null - ) - returns jsonb - language plpgsql - as $$ - DECLARE - server_version float; - BEGIN - server_version = (SELECT (SPLIT_PART((select version()), ' ', 2))::float); - - IF server_version >= 14 THEN - RETURN jsonb_build_object( - 'errors', jsonb_build_array( - jsonb_build_object( - 'message', 'pg_graphql extension is not enabled.' - ) - ) - ); - ELSE - RETURN jsonb_build_object( - 'errors', jsonb_build_array( - jsonb_build_object( - 'message', 'pg_graphql is only available on projects running Postgres 14 onwards.' - ) - ) - ); - END IF; - END; - $$; - END IF; - - END; -$_$; - - -ALTER FUNCTION extensions.set_graphql_placeholder() OWNER TO supabase_admin; - --- --- Name: FUNCTION set_graphql_placeholder(); Type: COMMENT; Schema: extensions; Owner: supabase_admin --- - -COMMENT ON FUNCTION extensions.set_graphql_placeholder() IS 'Reintroduces placeholder function for graphql_public.graphql'; - - --- --- Name: graphql(text, text, jsonb, jsonb); Type: FUNCTION; Schema: graphql_public; Owner: supabase_admin --- - -CREATE OR REPLACE FUNCTION graphql_public.graphql("operationName" text DEFAULT NULL::text, query text DEFAULT NULL::text, variables jsonb DEFAULT NULL::jsonb, extensions jsonb DEFAULT NULL::jsonb) RETURNS jsonb - LANGUAGE plpgsql - AS $$ - DECLARE - server_version float; - BEGIN - server_version = (SELECT (SPLIT_PART((select version()), ' ', 2))::float); - - IF server_version >= 14 THEN - RETURN jsonb_build_object( - 'errors', jsonb_build_array( - jsonb_build_object( - 'message', 'pg_graphql extension is not enabled.' - ) - ) - ); - ELSE - RETURN jsonb_build_object( - 'errors', jsonb_build_array( - jsonb_build_object( - 'message', 'pg_graphql is only available on projects running Postgres 14 onwards.' - ) - ) - ); - END IF; - END; -$$; - - -ALTER FUNCTION graphql_public.graphql("operationName" text, query text, variables jsonb, extensions jsonb) OWNER TO supabase_admin; - --- --- Name: can_insert_object(text, text, uuid, jsonb); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.can_insert_object(bucketid text, name text, owner uuid, metadata jsonb) RETURNS void - LANGUAGE plpgsql - AS $$ -BEGIN - INSERT INTO "storage"."objects" ("bucket_id", "name", "owner", "metadata") VALUES (bucketid, name, owner, metadata); - -- hack to rollback the successful insert - RAISE sqlstate 'PT200' using - message = 'ROLLBACK', - detail = 'rollback successful insert'; -END -$$; - - -ALTER FUNCTION storage.can_insert_object(bucketid text, name text, owner uuid, metadata jsonb) OWNER TO supabase_storage_admin; - --- --- Name: extension(text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.extension(name text) RETURNS text - LANGUAGE plpgsql - AS $$ -DECLARE -_parts text[]; -_filename text; -BEGIN - select string_to_array(name, '/') into _parts; - select _parts[array_length(_parts,1)] into _filename; - -- @todo return the last part instead of 2 - return split_part(_filename, '.', 2); -END -$$; - - -ALTER FUNCTION storage.extension(name text) OWNER TO supabase_storage_admin; - --- --- Name: filename(text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.filename(name text) RETURNS text - LANGUAGE plpgsql - AS $$ -DECLARE -_parts text[]; -BEGIN - select string_to_array(name, '/') into _parts; - return _parts[array_length(_parts,1)]; -END -$$; - - -ALTER FUNCTION storage.filename(name text) OWNER TO supabase_storage_admin; - --- --- Name: foldername(text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.foldername(name text) RETURNS text[] - LANGUAGE plpgsql - AS $$ -DECLARE -_parts text[]; -BEGIN - select string_to_array(name, '/') into _parts; - return _parts[1:array_length(_parts,1)-1]; -END -$$; - - -ALTER FUNCTION storage.foldername(name text) OWNER TO supabase_storage_admin; - --- --- Name: get_size_by_bucket(); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.get_size_by_bucket() RETURNS TABLE(size bigint, bucket_id text) - LANGUAGE plpgsql - AS $$ -BEGIN - return query - select sum((metadata->>'size')::int) as size, obj.bucket_id - from "storage".objects as obj - group by obj.bucket_id; -END -$$; - - -ALTER FUNCTION storage.get_size_by_bucket() OWNER TO supabase_storage_admin; - --- --- Name: search(text, text, integer, integer, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.search(prefix text, bucketname text, limits integer DEFAULT 100, levels integer DEFAULT 1, offsets integer DEFAULT 0, search text DEFAULT ''::text, sortcolumn text DEFAULT 'name'::text, sortorder text DEFAULT 'asc'::text) RETURNS TABLE(name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) - LANGUAGE plpgsql STABLE - AS $_$ -declare - v_order_by text; - v_sort_order text; -begin - case - when sortcolumn = 'name' then - v_order_by = 'name'; - when sortcolumn = 'updated_at' then - v_order_by = 'updated_at'; - when sortcolumn = 'created_at' then - v_order_by = 'created_at'; - when sortcolumn = 'last_accessed_at' then - v_order_by = 'last_accessed_at'; - else - v_order_by = 'name'; - end case; - - case - when sortorder = 'asc' then - v_sort_order = 'asc'; - when sortorder = 'desc' then - v_sort_order = 'desc'; - else - v_sort_order = 'asc'; - end case; - - v_order_by = v_order_by || ' ' || v_sort_order; - - return query execute - 'with folders as ( - select path_tokens[$1] as folder - from storage.objects - where objects.name ilike $2 || $3 || ''%'' - and bucket_id = $4 - and array_length(regexp_split_to_array(objects.name, ''/''), 1) <> $1 - group by folder - order by folder ' || v_sort_order || ' - ) - (select folder as "name", - null as id, - null as updated_at, - null as created_at, - null as last_accessed_at, - null as metadata from folders) - union all - (select path_tokens[$1] as "name", - id, - updated_at, - created_at, - last_accessed_at, - metadata - from storage.objects - where objects.name ilike $2 || $3 || ''%'' - and bucket_id = $4 - and array_length(regexp_split_to_array(objects.name, ''/''), 1) = $1 - order by ' || v_order_by || ') - limit $5 - offset $6' using levels, prefix, search, bucketname, limits, offsets; -end; -$_$; - - -ALTER FUNCTION storage.search(prefix text, bucketname text, limits integer, levels integer, offsets integer, search text, sortcolumn text, sortorder text) OWNER TO supabase_storage_admin; - --- --- Name: update_updated_at_column(); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.update_updated_at_column() RETURNS trigger - LANGUAGE plpgsql - AS $$ -BEGIN - NEW.updated_at = now(); - RETURN NEW; -END; -$$; - - -ALTER FUNCTION storage.update_updated_at_column() OWNER TO supabase_storage_admin; - -SET default_tablespace = ''; - -SET default_table_access_method = heap; - --- --- Name: extensions; Type: TABLE; Schema: _realtime; Owner: postgres --- - -CREATE TABLE IF NOT EXISTS _realtime.extensions ( - id uuid NOT NULL, - type text, - settings jsonb, - tenant_external_id text, - inserted_at timestamp(0) without time zone NOT NULL, - updated_at timestamp(0) without time zone NOT NULL -); - - -ALTER TABLE _realtime.extensions OWNER TO postgres; - --- --- Name: schema_migrations; Type: TABLE; Schema: _realtime; Owner: postgres --- - -CREATE TABLE IF NOT EXISTS _realtime.schema_migrations ( - version bigint NOT NULL, - inserted_at timestamp(0) without time zone -); - - -ALTER TABLE _realtime.schema_migrations OWNER TO postgres; - --- --- Name: tenants; Type: TABLE; Schema: _realtime; Owner: postgres --- - -CREATE TABLE IF NOT EXISTS _realtime.tenants ( - id uuid NOT NULL, - name text, - external_id text, - jwt_secret text, - max_concurrent_users integer DEFAULT 200 NOT NULL, - inserted_at timestamp(0) without time zone NOT NULL, - updated_at timestamp(0) without time zone NOT NULL, - max_events_per_second integer DEFAULT 100 NOT NULL, - postgres_cdc_default text DEFAULT 'postgres_cdc_rls'::text, - max_bytes_per_second integer DEFAULT 100000 NOT NULL, - max_channels_per_client integer DEFAULT 100 NOT NULL, - max_joins_per_second integer DEFAULT 500 NOT NULL, - suspend boolean DEFAULT false -); - - -ALTER TABLE _realtime.tenants OWNER TO postgres; - --- --- Name: audit_log_entries; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.audit_log_entries ( - instance_id uuid, - id uuid NOT NULL, - payload json, - created_at timestamp with time zone, - ip_address character varying(64) DEFAULT ''::character varying NOT NULL -); - - -ALTER TABLE auth.audit_log_entries OWNER TO supabase_auth_admin; - --- --- Name: TABLE audit_log_entries; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.audit_log_entries IS 'Auth: Audit trail for user actions.'; - - --- --- Name: flow_state; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.flow_state ( - id uuid NOT NULL, - user_id uuid, - auth_code text NOT NULL, - code_challenge_method auth.code_challenge_method NOT NULL, - code_challenge text NOT NULL, - provider_type text NOT NULL, - provider_access_token text, - provider_refresh_token text, - created_at timestamp with time zone, - updated_at timestamp with time zone, - authentication_method text NOT NULL -); - - -ALTER TABLE auth.flow_state OWNER TO supabase_auth_admin; - --- --- Name: TABLE flow_state; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.flow_state IS 'stores metadata for pkce logins'; - - --- --- Name: identities; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.identities ( - provider_id text NOT NULL, - user_id uuid NOT NULL, - identity_data jsonb NOT NULL, - provider text NOT NULL, - last_sign_in_at timestamp with time zone, - created_at timestamp with time zone, - updated_at timestamp with time zone, - email text GENERATED ALWAYS AS (lower((identity_data ->> 'email'::text))) STORED, - id uuid DEFAULT gen_random_uuid() NOT NULL -); - - -ALTER TABLE auth.identities OWNER TO supabase_auth_admin; - --- --- Name: TABLE identities; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.identities IS 'Auth: Stores identities associated to a user.'; - - --- --- Name: COLUMN identities.email; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON COLUMN auth.identities.email IS 'Auth: Email is a generated column that references the optional email property in the identity_data'; - - --- --- Name: instances; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.instances ( - id uuid NOT NULL, - uuid uuid, - raw_base_config text, - created_at timestamp with time zone, - updated_at timestamp with time zone -); - - -ALTER TABLE auth.instances OWNER TO supabase_auth_admin; - --- --- Name: TABLE instances; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.instances IS 'Auth: Manages users across multiple sites.'; - - --- --- Name: mfa_amr_claims; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.mfa_amr_claims ( - session_id uuid NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - authentication_method text NOT NULL, - id uuid NOT NULL -); - - -ALTER TABLE auth.mfa_amr_claims OWNER TO supabase_auth_admin; - --- --- Name: TABLE mfa_amr_claims; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.mfa_amr_claims IS 'auth: stores authenticator method reference claims for multi factor authentication'; - - --- --- Name: mfa_challenges; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.mfa_challenges ( - id uuid NOT NULL, - factor_id uuid NOT NULL, - created_at timestamp with time zone NOT NULL, - verified_at timestamp with time zone, - ip_address inet NOT NULL -); - - -ALTER TABLE auth.mfa_challenges OWNER TO supabase_auth_admin; - --- --- Name: TABLE mfa_challenges; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.mfa_challenges IS 'auth: stores metadata about challenge requests made'; - - --- --- Name: mfa_factors; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.mfa_factors ( - id uuid NOT NULL, - user_id uuid NOT NULL, - friendly_name text, - factor_type auth.factor_type NOT NULL, - status auth.factor_status NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - secret text -); - - -ALTER TABLE auth.mfa_factors OWNER TO supabase_auth_admin; - --- --- Name: TABLE mfa_factors; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.mfa_factors IS 'auth: stores metadata about factors'; - - --- --- Name: refresh_tokens; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.refresh_tokens ( - instance_id uuid, - id bigint NOT NULL, - token character varying(255), - user_id character varying(255), - revoked boolean, - created_at timestamp with time zone, - updated_at timestamp with time zone, - parent character varying(255), - session_id uuid -); - - -ALTER TABLE auth.refresh_tokens OWNER TO supabase_auth_admin; - --- --- Name: TABLE refresh_tokens; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.refresh_tokens IS 'Auth: Store of tokens used to refresh JWT tokens once they expire.'; - - --- --- Name: refresh_tokens_id_seq; Type: SEQUENCE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE SEQUENCE IF NOT EXISTS auth.refresh_tokens_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - -ALTER TABLE auth.refresh_tokens_id_seq OWNER TO supabase_auth_admin; - --- --- Name: refresh_tokens_id_seq; Type: SEQUENCE OWNED BY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER SEQUENCE auth.refresh_tokens_id_seq OWNED BY auth.refresh_tokens.id; - - --- --- Name: saml_providers; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.saml_providers ( - id uuid NOT NULL, - sso_provider_id uuid NOT NULL, - entity_id text NOT NULL, - metadata_xml text NOT NULL, - metadata_url text, - attribute_mapping jsonb, - created_at timestamp with time zone, - updated_at timestamp with time zone, - CONSTRAINT "entity_id not empty" CHECK ((char_length(entity_id) > 0)), - CONSTRAINT "metadata_url not empty" CHECK (((metadata_url = NULL::text) OR (char_length(metadata_url) > 0))), - CONSTRAINT "metadata_xml not empty" CHECK ((char_length(metadata_xml) > 0)) -); - - -ALTER TABLE auth.saml_providers OWNER TO supabase_auth_admin; - --- --- Name: TABLE saml_providers; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.saml_providers IS 'Auth: Manages SAML Identity Provider connections.'; - - --- --- Name: saml_relay_states; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.saml_relay_states ( - id uuid NOT NULL, - sso_provider_id uuid NOT NULL, - request_id text NOT NULL, - for_email text, - redirect_to text, - from_ip_address inet, - created_at timestamp with time zone, - updated_at timestamp with time zone, - flow_state_id uuid, - CONSTRAINT "request_id not empty" CHECK ((char_length(request_id) > 0)) -); - - -ALTER TABLE auth.saml_relay_states OWNER TO supabase_auth_admin; - --- --- Name: TABLE saml_relay_states; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.saml_relay_states IS 'Auth: Contains SAML Relay State information for each Service Provider initiated login.'; - - --- --- Name: schema_migrations; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.schema_migrations ( - version character varying(255) NOT NULL -); - - -ALTER TABLE auth.schema_migrations OWNER TO supabase_auth_admin; - --- --- Name: TABLE schema_migrations; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.schema_migrations IS 'Auth: Manages updates to the auth system.'; - - --- --- Name: sessions; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.sessions ( - id uuid NOT NULL, - user_id uuid NOT NULL, - created_at timestamp with time zone, - updated_at timestamp with time zone, - factor_id uuid, - aal auth.aal_level, - not_after timestamp with time zone, - refreshed_at timestamp without time zone, - user_agent text, - ip inet, - tag text -); - - -ALTER TABLE auth.sessions OWNER TO supabase_auth_admin; - --- --- Name: TABLE sessions; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.sessions IS 'Auth: Stores session data associated to a user.'; - - --- --- Name: COLUMN sessions.not_after; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON COLUMN auth.sessions.not_after IS 'Auth: Not after is a nullable column that contains a timestamp after which the session should be regarded as expired.'; - - --- --- Name: sso_domains; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.sso_domains ( - id uuid NOT NULL, - sso_provider_id uuid NOT NULL, - domain text NOT NULL, - created_at timestamp with time zone, - updated_at timestamp with time zone, - CONSTRAINT "domain not empty" CHECK ((char_length(domain) > 0)) -); - - -ALTER TABLE auth.sso_domains OWNER TO supabase_auth_admin; - --- --- Name: TABLE sso_domains; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.sso_domains IS 'Auth: Manages SSO email address domain mapping to an SSO Identity Provider.'; - - --- --- Name: sso_providers; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.sso_providers ( - id uuid NOT NULL, - resource_id text, - created_at timestamp with time zone, - updated_at timestamp with time zone, - CONSTRAINT "resource_id not empty" CHECK (((resource_id = NULL::text) OR (char_length(resource_id) > 0))) -); - - -ALTER TABLE auth.sso_providers OWNER TO supabase_auth_admin; - --- --- Name: TABLE sso_providers; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.sso_providers IS 'Auth: Manages SSO identity provider information; see saml_providers for SAML.'; - - --- --- Name: COLUMN sso_providers.resource_id; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON COLUMN auth.sso_providers.resource_id IS 'Auth: Uniquely identifies a SSO provider according to a user-chosen resource ID (case insensitive), useful in infrastructure as code.'; - - --- --- Name: users; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.users ( - instance_id uuid, - id uuid NOT NULL, - aud character varying(255), - role character varying(255), - email character varying(255), - encrypted_password character varying(255), - email_confirmed_at timestamp with time zone, - invited_at timestamp with time zone, - confirmation_token character varying(255), - confirmation_sent_at timestamp with time zone, - recovery_token character varying(255), - recovery_sent_at timestamp with time zone, - email_change_token_new character varying(255), - email_change character varying(255), - email_change_sent_at timestamp with time zone, - last_sign_in_at timestamp with time zone, - raw_app_meta_data jsonb, - raw_user_meta_data jsonb, - is_super_admin boolean, - created_at timestamp with time zone, - updated_at timestamp with time zone, - phone text DEFAULT NULL::character varying, - phone_confirmed_at timestamp with time zone, - phone_change text DEFAULT ''::character varying, - phone_change_token character varying(255) DEFAULT ''::character varying, - phone_change_sent_at timestamp with time zone, - confirmed_at timestamp with time zone GENERATED ALWAYS AS (LEAST(email_confirmed_at, phone_confirmed_at)) STORED, - email_change_token_current character varying(255) DEFAULT ''::character varying, - email_change_confirm_status smallint DEFAULT 0, - banned_until timestamp with time zone, - reauthentication_token character varying(255) DEFAULT ''::character varying, - reauthentication_sent_at timestamp with time zone, - is_sso_user boolean DEFAULT false NOT NULL, - deleted_at timestamp with time zone, - CONSTRAINT users_email_change_confirm_status_check CHECK (((email_change_confirm_status >= 0) AND (email_change_confirm_status <= 2))) -); - - -ALTER TABLE auth.users OWNER TO supabase_auth_admin; - --- --- Name: TABLE users; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.users IS 'Auth: Stores user login data within a secure schema.'; - - --- --- Name: COLUMN users.is_sso_user; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON COLUMN auth.users.is_sso_user IS 'Auth: Set this column to true when the account comes from SSO. These accounts can have duplicate emails.'; - - --- --- Name: buckets; Type: TABLE; Schema: storage; Owner: supabase_storage_admin --- - -CREATE TABLE IF NOT EXISTS storage.buckets ( - id text NOT NULL, - name text NOT NULL, - owner uuid, - created_at timestamp with time zone DEFAULT now(), - updated_at timestamp with time zone DEFAULT now(), - public boolean DEFAULT false, - avif_autodetection boolean DEFAULT false, - file_size_limit bigint, - allowed_mime_types text[], - owner_id text -); - - -ALTER TABLE storage.buckets OWNER TO supabase_storage_admin; - --- --- Name: COLUMN buckets.owner; Type: COMMENT; Schema: storage; Owner: supabase_storage_admin --- - -COMMENT ON COLUMN storage.buckets.owner IS 'Field is deprecated, use owner_id instead'; - - --- --- Name: migrations; Type: TABLE; Schema: storage; Owner: supabase_storage_admin --- - -CREATE TABLE IF NOT EXISTS storage.migrations ( - id integer NOT NULL, - name character varying(100) NOT NULL, - hash character varying(40) NOT NULL, - executed_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP -); - - -ALTER TABLE storage.migrations OWNER TO supabase_storage_admin; - --- --- Name: objects; Type: TABLE; Schema: storage; Owner: supabase_storage_admin --- - -CREATE TABLE IF NOT EXISTS storage.objects ( - id uuid DEFAULT extensions.uuid_generate_v4() NOT NULL, - bucket_id text, - name text, - owner uuid, - created_at timestamp with time zone DEFAULT now(), - updated_at timestamp with time zone DEFAULT now(), - last_accessed_at timestamp with time zone DEFAULT now(), - metadata jsonb, - path_tokens text[] GENERATED ALWAYS AS (string_to_array(name, '/'::text)) STORED, - version text, - owner_id text -); - - -ALTER TABLE storage.objects OWNER TO supabase_storage_admin; - --- --- Name: COLUMN objects.owner; Type: COMMENT; Schema: storage; Owner: supabase_storage_admin --- - -COMMENT ON COLUMN storage.objects.owner IS 'Field is deprecated, use owner_id instead'; - - --- --- Name: refresh_tokens id; Type: DEFAULT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.refresh_tokens ALTER COLUMN id SET DEFAULT nextval('auth.refresh_tokens_id_seq'::regclass); - - --- --- Data for Name: extensions; Type: TABLE DATA; Schema: _realtime; Owner: postgres --- - -INSERT INTO _realtime.extensions (id, type, settings, tenant_external_id, inserted_at, updated_at) VALUES ('5518e2da-548e-499c-ab1c-664fd6446714', 'postgres_cdc_rls', '{"region": "us-east-1", "db_host": "ABK7kBu27y/PVdL10i/b+A==", "db_name": "sWBpZNdjggEPTQVlI52Zfw==", "db_port": "+enMDFi1J/3IrrquHHwUmA==", "db_user": "uxbEq/zz8DXVD53TOI1zmw==", "slot_name": "supabase_realtime_replication_slot", "ip_version": 4, "db_password": "sWBpZNdjggEPTQVlI52Zfw==", "publication": "supabase_realtime", "ssl_enforced": false, "poll_interval_ms": 100, "poll_max_changes": 100, "poll_max_record_bytes": 1048576}', 'realtime-dev', '2023-12-12 15:25:21', '2023-12-12 15:25:21'); - - --- --- Data for Name: schema_migrations; Type: TABLE DATA; Schema: _realtime; Owner: postgres --- - -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20210706140551, '2023-01-09 07:15:13'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220329161857, '2023-01-09 07:15:13'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220410212326, '2023-01-09 07:15:13'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220506102948, '2023-01-09 07:15:13'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220527210857, '2023-01-09 07:15:13'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220815211129, '2023-01-09 07:15:13'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220815215024, '2023-01-09 07:15:13'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220818141501, '2023-01-09 07:15:13'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20221018173709, '2023-01-09 07:15:13'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20221102172703, '2023-01-09 07:15:13'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20221223010058, '2023-01-09 07:15:13'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20230110180046, '2023-04-06 09:25:28'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20230810220907, '2023-12-12 15:25:19'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20230810220924, '2023-12-12 15:25:19'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20231024094642, '2023-12-12 15:25:19'); - - --- --- Data for Name: tenants; Type: TABLE DATA; Schema: _realtime; Owner: postgres --- - -INSERT INTO _realtime.tenants (id, name, external_id, jwt_secret, max_concurrent_users, inserted_at, updated_at, max_events_per_second, postgres_cdc_default, max_bytes_per_second, max_channels_per_client, max_joins_per_second, suspend) VALUES ('e6edd492-6586-4c5d-9c2e-4e4de2e5c64d', 'realtime-dev', 'realtime-dev', 'iNjicxc4+llvc9wovDvqymwfnj9teWMlyOIbJ8Fh6j2WNU8CIJ2ZgjR6MUIKqSmeDmvpsKLsZ9jgXJmQPpwL8w==', 200, '2023-12-12 15:25:21', '2023-12-12 15:25:21', 100, 'postgres_cdc_rls', 100000, 100, 100, false); - - --- --- Data for Name: audit_log_entries; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: flow_state; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: identities; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: instances; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: mfa_amr_claims; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: mfa_challenges; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: mfa_factors; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: refresh_tokens; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: saml_providers; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: saml_relay_states; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: schema_migrations; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - -INSERT INTO auth.schema_migrations (version) VALUES ('20171026211738'); -INSERT INTO auth.schema_migrations (version) VALUES ('20171026211808'); -INSERT INTO auth.schema_migrations (version) VALUES ('20171026211834'); -INSERT INTO auth.schema_migrations (version) VALUES ('20180103212743'); -INSERT INTO auth.schema_migrations (version) VALUES ('20180108183307'); -INSERT INTO auth.schema_migrations (version) VALUES ('20180119214651'); -INSERT INTO auth.schema_migrations (version) VALUES ('20180125194653'); -INSERT INTO auth.schema_migrations (version) VALUES ('00'); -INSERT INTO auth.schema_migrations (version) VALUES ('20210710035447'); -INSERT INTO auth.schema_migrations (version) VALUES ('20210722035447'); -INSERT INTO auth.schema_migrations (version) VALUES ('20210730183235'); -INSERT INTO auth.schema_migrations (version) VALUES ('20210909172000'); -INSERT INTO auth.schema_migrations (version) VALUES ('20210927181326'); -INSERT INTO auth.schema_migrations (version) VALUES ('20211122151130'); -INSERT INTO auth.schema_migrations (version) VALUES ('20211124214934'); -INSERT INTO auth.schema_migrations (version) VALUES ('20211202183645'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220114185221'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220114185340'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220224000811'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220323170000'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220429102000'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220531120530'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220614074223'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220811173540'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221003041349'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221003041400'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221011041400'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221020193600'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221021073300'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221021082433'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221027105023'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221114143122'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221114143410'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221125140132'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221208132122'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221215195500'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221215195800'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221215195900'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230116124310'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230116124412'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230131181311'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230322519590'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230402418590'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230411005111'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230508135423'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230523124323'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230818113222'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230914180801'); -INSERT INTO auth.schema_migrations (version) VALUES ('20231027141322'); -INSERT INTO auth.schema_migrations (version) VALUES ('20231114161723'); -INSERT INTO auth.schema_migrations (version) VALUES ('20231117164230'); - - --- --- Data for Name: sessions; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: sso_domains; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: sso_providers; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: users; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: buckets; Type: TABLE DATA; Schema: storage; Owner: supabase_storage_admin --- - - - --- --- Data for Name: migrations; Type: TABLE DATA; Schema: storage; Owner: supabase_storage_admin --- - -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (0, 'create-migrations-table', 'e18db593bcde2aca2a408c4d1100f6abba2195df', '2022-04-14 09:52:39.214681'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (1, 'initialmigration', '6ab16121fbaa08bbd11b712d05f358f9b555d777', '2022-04-14 09:52:39.255149'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (2, 'pathtoken-column', '49756be03be4c17bb85fe70d4a861f27de7e49ad', '2022-04-14 09:52:39.276667'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (3, 'add-migrations-rls', 'bb5d124c53d68635a883e399426c6a5a25fc893d', '2022-04-14 09:52:39.587158'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (4, 'add-size-functions', '6d79007d04f5acd288c9c250c42d2d5fd286c54d', '2022-04-14 09:52:39.621747'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (5, 'change-column-name-in-get-size', 'fd65688505d2ffa9fbdc58a944348dd8604d688c', '2022-04-14 09:52:39.658796'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (6, 'add-rls-to-buckets', '63e2bab75a2040fee8e3fb3f15a0d26f3380e9b6', '2022-04-14 09:52:39.702011'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (7, 'add-public-to-buckets', '82568934f8a4d9e0a85f126f6fb483ad8214c418', '2022-04-14 09:52:39.739731'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (8, 'fix-search-function', '1a43a40eddb525f2e2f26efd709e6c06e58e059c', '2022-04-14 09:52:39.781062'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (9, 'search-files-search-function', '34c096597eb8b9d077fdfdde9878c88501b2fafc', '2022-08-23 04:48:07.728336'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (10, 'add-trigger-to-auto-update-updated_at-column', '37d6bb964a70a822e6d37f22f457b9bca7885928', '2022-08-23 04:48:07.736527'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (11, 'add-automatic-avif-detection-flag', 'bd76c53a9c564c80d98d119c1b3a28e16c8152db', '2023-04-06 09:25:29.356743'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (12, 'add-bucket-custom-limits', 'cbe0a4c32a0e891554a21020433b7a4423c07ee7', '2023-04-06 09:25:29.390303'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (13, 'use-bytes-for-max-size', '7a158ebce8a0c2801c9c65b7e9b2f98f68b3874e', '2023-04-06 09:25:29.402124'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (14, 'add-can-insert-object-function', '273193826bca7e0990b458d1ba72f8aa27c0d825', '2023-04-06 09:25:29.426206'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (15, 'add-version', 'e821a779d26612899b8c2dfe20245f904a327c4f', '2023-04-06 09:25:29.439561'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (16, 'drop-owner-foreign-key', '536b33f8878eed09d0144219777dcac96bdb25da', '2023-12-12 15:25:21.258059'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (17, 'add_owner_id_column_deprecate_owner', '7545f216a39358b5487df75d941d05dbcd75eb46', '2023-12-12 15:25:21.271667'); - - --- --- Data for Name: objects; Type: TABLE DATA; Schema: storage; Owner: supabase_storage_admin --- - - - --- --- Name: refresh_tokens_id_seq; Type: SEQUENCE SET; Schema: auth; Owner: supabase_auth_admin --- - -SELECT pg_catalog.setval('auth.refresh_tokens_id_seq', 1, false); - - --- --- Name: extensions extensions_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: postgres --- - -ALTER TABLE ONLY _realtime.extensions - ADD CONSTRAINT extensions_pkey PRIMARY KEY (id); - - --- --- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: postgres --- - -ALTER TABLE ONLY _realtime.schema_migrations - ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); - - --- --- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: postgres --- - -ALTER TABLE ONLY _realtime.tenants - ADD CONSTRAINT tenants_pkey PRIMARY KEY (id); - - --- --- Name: mfa_amr_claims amr_id_pk; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_amr_claims - ADD CONSTRAINT amr_id_pk PRIMARY KEY (id); - - --- --- Name: audit_log_entries audit_log_entries_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.audit_log_entries - ADD CONSTRAINT audit_log_entries_pkey PRIMARY KEY (id); - - --- --- Name: flow_state flow_state_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.flow_state - ADD CONSTRAINT flow_state_pkey PRIMARY KEY (id); - - --- --- Name: identities identities_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.identities - ADD CONSTRAINT identities_pkey PRIMARY KEY (id); - - --- --- Name: identities identities_provider_id_provider_unique; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.identities - ADD CONSTRAINT identities_provider_id_provider_unique UNIQUE (provider_id, provider); - - --- --- Name: instances instances_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.instances - ADD CONSTRAINT instances_pkey PRIMARY KEY (id); - - --- --- Name: mfa_amr_claims mfa_amr_claims_session_id_authentication_method_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_amr_claims - ADD CONSTRAINT mfa_amr_claims_session_id_authentication_method_pkey UNIQUE (session_id, authentication_method); - - --- --- Name: mfa_challenges mfa_challenges_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_challenges - ADD CONSTRAINT mfa_challenges_pkey PRIMARY KEY (id); - - --- --- Name: mfa_factors mfa_factors_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_factors - ADD CONSTRAINT mfa_factors_pkey PRIMARY KEY (id); - - --- --- Name: refresh_tokens refresh_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.refresh_tokens - ADD CONSTRAINT refresh_tokens_pkey PRIMARY KEY (id); - - --- --- Name: refresh_tokens refresh_tokens_token_unique; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.refresh_tokens - ADD CONSTRAINT refresh_tokens_token_unique UNIQUE (token); - - --- --- Name: saml_providers saml_providers_entity_id_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_providers - ADD CONSTRAINT saml_providers_entity_id_key UNIQUE (entity_id); - - --- --- Name: saml_providers saml_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_providers - ADD CONSTRAINT saml_providers_pkey PRIMARY KEY (id); - - --- --- Name: saml_relay_states saml_relay_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_relay_states - ADD CONSTRAINT saml_relay_states_pkey PRIMARY KEY (id); - - --- --- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.schema_migrations - ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); - - --- --- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sessions - ADD CONSTRAINT sessions_pkey PRIMARY KEY (id); - - --- --- Name: sso_domains sso_domains_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sso_domains - ADD CONSTRAINT sso_domains_pkey PRIMARY KEY (id); - - --- --- Name: sso_providers sso_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sso_providers - ADD CONSTRAINT sso_providers_pkey PRIMARY KEY (id); - - --- --- Name: users users_phone_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.users - ADD CONSTRAINT users_phone_key UNIQUE (phone); - - --- --- Name: users users_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.users - ADD CONSTRAINT users_pkey PRIMARY KEY (id); - - --- --- Name: buckets buckets_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.buckets - ADD CONSTRAINT buckets_pkey PRIMARY KEY (id); - - --- --- Name: migrations migrations_name_key; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.migrations - ADD CONSTRAINT migrations_name_key UNIQUE (name); - - --- --- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.migrations - ADD CONSTRAINT migrations_pkey PRIMARY KEY (id); - - --- --- Name: objects objects_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.objects - ADD CONSTRAINT objects_pkey PRIMARY KEY (id); - - --- --- Name: extensions_tenant_external_id_type_index; Type: INDEX; Schema: _realtime; Owner: postgres --- - -CREATE UNIQUE INDEX extensions_tenant_external_id_type_index ON _realtime.extensions USING btree (tenant_external_id, type); - - --- --- Name: tenants_external_id_index; Type: INDEX; Schema: _realtime; Owner: postgres --- - -CREATE UNIQUE INDEX tenants_external_id_index ON _realtime.tenants USING btree (external_id); - - --- --- Name: audit_logs_instance_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX audit_logs_instance_id_idx ON auth.audit_log_entries USING btree (instance_id); - - --- --- Name: confirmation_token_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX confirmation_token_idx ON auth.users USING btree (confirmation_token) WHERE ((confirmation_token)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: email_change_token_current_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX email_change_token_current_idx ON auth.users USING btree (email_change_token_current) WHERE ((email_change_token_current)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: email_change_token_new_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX email_change_token_new_idx ON auth.users USING btree (email_change_token_new) WHERE ((email_change_token_new)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: factor_id_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX factor_id_created_at_idx ON auth.mfa_factors USING btree (user_id, created_at); - - --- --- Name: flow_state_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX flow_state_created_at_idx ON auth.flow_state USING btree (created_at DESC); - - --- --- Name: identities_email_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX identities_email_idx ON auth.identities USING btree (email text_pattern_ops); - - --- --- Name: INDEX identities_email_idx; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON INDEX auth.identities_email_idx IS 'Auth: Ensures indexed queries on the email column'; - - --- --- Name: identities_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX identities_user_id_idx ON auth.identities USING btree (user_id); - - --- --- Name: idx_auth_code; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX idx_auth_code ON auth.flow_state USING btree (auth_code); - - --- --- Name: idx_user_id_auth_method; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX idx_user_id_auth_method ON auth.flow_state USING btree (user_id, authentication_method); - - --- --- Name: mfa_challenge_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX mfa_challenge_created_at_idx ON auth.mfa_challenges USING btree (created_at DESC); - - --- --- Name: mfa_factors_user_friendly_name_unique; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX mfa_factors_user_friendly_name_unique ON auth.mfa_factors USING btree (friendly_name, user_id) WHERE (btrim(friendly_name) <> ''::text); - - --- --- Name: mfa_factors_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX mfa_factors_user_id_idx ON auth.mfa_factors USING btree (user_id); - - --- --- Name: reauthentication_token_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX reauthentication_token_idx ON auth.users USING btree (reauthentication_token) WHERE ((reauthentication_token)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: recovery_token_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX recovery_token_idx ON auth.users USING btree (recovery_token) WHERE ((recovery_token)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: refresh_tokens_instance_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_instance_id_idx ON auth.refresh_tokens USING btree (instance_id); - - --- --- Name: refresh_tokens_instance_id_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_instance_id_user_id_idx ON auth.refresh_tokens USING btree (instance_id, user_id); - - --- --- Name: refresh_tokens_parent_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_parent_idx ON auth.refresh_tokens USING btree (parent); - - --- --- Name: refresh_tokens_session_id_revoked_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_session_id_revoked_idx ON auth.refresh_tokens USING btree (session_id, revoked); - - --- --- Name: refresh_tokens_updated_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_updated_at_idx ON auth.refresh_tokens USING btree (updated_at DESC); - - --- --- Name: saml_providers_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX saml_providers_sso_provider_id_idx ON auth.saml_providers USING btree (sso_provider_id); - - --- --- Name: saml_relay_states_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX saml_relay_states_created_at_idx ON auth.saml_relay_states USING btree (created_at DESC); - - --- --- Name: saml_relay_states_for_email_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX saml_relay_states_for_email_idx ON auth.saml_relay_states USING btree (for_email); - - --- --- Name: saml_relay_states_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX saml_relay_states_sso_provider_id_idx ON auth.saml_relay_states USING btree (sso_provider_id); - - --- --- Name: sessions_not_after_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX sessions_not_after_idx ON auth.sessions USING btree (not_after DESC); - - --- --- Name: sessions_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX sessions_user_id_idx ON auth.sessions USING btree (user_id); - - --- --- Name: sso_domains_domain_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX sso_domains_domain_idx ON auth.sso_domains USING btree (lower(domain)); - - --- --- Name: sso_domains_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX sso_domains_sso_provider_id_idx ON auth.sso_domains USING btree (sso_provider_id); - - --- --- Name: sso_providers_resource_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX sso_providers_resource_id_idx ON auth.sso_providers USING btree (lower(resource_id)); - - --- --- Name: user_id_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX user_id_created_at_idx ON auth.sessions USING btree (user_id, created_at); - - --- --- Name: users_email_partial_key; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX users_email_partial_key ON auth.users USING btree (email) WHERE (is_sso_user = false); - - --- --- Name: INDEX users_email_partial_key; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON INDEX auth.users_email_partial_key IS 'Auth: A partial unique index that applies only when is_sso_user is false'; - - --- --- Name: users_instance_id_email_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX users_instance_id_email_idx ON auth.users USING btree (instance_id, lower((email)::text)); - - --- --- Name: users_instance_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX users_instance_id_idx ON auth.users USING btree (instance_id); - - --- --- Name: bname; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE UNIQUE INDEX bname ON storage.buckets USING btree (name); - - --- --- Name: bucketid_objname; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE UNIQUE INDEX bucketid_objname ON storage.objects USING btree (bucket_id, name); - - --- --- Name: name_prefix_search; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE INDEX name_prefix_search ON storage.objects USING btree (name text_pattern_ops); - - --- --- Name: objects update_objects_updated_at; Type: TRIGGER; Schema: storage; Owner: supabase_storage_admin --- - -CREATE TRIGGER update_objects_updated_at BEFORE UPDATE ON storage.objects FOR EACH ROW EXECUTE FUNCTION storage.update_updated_at_column(); - - --- --- Name: extensions extensions_tenant_external_id_fkey; Type: FK CONSTRAINT; Schema: _realtime; Owner: postgres --- - -ALTER TABLE ONLY _realtime.extensions - ADD CONSTRAINT extensions_tenant_external_id_fkey FOREIGN KEY (tenant_external_id) REFERENCES _realtime.tenants(external_id) ON DELETE CASCADE; - - --- --- Name: identities identities_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.identities - ADD CONSTRAINT identities_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: mfa_amr_claims mfa_amr_claims_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_amr_claims - ADD CONSTRAINT mfa_amr_claims_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; - - --- --- Name: mfa_challenges mfa_challenges_auth_factor_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_challenges - ADD CONSTRAINT mfa_challenges_auth_factor_id_fkey FOREIGN KEY (factor_id) REFERENCES auth.mfa_factors(id) ON DELETE CASCADE; - - --- --- Name: mfa_factors mfa_factors_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_factors - ADD CONSTRAINT mfa_factors_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: refresh_tokens refresh_tokens_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.refresh_tokens - ADD CONSTRAINT refresh_tokens_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; - - --- --- Name: saml_providers saml_providers_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_providers - ADD CONSTRAINT saml_providers_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; - - --- --- Name: saml_relay_states saml_relay_states_flow_state_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_relay_states - ADD CONSTRAINT saml_relay_states_flow_state_id_fkey FOREIGN KEY (flow_state_id) REFERENCES auth.flow_state(id) ON DELETE CASCADE; - - --- --- Name: saml_relay_states saml_relay_states_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_relay_states - ADD CONSTRAINT saml_relay_states_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; - - --- --- Name: sessions sessions_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sessions - ADD CONSTRAINT sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: sso_domains sso_domains_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sso_domains - ADD CONSTRAINT sso_domains_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; - - --- --- Name: objects objects_bucketId_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.objects - ADD CONSTRAINT "objects_bucketId_fkey" FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); - - --- --- Name: buckets; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.buckets ENABLE ROW LEVEL SECURITY; - --- --- Name: migrations; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.migrations ENABLE ROW LEVEL SECURITY; - --- --- Name: objects; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY; - --- --- Name: supabase_realtime; Type: PUBLICATION; Schema: -; Owner: postgres --- - -CREATE PUBLICATION supabase_realtime WITH (publish = 'insert, update, delete, truncate'); - - -ALTER PUBLICATION supabase_realtime OWNER TO postgres; - --- --- Name: SCHEMA auth; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA auth TO anon; -GRANT USAGE ON SCHEMA auth TO authenticated; -GRANT USAGE ON SCHEMA auth TO service_role; -GRANT ALL ON SCHEMA auth TO supabase_auth_admin; -GRANT ALL ON SCHEMA auth TO dashboard_user; -GRANT ALL ON SCHEMA auth TO postgres; - - --- --- Name: SCHEMA extensions; Type: ACL; Schema: -; Owner: postgres --- - -GRANT USAGE ON SCHEMA extensions TO anon; -GRANT USAGE ON SCHEMA extensions TO authenticated; -GRANT USAGE ON SCHEMA extensions TO service_role; -GRANT ALL ON SCHEMA extensions TO dashboard_user; - - --- --- Name: SCHEMA graphql_public; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA graphql_public TO postgres; -GRANT USAGE ON SCHEMA graphql_public TO anon; -GRANT USAGE ON SCHEMA graphql_public TO authenticated; -GRANT USAGE ON SCHEMA graphql_public TO service_role; - - --- --- Name: SCHEMA public; Type: ACL; Schema: -; Owner: postgres --- - -REVOKE USAGE ON SCHEMA public FROM PUBLIC; -GRANT ALL ON SCHEMA public TO PUBLIC; -GRANT USAGE ON SCHEMA public TO anon; -GRANT USAGE ON SCHEMA public TO authenticated; -GRANT USAGE ON SCHEMA public TO service_role; - - --- --- Name: SCHEMA realtime; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA realtime TO postgres; - - --- --- Name: SCHEMA storage; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT ALL ON SCHEMA storage TO postgres; -GRANT USAGE ON SCHEMA storage TO anon; -GRANT USAGE ON SCHEMA storage TO authenticated; -GRANT USAGE ON SCHEMA storage TO service_role; -GRANT ALL ON SCHEMA storage TO supabase_storage_admin; -GRANT ALL ON SCHEMA storage TO dashboard_user; - - --- --- Name: FUNCTION email(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.email() TO dashboard_user; - - --- --- Name: FUNCTION jwt(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.jwt() TO postgres; -GRANT ALL ON FUNCTION auth.jwt() TO dashboard_user; - - --- --- Name: FUNCTION role(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.role() TO dashboard_user; - - --- --- Name: FUNCTION uid(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.uid() TO dashboard_user; - - --- --- Name: FUNCTION algorithm_sign(signables text, secret text, algorithm text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.algorithm_sign(signables text, secret text, algorithm text) TO dashboard_user; - - --- --- Name: FUNCTION armor(bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.armor(bytea) TO dashboard_user; - - --- --- Name: FUNCTION armor(bytea, text[], text[]); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.armor(bytea, text[], text[]) TO dashboard_user; - - --- --- Name: FUNCTION crypt(text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.crypt(text, text) TO dashboard_user; - - --- --- Name: FUNCTION dearmor(text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.dearmor(text) TO dashboard_user; - - --- --- Name: FUNCTION decrypt(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.decrypt(bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION decrypt_iv(bytea, bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.decrypt_iv(bytea, bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION digest(bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.digest(bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION digest(text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.digest(text, text) TO dashboard_user; - - --- --- Name: FUNCTION encrypt(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.encrypt(bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION encrypt_iv(bytea, bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.encrypt_iv(bytea, bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION gen_random_bytes(integer); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.gen_random_bytes(integer) TO dashboard_user; - - --- --- Name: FUNCTION gen_random_uuid(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.gen_random_uuid() TO dashboard_user; - - --- --- Name: FUNCTION gen_salt(text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.gen_salt(text) TO dashboard_user; - - --- --- Name: FUNCTION gen_salt(text, integer); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.gen_salt(text, integer) TO dashboard_user; - - --- --- Name: FUNCTION grant_pg_cron_access(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.grant_pg_cron_access() TO dashboard_user; - - --- --- Name: FUNCTION grant_pg_net_access(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.grant_pg_net_access() TO dashboard_user; - - --- --- Name: FUNCTION hmac(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.hmac(bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION hmac(text, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.hmac(text, text, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_armor_headers(text, OUT key text, OUT value text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_armor_headers(text, OUT key text, OUT value text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_key_id(bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_key_id(bytea) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_decrypt(bytea, bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_decrypt(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_decrypt(bytea, bytea, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_decrypt_bytea(bytea, bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_encrypt(text, bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt(text, bytea) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_encrypt(text, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt(text, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_encrypt_bytea(bytea, bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_encrypt_bytea(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_decrypt(bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt(bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_decrypt(bytea, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt(bytea, text, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_decrypt_bytea(bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_decrypt_bytea(bytea, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_encrypt(text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt(text, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_encrypt(text, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt(text, text, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_encrypt_bytea(bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_encrypt_bytea(bytea, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text, text) TO dashboard_user; - - --- --- Name: FUNCTION sign(payload json, secret text, algorithm text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.sign(payload json, secret text, algorithm text) TO dashboard_user; - - --- --- Name: FUNCTION url_decode(data text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.url_decode(data text) TO dashboard_user; - - --- --- Name: FUNCTION url_encode(data bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.url_encode(data bytea) TO dashboard_user; - - --- --- Name: FUNCTION uuid_generate_v1(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v1() TO dashboard_user; - - --- --- Name: FUNCTION uuid_generate_v1mc(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v1mc() TO dashboard_user; - - --- --- Name: FUNCTION uuid_generate_v3(namespace uuid, name text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v3(namespace uuid, name text) TO dashboard_user; - - --- --- Name: FUNCTION uuid_generate_v4(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v4() TO dashboard_user; - - --- --- Name: FUNCTION uuid_generate_v5(namespace uuid, name text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v5(namespace uuid, name text) TO dashboard_user; - - --- --- Name: FUNCTION uuid_nil(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_nil() TO dashboard_user; - - --- --- Name: FUNCTION uuid_ns_dns(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_dns() TO dashboard_user; - - --- --- Name: FUNCTION uuid_ns_oid(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_oid() TO dashboard_user; - - --- --- Name: FUNCTION uuid_ns_url(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_url() TO dashboard_user; - - --- --- Name: FUNCTION uuid_ns_x500(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_x500() TO dashboard_user; - - --- --- Name: FUNCTION verify(token text, secret text, algorithm text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.verify(token text, secret text, algorithm text) TO dashboard_user; - - --- --- Name: FUNCTION extension(name text); Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON FUNCTION storage.extension(name text) TO anon; -GRANT ALL ON FUNCTION storage.extension(name text) TO authenticated; -GRANT ALL ON FUNCTION storage.extension(name text) TO service_role; -GRANT ALL ON FUNCTION storage.extension(name text) TO dashboard_user; -GRANT ALL ON FUNCTION storage.extension(name text) TO postgres; - - --- --- Name: FUNCTION filename(name text); Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON FUNCTION storage.filename(name text) TO anon; -GRANT ALL ON FUNCTION storage.filename(name text) TO authenticated; -GRANT ALL ON FUNCTION storage.filename(name text) TO service_role; -GRANT ALL ON FUNCTION storage.filename(name text) TO dashboard_user; -GRANT ALL ON FUNCTION storage.filename(name text) TO postgres; - - --- --- Name: FUNCTION foldername(name text); Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON FUNCTION storage.foldername(name text) TO anon; -GRANT ALL ON FUNCTION storage.foldername(name text) TO authenticated; -GRANT ALL ON FUNCTION storage.foldername(name text) TO service_role; -GRANT ALL ON FUNCTION storage.foldername(name text) TO dashboard_user; -GRANT ALL ON FUNCTION storage.foldername(name text) TO postgres; - - --- --- Name: TABLE audit_log_entries; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.audit_log_entries TO dashboard_user; -GRANT ALL ON TABLE auth.audit_log_entries TO postgres; - - --- --- Name: TABLE flow_state; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.flow_state TO postgres; -GRANT ALL ON TABLE auth.flow_state TO dashboard_user; - - --- --- Name: TABLE identities; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.identities TO postgres; -GRANT ALL ON TABLE auth.identities TO dashboard_user; - - --- --- Name: TABLE instances; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.instances TO dashboard_user; -GRANT ALL ON TABLE auth.instances TO postgres; - - --- --- Name: TABLE mfa_amr_claims; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.mfa_amr_claims TO postgres; -GRANT ALL ON TABLE auth.mfa_amr_claims TO dashboard_user; - - --- --- Name: TABLE mfa_challenges; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.mfa_challenges TO postgres; -GRANT ALL ON TABLE auth.mfa_challenges TO dashboard_user; - - --- --- Name: TABLE mfa_factors; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.mfa_factors TO postgres; -GRANT ALL ON TABLE auth.mfa_factors TO dashboard_user; - - --- --- Name: TABLE refresh_tokens; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.refresh_tokens TO dashboard_user; -GRANT ALL ON TABLE auth.refresh_tokens TO postgres; - - --- --- Name: SEQUENCE refresh_tokens_id_seq; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON SEQUENCE auth.refresh_tokens_id_seq TO dashboard_user; -GRANT ALL ON SEQUENCE auth.refresh_tokens_id_seq TO postgres; - - --- --- Name: TABLE saml_providers; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.saml_providers TO postgres; -GRANT ALL ON TABLE auth.saml_providers TO dashboard_user; - - --- --- Name: TABLE saml_relay_states; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.saml_relay_states TO postgres; -GRANT ALL ON TABLE auth.saml_relay_states TO dashboard_user; - - --- --- Name: TABLE schema_migrations; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.schema_migrations TO dashboard_user; -GRANT ALL ON TABLE auth.schema_migrations TO postgres; - - --- --- Name: TABLE sessions; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.sessions TO postgres; -GRANT ALL ON TABLE auth.sessions TO dashboard_user; - - --- --- Name: TABLE sso_domains; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.sso_domains TO postgres; -GRANT ALL ON TABLE auth.sso_domains TO dashboard_user; - - --- --- Name: TABLE sso_providers; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.sso_providers TO postgres; -GRANT ALL ON TABLE auth.sso_providers TO dashboard_user; - - --- --- Name: TABLE users; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.users TO dashboard_user; -GRANT ALL ON TABLE auth.users TO postgres; - - --- --- Name: TABLE buckets; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.buckets TO anon; -GRANT ALL ON TABLE storage.buckets TO authenticated; -GRANT ALL ON TABLE storage.buckets TO service_role; -GRANT ALL ON TABLE storage.buckets TO postgres; - - --- --- Name: TABLE migrations; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.migrations TO anon; -GRANT ALL ON TABLE storage.migrations TO authenticated; -GRANT ALL ON TABLE storage.migrations TO service_role; -GRANT ALL ON TABLE storage.migrations TO postgres; - - --- --- Name: TABLE objects; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.objects TO anon; -GRANT ALL ON TABLE storage.objects TO authenticated; -GRANT ALL ON TABLE storage.objects TO service_role; -GRANT ALL ON TABLE storage.objects TO postgres; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: auth; Owner: supabase_auth_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON SEQUENCES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: auth; Owner: supabase_auth_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON FUNCTIONS TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: auth; Owner: supabase_auth_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON TABLES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: graphql_public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: graphql_public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: graphql_public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: public; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: realtime; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON SEQUENCES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: realtime; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON FUNCTIONS TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: realtime; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON TABLES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: storage; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: storage; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: storage; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO service_role; - - --- --- Name: issue_graphql_placeholder; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER issue_graphql_placeholder ON sql_drop - WHEN TAG IN ('DROP EXTENSION') - EXECUTE FUNCTION extensions.set_graphql_placeholder(); - - -ALTER EVENT TRIGGER issue_graphql_placeholder OWNER TO supabase_admin; - --- --- Name: issue_pg_cron_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER issue_pg_cron_access ON ddl_command_end - WHEN TAG IN ('CREATE SCHEMA') - EXECUTE FUNCTION extensions.grant_pg_cron_access(); - - -ALTER EVENT TRIGGER issue_pg_cron_access OWNER TO supabase_admin; - --- --- Name: issue_pg_graphql_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER issue_pg_graphql_access ON ddl_command_end - WHEN TAG IN ('CREATE FUNCTION') - EXECUTE FUNCTION extensions.grant_pg_graphql_access(); - - -ALTER EVENT TRIGGER issue_pg_graphql_access OWNER TO supabase_admin; - --- --- Name: issue_pg_net_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end - WHEN TAG IN ('CREATE EXTENSION') - EXECUTE FUNCTION extensions.grant_pg_net_access(); - - -ALTER EVENT TRIGGER issue_pg_net_access OWNER TO supabase_admin; - --- --- Name: pgrst_ddl_watch; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER pgrst_ddl_watch ON ddl_command_end - EXECUTE FUNCTION extensions.pgrst_ddl_watch(); - - -ALTER EVENT TRIGGER pgrst_ddl_watch OWNER TO supabase_admin; - --- --- Name: pgrst_drop_watch; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER pgrst_drop_watch ON sql_drop - EXECUTE FUNCTION extensions.pgrst_drop_watch(); - - -ALTER EVENT TRIGGER pgrst_drop_watch OWNER TO supabase_admin; - --- --- PostgreSQL database dump complete --- - -RESET ALL; diff --git a/apps/cli-go/internal/utils/templates/initial_schemas/14.sql b/apps/cli-go/internal/utils/templates/initial_schemas/14.sql deleted file mode 100644 index bef44153ec..0000000000 --- a/apps/cli-go/internal/utils/templates/initial_schemas/14.sql +++ /dev/null @@ -1,3690 +0,0 @@ --- --- PostgreSQL database dump --- - --- Dumped from database version 14.5 (Debian 14.5-2.pgdg110+2) --- Dumped by pg_dump version 15.3 - -SET statement_timeout = 0; -SET lock_timeout = 0; -SET idle_in_transaction_session_timeout = 0; -SET client_encoding = 'UTF8'; -SET standard_conforming_strings = on; -SELECT pg_catalog.set_config('search_path', '', false); -SET check_function_bodies = false; -SET xmloption = content; -SET client_min_messages = warning; -SET row_security = off; - --- --- Name: _analytics; Type: SCHEMA; Schema: -; Owner: postgres --- - -CREATE SCHEMA IF NOT EXISTS _analytics; - - -ALTER SCHEMA _analytics OWNER TO postgres; - --- --- Name: _realtime; Type: SCHEMA; Schema: -; Owner: postgres --- - -CREATE SCHEMA IF NOT EXISTS _realtime; - - -ALTER SCHEMA _realtime OWNER TO postgres; - --- --- Name: auth; Type: SCHEMA; Schema: -; Owner: supabase_admin --- - -CREATE SCHEMA IF NOT EXISTS auth; - - -ALTER SCHEMA auth OWNER TO supabase_admin; - --- --- Name: extensions; Type: SCHEMA; Schema: -; Owner: postgres --- - -CREATE SCHEMA IF NOT EXISTS extensions; - - -ALTER SCHEMA extensions OWNER TO postgres; - --- --- Name: graphql; Type: SCHEMA; Schema: -; Owner: supabase_admin --- - -CREATE SCHEMA IF NOT EXISTS graphql; - - -ALTER SCHEMA graphql OWNER TO supabase_admin; - --- --- Name: graphql_public; Type: SCHEMA; Schema: -; Owner: supabase_admin --- - -CREATE SCHEMA IF NOT EXISTS graphql_public; - - -ALTER SCHEMA graphql_public OWNER TO supabase_admin; - --- --- Name: pg_net; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS pg_net WITH SCHEMA extensions; - - --- --- Name: EXTENSION pg_net; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION pg_net IS 'Async HTTP'; - - --- --- Name: pgbouncer; Type: SCHEMA; Schema: -; Owner: pgbouncer --- - -CREATE SCHEMA IF NOT EXISTS pgbouncer; - - -ALTER SCHEMA pgbouncer OWNER TO pgbouncer; - --- --- Name: pgsodium; Type: SCHEMA; Schema: -; Owner: postgres --- - -CREATE SCHEMA IF NOT EXISTS pgsodium; - - -ALTER SCHEMA pgsodium OWNER TO postgres; - --- --- Name: pgsodium; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS pgsodium WITH SCHEMA pgsodium; - - --- --- Name: EXTENSION pgsodium; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION pgsodium IS 'Pgsodium is a modern cryptography library for Postgres.'; - - --- --- Name: public; Type: SCHEMA; Schema: -; Owner: postgres --- - --- *not* creating schema, since initdb creates it - - -ALTER SCHEMA public OWNER TO postgres; - --- --- Name: realtime; Type: SCHEMA; Schema: -; Owner: supabase_admin --- - -CREATE SCHEMA IF NOT EXISTS realtime; - - -ALTER SCHEMA realtime OWNER TO supabase_admin; - --- --- Name: storage; Type: SCHEMA; Schema: -; Owner: supabase_admin --- - -CREATE SCHEMA IF NOT EXISTS storage; - - -ALTER SCHEMA storage OWNER TO supabase_admin; - --- --- Name: supabase_functions; Type: SCHEMA; Schema: -; Owner: supabase_admin --- - -CREATE SCHEMA IF NOT EXISTS supabase_functions; - - -ALTER SCHEMA supabase_functions OWNER TO supabase_admin; - --- --- Name: pg_graphql; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS pg_graphql WITH SCHEMA graphql; - - --- --- Name: EXTENSION pg_graphql; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION pg_graphql IS 'pg_graphql: GraphQL support'; - - --- --- Name: pg_stat_statements; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA extensions; - - --- --- Name: EXTENSION pg_stat_statements; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION pg_stat_statements IS 'track planning and execution statistics of all SQL statements executed'; - - --- --- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA extensions; - - --- --- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions'; - - --- --- Name: pgjwt; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS pgjwt WITH SCHEMA extensions; - - --- --- Name: EXTENSION pgjwt; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION pgjwt IS 'JSON Web Token API for Postgresql'; - - --- --- Name: uuid-ossp; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA extensions; - - --- --- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: --- - -COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; - - --- --- Name: aal_level; Type: TYPE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TYPE auth.aal_level AS ENUM ( - 'aal1', - 'aal2', - 'aal3' -); - - -ALTER TYPE auth.aal_level OWNER TO supabase_auth_admin; - --- --- Name: code_challenge_method; Type: TYPE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TYPE auth.code_challenge_method AS ENUM ( - 's256', - 'plain' -); - - -ALTER TYPE auth.code_challenge_method OWNER TO supabase_auth_admin; - --- --- Name: factor_status; Type: TYPE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TYPE auth.factor_status AS ENUM ( - 'unverified', - 'verified' -); - - -ALTER TYPE auth.factor_status OWNER TO supabase_auth_admin; - --- --- Name: factor_type; Type: TYPE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TYPE auth.factor_type AS ENUM ( - 'totp', - 'webauthn' -); - - -ALTER TYPE auth.factor_type OWNER TO supabase_auth_admin; - --- --- Name: email(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin --- - -CREATE OR REPLACE FUNCTION auth.email() RETURNS text - LANGUAGE sql STABLE - AS $$ - select - coalesce( - nullif(current_setting('request.jwt.claim.email', true), ''), - (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'email') - )::text -$$; - - -ALTER FUNCTION auth.email() OWNER TO supabase_auth_admin; - --- --- Name: FUNCTION email(); Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON FUNCTION auth.email() IS 'Deprecated. Use auth.jwt() -> ''email'' instead.'; - - --- --- Name: jwt(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin --- - -CREATE OR REPLACE FUNCTION auth.jwt() RETURNS jsonb - LANGUAGE sql STABLE - AS $$ - select - coalesce( - nullif(current_setting('request.jwt.claim', true), ''), - nullif(current_setting('request.jwt.claims', true), '') - )::jsonb -$$; - - -ALTER FUNCTION auth.jwt() OWNER TO supabase_auth_admin; - --- --- Name: role(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin --- - -CREATE OR REPLACE FUNCTION auth.role() RETURNS text - LANGUAGE sql STABLE - AS $$ - select - coalesce( - nullif(current_setting('request.jwt.claim.role', true), ''), - (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'role') - )::text -$$; - - -ALTER FUNCTION auth.role() OWNER TO supabase_auth_admin; - --- --- Name: FUNCTION role(); Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON FUNCTION auth.role() IS 'Deprecated. Use auth.jwt() -> ''role'' instead.'; - - --- --- Name: uid(); Type: FUNCTION; Schema: auth; Owner: supabase_auth_admin --- - -CREATE OR REPLACE FUNCTION auth.uid() RETURNS uuid - LANGUAGE sql STABLE - AS $$ - select - coalesce( - nullif(current_setting('request.jwt.claim.sub', true), ''), - (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'sub') - )::uuid -$$; - - -ALTER FUNCTION auth.uid() OWNER TO supabase_auth_admin; - --- --- Name: FUNCTION uid(); Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON FUNCTION auth.uid() IS 'Deprecated. Use auth.jwt() -> ''sub'' instead.'; - - --- --- Name: grant_pg_cron_access(); Type: FUNCTION; Schema: extensions; Owner: postgres --- - -CREATE OR REPLACE FUNCTION extensions.grant_pg_cron_access() RETURNS event_trigger - LANGUAGE plpgsql - AS $$ -DECLARE - schema_is_cron bool; -BEGIN - schema_is_cron = ( - SELECT n.nspname = 'cron' - FROM pg_event_trigger_ddl_commands() AS ev - LEFT JOIN pg_catalog.pg_namespace AS n - ON ev.objid = n.oid - ); - - IF schema_is_cron - THEN - grant usage on schema cron to postgres with grant option; - - alter default privileges in schema cron grant all on tables to postgres with grant option; - alter default privileges in schema cron grant all on functions to postgres with grant option; - alter default privileges in schema cron grant all on sequences to postgres with grant option; - - alter default privileges for user supabase_admin in schema cron grant all - on sequences to postgres with grant option; - alter default privileges for user supabase_admin in schema cron grant all - on tables to postgres with grant option; - alter default privileges for user supabase_admin in schema cron grant all - on functions to postgres with grant option; - - grant all privileges on all tables in schema cron to postgres with grant option; - - END IF; - -END; -$$; - - -ALTER FUNCTION extensions.grant_pg_cron_access() OWNER TO postgres; - --- --- Name: FUNCTION grant_pg_cron_access(); Type: COMMENT; Schema: extensions; Owner: postgres --- - -COMMENT ON FUNCTION extensions.grant_pg_cron_access() IS 'Grants access to pg_cron'; - - --- --- Name: grant_pg_graphql_access(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin --- - -CREATE OR REPLACE FUNCTION extensions.grant_pg_graphql_access() RETURNS event_trigger - LANGUAGE plpgsql - AS $_$ -DECLARE - func_is_graphql_resolve bool; -BEGIN - func_is_graphql_resolve = ( - SELECT n.proname = 'resolve' - FROM pg_event_trigger_ddl_commands() AS ev - LEFT JOIN pg_catalog.pg_proc AS n - ON ev.objid = n.oid - ); - - IF func_is_graphql_resolve - THEN - -- Update public wrapper to pass all arguments through to the pg_graphql resolve func - DROP FUNCTION IF EXISTS graphql_public.graphql; - create or replace function graphql_public.graphql( - "operationName" text default null, - query text default null, - variables jsonb default null, - extensions jsonb default null - ) - returns jsonb - language sql - as $$ - select graphql.resolve( - query := query, - variables := coalesce(variables, '{}'), - "operationName" := "operationName", - extensions := extensions - ); - $$; - - -- This hook executes when `graphql.resolve` is created. That is not necessarily the last - -- function in the extension so we need to grant permissions on existing entities AND - -- update default permissions to any others that are created after `graphql.resolve` - grant usage on schema graphql to postgres, anon, authenticated, service_role; - grant select on all tables in schema graphql to postgres, anon, authenticated, service_role; - grant execute on all functions in schema graphql to postgres, anon, authenticated, service_role; - grant all on all sequences in schema graphql to postgres, anon, authenticated, service_role; - alter default privileges in schema graphql grant all on tables to postgres, anon, authenticated, service_role; - alter default privileges in schema graphql grant all on functions to postgres, anon, authenticated, service_role; - alter default privileges in schema graphql grant all on sequences to postgres, anon, authenticated, service_role; - END IF; - -END; -$_$; - - -ALTER FUNCTION extensions.grant_pg_graphql_access() OWNER TO supabase_admin; - --- --- Name: FUNCTION grant_pg_graphql_access(); Type: COMMENT; Schema: extensions; Owner: supabase_admin --- - -COMMENT ON FUNCTION extensions.grant_pg_graphql_access() IS 'Grants access to pg_graphql'; - - --- --- Name: grant_pg_net_access(); Type: FUNCTION; Schema: extensions; Owner: postgres --- - -CREATE OR REPLACE FUNCTION extensions.grant_pg_net_access() RETURNS event_trigger - LANGUAGE plpgsql - AS $$ -BEGIN - IF EXISTS ( - SELECT 1 - FROM pg_event_trigger_ddl_commands() AS ev - JOIN pg_extension AS ext - ON ev.objid = ext.oid - WHERE ext.extname = 'pg_net' - ) - THEN - GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; - - ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; - ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; - - ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; - ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; - - REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; - REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; - - GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; - GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; - END IF; -END; -$$; - - -ALTER FUNCTION extensions.grant_pg_net_access() OWNER TO postgres; - --- --- Name: FUNCTION grant_pg_net_access(); Type: COMMENT; Schema: extensions; Owner: postgres --- - -COMMENT ON FUNCTION extensions.grant_pg_net_access() IS 'Grants access to pg_net'; - - --- --- Name: pgrst_ddl_watch(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin --- - -CREATE OR REPLACE FUNCTION extensions.pgrst_ddl_watch() RETURNS event_trigger - LANGUAGE plpgsql - AS $$ -DECLARE - cmd record; -BEGIN - FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands() - LOOP - IF cmd.command_tag IN ( - 'CREATE SCHEMA', 'ALTER SCHEMA' - , 'CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO', 'ALTER TABLE' - , 'CREATE FOREIGN TABLE', 'ALTER FOREIGN TABLE' - , 'CREATE VIEW', 'ALTER VIEW' - , 'CREATE MATERIALIZED VIEW', 'ALTER MATERIALIZED VIEW' - , 'CREATE FUNCTION', 'ALTER FUNCTION' - , 'CREATE TRIGGER' - , 'CREATE TYPE', 'ALTER TYPE' - , 'CREATE RULE' - , 'COMMENT' - ) - -- don't notify in case of CREATE TEMP table or other objects created on pg_temp - AND cmd.schema_name is distinct from 'pg_temp' - THEN - NOTIFY pgrst, 'reload schema'; - END IF; - END LOOP; -END; $$; - - -ALTER FUNCTION extensions.pgrst_ddl_watch() OWNER TO supabase_admin; - --- --- Name: pgrst_drop_watch(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin --- - -CREATE OR REPLACE FUNCTION extensions.pgrst_drop_watch() RETURNS event_trigger - LANGUAGE plpgsql - AS $$ -DECLARE - obj record; -BEGIN - FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() - LOOP - IF obj.object_type IN ( - 'schema' - , 'table' - , 'foreign table' - , 'view' - , 'materialized view' - , 'function' - , 'trigger' - , 'type' - , 'rule' - ) - AND obj.is_temporary IS false -- no pg_temp objects - THEN - NOTIFY pgrst, 'reload schema'; - END IF; - END LOOP; -END; $$; - - -ALTER FUNCTION extensions.pgrst_drop_watch() OWNER TO supabase_admin; - --- --- Name: set_graphql_placeholder(); Type: FUNCTION; Schema: extensions; Owner: supabase_admin --- - -CREATE OR REPLACE FUNCTION extensions.set_graphql_placeholder() RETURNS event_trigger - LANGUAGE plpgsql - AS $_$ - DECLARE - graphql_is_dropped bool; - BEGIN - graphql_is_dropped = ( - SELECT ev.schema_name = 'graphql_public' - FROM pg_event_trigger_dropped_objects() AS ev - WHERE ev.schema_name = 'graphql_public' - ); - - IF graphql_is_dropped - THEN - create or replace function graphql_public.graphql( - "operationName" text default null, - query text default null, - variables jsonb default null, - extensions jsonb default null - ) - returns jsonb - language plpgsql - as $$ - DECLARE - server_version float; - BEGIN - server_version = (SELECT (SPLIT_PART((select version()), ' ', 2))::float); - - IF server_version >= 14 THEN - RETURN jsonb_build_object( - 'errors', jsonb_build_array( - jsonb_build_object( - 'message', 'pg_graphql extension is not enabled.' - ) - ) - ); - ELSE - RETURN jsonb_build_object( - 'errors', jsonb_build_array( - jsonb_build_object( - 'message', 'pg_graphql is only available on projects running Postgres 14 onwards.' - ) - ) - ); - END IF; - END; - $$; - END IF; - - END; -$_$; - - -ALTER FUNCTION extensions.set_graphql_placeholder() OWNER TO supabase_admin; - --- --- Name: FUNCTION set_graphql_placeholder(); Type: COMMENT; Schema: extensions; Owner: supabase_admin --- - -COMMENT ON FUNCTION extensions.set_graphql_placeholder() IS 'Reintroduces placeholder function for graphql_public.graphql'; - - --- --- Name: get_auth(text); Type: FUNCTION; Schema: pgbouncer; Owner: postgres --- - -CREATE OR REPLACE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text) - LANGUAGE plpgsql SECURITY DEFINER - AS $$ -BEGIN - RAISE WARNING 'PgBouncer auth request: %', p_usename; - - RETURN QUERY - SELECT usename::TEXT, passwd::TEXT FROM pg_catalog.pg_shadow - WHERE usename = p_usename; -END; -$$; - - -ALTER FUNCTION pgbouncer.get_auth(p_usename text) OWNER TO postgres; - --- --- Name: can_insert_object(text, text, uuid, jsonb); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.can_insert_object(bucketid text, name text, owner uuid, metadata jsonb) RETURNS void - LANGUAGE plpgsql - AS $$ -BEGIN - INSERT INTO "storage"."objects" ("bucket_id", "name", "owner", "metadata") VALUES (bucketid, name, owner, metadata); - -- hack to rollback the successful insert - RAISE sqlstate 'PT200' using - message = 'ROLLBACK', - detail = 'rollback successful insert'; -END -$$; - - -ALTER FUNCTION storage.can_insert_object(bucketid text, name text, owner uuid, metadata jsonb) OWNER TO supabase_storage_admin; - --- --- Name: extension(text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.extension(name text) RETURNS text - LANGUAGE plpgsql - AS $$ -DECLARE -_parts text[]; -_filename text; -BEGIN - select string_to_array(name, '/') into _parts; - select _parts[array_length(_parts,1)] into _filename; - -- @todo return the last part instead of 2 - return split_part(_filename, '.', 2); -END -$$; - - -ALTER FUNCTION storage.extension(name text) OWNER TO supabase_storage_admin; - --- --- Name: filename(text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.filename(name text) RETURNS text - LANGUAGE plpgsql - AS $$ -DECLARE -_parts text[]; -BEGIN - select string_to_array(name, '/') into _parts; - return _parts[array_length(_parts,1)]; -END -$$; - - -ALTER FUNCTION storage.filename(name text) OWNER TO supabase_storage_admin; - --- --- Name: foldername(text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.foldername(name text) RETURNS text[] - LANGUAGE plpgsql - AS $$ -DECLARE -_parts text[]; -BEGIN - select string_to_array(name, '/') into _parts; - return _parts[1:array_length(_parts,1)-1]; -END -$$; - - -ALTER FUNCTION storage.foldername(name text) OWNER TO supabase_storage_admin; - --- --- Name: get_size_by_bucket(); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.get_size_by_bucket() RETURNS TABLE(size bigint, bucket_id text) - LANGUAGE plpgsql - AS $$ -BEGIN - return query - select sum((metadata->>'size')::int) as size, obj.bucket_id - from "storage".objects as obj - group by obj.bucket_id; -END -$$; - - -ALTER FUNCTION storage.get_size_by_bucket() OWNER TO supabase_storage_admin; - --- --- Name: search(text, text, integer, integer, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.search(prefix text, bucketname text, limits integer DEFAULT 100, levels integer DEFAULT 1, offsets integer DEFAULT 0, search text DEFAULT ''::text, sortcolumn text DEFAULT 'name'::text, sortorder text DEFAULT 'asc'::text) RETURNS TABLE(name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) - LANGUAGE plpgsql STABLE - AS $_$ -declare - v_order_by text; - v_sort_order text; -begin - case - when sortcolumn = 'name' then - v_order_by = 'name'; - when sortcolumn = 'updated_at' then - v_order_by = 'updated_at'; - when sortcolumn = 'created_at' then - v_order_by = 'created_at'; - when sortcolumn = 'last_accessed_at' then - v_order_by = 'last_accessed_at'; - else - v_order_by = 'name'; - end case; - - case - when sortorder = 'asc' then - v_sort_order = 'asc'; - when sortorder = 'desc' then - v_sort_order = 'desc'; - else - v_sort_order = 'asc'; - end case; - - v_order_by = v_order_by || ' ' || v_sort_order; - - return query execute - 'with folders as ( - select path_tokens[$1] as folder - from storage.objects - where objects.name ilike $2 || $3 || ''%'' - and bucket_id = $4 - and array_length(regexp_split_to_array(objects.name, ''/''), 1) <> $1 - group by folder - order by folder ' || v_sort_order || ' - ) - (select folder as "name", - null as id, - null as updated_at, - null as created_at, - null as last_accessed_at, - null as metadata from folders) - union all - (select path_tokens[$1] as "name", - id, - updated_at, - created_at, - last_accessed_at, - metadata - from storage.objects - where objects.name ilike $2 || $3 || ''%'' - and bucket_id = $4 - and array_length(regexp_split_to_array(objects.name, ''/''), 1) = $1 - order by ' || v_order_by || ') - limit $5 - offset $6' using levels, prefix, search, bucketname, limits, offsets; -end; -$_$; - - -ALTER FUNCTION storage.search(prefix text, bucketname text, limits integer, levels integer, offsets integer, search text, sortcolumn text, sortorder text) OWNER TO supabase_storage_admin; - --- --- Name: update_updated_at_column(); Type: FUNCTION; Schema: storage; Owner: supabase_storage_admin --- - -CREATE OR REPLACE FUNCTION storage.update_updated_at_column() RETURNS trigger - LANGUAGE plpgsql - AS $$ -BEGIN - NEW.updated_at = now(); - RETURN NEW; -END; -$$; - - -ALTER FUNCTION storage.update_updated_at_column() OWNER TO supabase_storage_admin; - --- --- Name: http_request(); Type: FUNCTION; Schema: supabase_functions; Owner: supabase_functions_admin --- - -CREATE OR REPLACE FUNCTION supabase_functions.http_request() RETURNS trigger - LANGUAGE plpgsql SECURITY DEFINER - SET search_path TO 'supabase_functions' - AS $$ - DECLARE - request_id bigint; - payload jsonb; - url text := TG_ARGV[0]::text; - method text := TG_ARGV[1]::text; - headers jsonb DEFAULT '{}'::jsonb; - params jsonb DEFAULT '{}'::jsonb; - timeout_ms integer DEFAULT 1000; - BEGIN - IF url IS NULL OR url = 'null' THEN - RAISE EXCEPTION 'url argument is missing'; - END IF; - - IF method IS NULL OR method = 'null' THEN - RAISE EXCEPTION 'method argument is missing'; - END IF; - - IF TG_ARGV[2] IS NULL OR TG_ARGV[2] = 'null' THEN - headers = '{"Content-Type": "application/json"}'::jsonb; - ELSE - headers = TG_ARGV[2]::jsonb; - END IF; - - IF TG_ARGV[3] IS NULL OR TG_ARGV[3] = 'null' THEN - params = '{}'::jsonb; - ELSE - params = TG_ARGV[3]::jsonb; - END IF; - - IF TG_ARGV[4] IS NULL OR TG_ARGV[4] = 'null' THEN - timeout_ms = 1000; - ELSE - timeout_ms = TG_ARGV[4]::integer; - END IF; - - CASE - WHEN method = 'GET' THEN - SELECT http_get INTO request_id FROM net.http_get( - url, - params, - headers, - timeout_ms - ); - WHEN method = 'POST' THEN - payload = jsonb_build_object( - 'old_record', OLD, - 'record', NEW, - 'type', TG_OP, - 'table', TG_TABLE_NAME, - 'schema', TG_TABLE_SCHEMA - ); - - SELECT http_post INTO request_id FROM net.http_post( - url, - payload, - params, - headers, - timeout_ms - ); - ELSE - RAISE EXCEPTION 'method argument % is invalid', method; - END CASE; - - INSERT INTO supabase_functions.hooks - (hook_table_id, hook_name, request_id) - VALUES - (TG_RELID, TG_NAME, request_id); - - RETURN NEW; - END -$$; - - -ALTER FUNCTION supabase_functions.http_request() OWNER TO supabase_functions_admin; - -SET default_tablespace = ''; - -SET default_table_access_method = heap; - --- --- Name: extensions; Type: TABLE; Schema: _realtime; Owner: postgres --- - -CREATE TABLE IF NOT EXISTS _realtime.extensions ( - id uuid NOT NULL, - type text, - settings jsonb, - tenant_external_id text, - inserted_at timestamp(0) without time zone NOT NULL, - updated_at timestamp(0) without time zone NOT NULL -); - - -ALTER TABLE _realtime.extensions OWNER TO postgres; - --- --- Name: schema_migrations; Type: TABLE; Schema: _realtime; Owner: postgres --- - -CREATE TABLE IF NOT EXISTS _realtime.schema_migrations ( - version bigint NOT NULL, - inserted_at timestamp(0) without time zone -); - - -ALTER TABLE _realtime.schema_migrations OWNER TO postgres; - --- --- Name: tenants; Type: TABLE; Schema: _realtime; Owner: postgres --- - -CREATE TABLE IF NOT EXISTS _realtime.tenants ( - id uuid NOT NULL, - name text, - external_id text, - jwt_secret text, - max_concurrent_users integer DEFAULT 200 NOT NULL, - inserted_at timestamp(0) without time zone NOT NULL, - updated_at timestamp(0) without time zone NOT NULL, - max_events_per_second integer DEFAULT 100 NOT NULL, - postgres_cdc_default text DEFAULT 'postgres_cdc_rls'::text, - max_bytes_per_second integer DEFAULT 100000 NOT NULL, - max_channels_per_client integer DEFAULT 100 NOT NULL, - max_joins_per_second integer DEFAULT 500 NOT NULL, - suspend boolean DEFAULT false -); - - -ALTER TABLE _realtime.tenants OWNER TO postgres; - --- --- Name: audit_log_entries; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.audit_log_entries ( - instance_id uuid, - id uuid NOT NULL, - payload json, - created_at timestamp with time zone, - ip_address character varying(64) DEFAULT ''::character varying NOT NULL -); - - -ALTER TABLE auth.audit_log_entries OWNER TO supabase_auth_admin; - --- --- Name: TABLE audit_log_entries; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.audit_log_entries IS 'Auth: Audit trail for user actions.'; - - --- --- Name: flow_state; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.flow_state ( - id uuid NOT NULL, - user_id uuid, - auth_code text NOT NULL, - code_challenge_method auth.code_challenge_method NOT NULL, - code_challenge text NOT NULL, - provider_type text NOT NULL, - provider_access_token text, - provider_refresh_token text, - created_at timestamp with time zone, - updated_at timestamp with time zone, - authentication_method text NOT NULL -); - - -ALTER TABLE auth.flow_state OWNER TO supabase_auth_admin; - --- --- Name: TABLE flow_state; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.flow_state IS 'stores metadata for pkce logins'; - - --- --- Name: identities; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.identities ( - provider_id text NOT NULL, - user_id uuid NOT NULL, - identity_data jsonb NOT NULL, - provider text NOT NULL, - last_sign_in_at timestamp with time zone, - created_at timestamp with time zone, - updated_at timestamp with time zone, - email text GENERATED ALWAYS AS (lower((identity_data ->> 'email'::text))) STORED, - id uuid DEFAULT gen_random_uuid() NOT NULL -); - - -ALTER TABLE auth.identities OWNER TO supabase_auth_admin; - --- --- Name: TABLE identities; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.identities IS 'Auth: Stores identities associated to a user.'; - - --- --- Name: COLUMN identities.email; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON COLUMN auth.identities.email IS 'Auth: Email is a generated column that references the optional email property in the identity_data'; - - --- --- Name: instances; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.instances ( - id uuid NOT NULL, - uuid uuid, - raw_base_config text, - created_at timestamp with time zone, - updated_at timestamp with time zone -); - - -ALTER TABLE auth.instances OWNER TO supabase_auth_admin; - --- --- Name: TABLE instances; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.instances IS 'Auth: Manages users across multiple sites.'; - - --- --- Name: mfa_amr_claims; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.mfa_amr_claims ( - session_id uuid NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - authentication_method text NOT NULL, - id uuid NOT NULL -); - - -ALTER TABLE auth.mfa_amr_claims OWNER TO supabase_auth_admin; - --- --- Name: TABLE mfa_amr_claims; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.mfa_amr_claims IS 'auth: stores authenticator method reference claims for multi factor authentication'; - - --- --- Name: mfa_challenges; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.mfa_challenges ( - id uuid NOT NULL, - factor_id uuid NOT NULL, - created_at timestamp with time zone NOT NULL, - verified_at timestamp with time zone, - ip_address inet NOT NULL -); - - -ALTER TABLE auth.mfa_challenges OWNER TO supabase_auth_admin; - --- --- Name: TABLE mfa_challenges; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.mfa_challenges IS 'auth: stores metadata about challenge requests made'; - - --- --- Name: mfa_factors; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.mfa_factors ( - id uuid NOT NULL, - user_id uuid NOT NULL, - friendly_name text, - factor_type auth.factor_type NOT NULL, - status auth.factor_status NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - secret text -); - - -ALTER TABLE auth.mfa_factors OWNER TO supabase_auth_admin; - --- --- Name: TABLE mfa_factors; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.mfa_factors IS 'auth: stores metadata about factors'; - - --- --- Name: refresh_tokens; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.refresh_tokens ( - instance_id uuid, - id bigint NOT NULL, - token character varying(255), - user_id character varying(255), - revoked boolean, - created_at timestamp with time zone, - updated_at timestamp with time zone, - parent character varying(255), - session_id uuid -); - - -ALTER TABLE auth.refresh_tokens OWNER TO supabase_auth_admin; - --- --- Name: TABLE refresh_tokens; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.refresh_tokens IS 'Auth: Store of tokens used to refresh JWT tokens once they expire.'; - - --- --- Name: refresh_tokens_id_seq; Type: SEQUENCE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE SEQUENCE IF NOT EXISTS auth.refresh_tokens_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - -ALTER TABLE auth.refresh_tokens_id_seq OWNER TO supabase_auth_admin; - --- --- Name: refresh_tokens_id_seq; Type: SEQUENCE OWNED BY; Schema: auth; Owner: supabase_auth_admin --- - -ALTER SEQUENCE auth.refresh_tokens_id_seq OWNED BY auth.refresh_tokens.id; - - --- --- Name: saml_providers; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.saml_providers ( - id uuid NOT NULL, - sso_provider_id uuid NOT NULL, - entity_id text NOT NULL, - metadata_xml text NOT NULL, - metadata_url text, - attribute_mapping jsonb, - created_at timestamp with time zone, - updated_at timestamp with time zone, - CONSTRAINT "entity_id not empty" CHECK ((char_length(entity_id) > 0)), - CONSTRAINT "metadata_url not empty" CHECK (((metadata_url = NULL::text) OR (char_length(metadata_url) > 0))), - CONSTRAINT "metadata_xml not empty" CHECK ((char_length(metadata_xml) > 0)) -); - - -ALTER TABLE auth.saml_providers OWNER TO supabase_auth_admin; - --- --- Name: TABLE saml_providers; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.saml_providers IS 'Auth: Manages SAML Identity Provider connections.'; - - --- --- Name: saml_relay_states; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.saml_relay_states ( - id uuid NOT NULL, - sso_provider_id uuid NOT NULL, - request_id text NOT NULL, - for_email text, - redirect_to text, - from_ip_address inet, - created_at timestamp with time zone, - updated_at timestamp with time zone, - flow_state_id uuid, - CONSTRAINT "request_id not empty" CHECK ((char_length(request_id) > 0)) -); - - -ALTER TABLE auth.saml_relay_states OWNER TO supabase_auth_admin; - --- --- Name: TABLE saml_relay_states; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.saml_relay_states IS 'Auth: Contains SAML Relay State information for each Service Provider initiated login.'; - - --- --- Name: schema_migrations; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.schema_migrations ( - version character varying(255) NOT NULL -); - - -ALTER TABLE auth.schema_migrations OWNER TO supabase_auth_admin; - --- --- Name: TABLE schema_migrations; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.schema_migrations IS 'Auth: Manages updates to the auth system.'; - - --- --- Name: sessions; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.sessions ( - id uuid NOT NULL, - user_id uuid NOT NULL, - created_at timestamp with time zone, - updated_at timestamp with time zone, - factor_id uuid, - aal auth.aal_level, - not_after timestamp with time zone, - refreshed_at timestamp without time zone, - user_agent text, - ip inet, - tag text -); - - -ALTER TABLE auth.sessions OWNER TO supabase_auth_admin; - --- --- Name: TABLE sessions; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.sessions IS 'Auth: Stores session data associated to a user.'; - - --- --- Name: COLUMN sessions.not_after; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON COLUMN auth.sessions.not_after IS 'Auth: Not after is a nullable column that contains a timestamp after which the session should be regarded as expired.'; - - --- --- Name: sso_domains; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.sso_domains ( - id uuid NOT NULL, - sso_provider_id uuid NOT NULL, - domain text NOT NULL, - created_at timestamp with time zone, - updated_at timestamp with time zone, - CONSTRAINT "domain not empty" CHECK ((char_length(domain) > 0)) -); - - -ALTER TABLE auth.sso_domains OWNER TO supabase_auth_admin; - --- --- Name: TABLE sso_domains; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.sso_domains IS 'Auth: Manages SSO email address domain mapping to an SSO Identity Provider.'; - - --- --- Name: sso_providers; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.sso_providers ( - id uuid NOT NULL, - resource_id text, - created_at timestamp with time zone, - updated_at timestamp with time zone, - CONSTRAINT "resource_id not empty" CHECK (((resource_id = NULL::text) OR (char_length(resource_id) > 0))) -); - - -ALTER TABLE auth.sso_providers OWNER TO supabase_auth_admin; - --- --- Name: TABLE sso_providers; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.sso_providers IS 'Auth: Manages SSO identity provider information; see saml_providers for SAML.'; - - --- --- Name: COLUMN sso_providers.resource_id; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON COLUMN auth.sso_providers.resource_id IS 'Auth: Uniquely identifies a SSO provider according to a user-chosen resource ID (case insensitive), useful in infrastructure as code.'; - - --- --- Name: users; Type: TABLE; Schema: auth; Owner: supabase_auth_admin --- - -CREATE TABLE IF NOT EXISTS auth.users ( - instance_id uuid, - id uuid NOT NULL, - aud character varying(255), - role character varying(255), - email character varying(255), - encrypted_password character varying(255), - email_confirmed_at timestamp with time zone, - invited_at timestamp with time zone, - confirmation_token character varying(255), - confirmation_sent_at timestamp with time zone, - recovery_token character varying(255), - recovery_sent_at timestamp with time zone, - email_change_token_new character varying(255), - email_change character varying(255), - email_change_sent_at timestamp with time zone, - last_sign_in_at timestamp with time zone, - raw_app_meta_data jsonb, - raw_user_meta_data jsonb, - is_super_admin boolean, - created_at timestamp with time zone, - updated_at timestamp with time zone, - phone text DEFAULT NULL::character varying, - phone_confirmed_at timestamp with time zone, - phone_change text DEFAULT ''::character varying, - phone_change_token character varying(255) DEFAULT ''::character varying, - phone_change_sent_at timestamp with time zone, - confirmed_at timestamp with time zone GENERATED ALWAYS AS (LEAST(email_confirmed_at, phone_confirmed_at)) STORED, - email_change_token_current character varying(255) DEFAULT ''::character varying, - email_change_confirm_status smallint DEFAULT 0, - banned_until timestamp with time zone, - reauthentication_token character varying(255) DEFAULT ''::character varying, - reauthentication_sent_at timestamp with time zone, - is_sso_user boolean DEFAULT false NOT NULL, - deleted_at timestamp with time zone, - CONSTRAINT users_email_change_confirm_status_check CHECK (((email_change_confirm_status >= 0) AND (email_change_confirm_status <= 2))) -); - - -ALTER TABLE auth.users OWNER TO supabase_auth_admin; - --- --- Name: TABLE users; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON TABLE auth.users IS 'Auth: Stores user login data within a secure schema.'; - - --- --- Name: COLUMN users.is_sso_user; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON COLUMN auth.users.is_sso_user IS 'Auth: Set this column to true when the account comes from SSO. These accounts can have duplicate emails.'; - - --- --- Name: buckets; Type: TABLE; Schema: storage; Owner: supabase_storage_admin --- - -CREATE TABLE IF NOT EXISTS storage.buckets ( - id text NOT NULL, - name text NOT NULL, - owner uuid, - created_at timestamp with time zone DEFAULT now(), - updated_at timestamp with time zone DEFAULT now(), - public boolean DEFAULT false, - avif_autodetection boolean DEFAULT false, - file_size_limit bigint, - allowed_mime_types text[], - owner_id text -); - - -ALTER TABLE storage.buckets OWNER TO supabase_storage_admin; - --- --- Name: COLUMN buckets.owner; Type: COMMENT; Schema: storage; Owner: supabase_storage_admin --- - -COMMENT ON COLUMN storage.buckets.owner IS 'Field is deprecated, use owner_id instead'; - - --- --- Name: migrations; Type: TABLE; Schema: storage; Owner: supabase_storage_admin --- - -CREATE TABLE IF NOT EXISTS storage.migrations ( - id integer NOT NULL, - name character varying(100) NOT NULL, - hash character varying(40) NOT NULL, - executed_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP -); - - -ALTER TABLE storage.migrations OWNER TO supabase_storage_admin; - --- --- Name: objects; Type: TABLE; Schema: storage; Owner: supabase_storage_admin --- - -CREATE TABLE IF NOT EXISTS storage.objects ( - id uuid DEFAULT extensions.uuid_generate_v4() NOT NULL, - bucket_id text, - name text, - owner uuid, - created_at timestamp with time zone DEFAULT now(), - updated_at timestamp with time zone DEFAULT now(), - last_accessed_at timestamp with time zone DEFAULT now(), - metadata jsonb, - path_tokens text[] GENERATED ALWAYS AS (string_to_array(name, '/'::text)) STORED, - version text, - owner_id text -); - - -ALTER TABLE storage.objects OWNER TO supabase_storage_admin; - --- --- Name: COLUMN objects.owner; Type: COMMENT; Schema: storage; Owner: supabase_storage_admin --- - -COMMENT ON COLUMN storage.objects.owner IS 'Field is deprecated, use owner_id instead'; - - --- --- Name: hooks; Type: TABLE; Schema: supabase_functions; Owner: supabase_functions_admin --- - -CREATE TABLE IF NOT EXISTS supabase_functions.hooks ( - id bigint NOT NULL, - hook_table_id integer NOT NULL, - hook_name text NOT NULL, - created_at timestamp with time zone DEFAULT now() NOT NULL, - request_id bigint -); - - -ALTER TABLE supabase_functions.hooks OWNER TO supabase_functions_admin; - --- --- Name: TABLE hooks; Type: COMMENT; Schema: supabase_functions; Owner: supabase_functions_admin --- - -COMMENT ON TABLE supabase_functions.hooks IS 'Supabase Functions Hooks: Audit trail for triggered hooks.'; - - --- --- Name: hooks_id_seq; Type: SEQUENCE; Schema: supabase_functions; Owner: supabase_functions_admin --- - -CREATE SEQUENCE IF NOT EXISTS supabase_functions.hooks_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - -ALTER TABLE supabase_functions.hooks_id_seq OWNER TO supabase_functions_admin; - --- --- Name: hooks_id_seq; Type: SEQUENCE OWNED BY; Schema: supabase_functions; Owner: supabase_functions_admin --- - -ALTER SEQUENCE supabase_functions.hooks_id_seq OWNED BY supabase_functions.hooks.id; - - --- --- Name: migrations; Type: TABLE; Schema: supabase_functions; Owner: supabase_functions_admin --- - -CREATE TABLE IF NOT EXISTS supabase_functions.migrations ( - version text NOT NULL, - inserted_at timestamp with time zone DEFAULT now() NOT NULL -); - - -ALTER TABLE supabase_functions.migrations OWNER TO supabase_functions_admin; - --- --- Name: refresh_tokens id; Type: DEFAULT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.refresh_tokens ALTER COLUMN id SET DEFAULT nextval('auth.refresh_tokens_id_seq'::regclass); - - --- --- Name: hooks id; Type: DEFAULT; Schema: supabase_functions; Owner: supabase_functions_admin --- - -ALTER TABLE ONLY supabase_functions.hooks ALTER COLUMN id SET DEFAULT nextval('supabase_functions.hooks_id_seq'::regclass); - - --- --- Data for Name: extensions; Type: TABLE DATA; Schema: _realtime; Owner: postgres --- - -INSERT INTO _realtime.extensions (id, type, settings, tenant_external_id, inserted_at, updated_at) VALUES ('832b3aeb-9d7f-41aa-ad31-60ea054c294f', 'postgres_cdc_rls', '{"region": "us-east-1", "db_host": "ABK7kBu27y/PVdL10i/b+A==", "db_name": "sWBpZNdjggEPTQVlI52Zfw==", "db_port": "+enMDFi1J/3IrrquHHwUmA==", "db_user": "uxbEq/zz8DXVD53TOI1zmw==", "slot_name": "supabase_realtime_replication_slot", "ip_version": 4, "db_password": "sWBpZNdjggEPTQVlI52Zfw==", "publication": "supabase_realtime", "ssl_enforced": false, "poll_interval_ms": 100, "poll_max_changes": 100, "poll_max_record_bytes": 1048576}', 'realtime-dev', '2023-11-20 22:04:38', '2023-11-20 22:04:38'); - - --- --- Data for Name: schema_migrations; Type: TABLE DATA; Schema: _realtime; Owner: postgres --- - -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20210706140551, '2023-03-16 02:25:35'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220329161857, '2023-03-16 02:25:35'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220410212326, '2023-03-16 02:25:35'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220506102948, '2023-03-16 02:25:35'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220527210857, '2023-03-16 02:25:36'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220815211129, '2023-03-16 02:25:36'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220815215024, '2023-03-16 02:25:36'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20220818141501, '2023-03-16 02:25:36'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20221018173709, '2023-03-16 02:25:36'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20221102172703, '2023-03-16 02:25:36'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20221223010058, '2023-03-16 02:25:36'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20230110180046, '2023-03-16 02:25:36'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20230810220907, '2023-11-20 21:54:23'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20230810220924, '2023-11-20 21:54:23'); -INSERT INTO _realtime.schema_migrations (version, inserted_at) VALUES (20231024094642, '2023-11-20 21:54:23'); - - --- --- Data for Name: tenants; Type: TABLE DATA; Schema: _realtime; Owner: postgres --- - -INSERT INTO _realtime.tenants (id, name, external_id, jwt_secret, max_concurrent_users, inserted_at, updated_at, max_events_per_second, postgres_cdc_default, max_bytes_per_second, max_channels_per_client, max_joins_per_second, suspend) VALUES ('f418662c-07b5-4160-9338-4db7fbaaabbd', 'realtime-dev', 'realtime-dev', 'iNjicxc4+llvc9wovDvqymwfnj9teWMlyOIbJ8Fh6j2WNU8CIJ2ZgjR6MUIKqSmeDmvpsKLsZ9jgXJmQPpwL8w==', 200, '2023-11-20 22:04:38', '2023-11-20 22:04:38', 100, 'postgres_cdc_rls', 100000, 100, 100, false); - - --- --- Data for Name: audit_log_entries; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: flow_state; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: identities; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: instances; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: mfa_amr_claims; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: mfa_challenges; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: mfa_factors; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: refresh_tokens; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: saml_providers; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: saml_relay_states; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: schema_migrations; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - -INSERT INTO auth.schema_migrations (version) VALUES ('20171026211738'); -INSERT INTO auth.schema_migrations (version) VALUES ('20171026211808'); -INSERT INTO auth.schema_migrations (version) VALUES ('20171026211834'); -INSERT INTO auth.schema_migrations (version) VALUES ('20180103212743'); -INSERT INTO auth.schema_migrations (version) VALUES ('20180108183307'); -INSERT INTO auth.schema_migrations (version) VALUES ('20180119214651'); -INSERT INTO auth.schema_migrations (version) VALUES ('20180125194653'); -INSERT INTO auth.schema_migrations (version) VALUES ('00'); -INSERT INTO auth.schema_migrations (version) VALUES ('20210710035447'); -INSERT INTO auth.schema_migrations (version) VALUES ('20210722035447'); -INSERT INTO auth.schema_migrations (version) VALUES ('20210730183235'); -INSERT INTO auth.schema_migrations (version) VALUES ('20210909172000'); -INSERT INTO auth.schema_migrations (version) VALUES ('20210927181326'); -INSERT INTO auth.schema_migrations (version) VALUES ('20211122151130'); -INSERT INTO auth.schema_migrations (version) VALUES ('20211124214934'); -INSERT INTO auth.schema_migrations (version) VALUES ('20211202183645'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220114185221'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220114185340'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220224000811'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220323170000'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220429102000'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220531120530'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220614074223'); -INSERT INTO auth.schema_migrations (version) VALUES ('20220811173540'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221003041349'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221003041400'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221011041400'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221020193600'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221021073300'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221021082433'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221027105023'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221114143122'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221114143410'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221125140132'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221208132122'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221215195500'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221215195800'); -INSERT INTO auth.schema_migrations (version) VALUES ('20221215195900'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230116124310'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230116124412'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230131181311'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230322519590'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230402418590'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230411005111'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230508135423'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230523124323'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230818113222'); -INSERT INTO auth.schema_migrations (version) VALUES ('20230914180801'); -INSERT INTO auth.schema_migrations (version) VALUES ('20231027141322'); -INSERT INTO auth.schema_migrations (version) VALUES ('20231114161723'); -INSERT INTO auth.schema_migrations (version) VALUES ('20231117164230'); - - --- --- Data for Name: sessions; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: sso_domains; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: sso_providers; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: users; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin --- - - - --- --- Data for Name: buckets; Type: TABLE DATA; Schema: storage; Owner: supabase_storage_admin --- - - - --- --- Data for Name: migrations; Type: TABLE DATA; Schema: storage; Owner: supabase_storage_admin --- - -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (0, 'create-migrations-table', 'e18db593bcde2aca2a408c4d1100f6abba2195df', '2023-03-16 02:25:37.548482'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (1, 'initialmigration', '6ab16121fbaa08bbd11b712d05f358f9b555d777', '2023-03-16 02:25:37.55595'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (2, 'pathtoken-column', '49756be03be4c17bb85fe70d4a861f27de7e49ad', '2023-03-16 02:25:37.560258'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (3, 'add-migrations-rls', 'bb5d124c53d68635a883e399426c6a5a25fc893d', '2023-03-16 02:25:37.594153'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (4, 'add-size-functions', '6d79007d04f5acd288c9c250c42d2d5fd286c54d', '2023-03-16 02:25:37.599194'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (5, 'change-column-name-in-get-size', 'fd65688505d2ffa9fbdc58a944348dd8604d688c', '2023-03-16 02:25:37.604658'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (6, 'add-rls-to-buckets', '63e2bab75a2040fee8e3fb3f15a0d26f3380e9b6', '2023-03-16 02:25:37.609563'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (7, 'add-public-to-buckets', '82568934f8a4d9e0a85f126f6fb483ad8214c418', '2023-03-16 02:25:37.615929'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (8, 'fix-search-function', '1a43a40eddb525f2e2f26efd709e6c06e58e059c', '2023-03-16 02:25:37.620171'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (9, 'search-files-search-function', '34c096597eb8b9d077fdfdde9878c88501b2fafc', '2023-03-16 02:25:37.624484'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (10, 'add-trigger-to-auto-update-updated_at-column', '37d6bb964a70a822e6d37f22f457b9bca7885928', '2023-03-16 02:25:37.629534'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (11, 'add-automatic-avif-detection-flag', 'bd76c53a9c564c80d98d119c1b3a28e16c8152db', '2023-03-16 02:25:37.634022'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (12, 'add-bucket-custom-limits', 'cbe0a4c32a0e891554a21020433b7a4423c07ee7', '2023-03-16 02:25:37.639132'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (13, 'use-bytes-for-max-size', '7a158ebce8a0c2801c9c65b7e9b2f98f68b3874e', '2023-03-16 02:25:37.643598'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (14, 'add-can-insert-object-function', '273193826bca7e0990b458d1ba72f8aa27c0d825', '2023-04-06 09:18:55.232202'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (15, 'add-version', 'e821a779d26612899b8c2dfe20245f904a327c4f', '2023-04-06 09:18:55.239444'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (16, 'drop-owner-foreign-key', '536b33f8878eed09d0144219777dcac96bdb25da', '2023-11-20 21:54:33.540386'); -INSERT INTO storage.migrations (id, name, hash, executed_at) VALUES (17, 'add_owner_id_column_deprecate_owner', '7545f216a39358b5487df75d941d05dbcd75eb46', '2023-11-20 22:04:38.959686'); - - --- --- Data for Name: objects; Type: TABLE DATA; Schema: storage; Owner: supabase_storage_admin --- - - - --- --- Data for Name: hooks; Type: TABLE DATA; Schema: supabase_functions; Owner: supabase_functions_admin --- - - - --- --- Data for Name: migrations; Type: TABLE DATA; Schema: supabase_functions; Owner: supabase_functions_admin --- - -INSERT INTO supabase_functions.migrations (version, inserted_at) VALUES ('initial', '2023-03-16 02:25:30.966697+00'); -INSERT INTO supabase_functions.migrations (version, inserted_at) VALUES ('20210809183423_update_grants', '2023-03-16 02:25:30.966697+00'); - - --- --- Name: refresh_tokens_id_seq; Type: SEQUENCE SET; Schema: auth; Owner: supabase_auth_admin --- - -SELECT pg_catalog.setval('auth.refresh_tokens_id_seq', 1, false); - - --- --- Name: hooks_id_seq; Type: SEQUENCE SET; Schema: supabase_functions; Owner: supabase_functions_admin --- - -SELECT pg_catalog.setval('supabase_functions.hooks_id_seq', 1, false); - - --- --- Name: extensions extensions_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: postgres --- - -ALTER TABLE ONLY _realtime.extensions - ADD CONSTRAINT extensions_pkey PRIMARY KEY (id); - - --- --- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: postgres --- - -ALTER TABLE ONLY _realtime.schema_migrations - ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); - - --- --- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: postgres --- - -ALTER TABLE ONLY _realtime.tenants - ADD CONSTRAINT tenants_pkey PRIMARY KEY (id); - - --- --- Name: mfa_amr_claims amr_id_pk; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_amr_claims - ADD CONSTRAINT amr_id_pk PRIMARY KEY (id); - - --- --- Name: audit_log_entries audit_log_entries_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.audit_log_entries - ADD CONSTRAINT audit_log_entries_pkey PRIMARY KEY (id); - - --- --- Name: flow_state flow_state_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.flow_state - ADD CONSTRAINT flow_state_pkey PRIMARY KEY (id); - - --- --- Name: identities identities_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.identities - ADD CONSTRAINT identities_pkey PRIMARY KEY (id); - - --- --- Name: identities identities_provider_id_provider_unique; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.identities - ADD CONSTRAINT identities_provider_id_provider_unique UNIQUE (provider_id, provider); - - --- --- Name: instances instances_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.instances - ADD CONSTRAINT instances_pkey PRIMARY KEY (id); - - --- --- Name: mfa_amr_claims mfa_amr_claims_session_id_authentication_method_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_amr_claims - ADD CONSTRAINT mfa_amr_claims_session_id_authentication_method_pkey UNIQUE (session_id, authentication_method); - - --- --- Name: mfa_challenges mfa_challenges_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_challenges - ADD CONSTRAINT mfa_challenges_pkey PRIMARY KEY (id); - - --- --- Name: mfa_factors mfa_factors_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_factors - ADD CONSTRAINT mfa_factors_pkey PRIMARY KEY (id); - - --- --- Name: refresh_tokens refresh_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.refresh_tokens - ADD CONSTRAINT refresh_tokens_pkey PRIMARY KEY (id); - - --- --- Name: refresh_tokens refresh_tokens_token_unique; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.refresh_tokens - ADD CONSTRAINT refresh_tokens_token_unique UNIQUE (token); - - --- --- Name: saml_providers saml_providers_entity_id_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_providers - ADD CONSTRAINT saml_providers_entity_id_key UNIQUE (entity_id); - - --- --- Name: saml_providers saml_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_providers - ADD CONSTRAINT saml_providers_pkey PRIMARY KEY (id); - - --- --- Name: saml_relay_states saml_relay_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_relay_states - ADD CONSTRAINT saml_relay_states_pkey PRIMARY KEY (id); - - --- --- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.schema_migrations - ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); - - --- --- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sessions - ADD CONSTRAINT sessions_pkey PRIMARY KEY (id); - - --- --- Name: sso_domains sso_domains_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sso_domains - ADD CONSTRAINT sso_domains_pkey PRIMARY KEY (id); - - --- --- Name: sso_providers sso_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sso_providers - ADD CONSTRAINT sso_providers_pkey PRIMARY KEY (id); - - --- --- Name: users users_phone_key; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.users - ADD CONSTRAINT users_phone_key UNIQUE (phone); - - --- --- Name: users users_pkey; Type: CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.users - ADD CONSTRAINT users_pkey PRIMARY KEY (id); - - --- --- Name: buckets buckets_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.buckets - ADD CONSTRAINT buckets_pkey PRIMARY KEY (id); - - --- --- Name: migrations migrations_name_key; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.migrations - ADD CONSTRAINT migrations_name_key UNIQUE (name); - - --- --- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.migrations - ADD CONSTRAINT migrations_pkey PRIMARY KEY (id); - - --- --- Name: objects objects_pkey; Type: CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.objects - ADD CONSTRAINT objects_pkey PRIMARY KEY (id); - - --- --- Name: hooks hooks_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: supabase_functions_admin --- - -ALTER TABLE ONLY supabase_functions.hooks - ADD CONSTRAINT hooks_pkey PRIMARY KEY (id); - - --- --- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: supabase_functions_admin --- - -ALTER TABLE ONLY supabase_functions.migrations - ADD CONSTRAINT migrations_pkey PRIMARY KEY (version); - - --- --- Name: extensions_tenant_external_id_type_index; Type: INDEX; Schema: _realtime; Owner: postgres --- - -CREATE UNIQUE INDEX extensions_tenant_external_id_type_index ON _realtime.extensions USING btree (tenant_external_id, type); - - --- --- Name: tenants_external_id_index; Type: INDEX; Schema: _realtime; Owner: postgres --- - -CREATE UNIQUE INDEX tenants_external_id_index ON _realtime.tenants USING btree (external_id); - - --- --- Name: audit_logs_instance_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX audit_logs_instance_id_idx ON auth.audit_log_entries USING btree (instance_id); - - --- --- Name: confirmation_token_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX confirmation_token_idx ON auth.users USING btree (confirmation_token) WHERE ((confirmation_token)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: email_change_token_current_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX email_change_token_current_idx ON auth.users USING btree (email_change_token_current) WHERE ((email_change_token_current)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: email_change_token_new_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX email_change_token_new_idx ON auth.users USING btree (email_change_token_new) WHERE ((email_change_token_new)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: factor_id_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX factor_id_created_at_idx ON auth.mfa_factors USING btree (user_id, created_at); - - --- --- Name: flow_state_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX flow_state_created_at_idx ON auth.flow_state USING btree (created_at DESC); - - --- --- Name: identities_email_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX identities_email_idx ON auth.identities USING btree (email text_pattern_ops); - - --- --- Name: INDEX identities_email_idx; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON INDEX auth.identities_email_idx IS 'Auth: Ensures indexed queries on the email column'; - - --- --- Name: identities_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX identities_user_id_idx ON auth.identities USING btree (user_id); - - --- --- Name: idx_auth_code; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX idx_auth_code ON auth.flow_state USING btree (auth_code); - - --- --- Name: idx_user_id_auth_method; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX idx_user_id_auth_method ON auth.flow_state USING btree (user_id, authentication_method); - - --- --- Name: mfa_challenge_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX mfa_challenge_created_at_idx ON auth.mfa_challenges USING btree (created_at DESC); - - --- --- Name: mfa_factors_user_friendly_name_unique; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX mfa_factors_user_friendly_name_unique ON auth.mfa_factors USING btree (friendly_name, user_id) WHERE (TRIM(BOTH FROM friendly_name) <> ''::text); - - --- --- Name: mfa_factors_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX mfa_factors_user_id_idx ON auth.mfa_factors USING btree (user_id); - - --- --- Name: reauthentication_token_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX reauthentication_token_idx ON auth.users USING btree (reauthentication_token) WHERE ((reauthentication_token)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: recovery_token_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX recovery_token_idx ON auth.users USING btree (recovery_token) WHERE ((recovery_token)::text !~ '^[0-9 ]*$'::text); - - --- --- Name: refresh_tokens_instance_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_instance_id_idx ON auth.refresh_tokens USING btree (instance_id); - - --- --- Name: refresh_tokens_instance_id_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_instance_id_user_id_idx ON auth.refresh_tokens USING btree (instance_id, user_id); - - --- --- Name: refresh_tokens_parent_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_parent_idx ON auth.refresh_tokens USING btree (parent); - - --- --- Name: refresh_tokens_session_id_revoked_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_session_id_revoked_idx ON auth.refresh_tokens USING btree (session_id, revoked); - - --- --- Name: refresh_tokens_updated_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX refresh_tokens_updated_at_idx ON auth.refresh_tokens USING btree (updated_at DESC); - - --- --- Name: saml_providers_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX saml_providers_sso_provider_id_idx ON auth.saml_providers USING btree (sso_provider_id); - - --- --- Name: saml_relay_states_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX saml_relay_states_created_at_idx ON auth.saml_relay_states USING btree (created_at DESC); - - --- --- Name: saml_relay_states_for_email_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX saml_relay_states_for_email_idx ON auth.saml_relay_states USING btree (for_email); - - --- --- Name: saml_relay_states_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX saml_relay_states_sso_provider_id_idx ON auth.saml_relay_states USING btree (sso_provider_id); - - --- --- Name: sessions_not_after_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX sessions_not_after_idx ON auth.sessions USING btree (not_after DESC); - - --- --- Name: sessions_user_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX sessions_user_id_idx ON auth.sessions USING btree (user_id); - - --- --- Name: sso_domains_domain_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX sso_domains_domain_idx ON auth.sso_domains USING btree (lower(domain)); - - --- --- Name: sso_domains_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX sso_domains_sso_provider_id_idx ON auth.sso_domains USING btree (sso_provider_id); - - --- --- Name: sso_providers_resource_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX sso_providers_resource_id_idx ON auth.sso_providers USING btree (lower(resource_id)); - - --- --- Name: user_id_created_at_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX user_id_created_at_idx ON auth.sessions USING btree (user_id, created_at); - - --- --- Name: users_email_partial_key; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE UNIQUE INDEX users_email_partial_key ON auth.users USING btree (email) WHERE (is_sso_user = false); - - --- --- Name: INDEX users_email_partial_key; Type: COMMENT; Schema: auth; Owner: supabase_auth_admin --- - -COMMENT ON INDEX auth.users_email_partial_key IS 'Auth: A partial unique index that applies only when is_sso_user is false'; - - --- --- Name: users_instance_id_email_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX users_instance_id_email_idx ON auth.users USING btree (instance_id, lower((email)::text)); - - --- --- Name: users_instance_id_idx; Type: INDEX; Schema: auth; Owner: supabase_auth_admin --- - -CREATE INDEX users_instance_id_idx ON auth.users USING btree (instance_id); - - --- --- Name: bname; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE UNIQUE INDEX bname ON storage.buckets USING btree (name); - - --- --- Name: bucketid_objname; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE UNIQUE INDEX bucketid_objname ON storage.objects USING btree (bucket_id, name); - - --- --- Name: name_prefix_search; Type: INDEX; Schema: storage; Owner: supabase_storage_admin --- - -CREATE INDEX name_prefix_search ON storage.objects USING btree (name text_pattern_ops); - - --- --- Name: supabase_functions_hooks_h_table_id_h_name_idx; Type: INDEX; Schema: supabase_functions; Owner: supabase_functions_admin --- - -CREATE INDEX supabase_functions_hooks_h_table_id_h_name_idx ON supabase_functions.hooks USING btree (hook_table_id, hook_name); - - --- --- Name: supabase_functions_hooks_request_id_idx; Type: INDEX; Schema: supabase_functions; Owner: supabase_functions_admin --- - -CREATE INDEX supabase_functions_hooks_request_id_idx ON supabase_functions.hooks USING btree (request_id); - - --- --- Name: objects update_objects_updated_at; Type: TRIGGER; Schema: storage; Owner: supabase_storage_admin --- - -CREATE TRIGGER update_objects_updated_at BEFORE UPDATE ON storage.objects FOR EACH ROW EXECUTE FUNCTION storage.update_updated_at_column(); - - --- --- Name: extensions extensions_tenant_external_id_fkey; Type: FK CONSTRAINT; Schema: _realtime; Owner: postgres --- - -ALTER TABLE ONLY _realtime.extensions - ADD CONSTRAINT extensions_tenant_external_id_fkey FOREIGN KEY (tenant_external_id) REFERENCES _realtime.tenants(external_id) ON DELETE CASCADE; - - --- --- Name: identities identities_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.identities - ADD CONSTRAINT identities_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: mfa_amr_claims mfa_amr_claims_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_amr_claims - ADD CONSTRAINT mfa_amr_claims_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; - - --- --- Name: mfa_challenges mfa_challenges_auth_factor_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_challenges - ADD CONSTRAINT mfa_challenges_auth_factor_id_fkey FOREIGN KEY (factor_id) REFERENCES auth.mfa_factors(id) ON DELETE CASCADE; - - --- --- Name: mfa_factors mfa_factors_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.mfa_factors - ADD CONSTRAINT mfa_factors_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: refresh_tokens refresh_tokens_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.refresh_tokens - ADD CONSTRAINT refresh_tokens_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; - - --- --- Name: saml_providers saml_providers_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_providers - ADD CONSTRAINT saml_providers_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; - - --- --- Name: saml_relay_states saml_relay_states_flow_state_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_relay_states - ADD CONSTRAINT saml_relay_states_flow_state_id_fkey FOREIGN KEY (flow_state_id) REFERENCES auth.flow_state(id) ON DELETE CASCADE; - - --- --- Name: saml_relay_states saml_relay_states_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.saml_relay_states - ADD CONSTRAINT saml_relay_states_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; - - --- --- Name: sessions sessions_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sessions - ADD CONSTRAINT sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; - - --- --- Name: sso_domains sso_domains_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: supabase_auth_admin --- - -ALTER TABLE ONLY auth.sso_domains - ADD CONSTRAINT sso_domains_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; - - --- --- Name: objects objects_bucketId_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE ONLY storage.objects - ADD CONSTRAINT "objects_bucketId_fkey" FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); - - --- --- Name: buckets; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.buckets ENABLE ROW LEVEL SECURITY; - --- --- Name: migrations; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.migrations ENABLE ROW LEVEL SECURITY; - --- --- Name: objects; Type: ROW SECURITY; Schema: storage; Owner: supabase_storage_admin --- - -ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY; - --- --- Name: supabase_realtime; Type: PUBLICATION; Schema: -; Owner: postgres --- - -CREATE PUBLICATION supabase_realtime WITH (publish = 'insert, update, delete, truncate'); - - -ALTER PUBLICATION supabase_realtime OWNER TO postgres; - --- --- Name: SCHEMA auth; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA auth TO anon; -GRANT USAGE ON SCHEMA auth TO authenticated; -GRANT USAGE ON SCHEMA auth TO service_role; -GRANT ALL ON SCHEMA auth TO supabase_auth_admin; -GRANT ALL ON SCHEMA auth TO dashboard_user; -GRANT ALL ON SCHEMA auth TO postgres; - - --- --- Name: SCHEMA extensions; Type: ACL; Schema: -; Owner: postgres --- - -GRANT USAGE ON SCHEMA extensions TO anon; -GRANT USAGE ON SCHEMA extensions TO authenticated; -GRANT USAGE ON SCHEMA extensions TO service_role; -GRANT ALL ON SCHEMA extensions TO dashboard_user; - - --- --- Name: SCHEMA graphql_public; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA graphql_public TO postgres; -GRANT USAGE ON SCHEMA graphql_public TO anon; -GRANT USAGE ON SCHEMA graphql_public TO authenticated; -GRANT USAGE ON SCHEMA graphql_public TO service_role; - - --- --- Name: SCHEMA net; Type: ACL; Schema: -; Owner: postgres --- - -GRANT USAGE ON SCHEMA net TO supabase_functions_admin; -GRANT USAGE ON SCHEMA net TO anon; -GRANT USAGE ON SCHEMA net TO authenticated; -GRANT USAGE ON SCHEMA net TO service_role; - - --- --- Name: SCHEMA public; Type: ACL; Schema: -; Owner: postgres --- - -REVOKE USAGE ON SCHEMA public FROM PUBLIC; -GRANT ALL ON SCHEMA public TO PUBLIC; -GRANT USAGE ON SCHEMA public TO anon; -GRANT USAGE ON SCHEMA public TO authenticated; -GRANT USAGE ON SCHEMA public TO service_role; - - --- --- Name: SCHEMA realtime; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA realtime TO postgres; - - --- --- Name: SCHEMA storage; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT ALL ON SCHEMA storage TO postgres; -GRANT USAGE ON SCHEMA storage TO anon; -GRANT USAGE ON SCHEMA storage TO authenticated; -GRANT USAGE ON SCHEMA storage TO service_role; -GRANT ALL ON SCHEMA storage TO supabase_storage_admin; -GRANT ALL ON SCHEMA storage TO dashboard_user; - - --- --- Name: SCHEMA supabase_functions; Type: ACL; Schema: -; Owner: supabase_admin --- - -GRANT USAGE ON SCHEMA supabase_functions TO postgres; -GRANT USAGE ON SCHEMA supabase_functions TO anon; -GRANT USAGE ON SCHEMA supabase_functions TO authenticated; -GRANT USAGE ON SCHEMA supabase_functions TO service_role; -GRANT ALL ON SCHEMA supabase_functions TO supabase_functions_admin; - - --- --- Name: FUNCTION email(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.email() TO dashboard_user; - - --- --- Name: FUNCTION jwt(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.jwt() TO postgres; -GRANT ALL ON FUNCTION auth.jwt() TO dashboard_user; - - --- --- Name: FUNCTION role(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.role() TO dashboard_user; - - --- --- Name: FUNCTION uid(); Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON FUNCTION auth.uid() TO dashboard_user; - - --- --- Name: FUNCTION algorithm_sign(signables text, secret text, algorithm text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.algorithm_sign(signables text, secret text, algorithm text) TO dashboard_user; - - --- --- Name: FUNCTION armor(bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.armor(bytea) TO dashboard_user; - - --- --- Name: FUNCTION armor(bytea, text[], text[]); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.armor(bytea, text[], text[]) TO dashboard_user; - - --- --- Name: FUNCTION crypt(text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.crypt(text, text) TO dashboard_user; - - --- --- Name: FUNCTION dearmor(text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.dearmor(text) TO dashboard_user; - - --- --- Name: FUNCTION decrypt(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.decrypt(bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION decrypt_iv(bytea, bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.decrypt_iv(bytea, bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION digest(bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.digest(bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION digest(text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.digest(text, text) TO dashboard_user; - - --- --- Name: FUNCTION encrypt(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.encrypt(bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION encrypt_iv(bytea, bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.encrypt_iv(bytea, bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION gen_random_bytes(integer); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.gen_random_bytes(integer) TO dashboard_user; - - --- --- Name: FUNCTION gen_random_uuid(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.gen_random_uuid() TO dashboard_user; - - --- --- Name: FUNCTION gen_salt(text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.gen_salt(text) TO dashboard_user; - - --- --- Name: FUNCTION gen_salt(text, integer); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.gen_salt(text, integer) TO dashboard_user; - - --- --- Name: FUNCTION grant_pg_cron_access(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.grant_pg_cron_access() TO dashboard_user; - - --- --- Name: FUNCTION grant_pg_net_access(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.grant_pg_net_access() TO dashboard_user; - - --- --- Name: FUNCTION hmac(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.hmac(bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION hmac(text, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.hmac(text, text, text) TO dashboard_user; - - --- --- Name: FUNCTION pg_stat_statements(showtext boolean, OUT userid oid, OUT dbid oid, OUT toplevel boolean, OUT queryid bigint, OUT query text, OUT plans bigint, OUT total_plan_time double precision, OUT min_plan_time double precision, OUT max_plan_time double precision, OUT mean_plan_time double precision, OUT stddev_plan_time double precision, OUT calls bigint, OUT total_exec_time double precision, OUT min_exec_time double precision, OUT max_exec_time double precision, OUT mean_exec_time double precision, OUT stddev_exec_time double precision, OUT rows bigint, OUT shared_blks_hit bigint, OUT shared_blks_read bigint, OUT shared_blks_dirtied bigint, OUT shared_blks_written bigint, OUT local_blks_hit bigint, OUT local_blks_read bigint, OUT local_blks_dirtied bigint, OUT local_blks_written bigint, OUT temp_blks_read bigint, OUT temp_blks_written bigint, OUT blk_read_time double precision, OUT blk_write_time double precision, OUT wal_records bigint, OUT wal_fpi bigint, OUT wal_bytes numeric); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pg_stat_statements(showtext boolean, OUT userid oid, OUT dbid oid, OUT toplevel boolean, OUT queryid bigint, OUT query text, OUT plans bigint, OUT total_plan_time double precision, OUT min_plan_time double precision, OUT max_plan_time double precision, OUT mean_plan_time double precision, OUT stddev_plan_time double precision, OUT calls bigint, OUT total_exec_time double precision, OUT min_exec_time double precision, OUT max_exec_time double precision, OUT mean_exec_time double precision, OUT stddev_exec_time double precision, OUT rows bigint, OUT shared_blks_hit bigint, OUT shared_blks_read bigint, OUT shared_blks_dirtied bigint, OUT shared_blks_written bigint, OUT local_blks_hit bigint, OUT local_blks_read bigint, OUT local_blks_dirtied bigint, OUT local_blks_written bigint, OUT temp_blks_read bigint, OUT temp_blks_written bigint, OUT blk_read_time double precision, OUT blk_write_time double precision, OUT wal_records bigint, OUT wal_fpi bigint, OUT wal_bytes numeric) TO dashboard_user; - - --- --- Name: FUNCTION pg_stat_statements_info(OUT dealloc bigint, OUT stats_reset timestamp with time zone); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pg_stat_statements_info(OUT dealloc bigint, OUT stats_reset timestamp with time zone) TO dashboard_user; - - --- --- Name: FUNCTION pg_stat_statements_reset(userid oid, dbid oid, queryid bigint); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pg_stat_statements_reset(userid oid, dbid oid, queryid bigint) TO dashboard_user; - - --- --- Name: FUNCTION pgp_armor_headers(text, OUT key text, OUT value text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_armor_headers(text, OUT key text, OUT value text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_key_id(bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_key_id(bytea) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_decrypt(bytea, bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_decrypt(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_decrypt(bytea, bytea, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_decrypt_bytea(bytea, bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_encrypt(text, bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt(text, bytea) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_encrypt(text, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt(text, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_encrypt_bytea(bytea, bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea) TO dashboard_user; - - --- --- Name: FUNCTION pgp_pub_encrypt_bytea(bytea, bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_decrypt(bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt(bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_decrypt(bytea, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt(bytea, text, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_decrypt_bytea(bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_decrypt_bytea(bytea, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_encrypt(text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt(text, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_encrypt(text, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt(text, text, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_encrypt_bytea(bytea, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text) TO dashboard_user; - - --- --- Name: FUNCTION pgp_sym_encrypt_bytea(bytea, text, text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text, text) TO dashboard_user; - - --- --- Name: FUNCTION sign(payload json, secret text, algorithm text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.sign(payload json, secret text, algorithm text) TO dashboard_user; - - --- --- Name: FUNCTION try_cast_double(inp text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.try_cast_double(inp text) TO dashboard_user; - - --- --- Name: FUNCTION url_decode(data text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.url_decode(data text) TO dashboard_user; - - --- --- Name: FUNCTION url_encode(data bytea); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.url_encode(data bytea) TO dashboard_user; - - --- --- Name: FUNCTION uuid_generate_v1(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v1() TO dashboard_user; - - --- --- Name: FUNCTION uuid_generate_v1mc(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v1mc() TO dashboard_user; - - --- --- Name: FUNCTION uuid_generate_v3(namespace uuid, name text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v3(namespace uuid, name text) TO dashboard_user; - - --- --- Name: FUNCTION uuid_generate_v4(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v4() TO dashboard_user; - - --- --- Name: FUNCTION uuid_generate_v5(namespace uuid, name text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_generate_v5(namespace uuid, name text) TO dashboard_user; - - --- --- Name: FUNCTION uuid_nil(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_nil() TO dashboard_user; - - --- --- Name: FUNCTION uuid_ns_dns(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_dns() TO dashboard_user; - - --- --- Name: FUNCTION uuid_ns_oid(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_oid() TO dashboard_user; - - --- --- Name: FUNCTION uuid_ns_url(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_url() TO dashboard_user; - - --- --- Name: FUNCTION uuid_ns_x500(); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.uuid_ns_x500() TO dashboard_user; - - --- --- Name: FUNCTION verify(token text, secret text, algorithm text); Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON FUNCTION extensions.verify(token text, secret text, algorithm text) TO dashboard_user; - - --- --- Name: FUNCTION comment_directive(comment_ text); Type: ACL; Schema: graphql; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION graphql.comment_directive(comment_ text) TO postgres; -GRANT ALL ON FUNCTION graphql.comment_directive(comment_ text) TO anon; -GRANT ALL ON FUNCTION graphql.comment_directive(comment_ text) TO authenticated; -GRANT ALL ON FUNCTION graphql.comment_directive(comment_ text) TO service_role; - - --- --- Name: FUNCTION exception(message text); Type: ACL; Schema: graphql; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION graphql.exception(message text) TO postgres; -GRANT ALL ON FUNCTION graphql.exception(message text) TO anon; -GRANT ALL ON FUNCTION graphql.exception(message text) TO authenticated; -GRANT ALL ON FUNCTION graphql.exception(message text) TO service_role; - - --- --- Name: FUNCTION get_schema_version(); Type: ACL; Schema: graphql; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION graphql.get_schema_version() TO postgres; -GRANT ALL ON FUNCTION graphql.get_schema_version() TO anon; -GRANT ALL ON FUNCTION graphql.get_schema_version() TO authenticated; -GRANT ALL ON FUNCTION graphql.get_schema_version() TO service_role; - - --- --- Name: FUNCTION increment_schema_version(); Type: ACL; Schema: graphql; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION graphql.increment_schema_version() TO postgres; -GRANT ALL ON FUNCTION graphql.increment_schema_version() TO anon; -GRANT ALL ON FUNCTION graphql.increment_schema_version() TO authenticated; -GRANT ALL ON FUNCTION graphql.increment_schema_version() TO service_role; - - --- --- Name: FUNCTION sequential_executor(prepared_statement_names text[]); Type: ACL; Schema: graphql; Owner: supabase_admin --- - -GRANT ALL ON FUNCTION graphql.sequential_executor(prepared_statement_names text[]) TO postgres; -GRANT ALL ON FUNCTION graphql.sequential_executor(prepared_statement_names text[]) TO anon; -GRANT ALL ON FUNCTION graphql.sequential_executor(prepared_statement_names text[]) TO authenticated; -GRANT ALL ON FUNCTION graphql.sequential_executor(prepared_statement_names text[]) TO service_role; - - --- --- Name: FUNCTION graphql("operationName" text, query text, variables jsonb, extensions jsonb); Type: ACL; Schema: graphql_public; Owner: supabase_admin --- - --- GRANT ALL ON FUNCTION graphql_public.graphql("operationName" text, query text, variables jsonb, extensions jsonb) TO postgres; --- GRANT ALL ON FUNCTION graphql_public.graphql("operationName" text, query text, variables jsonb, extensions jsonb) TO anon; --- GRANT ALL ON FUNCTION graphql_public.graphql("operationName" text, query text, variables jsonb, extensions jsonb) TO authenticated; --- GRANT ALL ON FUNCTION graphql_public.graphql("operationName" text, query text, variables jsonb, extensions jsonb) TO service_role; - - --- --- Name: FUNCTION http_collect_response(request_id bigint, async boolean); Type: ACL; Schema: net; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION net.http_collect_response(request_id bigint, async boolean) FROM PUBLIC; -GRANT ALL ON FUNCTION net.http_collect_response(request_id bigint, async boolean) TO supabase_functions_admin; -GRANT ALL ON FUNCTION net.http_collect_response(request_id bigint, async boolean) TO postgres; -GRANT ALL ON FUNCTION net.http_collect_response(request_id bigint, async boolean) TO anon; -GRANT ALL ON FUNCTION net.http_collect_response(request_id bigint, async boolean) TO authenticated; -GRANT ALL ON FUNCTION net.http_collect_response(request_id bigint, async boolean) TO service_role; - - --- --- Name: FUNCTION http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer); Type: ACL; Schema: net; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO postgres; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO anon; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO authenticated; -GRANT ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO service_role; - - --- --- Name: FUNCTION http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer); Type: ACL; Schema: net; Owner: supabase_admin --- - -REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO postgres; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO anon; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO authenticated; -GRANT ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO service_role; - - --- --- Name: FUNCTION get_auth(p_usename text); Type: ACL; Schema: pgbouncer; Owner: postgres --- - -REVOKE ALL ON FUNCTION pgbouncer.get_auth(p_usename text) FROM PUBLIC; -GRANT ALL ON FUNCTION pgbouncer.get_auth(p_usename text) TO pgbouncer; - - --- --- Name: SEQUENCE key_key_id_seq; Type: ACL; Schema: pgsodium; Owner: postgres --- - -GRANT ALL ON SEQUENCE pgsodium.key_key_id_seq TO pgsodium_keyiduser; - - --- --- Name: FUNCTION extension(name text); Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON FUNCTION storage.extension(name text) TO anon; -GRANT ALL ON FUNCTION storage.extension(name text) TO authenticated; -GRANT ALL ON FUNCTION storage.extension(name text) TO service_role; -GRANT ALL ON FUNCTION storage.extension(name text) TO dashboard_user; -GRANT ALL ON FUNCTION storage.extension(name text) TO postgres; - - --- --- Name: FUNCTION filename(name text); Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON FUNCTION storage.filename(name text) TO anon; -GRANT ALL ON FUNCTION storage.filename(name text) TO authenticated; -GRANT ALL ON FUNCTION storage.filename(name text) TO service_role; -GRANT ALL ON FUNCTION storage.filename(name text) TO dashboard_user; -GRANT ALL ON FUNCTION storage.filename(name text) TO postgres; - - --- --- Name: FUNCTION foldername(name text); Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON FUNCTION storage.foldername(name text) TO anon; -GRANT ALL ON FUNCTION storage.foldername(name text) TO authenticated; -GRANT ALL ON FUNCTION storage.foldername(name text) TO service_role; -GRANT ALL ON FUNCTION storage.foldername(name text) TO dashboard_user; -GRANT ALL ON FUNCTION storage.foldername(name text) TO postgres; - - --- --- Name: FUNCTION http_request(); Type: ACL; Schema: supabase_functions; Owner: supabase_functions_admin --- - -REVOKE ALL ON FUNCTION supabase_functions.http_request() FROM PUBLIC; -GRANT ALL ON FUNCTION supabase_functions.http_request() TO postgres; -GRANT ALL ON FUNCTION supabase_functions.http_request() TO anon; -GRANT ALL ON FUNCTION supabase_functions.http_request() TO authenticated; -GRANT ALL ON FUNCTION supabase_functions.http_request() TO service_role; - - --- --- Name: TABLE audit_log_entries; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.audit_log_entries TO dashboard_user; -GRANT ALL ON TABLE auth.audit_log_entries TO postgres; - - --- --- Name: TABLE flow_state; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.flow_state TO postgres; -GRANT ALL ON TABLE auth.flow_state TO dashboard_user; - - --- --- Name: TABLE identities; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.identities TO postgres; -GRANT ALL ON TABLE auth.identities TO dashboard_user; - - --- --- Name: TABLE instances; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.instances TO dashboard_user; -GRANT ALL ON TABLE auth.instances TO postgres; - - --- --- Name: TABLE mfa_amr_claims; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.mfa_amr_claims TO postgres; -GRANT ALL ON TABLE auth.mfa_amr_claims TO dashboard_user; - - --- --- Name: TABLE mfa_challenges; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.mfa_challenges TO postgres; -GRANT ALL ON TABLE auth.mfa_challenges TO dashboard_user; - - --- --- Name: TABLE mfa_factors; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.mfa_factors TO postgres; -GRANT ALL ON TABLE auth.mfa_factors TO dashboard_user; - - --- --- Name: TABLE refresh_tokens; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.refresh_tokens TO dashboard_user; -GRANT ALL ON TABLE auth.refresh_tokens TO postgres; - - --- --- Name: SEQUENCE refresh_tokens_id_seq; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON SEQUENCE auth.refresh_tokens_id_seq TO dashboard_user; -GRANT ALL ON SEQUENCE auth.refresh_tokens_id_seq TO postgres; - - --- --- Name: TABLE saml_providers; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.saml_providers TO postgres; -GRANT ALL ON TABLE auth.saml_providers TO dashboard_user; - - --- --- Name: TABLE saml_relay_states; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.saml_relay_states TO postgres; -GRANT ALL ON TABLE auth.saml_relay_states TO dashboard_user; - - --- --- Name: TABLE schema_migrations; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.schema_migrations TO dashboard_user; -GRANT ALL ON TABLE auth.schema_migrations TO postgres; - - --- --- Name: TABLE sessions; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.sessions TO postgres; -GRANT ALL ON TABLE auth.sessions TO dashboard_user; - - --- --- Name: TABLE sso_domains; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.sso_domains TO postgres; -GRANT ALL ON TABLE auth.sso_domains TO dashboard_user; - - --- --- Name: TABLE sso_providers; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.sso_providers TO postgres; -GRANT ALL ON TABLE auth.sso_providers TO dashboard_user; - - --- --- Name: TABLE users; Type: ACL; Schema: auth; Owner: supabase_auth_admin --- - -GRANT ALL ON TABLE auth.users TO dashboard_user; -GRANT ALL ON TABLE auth.users TO postgres; - - --- --- Name: TABLE pg_stat_statements; Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON TABLE extensions.pg_stat_statements TO dashboard_user; - - --- --- Name: TABLE pg_stat_statements_info; Type: ACL; Schema: extensions; Owner: postgres --- - -GRANT ALL ON TABLE extensions.pg_stat_statements_info TO dashboard_user; - - --- --- Name: SEQUENCE seq_schema_version; Type: ACL; Schema: graphql; Owner: supabase_admin --- - -GRANT ALL ON SEQUENCE graphql.seq_schema_version TO postgres; -GRANT ALL ON SEQUENCE graphql.seq_schema_version TO anon; -GRANT ALL ON SEQUENCE graphql.seq_schema_version TO authenticated; -GRANT ALL ON SEQUENCE graphql.seq_schema_version TO service_role; - - --- --- Name: TABLE valid_key; Type: ACL; Schema: pgsodium; Owner: postgres --- - -GRANT ALL ON TABLE pgsodium.valid_key TO pgsodium_keyiduser; - - --- --- Name: TABLE buckets; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.buckets TO anon; -GRANT ALL ON TABLE storage.buckets TO authenticated; -GRANT ALL ON TABLE storage.buckets TO service_role; -GRANT ALL ON TABLE storage.buckets TO postgres; - - --- --- Name: TABLE migrations; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.migrations TO anon; -GRANT ALL ON TABLE storage.migrations TO authenticated; -GRANT ALL ON TABLE storage.migrations TO service_role; -GRANT ALL ON TABLE storage.migrations TO postgres; - - --- --- Name: TABLE objects; Type: ACL; Schema: storage; Owner: supabase_storage_admin --- - -GRANT ALL ON TABLE storage.objects TO anon; -GRANT ALL ON TABLE storage.objects TO authenticated; -GRANT ALL ON TABLE storage.objects TO service_role; -GRANT ALL ON TABLE storage.objects TO postgres; - - --- --- Name: TABLE hooks; Type: ACL; Schema: supabase_functions; Owner: supabase_functions_admin --- - -GRANT ALL ON TABLE supabase_functions.hooks TO postgres; -GRANT ALL ON TABLE supabase_functions.hooks TO anon; -GRANT ALL ON TABLE supabase_functions.hooks TO authenticated; -GRANT ALL ON TABLE supabase_functions.hooks TO service_role; - - --- --- Name: SEQUENCE hooks_id_seq; Type: ACL; Schema: supabase_functions; Owner: supabase_functions_admin --- - -GRANT ALL ON SEQUENCE supabase_functions.hooks_id_seq TO postgres; -GRANT ALL ON SEQUENCE supabase_functions.hooks_id_seq TO anon; -GRANT ALL ON SEQUENCE supabase_functions.hooks_id_seq TO authenticated; -GRANT ALL ON SEQUENCE supabase_functions.hooks_id_seq TO service_role; - - --- --- Name: TABLE migrations; Type: ACL; Schema: supabase_functions; Owner: supabase_functions_admin --- - -GRANT ALL ON TABLE supabase_functions.migrations TO postgres; -GRANT ALL ON TABLE supabase_functions.migrations TO anon; -GRANT ALL ON TABLE supabase_functions.migrations TO authenticated; -GRANT ALL ON TABLE supabase_functions.migrations TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: auth; Owner: supabase_auth_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON SEQUENCES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: auth; Owner: supabase_auth_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON FUNCTIONS TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: auth; Owner: supabase_auth_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_auth_admin IN SCHEMA auth GRANT ALL ON TABLES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: graphql; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: graphql; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA graphql GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA graphql GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA graphql GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA graphql GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: graphql; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: graphql; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA graphql GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: graphql; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: graphql; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA graphql GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA graphql GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA graphql GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA graphql GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: graphql_public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: graphql_public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: graphql_public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA graphql_public GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: pgsodium; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA pgsodium GRANT ALL ON SEQUENCES TO pgsodium_keyiduser; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: pgsodium; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA pgsodium GRANT ALL ON TABLES TO pgsodium_keyiduser; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: public; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: realtime; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON SEQUENCES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: realtime; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON FUNCTIONS TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: realtime; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA realtime GRANT ALL ON TABLES TO dashboard_user; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: storage; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: storage; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: storage; Owner: postgres --- - -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA storage GRANT ALL ON TABLES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: supabase_functions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: supabase_functions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO service_role; - - --- --- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: supabase_functions; Owner: supabase_admin --- - -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON TABLES TO postgres; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON TABLES TO anon; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON TABLES TO authenticated; -ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA supabase_functions GRANT ALL ON TABLES TO service_role; - - --- --- Name: issue_graphql_placeholder; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER issue_graphql_placeholder ON sql_drop - WHEN TAG IN ('DROP EXTENSION') - EXECUTE FUNCTION extensions.set_graphql_placeholder(); - - -ALTER EVENT TRIGGER issue_graphql_placeholder OWNER TO supabase_admin; - --- --- Name: issue_pg_cron_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER issue_pg_cron_access ON ddl_command_end - WHEN TAG IN ('CREATE SCHEMA') - EXECUTE FUNCTION extensions.grant_pg_cron_access(); - - -ALTER EVENT TRIGGER issue_pg_cron_access OWNER TO supabase_admin; - --- --- Name: issue_pg_graphql_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER issue_pg_graphql_access ON ddl_command_end - WHEN TAG IN ('CREATE FUNCTION') - EXECUTE FUNCTION extensions.grant_pg_graphql_access(); - - -ALTER EVENT TRIGGER issue_pg_graphql_access OWNER TO supabase_admin; - --- --- Name: issue_pg_net_access; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end - WHEN TAG IN ('CREATE EXTENSION') - EXECUTE FUNCTION extensions.grant_pg_net_access(); - - -ALTER EVENT TRIGGER issue_pg_net_access OWNER TO supabase_admin; - --- --- Name: pgrst_ddl_watch; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER pgrst_ddl_watch ON ddl_command_end - EXECUTE FUNCTION extensions.pgrst_ddl_watch(); - - -ALTER EVENT TRIGGER pgrst_ddl_watch OWNER TO supabase_admin; - --- --- Name: pgrst_drop_watch; Type: EVENT TRIGGER; Schema: -; Owner: supabase_admin --- - -CREATE EVENT TRIGGER pgrst_drop_watch ON sql_drop - EXECUTE FUNCTION extensions.pgrst_drop_watch(); - - -ALTER EVENT TRIGGER pgrst_drop_watch OWNER TO supabase_admin; - --- --- PostgreSQL database dump complete --- - -DROP EXTENSION pg_graphql; CREATE EXTENSION pg_graphql WITH SCHEMA graphql; -RESET ALL; diff --git a/apps/cli-go/internal/utils/templates/initial_schemas/README.md b/apps/cli-go/internal/utils/templates/initial_schemas/README.md deleted file mode 100644 index 77cfae04c6..0000000000 --- a/apps/cli-go/internal/utils/templates/initial_schemas/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# About Initial Schemas - -These SQL files represent initial schemas needed to set up the database with Supabase stuff. These need to be manually generated for each Postgres major version. Which initial schema used depends on the Docker image tag used to run the local db, which in turn depends on the CLI's `db.major_version` config. - -The initial schema for PG12 is not available because the latest image (`supabase/postgres:12.5.0`) doesn't contain `wal2json`, which is required for Realtime to work. - -# Why use the pg_dump output instead of running the `init.sql` directly? - -Because Realtime, GoTrue, Logflare, and Storage have their own migrations, and these need to be included in the initial schema for e.g. `supabase db reset` to work. - -# How to Generate Initial Schemas - -1. Start supabase local development stack with default config - -```bash -go run . init -go run . start -x gotrue,storage-api,imgproxy -``` - -2. Run the initial dump script and pipe output to `15.sql` - -```bash -./tools/dump_initial_schema.sh > internal/utils/templates/initial_schemas/15.sql -``` - -3. Commit changes that are relevant for the update - -- `INSERT INTO _realtime.extensions` and `_realtime.tenants` usually do not require updating. -- `INSERT INTO *.schema_migrations` statements are only required if there are new migrations. -- `ALTER EVENT TRIGGER issue_pg_cron_access OWNER TO` should be followed by `supabase_admin`. - -4. Set `major_version = 14` in `supabase/config.toml` and repeat steps 1-3 to update `14.sql` -5. Set `major_version = 13` in `supabase/config.toml` and repeat steps 1-3 to update `13.sql` diff --git a/apps/cli-go/internal/utils/tenant/client.go b/apps/cli-go/internal/utils/tenant/client.go deleted file mode 100644 index 912387739e..0000000000 --- a/apps/cli-go/internal/utils/tenant/client.go +++ /dev/null @@ -1,97 +0,0 @@ -package tenant - -import ( - "context" - "strings" - - "github.com/go-errors/errors" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" - "github.com/supabase/cli/pkg/fetcher" -) - -var ( - ErrAuthToken = errors.New("Authorization failed for the access token and project ref pair") - errMissingKey = errors.New("Anon key not found.") -) - -type ApiKey struct { - Anon string - ServiceRole string -} - -func (a ApiKey) IsEmpty() bool { - return len(a.Anon) == 0 && len(a.ServiceRole) == 0 -} - -func NewApiKey(resp []api.ApiKeyResponseOutput) ApiKey { - var result ApiKey - for _, key := range resp { - value, err := key.ApiKey.Get() - if err != nil { - continue - } - if t, err := key.Type.Get(); err == nil { - switch t { - case api.ApiKeyResponseOutputTypePublishable: - result.Anon = value - continue - case api.ApiKeyResponseOutputTypeSecret: - if isServiceRole(key) { - result.ServiceRole = value - } - continue - } - } - switch key.Name { - case "anon": - if len(result.Anon) == 0 { - result.Anon = value - } - case "service_role": - if len(result.ServiceRole) == 0 { - result.ServiceRole = value - } - } - } - return result -} - -func isServiceRole(key api.ApiKeyResponseOutput) bool { - if tmpl, err := key.SecretJwtTemplate.Get(); err == nil { - if role, ok := tmpl["role"].(string); ok { - return strings.EqualFold(role, "service_role") - } - } - return false -} - -func GetApiKeys(ctx context.Context, projectRef string) (ApiKey, error) { - resp, err := utils.GetSupabase().V1GetProjectApiKeysWithResponse(ctx, projectRef, &api.V1GetProjectApiKeysParams{ - Reveal: cast.Ptr("true"), - }) - if err != nil { - return ApiKey{}, errors.Errorf("failed to get api keys: %w", err) - } - if resp.JSON200 == nil { - return ApiKey{}, errors.Errorf("%w: %s", ErrAuthToken, string(resp.Body)) - } - keys := NewApiKey(*resp.JSON200) - if keys.IsEmpty() { - return ApiKey{}, errors.New(errMissingKey) - } - return keys, nil -} - -type TenantAPI struct { - *fetcher.Fetcher -} - -func NewTenantAPI(ctx context.Context, projectRef, serviceKey string) TenantAPI { - return TenantAPI{Fetcher: fetcher.NewServiceGateway( - "https://"+utils.GetSupabaseHost(projectRef), - serviceKey, - fetcher.WithUserAgent("SupabaseCLI/"+utils.Version), - )} -} diff --git a/apps/cli-go/internal/utils/tenant/client_test.go b/apps/cli-go/internal/utils/tenant/client_test.go deleted file mode 100644 index ac29cfc403..0000000000 --- a/apps/cli-go/internal/utils/tenant/client_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package tenant - -import ( - "context" - "net/http" - "testing" - - "github.com/h2non/gock" - "github.com/oapi-codegen/nullable" - "github.com/stretchr/testify/assert" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/api" -) - -func TestApiKey(t *testing.T) { - t.Run("creates api key from response", func(t *testing.T) { - resp := []api.ApiKeyResponseOutput{ - {Name: "anon", ApiKey: nullable.NewNullableWithValue("anon-key")}, - {Name: "service_role", ApiKey: nullable.NewNullableWithValue("service-key")}, - } - - keys := NewApiKey(resp) - - assert.Equal(t, "anon-key", keys.Anon) - assert.Equal(t, "service-key", keys.ServiceRole) - assert.False(t, keys.IsEmpty()) - }) - - t.Run("handles empty response", func(t *testing.T) { - resp := []api.ApiKeyResponseOutput{ - {Name: "service_role", ApiKey: nullable.NewNullNullable[string]()}, - } - - keys := NewApiKey(resp) - - assert.Empty(t, keys.Anon) - assert.Empty(t, keys.ServiceRole) - assert.True(t, keys.IsEmpty()) - }) - - t.Run("handles partial response", func(t *testing.T) { - resp := []api.ApiKeyResponseOutput{ - {Name: "anon", ApiKey: nullable.NewNullableWithValue("anon-key")}, - } - - keys := NewApiKey(resp) - - assert.Equal(t, "anon-key", keys.Anon) - assert.Empty(t, keys.ServiceRole) - assert.False(t, keys.IsEmpty()) - }) -} - -func TestGetApiKeys(t *testing.T) { - t.Run("retrieves api keys successfully", func(t *testing.T) { - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - - defer gock.OffAll() - projectRef := apitest.RandomProjectRef() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + projectRef + "/api-keys"). - Reply(http.StatusOK). - JSON([]api.ApiKeyResponseOutput{ - {Name: "anon", ApiKey: nullable.NewNullableWithValue("anon-key")}, - {Name: "service_role", ApiKey: nullable.NewNullableWithValue("service-key")}, - }) - - keys, err := GetApiKeys(context.Background(), projectRef) - - assert.NoError(t, err) - assert.Equal(t, "anon-key", keys.Anon) - assert.Equal(t, "service-key", keys.ServiceRole) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("handles network error", func(t *testing.T) { - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - - defer gock.OffAll() - projectRef := apitest.RandomProjectRef() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + projectRef + "/api-keys"). - ReplyError(gock.ErrCannotMatch) - - keys, err := GetApiKeys(context.Background(), projectRef) - - assert.Error(t, err) - assert.Contains(t, err.Error(), "failed to get api keys") - assert.True(t, keys.IsEmpty()) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("handles unauthorized error", func(t *testing.T) { - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - - defer gock.OffAll() - projectRef := apitest.RandomProjectRef() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + projectRef + "/api-keys"). - Reply(http.StatusUnauthorized). - JSON(map[string]string{"message": "unauthorized"}) - - keys, err := GetApiKeys(context.Background(), projectRef) - - assert.ErrorIs(t, err, ErrAuthToken) - assert.True(t, keys.IsEmpty()) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("handles missing anon key", func(t *testing.T) { - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - - defer gock.OffAll() - projectRef := apitest.RandomProjectRef() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + projectRef + "/api-keys"). - Reply(http.StatusOK). - JSON([]api.ApiKeyResponseOutput{}) // should this error if response has only service_role key? - - keys, err := GetApiKeys(context.Background(), projectRef) - - assert.ErrorIs(t, err, errMissingKey) - assert.True(t, keys.IsEmpty()) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestNewTenantAPI(t *testing.T) { - projectRef := apitest.RandomProjectRef() - anonKey := "test-key" - - api := NewTenantAPI(context.Background(), projectRef, anonKey) - assert.NotNil(t, api.Fetcher) - - defer gock.OffAll() - gock.New("https://"+utils.GetSupabaseHost(projectRef)). - Get("/test"). - MatchHeader("Authorization", "Bearer "+anonKey). - MatchHeader("User-Agent", "SupabaseCLI/"+utils.Version). - Reply(http.StatusOK) - - _, err := api.Send(context.Background(), http.MethodGet, "/test", nil) - - assert.NoError(t, err) - assert.Empty(t, apitest.ListUnmatchedRequests()) -} diff --git a/apps/cli-go/internal/utils/tenant/database.go b/apps/cli-go/internal/utils/tenant/database.go deleted file mode 100644 index ababf74af2..0000000000 --- a/apps/cli-go/internal/utils/tenant/database.go +++ /dev/null @@ -1,24 +0,0 @@ -package tenant - -import ( - "context" - - "github.com/go-errors/errors" - "github.com/supabase/cli/internal/utils" -) - -var errDatabaseVersion = errors.New("Database version not found.") - -func GetDatabaseVersion(ctx context.Context, projectRef string) (string, error) { - resp, err := utils.GetSupabase().V1GetProjectWithResponse(ctx, projectRef) - if err != nil { - return "", errors.Errorf("failed to retrieve project: %w", err) - } - if resp.JSON200 == nil { - return "", errors.Errorf("unexpected retrieve project status %d: %s", resp.StatusCode(), string(resp.Body)) - } - if len(resp.JSON200.Database.Version) > 0 { - return resp.JSON200.Database.Version, nil - } - return "", errors.New(errDatabaseVersion) -} diff --git a/apps/cli-go/internal/utils/tenant/database_test.go b/apps/cli-go/internal/utils/tenant/database_test.go deleted file mode 100644 index 5fbbdadb1a..0000000000 --- a/apps/cli-go/internal/utils/tenant/database_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package tenant - -import ( - "context" - "net/http" - "testing" - - "github.com/h2non/gock" - "github.com/stretchr/testify/assert" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/api" -) - -func TestGetDatabaseVersion(t *testing.T) { - // Setup valid access token - token := apitest.RandomAccessToken(t) - t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) - // Setup valid project ref - projectRef := apitest.RandomProjectRef() - - t.Run("retrieves database version successfully", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - mockPostgres := api.V1ProjectWithDatabaseResponseOutput{} - mockPostgres.Database.Version = "14.1.0.99" - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + projectRef). - Reply(http.StatusOK). - JSON(mockPostgres) - // Run test - version, err := GetDatabaseVersion(context.Background(), projectRef) - // Check error - assert.NoError(t, err) - assert.Equal(t, "14.1.0.99", version) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("handles project not found", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - projectRef := apitest.RandomProjectRef() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + projectRef). - Reply(http.StatusNotFound) - // Run test - version, err := GetDatabaseVersion(context.Background(), projectRef) - // Check error - assert.ErrorContains(t, err, "unexpected retrieve project status 404:") - assert.Empty(t, version) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("handles empty database version", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - projectRef := apitest.RandomProjectRef() - gock.New(utils.DefaultApiHost). - Get("/v1/projects/" + projectRef). - Reply(http.StatusOK). - JSON(api.V1ProjectWithDatabaseResponseOutput{}) - // Run test - version, err := GetDatabaseVersion(context.Background(), projectRef) - // Check error - assert.ErrorIs(t, err, errDatabaseVersion) - assert.Empty(t, version) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} diff --git a/apps/cli-go/internal/utils/tenant/gotrue.go b/apps/cli-go/internal/utils/tenant/gotrue.go deleted file mode 100644 index 507a63af5f..0000000000 --- a/apps/cli-go/internal/utils/tenant/gotrue.go +++ /dev/null @@ -1,32 +0,0 @@ -package tenant - -import ( - "context" - "net/http" - - "github.com/go-errors/errors" - "github.com/supabase/cli/pkg/fetcher" -) - -var errGotrueVersion = errors.New("GoTrue version not found.") - -type HealthResponse struct { - Version string `json:"version"` - Name string `json:"name"` - Description string `json:"description"` -} - -func (t *TenantAPI) GetGotrueVersion(ctx context.Context) (string, error) { - resp, err := t.Send(ctx, http.MethodGet, "/auth/v1/health", nil) - if err != nil { - return "", err - } - data, err := fetcher.ParseJSON[HealthResponse](resp.Body) - if err != nil { - return "", err - } - if len(data.Version) == 0 { - return "", errors.New(errGotrueVersion) - } - return data.Version, nil -} diff --git a/apps/cli-go/internal/utils/tenant/gotrue_test.go b/apps/cli-go/internal/utils/tenant/gotrue_test.go deleted file mode 100644 index fcc646e0d7..0000000000 --- a/apps/cli-go/internal/utils/tenant/gotrue_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package tenant - -import ( - "context" - "errors" - "net/http" - "testing" - - "github.com/h2non/gock" - "github.com/stretchr/testify/assert" - "github.com/supabase/cli/pkg/fetcher" -) - -var mockApi = TenantAPI{Fetcher: fetcher.NewFetcher( - "http://127.0.0.1", -)} - -func TestGotrueVersion(t *testing.T) { - t.Run("gets gotrue version", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New("http://127.0.0.1"). - Get("/auth/v1/health"). - Reply(http.StatusOK). - JSON(HealthResponse{Version: "v2.92.1"}) - // Run test - version, err := mockApi.GetGotrueVersion(context.Background()) - // Check error - assert.NoError(t, err) - assert.Equal(t, "v2.92.1", version) - }) - - t.Run("throws error on network error", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New("http://127.0.0.1"). - Get("/auth/v1/health"). - ReplyError(errors.New("network error")) - // Run test - version, err := mockApi.GetGotrueVersion(context.Background()) - // Check error - assert.ErrorContains(t, err, "network error") - assert.Empty(t, version) - }) - - t.Run("throws error on missing version", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New("http://127.0.0.1"). - Get("/auth/v1/health"). - Reply(http.StatusOK). - JSON(HealthResponse{}) - // Run test - version, err := mockApi.GetGotrueVersion(context.Background()) - // Check error - assert.ErrorIs(t, err, errGotrueVersion) - assert.Empty(t, version) - }) -} diff --git a/apps/cli-go/internal/utils/tenant/postgrest.go b/apps/cli-go/internal/utils/tenant/postgrest.go deleted file mode 100644 index cb8c96d28b..0000000000 --- a/apps/cli-go/internal/utils/tenant/postgrest.go +++ /dev/null @@ -1,39 +0,0 @@ -package tenant - -import ( - "context" - "net/http" - "strings" - - "github.com/go-errors/errors" - "github.com/supabase/cli/pkg/fetcher" -) - -var errPostgrestVersion = errors.New("PostgREST version not found.") - -type SwaggerInfo struct { - Title string `json:"title"` - Description string `json:"description"` - Version string `json:"version"` -} - -type SwaggerResponse struct { - Swagger string `json:"swagger"` - Info SwaggerInfo `json:"info"` -} - -func (t *TenantAPI) GetPostgrestVersion(ctx context.Context) (string, error) { - resp, err := t.Send(ctx, http.MethodGet, "/rest/v1/", nil) - if err != nil { - return "", err - } - data, err := fetcher.ParseJSON[SwaggerResponse](resp.Body) - if err != nil { - return "", err - } - if len(data.Info.Version) == 0 { - return "", errors.New(errPostgrestVersion) - } - parts := strings.Fields(data.Info.Version) - return "v" + parts[0], nil -} diff --git a/apps/cli-go/internal/utils/tenant/postgrest_test.go b/apps/cli-go/internal/utils/tenant/postgrest_test.go deleted file mode 100644 index 2be44db441..0000000000 --- a/apps/cli-go/internal/utils/tenant/postgrest_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package tenant - -import ( - "context" - "errors" - "net/http" - "testing" - - "github.com/h2non/gock" - "github.com/stretchr/testify/assert" -) - -func TestPostgrestVersion(t *testing.T) { - t.Run("appends prefix v", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New("http://127.0.0.1"). - Get("/rest/v1/"). - Reply(http.StatusOK). - JSON(SwaggerResponse{Info: SwaggerInfo{Version: "11.1.0"}}) - // Run test - version, err := mockApi.GetPostgrestVersion(context.Background()) - // Check error - assert.NoError(t, err) - assert.Equal(t, "v11.1.0", version) - }) - - t.Run("ignores commit hash", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New("http://127.0.0.1"). - Get("/rest/v1/"). - Reply(http.StatusOK). - JSON(SwaggerResponse{Info: SwaggerInfo{Version: "11.2.0 (c820efb)"}}) - // Run test - version, err := mockApi.GetPostgrestVersion(context.Background()) - // Check error - assert.NoError(t, err) - assert.Equal(t, "v11.2.0", version) - }) - - t.Run("throws error on network error", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New("http://127.0.0.1"). - Get("/rest/v1/"). - ReplyError(errors.New("network error")) - // Run test - version, err := mockApi.GetPostgrestVersion(context.Background()) - // Check error - assert.ErrorContains(t, err, "network error") - assert.Empty(t, version) - }) - - t.Run("throws error on missing version", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New("http://127.0.0.1"). - Get("/rest/v1/"). - Reply(http.StatusOK). - JSON(SwaggerResponse{}) - // Run test - version, err := mockApi.GetPostgrestVersion(context.Background()) - // Check error - assert.ErrorIs(t, err, errPostgrestVersion) - assert.Empty(t, version) - }) -} diff --git a/apps/cli-go/internal/utils/tenant/storage.go b/apps/cli-go/internal/utils/tenant/storage.go deleted file mode 100644 index 02d1490fc4..0000000000 --- a/apps/cli-go/internal/utils/tenant/storage.go +++ /dev/null @@ -1,27 +0,0 @@ -package tenant - -import ( - "context" - "io" - "net/http" - - "github.com/go-errors/errors" -) - -var errStorageVersion = errors.New("Storage version not found.") - -func (t *TenantAPI) GetStorageVersion(ctx context.Context) (string, error) { - resp, err := t.Send(ctx, http.MethodGet, "/storage/v1/version", nil) - if err != nil { - return "", err - } - defer resp.Body.Close() - data, err := io.ReadAll(resp.Body) - if err != nil { - return "", errors.Errorf("failed to read response body: %w", err) - } - if len(data) == 0 || string(data) == "0.0.0" { - return "", errors.New(errStorageVersion) - } - return "v" + string(data), nil -} diff --git a/apps/cli-go/internal/utils/tenant/storage_test.go b/apps/cli-go/internal/utils/tenant/storage_test.go deleted file mode 100644 index f3c0545b37..0000000000 --- a/apps/cli-go/internal/utils/tenant/storage_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package tenant - -import ( - "context" - "errors" - "net/http" - "testing" - - "github.com/h2non/gock" - "github.com/stretchr/testify/assert" -) - -func TestStorageVersion(t *testing.T) { - t.Run("appends prefix v", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New("http://127.0.0.1"). - Get("/storage/v1/version"). - Reply(http.StatusOK). - BodyString("0.40.4") - // Run test - version, err := mockApi.GetStorageVersion(context.Background()) - // Check error - assert.NoError(t, err) - assert.Equal(t, "v0.40.4", version) - }) - - t.Run("throws error on network error", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New("http://127.0.0.1"). - Get("/storage/v1/version"). - ReplyError(errors.New("network error")) - // Run test - version, err := mockApi.GetStorageVersion(context.Background()) - // Check error - assert.ErrorContains(t, err, "network error") - assert.Empty(t, version) - }) - - t.Run("throws error on missing version", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New("http://127.0.0.1"). - Get("/storage/v1/version"). - Reply(http.StatusOK). - BodyString("0.0.0") - // Run test - version, err := mockApi.GetStorageVersion(context.Background()) - // Check error - assert.ErrorIs(t, err, errStorageVersion) - assert.Empty(t, version) - }) -} diff --git a/apps/cli-go/internal/utils/testdata/invalid.json b/apps/cli-go/internal/utils/testdata/invalid.json deleted file mode 100644 index 4182e56951..0000000000 --- a/apps/cli-go/internal/utils/testdata/invalid.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "supabase", - "api_url": "api.supabase.com", - "dashboard_url": "https://supabase.com/dashboard", - "project_host": "https://supabase.co" -} diff --git a/apps/cli-go/internal/utils/testdata/malformed.json b/apps/cli-go/internal/utils/testdata/malformed.json deleted file mode 100644 index 55a4e3c2a8..0000000000 --- a/apps/cli-go/internal/utils/testdata/malformed.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "supabase", - "test_url": "https://api.supabase.com" -} diff --git a/apps/cli-go/internal/utils/testdata/profile.json b/apps/cli-go/internal/utils/testdata/profile.json deleted file mode 100644 index 66ba4ae450..0000000000 --- a/apps/cli-go/internal/utils/testdata/profile.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "supabase", - "api_url": "https://api.supabase.com", - "dashboard_url": "https://supabase.com/dashboard", - "project_host": "supabase.co" -} diff --git a/apps/cli-go/legacy/branch/create/create.go b/apps/cli-go/legacy/branch/create/create.go deleted file mode 100644 index 5501283763..0000000000 --- a/apps/cli-go/legacy/branch/create/create.go +++ /dev/null @@ -1,103 +0,0 @@ -package create - -import ( - "bytes" - "context" - _ "embed" - "fmt" - "io" - "os" - "path/filepath" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/pkg/stdcopy" - "github.com/go-errors/errors" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/flags" -) - -var ( - //go:embed templates/clone.sh - cloneScript string -) - -func Run(branch string, fsys afero.Fs) error { - if err := flags.LoadConfig(fsys); err != nil { - return err - } - if err := utils.AssertSupabaseDbIsRunning(); err != nil { - return err - } - - branchPath := filepath.Join(filepath.Dir(utils.CurrBranchPath), branch) - if err := assertNewBranchIsValid(branchPath, fsys); err != nil { - return nil - } - - var ctx = context.Background() - if err := createBranch(ctx, branch); err != nil { - return err - } - - if err := fsys.MkdirAll(branchPath, 0755); err != nil { - return err - } - - fmt.Println("Created branch " + utils.Aqua(branch) + ".") - return nil -} - -func assertNewBranchIsValid(branchPath string, fsys afero.Fs) error { - branch := filepath.Base(branchPath) - - if utils.IsBranchNameReserved(branch) { - return errors.New("Cannot create branch " + utils.Aqua(branch) + ": branch name is reserved.") - } - - if !utils.BranchNamePattern.MatchString(branch) { - return errors.New("Branch name " + utils.Aqua(branch) + " is invalid. Must match [0-9A-Za-z_-]+.") - } - - if _, err := afero.ReadDir(fsys, branchPath); errors.Is(err, os.ErrNotExist) { - // skip - } else if err != nil { - return err - } else { - return errors.New("Branch " + utils.Aqua(branch) + " already exists.") - } - - return nil -} - -func createBranch(ctx context.Context, branch string) error { - exec, err := utils.Docker.ContainerExecCreate(ctx, utils.DbId, container.ExecOptions{ - Cmd: []string{"/bin/bash", "-c", cloneScript}, - Env: []string{"DB_NAME=" + branch}, - AttachStderr: true, - AttachStdout: true, - }) - if err != nil { - return err - } - // Read exec output - resp, err := utils.Docker.ContainerExecAttach(ctx, exec.ID, container.ExecStartOptions{}) - if err != nil { - return err - } - defer resp.Close() - // Capture error details - var errBuf bytes.Buffer - if _, err := stdcopy.StdCopy(io.Discard, &errBuf, resp.Reader); err != nil { - return err - } - // Get the exit code - iresp, err := utils.Docker.ContainerExecInspect(ctx, exec.ID) - if err != nil { - return err - } - if iresp.ExitCode > 0 { - return errors.New("Error creating branch: " + errBuf.String()) - } - return nil -} diff --git a/apps/cli-go/legacy/branch/create/create_test.go b/apps/cli-go/legacy/branch/create/create_test.go deleted file mode 100644 index 66b919ecbd..0000000000 --- a/apps/cli-go/legacy/branch/create/create_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package create - -import ( - "context" - "net/http" - "testing" - - "github.com/docker/docker/api/types/container" - "github.com/h2non/gock" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/utils" -) - -func TestBranchValidation(t *testing.T) { - t.Run("branch name is valid", func(t *testing.T) { - assert.NoError(t, assertNewBranchIsValid("test-branch", afero.NewMemMapFs())) - }) - - t.Run("branch name is reserved", func(t *testing.T) { - assert.Error(t, assertNewBranchIsValid("main", afero.NewMemMapFs())) - }) - - t.Run("branch name is invalid", func(t *testing.T) { - assert.Error(t, assertNewBranchIsValid("@", afero.NewMemMapFs())) - }) - - t.Run("branch not a directory", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := "/supabase/.branches/test-branch" - _, err := fsys.Create(path) - require.NoError(t, err) - // Run test - assert.Error(t, assertNewBranchIsValid(path, fsys)) - }) - - t.Run("branch already exists", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := "/supabase/.branches/test-branch" - require.NoError(t, fsys.MkdirAll(path, 0755)) - // Run test - assert.Error(t, assertNewBranchIsValid(path, fsys)) - }) -} - -func TestBranchCreation(t *testing.T) { - utils.DbId = "test-db" - - t.Run("docker exec failure", func(t *testing.T) { - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/exec"). - Reply(http.StatusServiceUnavailable) - // Run test - err := createBranch(context.Background(), "test-branch") - // Validate api - assert.ErrorContains(t, err, "request returned 503 Service Unavailable for API route and version") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("docker attach failure", func(t *testing.T) { - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/exec"). - Reply(http.StatusCreated). - JSON(container.InspectResponse{}) - // Run test - err := createBranch(context.Background(), "test-branch") - // Validate api - assert.ErrorContains(t, err, "unable to upgrade to tcp, received 404") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestCreateCommand(t *testing.T) { - const branch = "test-branch" - - t.Run("throws error on missing config", func(t *testing.T) { - assert.Error(t, Run(branch, afero.NewMemMapFs())) - }) - - t.Run("throws error on stopped db", func(t *testing.T) { - // Setup in-memory fs - fsys := &afero.MemMapFs{} - require.NoError(t, utils.WriteConfig(fsys, false)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers"). - Reply(http.StatusNotFound) - // Run test - err := Run(branch, fsys) - // Validate api - assert.ErrorIs(t, err, utils.ErrNotRunning) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} diff --git a/apps/cli-go/legacy/branch/create/templates/clone.sh b/apps/cli-go/legacy/branch/create/templates/clone.sh deleted file mode 100755 index ac8d6ccaa2..0000000000 --- a/apps/cli-go/legacy/branch/create/templates/clone.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -set -euo pipefail - -createdb --username postgres --host 127.0.0.1 "$DB_NAME" -pg_dump --username postgres --host 127.0.0.1 postgres | psql --username postgres --host 127.0.0.1 "$DB_NAME" diff --git a/apps/cli-go/legacy/branch/delete/delete.go b/apps/cli-go/legacy/branch/delete/delete.go deleted file mode 100644 index ea14cd34d6..0000000000 --- a/apps/cli-go/legacy/branch/delete/delete.go +++ /dev/null @@ -1,90 +0,0 @@ -package delete - -import ( - "bytes" - "context" - "fmt" - "io" - "path/filepath" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/pkg/stdcopy" - "github.com/go-errors/errors" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/flags" -) - -func Run(branch string, fsys afero.Fs) error { - if err := flags.LoadConfig(fsys); err != nil { - return err - } - if err := utils.AssertSupabaseDbIsRunning(); err != nil { - return err - } - - // TODO: update branch history atomically with database - if err := deleteBranchDir(branch, fsys); err != nil { - return err - } - - ctx := context.Background() - if err := deleteBranchPG(ctx, branch); err != nil { - return err - } - - fmt.Println("Deleted branch " + utils.Aqua(branch) + ".") - return nil -} - -func deleteBranchDir(branch string, fsys afero.Fs) error { - if currBranch, _ := utils.GetCurrentBranchFS(fsys); branch == currBranch { - return errors.New("Cannot delete current branch.") - } - - if utils.IsBranchNameReserved(branch) { - return errors.New("Cannot delete branch " + utils.Aqua(branch) + ": branch name is reserved.") - } - - branchPath := filepath.Join(filepath.Dir(utils.CurrBranchPath), branch) - if _, err := afero.ReadDir(fsys, branchPath); err != nil { - return errors.New("Branch " + utils.Aqua(branch) + " does not exist.") - } - - if err := fsys.RemoveAll(branchPath); err != nil { - return errors.Errorf("Failed deleting branch %s: %w", utils.Aqua(branch), err) - } - - return nil -} - -func deleteBranchPG(ctx context.Context, branch string) error { - exec, err := utils.Docker.ContainerExecCreate(ctx, utils.DbId, container.ExecOptions{ - Cmd: []string{"dropdb", "--username", "postgres", "--host", "127.0.0.1", branch}, - AttachStderr: true, - AttachStdout: true, - }) - if err != nil { - return err - } - // Read exec output - resp, err := utils.Docker.ContainerExecAttach(ctx, exec.ID, container.ExecStartOptions{}) - if err != nil { - return err - } - defer resp.Close() - // Capture error details - var errBuf bytes.Buffer - if _, err := stdcopy.StdCopy(io.Discard, &errBuf, resp.Reader); err != nil { - return err - } - // Get the exit code - iresp, err := utils.Docker.ContainerExecInspect(ctx, exec.ID) - if err != nil { - return err - } - if iresp.ExitCode > 0 { - return errors.New("Error deleting branch: " + errBuf.String()) - } - return nil -} diff --git a/apps/cli-go/legacy/branch/delete/delete_test.go b/apps/cli-go/legacy/branch/delete/delete_test.go deleted file mode 100644 index 76957e2460..0000000000 --- a/apps/cli-go/legacy/branch/delete/delete_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package delete - -import ( - "net/http" - "path/filepath" - "testing" - - "github.com/h2non/gock" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/utils" -) - -func TestBranchDir(t *testing.T) { - t.Run("removes a branch directory", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - path := filepath.Join(filepath.Dir(utils.CurrBranchPath), "test-branch") - require.NoError(t, fsys.Mkdir(path, 0755)) - // Run test - assert.NoError(t, deleteBranchDir("test-branch", fsys)) - // Validate removal - exists, err := afero.Exists(fsys, path) - assert.NoError(t, err) - assert.False(t, exists) - }) - - t.Run("branch is current", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, afero.WriteFile(fsys, utils.CurrBranchPath, []byte("main"), 0644)) - // Run test - assert.Error(t, deleteBranchDir("main", fsys)) - }) - - t.Run("branch is reserved", func(t *testing.T) { - assert.Error(t, deleteBranchDir("main", afero.NewMemMapFs())) - }) - - t.Run("branch does not exist", func(t *testing.T) { - assert.Error(t, deleteBranchDir("test-branch", afero.NewMemMapFs())) - }) - - t.Run("branch permission denied", func(t *testing.T) { - // Setup read-only fs - fsys := afero.NewMemMapFs() - path := filepath.Join(filepath.Dir(utils.CurrBranchPath), "test-branch") - require.NoError(t, fsys.Mkdir(path, 0755)) - // Run test - assert.Error(t, deleteBranchDir("test-branch", afero.NewReadOnlyFs(fsys))) - }) -} - -func TestDeleteCommand(t *testing.T) { - const branch = "test-branch" - - t.Run("throws error on missing config", func(t *testing.T) { - assert.Error(t, Run(branch, afero.NewMemMapFs())) - }) - - t.Run("throws error on stopped db", func(t *testing.T) { - // Setup in-memory fs - fsys := &afero.MemMapFs{} - require.NoError(t, utils.WriteConfig(fsys, false)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers"). - Reply(http.StatusNotFound) - // Run test - err := Run(branch, fsys) - // Validate api - assert.ErrorIs(t, err, utils.ErrNotRunning) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} diff --git a/apps/cli-go/legacy/branch/list/list.go b/apps/cli-go/legacy/branch/list/list.go deleted file mode 100644 index b1037ac1b7..0000000000 --- a/apps/cli-go/legacy/branch/list/list.go +++ /dev/null @@ -1,36 +0,0 @@ -package list - -import ( - "fmt" - "io" - "os" - "path/filepath" - - "github.com/go-errors/errors" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils" -) - -func Run(fsys afero.Fs, out io.Writer) error { - branches, err := afero.ReadDir(fsys, filepath.Dir(utils.CurrBranchPath)) - if errors.Is(err, os.ErrNotExist) { - return nil - } else if err != nil { - return err - } - - currBranch, _ := utils.GetCurrentBranchFS(fsys) - for _, branch := range branches { - if branch.Name() == filepath.Base(utils.CurrBranchPath) { - continue - } - - if branch.Name() == currBranch { - fmt.Fprintln(out, "*", branch.Name()) - } else { - fmt.Fprintln(out, " ", branch.Name()) - } - } - - return nil -} diff --git a/apps/cli-go/legacy/branch/list/list_test.go b/apps/cli-go/legacy/branch/list/list_test.go deleted file mode 100644 index 24e34d5e53..0000000000 --- a/apps/cli-go/legacy/branch/list/list_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package list - -import ( - "bytes" - "io" - "path/filepath" - "strings" - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/utils" -) - -func TestListCommand(t *testing.T) { - t.Run("lists all branches", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, afero.WriteFile(fsys, utils.CurrBranchPath, []byte("main"), 0644)) - base := filepath.Dir(utils.CurrBranchPath) - require.NoError(t, fsys.Mkdir(filepath.Join(base, "main"), 0755)) - require.NoError(t, fsys.Mkdir(filepath.Join(base, "test"), 0755)) - // Run test - var out bytes.Buffer - require.NoError(t, Run(fsys, &out)) - // Validate output - lines := strings.Split(out.String(), "\n") - assert.ElementsMatch(t, []string{ - "* main", - " test", - "", - }, lines) - }) - - t.Run("lists without current branch", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - base := filepath.Dir(utils.CurrBranchPath) - require.NoError(t, fsys.Mkdir(filepath.Join(base, "main"), 0755)) - require.NoError(t, fsys.Mkdir(filepath.Join(base, "test"), 0755)) - // Run test - var out bytes.Buffer - require.NoError(t, Run(fsys, &out)) - // Validate output - lines := strings.Split(out.String(), "\n") - assert.ElementsMatch(t, []string{ - " main", - " test", - "", - }, lines) - }) - - t.Run("lists uninitialized branch", func(t *testing.T) { - require.NoError(t, Run(afero.NewMemMapFs(), io.Discard)) - }) - - t.Run("throws error on unreadable directory", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - _, err := fsys.Create(filepath.Dir(utils.CurrBranchPath)) - require.NoError(t, err) - // Run test - require.Error(t, Run(fsys, io.Discard)) - }) -} diff --git a/apps/cli-go/legacy/branch/switch_/switch_.go b/apps/cli-go/legacy/branch/switch_/switch_.go deleted file mode 100644 index d11803e884..0000000000 --- a/apps/cli-go/legacy/branch/switch_/switch_.go +++ /dev/null @@ -1,88 +0,0 @@ -package switch_ - -import ( - "context" - "fmt" - "os" - "path/filepath" - - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/db/reset" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/flags" -) - -func Run(ctx context.Context, target string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - // 1. Sanity checks - { - if err := flags.LoadConfig(fsys); err != nil { - return err - } - if err := utils.AssertSupabaseDbIsRunning(); err != nil { - return err - } - if target != "main" && utils.IsBranchNameReserved(target) { - return errors.New("Cannot switch branch " + utils.Aqua(target) + ": branch name is reserved.") - } - branchPath := filepath.Join(filepath.Dir(utils.CurrBranchPath), target) - if _, err := fsys.Stat(branchPath); errors.Is(err, os.ErrNotExist) { - return errors.New("Branch " + utils.Aqua(target) + " does not exist.") - } else if err != nil { - return err - } - } - - // 2. Check current branch - currBranch, err := utils.GetCurrentBranchFS(fsys) - if err != nil { - // Assume we are on main branch - currBranch = "main" - } - - // 3. Switch Postgres database - if currBranch == target { - fmt.Println("Already on branch " + utils.Aqua(target) + ".") - } else if err := switchDatabase(ctx, currBranch, target, options...); err != nil { - return errors.New("Error switching to branch " + utils.Aqua(target) + ": " + err.Error()) - } else { - fmt.Println("Switched to branch " + utils.Aqua(target) + ".") - } - - // 4. Update current branch - if err := afero.WriteFile(fsys, utils.CurrBranchPath, []byte(target), 0644); err != nil { - return errors.New("Unable to update local branch file. Fix by running: echo '" + target + "' > " + utils.CurrBranchPath) - } - return nil -} - -func switchDatabase(ctx context.Context, source, target string, options ...func(*pgx.ConnConfig)) error { - conn, err := utils.ConnectLocalPostgres(ctx, pgconn.Config{Database: "template1"}, options...) - if err != nil { - return err - } - defer conn.Close(context.Background()) - if err := reset.DisconnectClients(ctx, conn); err != nil { - return err - } - defer func() { - if err := reset.RestartDatabase(context.Background(), os.Stderr); err != nil { - fmt.Fprintln(os.Stderr, "Failed to restart database:", err) - } - }() - backup := "ALTER DATABASE postgres RENAME TO " + source + ";" - if _, err := conn.Exec(ctx, backup); err != nil { - return err - } - rename := "ALTER DATABASE " + target + " RENAME TO postgres;" - if _, err := conn.Exec(ctx, rename); err != nil { - rollback := "ALTER DATABASE " + source + " RENAME TO postgres;" - if _, err := conn.Exec(ctx, rollback); err != nil { - fmt.Fprintln(os.Stderr, "Failed to rollback database:", err) - } - return err - } - return nil -} diff --git a/apps/cli-go/legacy/branch/switch_/switch__test.go b/apps/cli-go/legacy/branch/switch_/switch__test.go deleted file mode 100644 index f0e51d521e..0000000000 --- a/apps/cli-go/legacy/branch/switch_/switch__test.go +++ /dev/null @@ -1,290 +0,0 @@ -package switch_ - -import ( - "context" - "net/http" - "path/filepath" - "testing" - - "github.com/docker/docker/api/types/container" - "github.com/h2non/gock" - "github.com/jackc/pgerrcode" - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/internal/db/reset" - "github.com/supabase/cli/internal/testing/apitest" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/pgtest" -) - -func TestSwitchCommand(t *testing.T) { - t.Run("switches local branch", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - // Setup target branch - branch := "target" - branchPath := filepath.Join(filepath.Dir(utils.CurrBranchPath), branch) - require.NoError(t, fsys.Mkdir(branchPath, 0755)) - require.NoError(t, afero.WriteFile(fsys, utils.CurrBranchPath, []byte("main"), 0644)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers"). - Reply(http.StatusOK). - JSON(container.InspectResponse{}) - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers"). - Reply(http.StatusServiceUnavailable) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query("ALTER DATABASE postgres ALLOW_CONNECTIONS false"). - Reply("ALTER DATABASE"). - Query("ALTER DATABASE _supabase ALLOW_CONNECTIONS false"). - Reply("ALTER DATABASE"). - Query(reset.TERMINATE_BACKENDS). - Reply("SELECT 1"). - Query(reset.COUNT_REPLICATION_SLOTS). - Reply("SELECT 1", []any{0}). - Query("ALTER DATABASE postgres RENAME TO main;"). - Reply("ALTER DATABASE"). - Query("ALTER DATABASE " + branch + " RENAME TO postgres;"). - Reply("ALTER DATABASE") - // Run test - assert.NoError(t, Run(context.Background(), branch, fsys, conn.Intercept)) - // Validate output - assert.Empty(t, apitest.ListUnmatchedRequests()) - contents, err := afero.ReadFile(fsys, utils.CurrBranchPath) - assert.NoError(t, err) - assert.Equal(t, []byte(branch), contents) - }) - - t.Run("throws error on malformed config", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, afero.WriteFile(fsys, utils.ConfigPath, []byte("malformed"), 0644)) - // Run test - err := Run(context.Background(), "target", fsys) - // Check error - assert.ErrorContains(t, err, "toml: expected = after a key, but the document ends there") - }) - - t.Run("throws error on missing database", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers"). - Reply(http.StatusNotFound) - // Run test - err := Run(context.Background(), "target", fsys) - // Check error - assert.ErrorIs(t, err, utils.ErrNotRunning) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on reserved branch", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers"). - Reply(http.StatusOK). - JSON(container.InspectResponse{}) - // Run test - err := Run(context.Background(), "postgres", fsys) - // Check error - assert.ErrorContains(t, err, "branch name is reserved.") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on missing branch", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers"). - Reply(http.StatusOK). - JSON(container.InspectResponse{}) - // Run test - err := Run(context.Background(), "main", fsys) - // Check error - assert.ErrorContains(t, err, "Branch main does not exist.") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("noop on current branch", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers"). - Reply(http.StatusOK). - JSON(container.InspectResponse{}) - // Setup target branch - branch := "main" - branchPath := filepath.Join(filepath.Dir(utils.CurrBranchPath), branch) - require.NoError(t, fsys.Mkdir(branchPath, 0755)) - // Run test - assert.NoError(t, Run(context.Background(), branch, fsys)) - // Check error - assert.Empty(t, apitest.ListUnmatchedRequests()) - contents, err := afero.ReadFile(fsys, utils.CurrBranchPath) - assert.NoError(t, err) - assert.Equal(t, []byte(branch), contents) - }) - - t.Run("throws error on failure to switch", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers"). - Reply(http.StatusOK). - JSON(container.InspectResponse{}) - // Setup target branch - branch := "target" - branchPath := filepath.Join(filepath.Dir(utils.CurrBranchPath), branch) - require.NoError(t, fsys.Mkdir(branchPath, 0755)) - // Setup mock postgres - conn := pgtest.NewConn() - // Run test - err := Run(context.Background(), branch, fsys, conn.Intercept) - // Check error - assert.ErrorContains(t, err, "Error switching to branch target") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on failure to write", func(t *testing.T) { - // Setup in-memory fs - fsys := afero.NewMemMapFs() - require.NoError(t, utils.WriteConfig(fsys, false)) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Get("/v" + utils.Docker.ClientVersion() + "/containers"). - Reply(http.StatusOK). - JSON(container.InspectResponse{}) - // Setup target branch - branch := "main" - branchPath := filepath.Join(filepath.Dir(utils.CurrBranchPath), branch) - require.NoError(t, fsys.Mkdir(branchPath, 0755)) - // Run test - err := Run(context.Background(), branch, afero.NewReadOnlyFs(fsys)) - // Check error - assert.ErrorContains(t, err, "Unable to update local branch file.") - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} - -func TestSwitchDatabase(t *testing.T) { - t.Run("throws error on failure to connect", func(t *testing.T) { - // Setup invalid port - utils.Config.Db.Port = 0 - // Run test - err := switchDatabase(context.Background(), "main", "target") - // Check error - assert.ErrorContains(t, err, "invalid port") - }) - - t.Run("throws error on failure to disconnect", func(t *testing.T) { - // Setup valid config - utils.Config.Db.Port = 54322 - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query("ALTER DATABASE postgres ALLOW_CONNECTIONS false"). - ReplyError(pgerrcode.InvalidParameterValue, `cannot disallow connections for current database`). - Query("ALTER DATABASE _supabase ALLOW_CONNECTIONS false"). - Query(reset.TERMINATE_BACKENDS) - // Run test - err := switchDatabase(context.Background(), "main", "target", conn.Intercept) - // Check error - assert.ErrorContains(t, err, pgerrcode.InvalidParameterValue) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on failure to backup", func(t *testing.T) { - // Setup valid config - utils.DbId = "test-switch" - utils.Config.Db.Port = 54322 - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query("ALTER DATABASE postgres ALLOW_CONNECTIONS false"). - Reply("ALTER DATABASE"). - Query("ALTER DATABASE _supabase ALLOW_CONNECTIONS false"). - Reply("ALTER DATABASE"). - Query(reset.TERMINATE_BACKENDS). - Reply("SELECT 1"). - Query(reset.COUNT_REPLICATION_SLOTS). - Reply("SELECT 1", []any{0}). - Query("ALTER DATABASE postgres RENAME TO main;"). - ReplyError(pgerrcode.DuplicateDatabase, `database "main" already exists`) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/restart"). - Reply(http.StatusServiceUnavailable) - // Run test - err := switchDatabase(context.Background(), "main", "target", conn.Intercept) - // Check error - assert.ErrorContains(t, err, pgerrcode.DuplicateDatabase) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) - - t.Run("throws error on failure to rename", func(t *testing.T) { - // Setup valid config - utils.DbId = "test-switch" - utils.Config.Db.Port = 54322 - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query("ALTER DATABASE postgres ALLOW_CONNECTIONS false"). - Reply("ALTER DATABASE"). - Query("ALTER DATABASE _supabase ALLOW_CONNECTIONS false"). - Reply("ALTER DATABASE"). - Query(reset.TERMINATE_BACKENDS). - Reply("SELECT 1"). - Query(reset.COUNT_REPLICATION_SLOTS). - Reply("SELECT 1", []any{0}). - Query("ALTER DATABASE postgres RENAME TO main;"). - Reply("ALTER DATABASE"). - Query("ALTER DATABASE target RENAME TO postgres;"). - ReplyError(pgerrcode.InvalidCatalogName, `database "target" does not exist`). - // Attempt to rollback - Query("ALTER DATABASE main RENAME TO postgres;"). - ReplyError(pgerrcode.DuplicateDatabase, `database "postgres" already exists`) - // Setup mock docker - require.NoError(t, apitest.MockDocker(utils.Docker)) - defer gock.OffAll() - gock.New(utils.Docker.DaemonHost()). - Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/restart"). - Reply(http.StatusServiceUnavailable) - // Run test - err := switchDatabase(context.Background(), "main", "target", conn.Intercept) - // Check error - assert.ErrorContains(t, err, pgerrcode.InvalidCatalogName) - assert.Empty(t, apitest.ListUnmatchedRequests()) - }) -} diff --git a/apps/cli-go/legacy/keys/keys.go b/apps/cli-go/legacy/keys/keys.go deleted file mode 100644 index 560a4ce917..0000000000 --- a/apps/cli-go/legacy/keys/keys.go +++ /dev/null @@ -1,75 +0,0 @@ -package keys - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "fmt" - "os" - "strings" - - "github.com/go-errors/errors" - "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/config" -) - -type CustomName struct { - DbHost string `env:"db.host,default=NEXT_PUBLIC_SUPABASE_URL"` - DbPassword string `env:"db.password,default=SUPABASE_DB_PASSWORD"` - JWTSecret string `env:"db.password,default=SUPABASE_AUTH_JWT_SECRET"` - AnonKey string `env:"auth.anon_key,default=SUPABASE_AUTH_ANON_KEY"` - ServiceRoleKey string `env:"auth.service_role_key,default=SUPABASE_AUTH_SERVICE_ROLE_KEY"` -} - -func Run(ctx context.Context, projectRef, format string, names CustomName, fsys afero.Fs) error { - branch := utils.GetGitBranch(fsys) - if err := GenerateSecrets(ctx, projectRef, branch, fsys); err != nil { - return err - } - return utils.EncodeOutput(format, os.Stdout, map[string]string{ - names.DbHost: fmt.Sprintf("%s-%s.fly.dev", projectRef, branch), - names.DbPassword: utils.Config.Db.Password, - names.JWTSecret: utils.Config.Auth.JwtSecret.Value, - names.AnonKey: utils.Config.Auth.AnonKey.Value, - names.ServiceRoleKey: utils.Config.Auth.ServiceRoleKey.Value, - }) -} - -func GenerateSecrets(ctx context.Context, projectRef, branch string, fsys afero.Fs) error { - // Load JWT secret from api - resp, err := utils.GetSupabase().V1GetPostgrestServiceConfigWithResponse(ctx, projectRef) - if err != nil { - return errors.Errorf("failed to get postgrest config: %w", err) - } - if resp.JSON200 == nil { - return errors.New("Unexpected error retrieving JWT secret: " + string(resp.Body)) - } - utils.Config.Auth.JwtSecret.Value = *resp.JSON200.JwtSecret - // Generate database password - key := strings.Join([]string{ - projectRef, - utils.Config.Auth.JwtSecret.Value, - branch, - }, ":") - hash := sha256.Sum256([]byte(key)) - utils.Config.Db.Password = hex.EncodeToString(hash[:]) - // Generate JWT tokens - anonToken := config.CustomClaims{ - Issuer: "supabase", - Ref: projectRef, - Role: "anon", - }.NewToken() - if utils.Config.Auth.AnonKey.Value, err = anonToken.SignedString([]byte(utils.Config.Auth.JwtSecret.Value)); err != nil { - return errors.Errorf("failed to sign anon key: %w", err) - } - serviceToken := config.CustomClaims{ - Issuer: "supabase", - Ref: projectRef, - Role: "service_role", - }.NewToken() - if utils.Config.Auth.ServiceRoleKey.Value, err = serviceToken.SignedString([]byte(utils.Config.Auth.JwtSecret.Value)); err != nil { - return errors.Errorf("failed to sign service_role key: %w", err) - } - return nil -} diff --git a/apps/cli-go/main.go b/apps/cli-go/main.go deleted file mode 100644 index bcc8fa3db1..0000000000 --- a/apps/cli-go/main.go +++ /dev/null @@ -1,16 +0,0 @@ -package main - -import ( - "github.com/supabase/cli/cmd" -) - -// Codegen reads the committed api/v1-openapi.yaml snapshot instead of fetching -// the live upstream spec, so `go generate` is reproducible offline and the -// Codegen CI check only fails on a PR that leaves pkg/api out of sync with the -// snapshot. The API Sync workflow refreshes the snapshot and pkg/api together. -//go:generate go tool oapi-codegen -config pkg/api/types.cfg.yaml api/v1-openapi.yaml -//go:generate go tool oapi-codegen -config pkg/api/client.cfg.yaml api/v1-openapi.yaml - -func main() { - cmd.Execute() -} diff --git a/apps/cli-go/package.json b/apps/cli-go/package.json deleted file mode 100644 index 197015c913..0000000000 --- a/apps/cli-go/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "@supabase/cli-go", - "private": true, - "scripts": { - "build": "go build -o supabase-go .", - "lint:check": "golangci-lint run --timeout 5m", - "lint:fix": "golangci-lint run --fix", - "test:unit": "pnpm exec turbo run @supabase/cli-go#test:unit:run --", - "test:unit:run": "go test ./..." - } -} diff --git a/apps/cli-go/pkg/api/client.cfg.yaml b/apps/cli-go/pkg/api/client.cfg.yaml deleted file mode 100755 index f06e6cad13..0000000000 --- a/apps/cli-go/pkg/api/client.cfg.yaml +++ /dev/null @@ -1,7 +0,0 @@ -package: api -generate: - client: true -output-options: - overlay: - path: api/overlay.yaml -output: pkg/api/client.gen.go diff --git a/apps/cli-go/pkg/api/client.gen.go b/apps/cli-go/pkg/api/client.gen.go deleted file mode 100644 index 179c078bc5..0000000000 --- a/apps/cli-go/pkg/api/client.gen.go +++ /dev/null @@ -1,23229 +0,0 @@ -// Package api provides primitives to interact with the openapi HTTP API. -// -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.7.1 DO NOT EDIT. -package api - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "strings" - - "github.com/oapi-codegen/runtime" - openapi_types "github.com/oapi-codegen/runtime/types" -) - -// RequestEditorFn is the function signature for the RequestEditor callback function -type RequestEditorFn func(ctx context.Context, req *http.Request) error - -// Doer performs HTTP requests. -// -// The standard http.Client implements this interface. -type HttpRequestDoer interface { - Do(req *http.Request) (*http.Response, error) -} - -// Client which conforms to the OpenAPI3 specification for this service. -type Client struct { - // The endpoint of the server conforming to this interface, with scheme, - // https://api.deepmap.com for example. This can contain a path relative - // to the server, such as https://api.deepmap.com/dev-test, and all the - // paths in the swagger spec will be appended to the server. - Server string - - // Doer for performing requests, typically a *http.Client with any - // customized settings, such as certificate chains. - Client HttpRequestDoer - - // A list of callbacks for modifying requests which are generated before sending over - // the network. - RequestEditors []RequestEditorFn -} - -// ClientOption allows setting custom parameters during construction -type ClientOption func(*Client) error - -// Creates a new Client, with reasonable defaults -func NewClient(server string, opts ...ClientOption) (*Client, error) { - // create a client with sane default values - client := Client{ - Server: server, - } - // mutate client and add all optional params - for _, o := range opts { - if err := o(&client); err != nil { - return nil, err - } - } - // ensure the server URL always has a trailing slash - if !strings.HasSuffix(client.Server, "/") { - client.Server += "/" - } - // create httpClient, if not already present - if client.Client == nil { - client.Client = &http.Client{} - } - return &client, nil -} - -// WithHTTPClient allows overriding the default Doer, which is -// automatically created using http.Client. This is useful for tests. -func WithHTTPClient(doer HttpRequestDoer) ClientOption { - return func(c *Client) error { - c.Client = doer - return nil - } -} - -// WithRequestEditorFn allows setting up a callback function, which will be -// called right before sending the request. This can be used to mutate the request. -func WithRequestEditorFn(fn RequestEditorFn) ClientOption { - return func(c *Client) error { - c.RequestEditors = append(c.RequestEditors, fn) - return nil - } -} - -// The interface specification for the client above. -type ClientInterface interface { - // V1DeleteABranch request - V1DeleteABranch(ctx context.Context, branchIdOrRef string, params *V1DeleteABranchParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetABranchConfig request - V1GetABranchConfig(ctx context.Context, branchIdOrRef string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateABranchConfigWithBody request with any body - V1UpdateABranchConfigWithBody(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateABranchConfig(ctx context.Context, branchIdOrRef string, body V1UpdateABranchConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DiffABranch request - V1DiffABranch(ctx context.Context, branchIdOrRef string, params *V1DiffABranchParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1MergeABranchWithBody request with any body - V1MergeABranchWithBody(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1MergeABranch(ctx context.Context, branchIdOrRef string, body V1MergeABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1PushABranchWithBody request with any body - V1PushABranchWithBody(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1PushABranch(ctx context.Context, branchIdOrRef string, body V1PushABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ResetABranchWithBody request with any body - V1ResetABranchWithBody(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1ResetABranch(ctx context.Context, branchIdOrRef string, body V1ResetABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1RestoreABranch request - V1RestoreABranch(ctx context.Context, branchIdOrRef string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1AuthorizeUser request - V1AuthorizeUser(ctx context.Context, params *V1AuthorizeUserParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1OauthAuthorizeProjectClaim request - V1OauthAuthorizeProjectClaim(ctx context.Context, params *V1OauthAuthorizeProjectClaimParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1RevokeTokenWithBody request with any body - V1RevokeTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1RevokeToken(ctx context.Context, body V1RevokeTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ExchangeOauthTokenWithBody request with any body - V1ExchangeOauthTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1ExchangeOauthTokenWithFormdataBody(ctx context.Context, body V1ExchangeOauthTokenFormdataRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListAllOrganizations request - V1ListAllOrganizations(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CreateAnOrganizationWithBody request with any body - V1CreateAnOrganizationWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1CreateAnOrganization(ctx context.Context, body V1CreateAnOrganizationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetAnOrganization request - V1GetAnOrganization(ctx context.Context, slug string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetOrganizationEntitlements request - V1GetOrganizationEntitlements(ctx context.Context, slug string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListOrganizationMembers request - V1ListOrganizationMembers(ctx context.Context, slug string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetOrganizationProjectClaim request - V1GetOrganizationProjectClaim(ctx context.Context, slug string, token string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ClaimProjectForOrganization request - V1ClaimProjectForOrganization(ctx context.Context, slug string, token string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetAllProjectsForOrganization request - V1GetAllProjectsForOrganization(ctx context.Context, slug string, params *V1GetAllProjectsForOrganizationParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProfile request - V1GetProfile(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListAllProjects request - V1ListAllProjects(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CreateAProjectWithBody request with any body - V1CreateAProjectWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1CreateAProject(ctx context.Context, body V1CreateAProjectJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetAvailableRegions request - V1GetAvailableRegions(ctx context.Context, params *V1GetAvailableRegionsParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DeleteAProject request - V1DeleteAProject(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProject request - V1GetProject(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateAProjectWithBody request with any body - V1UpdateAProjectWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateAProject(ctx context.Context, ref string, body V1UpdateAProjectJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListActionRuns request - V1ListActionRuns(ctx context.Context, ref string, params *V1ListActionRunsParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CountActionRuns request - V1CountActionRuns(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetActionRun request - V1GetActionRun(ctx context.Context, ref string, runId string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetActionRunLogs request - V1GetActionRunLogs(ctx context.Context, ref string, runId string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateActionRunStatusWithBody request with any body - V1UpdateActionRunStatusWithBody(ctx context.Context, ref string, runId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateActionRunStatus(ctx context.Context, ref string, runId string, body V1UpdateActionRunStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetPerformanceAdvisors request - V1GetPerformanceAdvisors(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetSecurityAdvisors request - V1GetSecurityAdvisors(ctx context.Context, ref string, params *V1GetSecurityAdvisorsParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectFunctionCombinedStats request - V1GetProjectFunctionCombinedStats(ctx context.Context, ref string, params *V1GetProjectFunctionCombinedStatsParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectLogs request - V1GetProjectLogs(ctx context.Context, ref string, params *V1GetProjectLogsParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectLogsAll request - V1GetProjectLogsAll(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ScrapeProjectMetrics request - V1ScrapeProjectMetrics(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectUsageApiCount request - V1GetProjectUsageApiCount(ctx context.Context, ref string, params *V1GetProjectUsageApiCountParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectUsageRequestCount request - V1GetProjectUsageRequestCount(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectApiKeys request - V1GetProjectApiKeys(ctx context.Context, ref string, params *V1GetProjectApiKeysParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CreateProjectApiKeyWithBody request with any body - V1CreateProjectApiKeyWithBody(ctx context.Context, ref string, params *V1CreateProjectApiKeyParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1CreateProjectApiKey(ctx context.Context, ref string, params *V1CreateProjectApiKeyParams, body V1CreateProjectApiKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectLegacyApiKeys request - V1GetProjectLegacyApiKeys(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateProjectLegacyApiKeys request - V1UpdateProjectLegacyApiKeys(ctx context.Context, ref string, params *V1UpdateProjectLegacyApiKeysParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DeleteProjectApiKey request - V1DeleteProjectApiKey(ctx context.Context, ref string, id openapi_types.UUID, params *V1DeleteProjectApiKeyParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectApiKey request - V1GetProjectApiKey(ctx context.Context, ref string, id openapi_types.UUID, params *V1GetProjectApiKeyParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateProjectApiKeyWithBody request with any body - V1UpdateProjectApiKeyWithBody(ctx context.Context, ref string, id openapi_types.UUID, params *V1UpdateProjectApiKeyParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateProjectApiKey(ctx context.Context, ref string, id openapi_types.UUID, params *V1UpdateProjectApiKeyParams, body V1UpdateProjectApiKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListProjectAddons request - V1ListProjectAddons(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ApplyProjectAddonWithBody request with any body - V1ApplyProjectAddonWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1ApplyProjectAddon(ctx context.Context, ref string, body V1ApplyProjectAddonJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1RemoveProjectAddon request - V1RemoveProjectAddon(ctx context.Context, ref string, addonVariant struct { - union json.RawMessage - }, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DisablePreviewBranching request - V1DisablePreviewBranching(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListAllBranches request - V1ListAllBranches(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CreateABranchWithBody request with any body - V1CreateABranchWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1CreateABranch(ctx context.Context, ref string, body V1CreateABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetABranch request - V1GetABranch(ctx context.Context, ref string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DeleteProjectClaimToken request - V1DeleteProjectClaimToken(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectClaimToken request - V1GetProjectClaimToken(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CreateProjectClaimToken request - V1CreateProjectClaimToken(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DeleteLoginRoles request - V1DeleteLoginRoles(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CreateLoginRoleWithBody request with any body - V1CreateLoginRoleWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1CreateLoginRole(ctx context.Context, ref string, body V1CreateLoginRoleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetAuthServiceConfig request - V1GetAuthServiceConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateAuthServiceConfigWithBody request with any body - V1UpdateAuthServiceConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateAuthServiceConfig(ctx context.Context, ref string, body V1UpdateAuthServiceConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectSigningKeys request - V1GetProjectSigningKeys(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CreateProjectSigningKeyWithBody request with any body - V1CreateProjectSigningKeyWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1CreateProjectSigningKey(ctx context.Context, ref string, body V1CreateProjectSigningKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetLegacySigningKey request - V1GetLegacySigningKey(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CreateLegacySigningKey request - V1CreateLegacySigningKey(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1RemoveProjectSigningKey request - V1RemoveProjectSigningKey(ctx context.Context, ref string, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectSigningKey request - V1GetProjectSigningKey(ctx context.Context, ref string, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateProjectSigningKeyWithBody request with any body - V1UpdateProjectSigningKeyWithBody(ctx context.Context, ref string, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateProjectSigningKey(ctx context.Context, ref string, id openapi_types.UUID, body V1UpdateProjectSigningKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListAllSsoProvider request - V1ListAllSsoProvider(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CreateASsoProviderWithBody request with any body - V1CreateASsoProviderWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1CreateASsoProvider(ctx context.Context, ref string, body V1CreateASsoProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DeleteASsoProvider request - V1DeleteASsoProvider(ctx context.Context, ref string, providerId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetASsoProvider request - V1GetASsoProvider(ctx context.Context, ref string, providerId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateASsoProviderWithBody request with any body - V1UpdateASsoProviderWithBody(ctx context.Context, ref string, providerId openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateASsoProvider(ctx context.Context, ref string, providerId openapi_types.UUID, body V1UpdateASsoProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListProjectTpaIntegrations request - V1ListProjectTpaIntegrations(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CreateProjectTpaIntegrationWithBody request with any body - V1CreateProjectTpaIntegrationWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1CreateProjectTpaIntegration(ctx context.Context, ref string, body V1CreateProjectTpaIntegrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DeleteProjectTpaIntegration request - V1DeleteProjectTpaIntegration(ctx context.Context, ref string, tpaId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectTpaIntegration request - V1GetProjectTpaIntegration(ctx context.Context, ref string, tpaId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectPgbouncerConfig request - V1GetProjectPgbouncerConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetPoolerConfig request - V1GetPoolerConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdatePoolerConfigWithBody request with any body - V1UpdatePoolerConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdatePoolerConfig(ctx context.Context, ref string, body V1UpdatePoolerConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetPostgresConfig request - V1GetPostgresConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdatePostgresConfigWithBody request with any body - V1UpdatePostgresConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdatePostgresConfig(ctx context.Context, ref string, body V1UpdatePostgresConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetDatabaseDisk request - V1GetDatabaseDisk(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ModifyDatabaseDiskWithBody request with any body - V1ModifyDatabaseDiskWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1ModifyDatabaseDisk(ctx context.Context, ref string, body V1ModifyDatabaseDiskJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetProjectDiskAutoscaleConfig request - V1GetProjectDiskAutoscaleConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetDiskUtilization request - V1GetDiskUtilization(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetRealtimeConfig request - V1GetRealtimeConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateRealtimeConfigWithBody request with any body - V1UpdateRealtimeConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateRealtimeConfig(ctx context.Context, ref string, body V1UpdateRealtimeConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ShutdownRealtime request - V1ShutdownRealtime(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetStorageConfig request - V1GetStorageConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateStorageConfigWithBody request with any body - V1UpdateStorageConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateStorageConfig(ctx context.Context, ref string, body V1UpdateStorageConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DeleteHostnameConfig request - V1DeleteHostnameConfig(ctx context.Context, ref string, params *V1DeleteHostnameConfigParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetHostnameConfig request - V1GetHostnameConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ActivateCustomHostname request - V1ActivateCustomHostname(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateHostnameConfigWithBody request with any body - V1UpdateHostnameConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateHostnameConfig(ctx context.Context, ref string, body V1UpdateHostnameConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1VerifyDnsConfig request - V1VerifyDnsConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListAllBackups request - V1ListAllBackups(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1RestorePhysicalBackupWithBody request with any body - V1RestorePhysicalBackupWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1RestorePhysicalBackup(ctx context.Context, ref string, body V1RestorePhysicalBackupJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1RestorePitrBackupWithBody request with any body - V1RestorePitrBackupWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1RestorePitrBackup(ctx context.Context, ref string, body V1RestorePitrBackupJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetRestorePoint request - V1GetRestorePoint(ctx context.Context, ref string, params *V1GetRestorePointParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CreateRestorePointWithBody request with any body - V1CreateRestorePointWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1CreateRestorePoint(ctx context.Context, ref string, body V1CreateRestorePointJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetBackupSchedule request - V1GetBackupSchedule(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateBackupScheduleWithBody request with any body - V1UpdateBackupScheduleWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateBackupSchedule(ctx context.Context, ref string, body V1UpdateBackupScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UndoWithBody request with any body - V1UndoWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1Undo(ctx context.Context, ref string, body V1UndoJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetDatabaseMetadata request - V1GetDatabaseMetadata(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetJitAccess request - V1GetJitAccess(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1AuthorizeJitAccessWithBody request with any body - V1AuthorizeJitAccessWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1AuthorizeJitAccess(ctx context.Context, ref string, body V1AuthorizeJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateJitAccessWithBody request with any body - V1UpdateJitAccessWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateJitAccess(ctx context.Context, ref string, body V1UpdateJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1InviteExternalJitAccessWithBody request with any body - V1InviteExternalJitAccessWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1InviteExternalJitAccess(ctx context.Context, ref string, body V1InviteExternalJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1AcceptInviteExternalJitAccessWithBody request with any body - V1AcceptInviteExternalJitAccessWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1AcceptInviteExternalJitAccess(ctx context.Context, ref string, body V1AcceptInviteExternalJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DeleteInviteExternalJitAccess request - V1DeleteInviteExternalJitAccess(ctx context.Context, ref string, inviteId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListJitAccess request - V1ListJitAccess(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DeleteJitAccess request - V1DeleteJitAccess(ctx context.Context, ref string, userId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1RollbackMigrations request - V1RollbackMigrations(ctx context.Context, ref string, params *V1RollbackMigrationsParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListMigrationHistory request - V1ListMigrationHistory(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ApplyAMigrationWithBody request with any body - V1ApplyAMigrationWithBody(ctx context.Context, ref string, params *V1ApplyAMigrationParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1ApplyAMigration(ctx context.Context, ref string, params *V1ApplyAMigrationParams, body V1ApplyAMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpsertAMigrationWithBody request with any body - V1UpsertAMigrationWithBody(ctx context.Context, ref string, params *V1UpsertAMigrationParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpsertAMigration(ctx context.Context, ref string, params *V1UpsertAMigrationParams, body V1UpsertAMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetAMigration request - V1GetAMigration(ctx context.Context, ref string, version string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1PatchAMigrationWithBody request with any body - V1PatchAMigrationWithBody(ctx context.Context, ref string, version string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1PatchAMigration(ctx context.Context, ref string, version string, body V1PatchAMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetDatabaseOpenapi request - V1GetDatabaseOpenapi(ctx context.Context, ref string, params *V1GetDatabaseOpenapiParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateDatabasePasswordWithBody request with any body - V1UpdateDatabasePasswordWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateDatabasePassword(ctx context.Context, ref string, body V1UpdateDatabasePasswordJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1RunAQueryWithBody request with any body - V1RunAQueryWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1RunAQuery(ctx context.Context, ref string, body V1RunAQueryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ReadOnlyQueryWithBody request with any body - V1ReadOnlyQueryWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1ReadOnlyQuery(ctx context.Context, ref string, body V1ReadOnlyQueryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1EnableDatabaseWebhook request - V1EnableDatabaseWebhook(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListAllFunctions request - V1ListAllFunctions(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CreateAFunctionWithBody request with any body - V1CreateAFunctionWithBody(ctx context.Context, ref string, params *V1CreateAFunctionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1CreateAFunction(ctx context.Context, ref string, params *V1CreateAFunctionParams, body V1CreateAFunctionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1BulkUpdateFunctionsWithBody request with any body - V1BulkUpdateFunctionsWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1BulkUpdateFunctions(ctx context.Context, ref string, body V1BulkUpdateFunctionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DeployAFunctionWithBody request with any body - V1DeployAFunctionWithBody(ctx context.Context, ref string, params *V1DeployAFunctionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DeleteAFunction request - V1DeleteAFunction(ctx context.Context, ref string, functionSlug string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetAFunction request - V1GetAFunction(ctx context.Context, ref string, functionSlug string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateAFunctionWithBody request with any body - V1UpdateAFunctionWithBody(ctx context.Context, ref string, functionSlug string, params *V1UpdateAFunctionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateAFunction(ctx context.Context, ref string, functionSlug string, params *V1UpdateAFunctionParams, body V1UpdateAFunctionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetAFunctionBody request - V1GetAFunctionBody(ctx context.Context, ref string, functionSlug string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetServicesHealth request - V1GetServicesHealth(ctx context.Context, ref string, params *V1GetServicesHealthParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetJitAccessConfig request - V1GetJitAccessConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateJitAccessConfigWithBody request with any body - V1UpdateJitAccessConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateJitAccessConfig(ctx context.Context, ref string, body V1UpdateJitAccessConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DeleteNetworkBansWithBody request with any body - V1DeleteNetworkBansWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1DeleteNetworkBans(ctx context.Context, ref string, body V1DeleteNetworkBansJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListAllNetworkBans request - V1ListAllNetworkBans(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListAllNetworkBansEnriched request - V1ListAllNetworkBansEnriched(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetNetworkRestrictions request - V1GetNetworkRestrictions(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1PatchNetworkRestrictionsWithBody request with any body - V1PatchNetworkRestrictionsWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1PatchNetworkRestrictions(ctx context.Context, ref string, body V1PatchNetworkRestrictionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateNetworkRestrictionsWithBody request with any body - V1UpdateNetworkRestrictionsWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateNetworkRestrictions(ctx context.Context, ref string, body V1UpdateNetworkRestrictionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1PauseAProject request - V1PauseAProject(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetPgsodiumConfig request - V1GetPgsodiumConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdatePgsodiumConfigWithBody request with any body - V1UpdatePgsodiumConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdatePgsodiumConfig(ctx context.Context, ref string, body V1UpdatePgsodiumConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetPostgrestServiceConfig request - V1GetPostgrestServiceConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdatePostgrestServiceConfigWithBody request with any body - V1UpdatePostgrestServiceConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdatePostgrestServiceConfig(ctx context.Context, ref string, body V1UpdatePostgrestServiceConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1RemoveAReadReplicaWithBody request with any body - V1RemoveAReadReplicaWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1RemoveAReadReplica(ctx context.Context, ref string, body V1RemoveAReadReplicaJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1SetupAReadReplicaWithBody request with any body - V1SetupAReadReplicaWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1SetupAReadReplica(ctx context.Context, ref string, body V1SetupAReadReplicaJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetReadonlyModeStatus request - V1GetReadonlyModeStatus(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DisableReadonlyModeTemporarily request - V1DisableReadonlyModeTemporarily(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1RestartAProject request - V1RestartAProject(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListAvailableRestoreVersions request - V1ListAvailableRestoreVersions(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1RestoreAProject request - V1RestoreAProject(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CancelAProjectRestoration request - V1CancelAProjectRestoration(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1BulkDeleteSecretsWithBody request with any body - V1BulkDeleteSecretsWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1BulkDeleteSecrets(ctx context.Context, ref string, body V1BulkDeleteSecretsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListAllSecrets request - V1ListAllSecrets(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1BulkCreateSecretsWithBody request with any body - V1BulkCreateSecretsWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1BulkCreateSecrets(ctx context.Context, ref string, body V1BulkCreateSecretsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetSslEnforcementConfig request - V1GetSslEnforcementConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpdateSslEnforcementConfigWithBody request with any body - V1UpdateSslEnforcementConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpdateSslEnforcementConfig(ctx context.Context, ref string, body V1UpdateSslEnforcementConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListAllBuckets request - V1ListAllBuckets(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GenerateTypescriptTypes request - V1GenerateTypescriptTypes(ctx context.Context, ref string, params *V1GenerateTypescriptTypesParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1UpgradePostgresVersionWithBody request with any body - V1UpgradePostgresVersionWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1UpgradePostgresVersion(ctx context.Context, ref string, body V1UpgradePostgresVersionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetPostgresUpgradeEligibility request - V1GetPostgresUpgradeEligibility(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetPostgresUpgradeStatus request - V1GetPostgresUpgradeStatus(ctx context.Context, ref string, params *V1GetPostgresUpgradeStatusParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1DeactivateVanitySubdomainConfig request - V1DeactivateVanitySubdomainConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetVanitySubdomainConfig request - V1GetVanitySubdomainConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ActivateVanitySubdomainConfigWithBody request with any body - V1ActivateVanitySubdomainConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1ActivateVanitySubdomainConfig(ctx context.Context, ref string, body V1ActivateVanitySubdomainConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1CheckVanitySubdomainAvailabilityWithBody request with any body - V1CheckVanitySubdomainAvailabilityWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - - V1CheckVanitySubdomainAvailability(ctx context.Context, ref string, body V1CheckVanitySubdomainAvailabilityJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1ListAllSnippets request - V1ListAllSnippets(ctx context.Context, params *V1ListAllSnippetsParams, reqEditors ...RequestEditorFn) (*http.Response, error) - - // V1GetASnippet request - V1GetASnippet(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) -} - -func (c *Client) V1DeleteABranch(ctx context.Context, branchIdOrRef string, params *V1DeleteABranchParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeleteABranchRequest(c.Server, branchIdOrRef, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetABranchConfig(ctx context.Context, branchIdOrRef string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetABranchConfigRequest(c.Server, branchIdOrRef) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateABranchConfigWithBody(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateABranchConfigRequestWithBody(c.Server, branchIdOrRef, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateABranchConfig(ctx context.Context, branchIdOrRef string, body V1UpdateABranchConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateABranchConfigRequest(c.Server, branchIdOrRef, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DiffABranch(ctx context.Context, branchIdOrRef string, params *V1DiffABranchParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DiffABranchRequest(c.Server, branchIdOrRef, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1MergeABranchWithBody(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1MergeABranchRequestWithBody(c.Server, branchIdOrRef, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1MergeABranch(ctx context.Context, branchIdOrRef string, body V1MergeABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1MergeABranchRequest(c.Server, branchIdOrRef, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1PushABranchWithBody(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1PushABranchRequestWithBody(c.Server, branchIdOrRef, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1PushABranch(ctx context.Context, branchIdOrRef string, body V1PushABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1PushABranchRequest(c.Server, branchIdOrRef, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ResetABranchWithBody(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ResetABranchRequestWithBody(c.Server, branchIdOrRef, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ResetABranch(ctx context.Context, branchIdOrRef string, body V1ResetABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ResetABranchRequest(c.Server, branchIdOrRef, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RestoreABranch(ctx context.Context, branchIdOrRef string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RestoreABranchRequest(c.Server, branchIdOrRef) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1AuthorizeUser(ctx context.Context, params *V1AuthorizeUserParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1AuthorizeUserRequest(c.Server, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1OauthAuthorizeProjectClaim(ctx context.Context, params *V1OauthAuthorizeProjectClaimParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1OauthAuthorizeProjectClaimRequest(c.Server, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RevokeTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RevokeTokenRequestWithBody(c.Server, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RevokeToken(ctx context.Context, body V1RevokeTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RevokeTokenRequest(c.Server, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ExchangeOauthTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ExchangeOauthTokenRequestWithBody(c.Server, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ExchangeOauthTokenWithFormdataBody(ctx context.Context, body V1ExchangeOauthTokenFormdataRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ExchangeOauthTokenRequestWithFormdataBody(c.Server, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListAllOrganizations(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListAllOrganizationsRequest(c.Server) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateAnOrganizationWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateAnOrganizationRequestWithBody(c.Server, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateAnOrganization(ctx context.Context, body V1CreateAnOrganizationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateAnOrganizationRequest(c.Server, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetAnOrganization(ctx context.Context, slug string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetAnOrganizationRequest(c.Server, slug) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetOrganizationEntitlements(ctx context.Context, slug string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetOrganizationEntitlementsRequest(c.Server, slug) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListOrganizationMembers(ctx context.Context, slug string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListOrganizationMembersRequest(c.Server, slug) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetOrganizationProjectClaim(ctx context.Context, slug string, token string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetOrganizationProjectClaimRequest(c.Server, slug, token) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ClaimProjectForOrganization(ctx context.Context, slug string, token string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ClaimProjectForOrganizationRequest(c.Server, slug, token) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetAllProjectsForOrganization(ctx context.Context, slug string, params *V1GetAllProjectsForOrganizationParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetAllProjectsForOrganizationRequest(c.Server, slug, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProfile(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProfileRequest(c.Server) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListAllProjects(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListAllProjectsRequest(c.Server) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateAProjectWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateAProjectRequestWithBody(c.Server, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateAProject(ctx context.Context, body V1CreateAProjectJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateAProjectRequest(c.Server, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetAvailableRegions(ctx context.Context, params *V1GetAvailableRegionsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetAvailableRegionsRequest(c.Server, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeleteAProject(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeleteAProjectRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProject(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateAProjectWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateAProjectRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateAProject(ctx context.Context, ref string, body V1UpdateAProjectJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateAProjectRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListActionRuns(ctx context.Context, ref string, params *V1ListActionRunsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListActionRunsRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CountActionRuns(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CountActionRunsRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetActionRun(ctx context.Context, ref string, runId string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetActionRunRequest(c.Server, ref, runId) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetActionRunLogs(ctx context.Context, ref string, runId string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetActionRunLogsRequest(c.Server, ref, runId) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateActionRunStatusWithBody(ctx context.Context, ref string, runId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateActionRunStatusRequestWithBody(c.Server, ref, runId, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateActionRunStatus(ctx context.Context, ref string, runId string, body V1UpdateActionRunStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateActionRunStatusRequest(c.Server, ref, runId, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetPerformanceAdvisors(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetPerformanceAdvisorsRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetSecurityAdvisors(ctx context.Context, ref string, params *V1GetSecurityAdvisorsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetSecurityAdvisorsRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectFunctionCombinedStats(ctx context.Context, ref string, params *V1GetProjectFunctionCombinedStatsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectFunctionCombinedStatsRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectLogs(ctx context.Context, ref string, params *V1GetProjectLogsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectLogsRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectLogsAll(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectLogsAllRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ScrapeProjectMetrics(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ScrapeProjectMetricsRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectUsageApiCount(ctx context.Context, ref string, params *V1GetProjectUsageApiCountParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectUsageApiCountRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectUsageRequestCount(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectUsageRequestCountRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectApiKeys(ctx context.Context, ref string, params *V1GetProjectApiKeysParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectApiKeysRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateProjectApiKeyWithBody(ctx context.Context, ref string, params *V1CreateProjectApiKeyParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateProjectApiKeyRequestWithBody(c.Server, ref, params, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateProjectApiKey(ctx context.Context, ref string, params *V1CreateProjectApiKeyParams, body V1CreateProjectApiKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateProjectApiKeyRequest(c.Server, ref, params, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectLegacyApiKeys(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectLegacyApiKeysRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateProjectLegacyApiKeys(ctx context.Context, ref string, params *V1UpdateProjectLegacyApiKeysParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateProjectLegacyApiKeysRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeleteProjectApiKey(ctx context.Context, ref string, id openapi_types.UUID, params *V1DeleteProjectApiKeyParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeleteProjectApiKeyRequest(c.Server, ref, id, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectApiKey(ctx context.Context, ref string, id openapi_types.UUID, params *V1GetProjectApiKeyParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectApiKeyRequest(c.Server, ref, id, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateProjectApiKeyWithBody(ctx context.Context, ref string, id openapi_types.UUID, params *V1UpdateProjectApiKeyParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateProjectApiKeyRequestWithBody(c.Server, ref, id, params, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateProjectApiKey(ctx context.Context, ref string, id openapi_types.UUID, params *V1UpdateProjectApiKeyParams, body V1UpdateProjectApiKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateProjectApiKeyRequest(c.Server, ref, id, params, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListProjectAddons(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListProjectAddonsRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ApplyProjectAddonWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ApplyProjectAddonRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ApplyProjectAddon(ctx context.Context, ref string, body V1ApplyProjectAddonJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ApplyProjectAddonRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RemoveProjectAddon(ctx context.Context, ref string, addonVariant struct { - union json.RawMessage -}, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RemoveProjectAddonRequest(c.Server, ref, addonVariant) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DisablePreviewBranching(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DisablePreviewBranchingRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListAllBranches(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListAllBranchesRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateABranchWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateABranchRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateABranch(ctx context.Context, ref string, body V1CreateABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateABranchRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetABranch(ctx context.Context, ref string, name string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetABranchRequest(c.Server, ref, name) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeleteProjectClaimToken(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeleteProjectClaimTokenRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectClaimToken(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectClaimTokenRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateProjectClaimToken(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateProjectClaimTokenRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeleteLoginRoles(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeleteLoginRolesRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateLoginRoleWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateLoginRoleRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateLoginRole(ctx context.Context, ref string, body V1CreateLoginRoleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateLoginRoleRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetAuthServiceConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetAuthServiceConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateAuthServiceConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateAuthServiceConfigRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateAuthServiceConfig(ctx context.Context, ref string, body V1UpdateAuthServiceConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateAuthServiceConfigRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectSigningKeys(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectSigningKeysRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateProjectSigningKeyWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateProjectSigningKeyRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateProjectSigningKey(ctx context.Context, ref string, body V1CreateProjectSigningKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateProjectSigningKeyRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetLegacySigningKey(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetLegacySigningKeyRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateLegacySigningKey(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateLegacySigningKeyRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RemoveProjectSigningKey(ctx context.Context, ref string, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RemoveProjectSigningKeyRequest(c.Server, ref, id) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectSigningKey(ctx context.Context, ref string, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectSigningKeyRequest(c.Server, ref, id) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateProjectSigningKeyWithBody(ctx context.Context, ref string, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateProjectSigningKeyRequestWithBody(c.Server, ref, id, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateProjectSigningKey(ctx context.Context, ref string, id openapi_types.UUID, body V1UpdateProjectSigningKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateProjectSigningKeyRequest(c.Server, ref, id, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListAllSsoProvider(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListAllSsoProviderRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateASsoProviderWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateASsoProviderRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateASsoProvider(ctx context.Context, ref string, body V1CreateASsoProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateASsoProviderRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeleteASsoProvider(ctx context.Context, ref string, providerId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeleteASsoProviderRequest(c.Server, ref, providerId) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetASsoProvider(ctx context.Context, ref string, providerId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetASsoProviderRequest(c.Server, ref, providerId) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateASsoProviderWithBody(ctx context.Context, ref string, providerId openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateASsoProviderRequestWithBody(c.Server, ref, providerId, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateASsoProvider(ctx context.Context, ref string, providerId openapi_types.UUID, body V1UpdateASsoProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateASsoProviderRequest(c.Server, ref, providerId, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListProjectTpaIntegrations(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListProjectTpaIntegrationsRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateProjectTpaIntegrationWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateProjectTpaIntegrationRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateProjectTpaIntegration(ctx context.Context, ref string, body V1CreateProjectTpaIntegrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateProjectTpaIntegrationRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeleteProjectTpaIntegration(ctx context.Context, ref string, tpaId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeleteProjectTpaIntegrationRequest(c.Server, ref, tpaId) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectTpaIntegration(ctx context.Context, ref string, tpaId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectTpaIntegrationRequest(c.Server, ref, tpaId) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectPgbouncerConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectPgbouncerConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetPoolerConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetPoolerConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdatePoolerConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdatePoolerConfigRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdatePoolerConfig(ctx context.Context, ref string, body V1UpdatePoolerConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdatePoolerConfigRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetPostgresConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetPostgresConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdatePostgresConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdatePostgresConfigRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdatePostgresConfig(ctx context.Context, ref string, body V1UpdatePostgresConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdatePostgresConfigRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetDatabaseDisk(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetDatabaseDiskRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ModifyDatabaseDiskWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ModifyDatabaseDiskRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ModifyDatabaseDisk(ctx context.Context, ref string, body V1ModifyDatabaseDiskJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ModifyDatabaseDiskRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetProjectDiskAutoscaleConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetProjectDiskAutoscaleConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetDiskUtilization(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetDiskUtilizationRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetRealtimeConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetRealtimeConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateRealtimeConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateRealtimeConfigRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateRealtimeConfig(ctx context.Context, ref string, body V1UpdateRealtimeConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateRealtimeConfigRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ShutdownRealtime(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ShutdownRealtimeRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetStorageConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetStorageConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateStorageConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateStorageConfigRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateStorageConfig(ctx context.Context, ref string, body V1UpdateStorageConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateStorageConfigRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeleteHostnameConfig(ctx context.Context, ref string, params *V1DeleteHostnameConfigParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeleteHostnameConfigRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetHostnameConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetHostnameConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ActivateCustomHostname(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ActivateCustomHostnameRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateHostnameConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateHostnameConfigRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateHostnameConfig(ctx context.Context, ref string, body V1UpdateHostnameConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateHostnameConfigRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1VerifyDnsConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1VerifyDnsConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListAllBackups(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListAllBackupsRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RestorePhysicalBackupWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RestorePhysicalBackupRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RestorePhysicalBackup(ctx context.Context, ref string, body V1RestorePhysicalBackupJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RestorePhysicalBackupRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RestorePitrBackupWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RestorePitrBackupRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RestorePitrBackup(ctx context.Context, ref string, body V1RestorePitrBackupJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RestorePitrBackupRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetRestorePoint(ctx context.Context, ref string, params *V1GetRestorePointParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetRestorePointRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateRestorePointWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateRestorePointRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateRestorePoint(ctx context.Context, ref string, body V1CreateRestorePointJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateRestorePointRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetBackupSchedule(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetBackupScheduleRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateBackupScheduleWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateBackupScheduleRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateBackupSchedule(ctx context.Context, ref string, body V1UpdateBackupScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateBackupScheduleRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UndoWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UndoRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1Undo(ctx context.Context, ref string, body V1UndoJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UndoRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetDatabaseMetadata(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetDatabaseMetadataRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetJitAccess(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetJitAccessRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1AuthorizeJitAccessWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1AuthorizeJitAccessRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1AuthorizeJitAccess(ctx context.Context, ref string, body V1AuthorizeJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1AuthorizeJitAccessRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateJitAccessWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateJitAccessRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateJitAccess(ctx context.Context, ref string, body V1UpdateJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateJitAccessRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1InviteExternalJitAccessWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1InviteExternalJitAccessRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1InviteExternalJitAccess(ctx context.Context, ref string, body V1InviteExternalJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1InviteExternalJitAccessRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1AcceptInviteExternalJitAccessWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1AcceptInviteExternalJitAccessRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1AcceptInviteExternalJitAccess(ctx context.Context, ref string, body V1AcceptInviteExternalJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1AcceptInviteExternalJitAccessRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeleteInviteExternalJitAccess(ctx context.Context, ref string, inviteId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeleteInviteExternalJitAccessRequest(c.Server, ref, inviteId) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListJitAccess(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListJitAccessRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeleteJitAccess(ctx context.Context, ref string, userId openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeleteJitAccessRequest(c.Server, ref, userId) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RollbackMigrations(ctx context.Context, ref string, params *V1RollbackMigrationsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RollbackMigrationsRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListMigrationHistory(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListMigrationHistoryRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ApplyAMigrationWithBody(ctx context.Context, ref string, params *V1ApplyAMigrationParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ApplyAMigrationRequestWithBody(c.Server, ref, params, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ApplyAMigration(ctx context.Context, ref string, params *V1ApplyAMigrationParams, body V1ApplyAMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ApplyAMigrationRequest(c.Server, ref, params, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpsertAMigrationWithBody(ctx context.Context, ref string, params *V1UpsertAMigrationParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpsertAMigrationRequestWithBody(c.Server, ref, params, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpsertAMigration(ctx context.Context, ref string, params *V1UpsertAMigrationParams, body V1UpsertAMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpsertAMigrationRequest(c.Server, ref, params, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetAMigration(ctx context.Context, ref string, version string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetAMigrationRequest(c.Server, ref, version) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1PatchAMigrationWithBody(ctx context.Context, ref string, version string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1PatchAMigrationRequestWithBody(c.Server, ref, version, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1PatchAMigration(ctx context.Context, ref string, version string, body V1PatchAMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1PatchAMigrationRequest(c.Server, ref, version, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetDatabaseOpenapi(ctx context.Context, ref string, params *V1GetDatabaseOpenapiParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetDatabaseOpenapiRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateDatabasePasswordWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateDatabasePasswordRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateDatabasePassword(ctx context.Context, ref string, body V1UpdateDatabasePasswordJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateDatabasePasswordRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RunAQueryWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RunAQueryRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RunAQuery(ctx context.Context, ref string, body V1RunAQueryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RunAQueryRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ReadOnlyQueryWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ReadOnlyQueryRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ReadOnlyQuery(ctx context.Context, ref string, body V1ReadOnlyQueryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ReadOnlyQueryRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1EnableDatabaseWebhook(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1EnableDatabaseWebhookRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListAllFunctions(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListAllFunctionsRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateAFunctionWithBody(ctx context.Context, ref string, params *V1CreateAFunctionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateAFunctionRequestWithBody(c.Server, ref, params, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CreateAFunction(ctx context.Context, ref string, params *V1CreateAFunctionParams, body V1CreateAFunctionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CreateAFunctionRequest(c.Server, ref, params, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1BulkUpdateFunctionsWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1BulkUpdateFunctionsRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1BulkUpdateFunctions(ctx context.Context, ref string, body V1BulkUpdateFunctionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1BulkUpdateFunctionsRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeployAFunctionWithBody(ctx context.Context, ref string, params *V1DeployAFunctionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeployAFunctionRequestWithBody(c.Server, ref, params, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeleteAFunction(ctx context.Context, ref string, functionSlug string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeleteAFunctionRequest(c.Server, ref, functionSlug) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetAFunction(ctx context.Context, ref string, functionSlug string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetAFunctionRequest(c.Server, ref, functionSlug) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateAFunctionWithBody(ctx context.Context, ref string, functionSlug string, params *V1UpdateAFunctionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateAFunctionRequestWithBody(c.Server, ref, functionSlug, params, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateAFunction(ctx context.Context, ref string, functionSlug string, params *V1UpdateAFunctionParams, body V1UpdateAFunctionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateAFunctionRequest(c.Server, ref, functionSlug, params, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetAFunctionBody(ctx context.Context, ref string, functionSlug string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetAFunctionBodyRequest(c.Server, ref, functionSlug) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetServicesHealth(ctx context.Context, ref string, params *V1GetServicesHealthParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetServicesHealthRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetJitAccessConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetJitAccessConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateJitAccessConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateJitAccessConfigRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateJitAccessConfig(ctx context.Context, ref string, body V1UpdateJitAccessConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateJitAccessConfigRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeleteNetworkBansWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeleteNetworkBansRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeleteNetworkBans(ctx context.Context, ref string, body V1DeleteNetworkBansJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeleteNetworkBansRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListAllNetworkBans(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListAllNetworkBansRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListAllNetworkBansEnriched(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListAllNetworkBansEnrichedRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetNetworkRestrictions(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetNetworkRestrictionsRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1PatchNetworkRestrictionsWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1PatchNetworkRestrictionsRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1PatchNetworkRestrictions(ctx context.Context, ref string, body V1PatchNetworkRestrictionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1PatchNetworkRestrictionsRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateNetworkRestrictionsWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateNetworkRestrictionsRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateNetworkRestrictions(ctx context.Context, ref string, body V1UpdateNetworkRestrictionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateNetworkRestrictionsRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1PauseAProject(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1PauseAProjectRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetPgsodiumConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetPgsodiumConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdatePgsodiumConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdatePgsodiumConfigRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdatePgsodiumConfig(ctx context.Context, ref string, body V1UpdatePgsodiumConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdatePgsodiumConfigRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetPostgrestServiceConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetPostgrestServiceConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdatePostgrestServiceConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdatePostgrestServiceConfigRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdatePostgrestServiceConfig(ctx context.Context, ref string, body V1UpdatePostgrestServiceConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdatePostgrestServiceConfigRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RemoveAReadReplicaWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RemoveAReadReplicaRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RemoveAReadReplica(ctx context.Context, ref string, body V1RemoveAReadReplicaJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RemoveAReadReplicaRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1SetupAReadReplicaWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1SetupAReadReplicaRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1SetupAReadReplica(ctx context.Context, ref string, body V1SetupAReadReplicaJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1SetupAReadReplicaRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetReadonlyModeStatus(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetReadonlyModeStatusRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DisableReadonlyModeTemporarily(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DisableReadonlyModeTemporarilyRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RestartAProject(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RestartAProjectRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListAvailableRestoreVersions(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListAvailableRestoreVersionsRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1RestoreAProject(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1RestoreAProjectRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CancelAProjectRestoration(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CancelAProjectRestorationRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1BulkDeleteSecretsWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1BulkDeleteSecretsRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1BulkDeleteSecrets(ctx context.Context, ref string, body V1BulkDeleteSecretsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1BulkDeleteSecretsRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListAllSecrets(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListAllSecretsRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1BulkCreateSecretsWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1BulkCreateSecretsRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1BulkCreateSecrets(ctx context.Context, ref string, body V1BulkCreateSecretsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1BulkCreateSecretsRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetSslEnforcementConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetSslEnforcementConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateSslEnforcementConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateSslEnforcementConfigRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpdateSslEnforcementConfig(ctx context.Context, ref string, body V1UpdateSslEnforcementConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpdateSslEnforcementConfigRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListAllBuckets(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListAllBucketsRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GenerateTypescriptTypes(ctx context.Context, ref string, params *V1GenerateTypescriptTypesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GenerateTypescriptTypesRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpgradePostgresVersionWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpgradePostgresVersionRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1UpgradePostgresVersion(ctx context.Context, ref string, body V1UpgradePostgresVersionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1UpgradePostgresVersionRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetPostgresUpgradeEligibility(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetPostgresUpgradeEligibilityRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetPostgresUpgradeStatus(ctx context.Context, ref string, params *V1GetPostgresUpgradeStatusParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetPostgresUpgradeStatusRequest(c.Server, ref, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1DeactivateVanitySubdomainConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1DeactivateVanitySubdomainConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetVanitySubdomainConfig(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetVanitySubdomainConfigRequest(c.Server, ref) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ActivateVanitySubdomainConfigWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ActivateVanitySubdomainConfigRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ActivateVanitySubdomainConfig(ctx context.Context, ref string, body V1ActivateVanitySubdomainConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ActivateVanitySubdomainConfigRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CheckVanitySubdomainAvailabilityWithBody(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CheckVanitySubdomainAvailabilityRequestWithBody(c.Server, ref, contentType, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1CheckVanitySubdomainAvailability(ctx context.Context, ref string, body V1CheckVanitySubdomainAvailabilityJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1CheckVanitySubdomainAvailabilityRequest(c.Server, ref, body) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1ListAllSnippets(ctx context.Context, params *V1ListAllSnippetsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1ListAllSnippetsRequest(c.Server, params) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -func (c *Client) V1GetASnippet(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewV1GetASnippetRequest(c.Server, id) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - if err := c.applyEditors(ctx, req, reqEditors); err != nil { - return nil, err - } - return c.Client.Do(req) -} - -// NewV1DeleteABranchRequest generates requests for V1DeleteABranch -func NewV1DeleteABranchRequest(server string, branchIdOrRef string, params *V1DeleteABranchParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "branch_id_or_ref", branchIdOrRef, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/branches/%s", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Force != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "force", *params.Force, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetABranchConfigRequest generates requests for V1GetABranchConfig -func NewV1GetABranchConfigRequest(server string, branchIdOrRef string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "branch_id_or_ref", branchIdOrRef, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/branches/%s", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateABranchConfigRequest calls the generic V1UpdateABranchConfig builder with application/json body -func NewV1UpdateABranchConfigRequest(server string, branchIdOrRef string, body V1UpdateABranchConfigJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateABranchConfigRequestWithBody(server, branchIdOrRef, "application/json", bodyReader) -} - -// NewV1UpdateABranchConfigRequestWithBody generates requests for V1UpdateABranchConfig with any type of body -func NewV1UpdateABranchConfigRequestWithBody(server string, branchIdOrRef string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "branch_id_or_ref", branchIdOrRef, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/branches/%s", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1DiffABranchRequest generates requests for V1DiffABranch -func NewV1DiffABranchRequest(server string, branchIdOrRef string, params *V1DiffABranchParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "branch_id_or_ref", branchIdOrRef, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/branches/%s/diff", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.IncludedSchemas != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "included_schemas", *params.IncludedSchemas, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.Pgdelta != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "pgdelta", *params.Pgdelta, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1MergeABranchRequest calls the generic V1MergeABranch builder with application/json body -func NewV1MergeABranchRequest(server string, branchIdOrRef string, body V1MergeABranchJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1MergeABranchRequestWithBody(server, branchIdOrRef, "application/json", bodyReader) -} - -// NewV1MergeABranchRequestWithBody generates requests for V1MergeABranch with any type of body -func NewV1MergeABranchRequestWithBody(server string, branchIdOrRef string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "branch_id_or_ref", branchIdOrRef, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/branches/%s/merge", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1PushABranchRequest calls the generic V1PushABranch builder with application/json body -func NewV1PushABranchRequest(server string, branchIdOrRef string, body V1PushABranchJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1PushABranchRequestWithBody(server, branchIdOrRef, "application/json", bodyReader) -} - -// NewV1PushABranchRequestWithBody generates requests for V1PushABranch with any type of body -func NewV1PushABranchRequestWithBody(server string, branchIdOrRef string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "branch_id_or_ref", branchIdOrRef, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/branches/%s/push", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1ResetABranchRequest calls the generic V1ResetABranch builder with application/json body -func NewV1ResetABranchRequest(server string, branchIdOrRef string, body V1ResetABranchJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1ResetABranchRequestWithBody(server, branchIdOrRef, "application/json", bodyReader) -} - -// NewV1ResetABranchRequestWithBody generates requests for V1ResetABranch with any type of body -func NewV1ResetABranchRequestWithBody(server string, branchIdOrRef string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "branch_id_or_ref", branchIdOrRef, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/branches/%s/reset", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1RestoreABranchRequest generates requests for V1RestoreABranch -func NewV1RestoreABranchRequest(server string, branchIdOrRef string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "branch_id_or_ref", branchIdOrRef, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/branches/%s/restore", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1AuthorizeUserRequest generates requests for V1AuthorizeUser -func NewV1AuthorizeUserRequest(server string, params *V1AuthorizeUserParams) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/oauth/authorize") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "client_id", params.ClientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "uuid"}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "response_type", params.ResponseType, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "redirect_uri", params.RedirectUri, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if params.Scope != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "scope", *params.Scope, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.State != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "state", *params.State, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.ResponseMode != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "response_mode", *params.ResponseMode, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.CodeChallenge != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "code_challenge", *params.CodeChallenge, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.CodeChallengeMethod != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "code_challenge_method", *params.CodeChallengeMethod, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.OrganizationSlug != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "organization_slug", *params.OrganizationSlug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.TargetFlow != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "target_flow", *params.TargetFlow, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.Resource != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "resource", *params.Resource, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "uri"}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1OauthAuthorizeProjectClaimRequest generates requests for V1OauthAuthorizeProjectClaim -func NewV1OauthAuthorizeProjectClaimRequest(server string, params *V1OauthAuthorizeProjectClaimParams) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/oauth/authorize/project-claim") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "project_ref", params.ProjectRef, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "client_id", params.ClientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "uuid"}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "response_type", params.ResponseType, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "redirect_uri", params.RedirectUri, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if params.State != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "state", *params.State, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.ResponseMode != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "response_mode", *params.ResponseMode, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.CodeChallenge != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "code_challenge", *params.CodeChallenge, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.CodeChallengeMethod != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "code_challenge_method", *params.CodeChallengeMethod, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1RevokeTokenRequest calls the generic V1RevokeToken builder with application/json body -func NewV1RevokeTokenRequest(server string, body V1RevokeTokenJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1RevokeTokenRequestWithBody(server, "application/json", bodyReader) -} - -// NewV1RevokeTokenRequestWithBody generates requests for V1RevokeToken with any type of body -func NewV1RevokeTokenRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/oauth/revoke") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1ExchangeOauthTokenRequestWithFormdataBody calls the generic V1ExchangeOauthToken builder with application/x-www-form-urlencoded body -func NewV1ExchangeOauthTokenRequestWithFormdataBody(server string, body V1ExchangeOauthTokenFormdataRequestBody) (*http.Request, error) { - var bodyReader io.Reader - bodyStr, err := runtime.MarshalForm(body, nil) - if err != nil { - return nil, err - } - bodyReader = strings.NewReader(bodyStr.Encode()) - return NewV1ExchangeOauthTokenRequestWithBody(server, "application/x-www-form-urlencoded", bodyReader) -} - -// NewV1ExchangeOauthTokenRequestWithBody generates requests for V1ExchangeOauthToken with any type of body -func NewV1ExchangeOauthTokenRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/oauth/token") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1ListAllOrganizationsRequest generates requests for V1ListAllOrganizations -func NewV1ListAllOrganizationsRequest(server string) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/organizations") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CreateAnOrganizationRequest calls the generic V1CreateAnOrganization builder with application/json body -func NewV1CreateAnOrganizationRequest(server string, body V1CreateAnOrganizationJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1CreateAnOrganizationRequestWithBody(server, "application/json", bodyReader) -} - -// NewV1CreateAnOrganizationRequestWithBody generates requests for V1CreateAnOrganization with any type of body -func NewV1CreateAnOrganizationRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/organizations") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetAnOrganizationRequest generates requests for V1GetAnOrganization -func NewV1GetAnOrganizationRequest(server string, slug string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "slug", slug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/organizations/%s", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetOrganizationEntitlementsRequest generates requests for V1GetOrganizationEntitlements -func NewV1GetOrganizationEntitlementsRequest(server string, slug string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "slug", slug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/organizations/%s/entitlements", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ListOrganizationMembersRequest generates requests for V1ListOrganizationMembers -func NewV1ListOrganizationMembersRequest(server string, slug string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "slug", slug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/organizations/%s/members", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetOrganizationProjectClaimRequest generates requests for V1GetOrganizationProjectClaim -func NewV1GetOrganizationProjectClaimRequest(server string, slug string, token string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "slug", slug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "token", token, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/organizations/%s/project-claim/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ClaimProjectForOrganizationRequest generates requests for V1ClaimProjectForOrganization -func NewV1ClaimProjectForOrganizationRequest(server string, slug string, token string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "slug", slug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "token", token, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/organizations/%s/project-claim/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetAllProjectsForOrganizationRequest generates requests for V1GetAllProjectsForOrganization -func NewV1GetAllProjectsForOrganizationRequest(server string, slug string, params *V1GetAllProjectsForOrganizationParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "slug", slug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/organizations/%s/projects", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Offset != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "offset", *params.Offset, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.Limit != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.Search != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "search", *params.Search, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.Sort != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "sort", *params.Sort, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.Statuses != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "statuses", *params.Statuses, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetProfileRequest generates requests for V1GetProfile -func NewV1GetProfileRequest(server string) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/profile") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ListAllProjectsRequest generates requests for V1ListAllProjects -func NewV1ListAllProjectsRequest(server string) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CreateAProjectRequest calls the generic V1CreateAProject builder with application/json body -func NewV1CreateAProjectRequest(server string, body V1CreateAProjectJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1CreateAProjectRequestWithBody(server, "application/json", bodyReader) -} - -// NewV1CreateAProjectRequestWithBody generates requests for V1CreateAProject with any type of body -func NewV1CreateAProjectRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetAvailableRegionsRequest generates requests for V1GetAvailableRegions -func NewV1GetAvailableRegionsRequest(server string, params *V1GetAvailableRegionsParams) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/available-regions") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "organization_slug", params.OrganizationSlug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if params.Continent != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "continent", *params.Continent, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.DesiredInstanceSize != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "desired_instance_size", *params.DesiredInstanceSize, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1DeleteAProjectRequest generates requests for V1DeleteAProject -func NewV1DeleteAProjectRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetProjectRequest generates requests for V1GetProject -func NewV1GetProjectRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateAProjectRequest calls the generic V1UpdateAProject builder with application/json body -func NewV1UpdateAProjectRequest(server string, ref string, body V1UpdateAProjectJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateAProjectRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdateAProjectRequestWithBody generates requests for V1UpdateAProject with any type of body -func NewV1UpdateAProjectRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1ListActionRunsRequest generates requests for V1ListActionRuns -func NewV1ListActionRunsRequest(server string, ref string, params *V1ListActionRunsParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/actions", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Offset != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "offset", *params.Offset, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "number", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.Limit != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "number", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CountActionRunsRequest generates requests for V1CountActionRuns -func NewV1CountActionRunsRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/actions", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodHead, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetActionRunRequest generates requests for V1GetActionRun -func NewV1GetActionRunRequest(server string, ref string, runId string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "run_id", runId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/actions/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetActionRunLogsRequest generates requests for V1GetActionRunLogs -func NewV1GetActionRunLogsRequest(server string, ref string, runId string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "run_id", runId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/actions/%s/logs", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateActionRunStatusRequest calls the generic V1UpdateActionRunStatus builder with application/json body -func NewV1UpdateActionRunStatusRequest(server string, ref string, runId string, body V1UpdateActionRunStatusJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateActionRunStatusRequestWithBody(server, ref, runId, "application/json", bodyReader) -} - -// NewV1UpdateActionRunStatusRequestWithBody generates requests for V1UpdateActionRunStatus with any type of body -func NewV1UpdateActionRunStatusRequestWithBody(server string, ref string, runId string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "run_id", runId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/actions/%s/status", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetPerformanceAdvisorsRequest generates requests for V1GetPerformanceAdvisors -func NewV1GetPerformanceAdvisorsRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/advisors/performance", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetSecurityAdvisorsRequest generates requests for V1GetSecurityAdvisors -func NewV1GetSecurityAdvisorsRequest(server string, ref string, params *V1GetSecurityAdvisorsParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/advisors/security", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.LintType != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "lint_type", *params.LintType, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetProjectFunctionCombinedStatsRequest generates requests for V1GetProjectFunctionCombinedStats -func NewV1GetProjectFunctionCombinedStatsRequest(server string, ref string, params *V1GetProjectFunctionCombinedStatsParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/analytics/endpoints/functions.combined-stats", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "interval", params.Interval, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "function_id", params.FunctionId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetProjectLogsRequest generates requests for V1GetProjectLogs -func NewV1GetProjectLogsRequest(server string, ref string, params *V1GetProjectLogsParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/analytics/endpoints/logs", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Sql != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "sql", *params.Sql, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.IsoTimestampStart != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "iso_timestamp_start", *params.IsoTimestampStart, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.IsoTimestampEnd != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "iso_timestamp_end", *params.IsoTimestampEnd, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: "date-time"}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetProjectLogsAllRequest generates requests for V1GetProjectLogsAll -func NewV1GetProjectLogsAllRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/analytics/endpoints/logs.all", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ScrapeProjectMetricsRequest generates requests for V1ScrapeProjectMetrics -func NewV1ScrapeProjectMetricsRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/analytics/endpoints/metrics", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetProjectUsageApiCountRequest generates requests for V1GetProjectUsageApiCount -func NewV1GetProjectUsageApiCountRequest(server string, ref string, params *V1GetProjectUsageApiCountParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/analytics/endpoints/usage.api-counts", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Interval != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "interval", *params.Interval, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetProjectUsageRequestCountRequest generates requests for V1GetProjectUsageRequestCount -func NewV1GetProjectUsageRequestCountRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/analytics/endpoints/usage.api-requests-count", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetProjectApiKeysRequest generates requests for V1GetProjectApiKeys -func NewV1GetProjectApiKeysRequest(server string, ref string, params *V1GetProjectApiKeysParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/api-keys", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Reveal != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "reveal", *params.Reveal, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CreateProjectApiKeyRequest calls the generic V1CreateProjectApiKey builder with application/json body -func NewV1CreateProjectApiKeyRequest(server string, ref string, params *V1CreateProjectApiKeyParams, body V1CreateProjectApiKeyJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1CreateProjectApiKeyRequestWithBody(server, ref, params, "application/json", bodyReader) -} - -// NewV1CreateProjectApiKeyRequestWithBody generates requests for V1CreateProjectApiKey with any type of body -func NewV1CreateProjectApiKeyRequestWithBody(server string, ref string, params *V1CreateProjectApiKeyParams, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/api-keys", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Reveal != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "reveal", *params.Reveal, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetProjectLegacyApiKeysRequest generates requests for V1GetProjectLegacyApiKeys -func NewV1GetProjectLegacyApiKeysRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/api-keys/legacy", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateProjectLegacyApiKeysRequest generates requests for V1UpdateProjectLegacyApiKeys -func NewV1UpdateProjectLegacyApiKeysRequest(server string, ref string, params *V1UpdateProjectLegacyApiKeysParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/api-keys/legacy", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "enabled", params.Enabled, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodPut, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1DeleteProjectApiKeyRequest generates requests for V1DeleteProjectApiKey -func NewV1DeleteProjectApiKeyRequest(server string, ref string, id openapi_types.UUID, params *V1DeleteProjectApiKeyParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/api-keys/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Reveal != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "reveal", *params.Reveal, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.WasCompromised != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "was_compromised", *params.WasCompromised, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.Reason != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "reason", *params.Reason, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetProjectApiKeyRequest generates requests for V1GetProjectApiKey -func NewV1GetProjectApiKeyRequest(server string, ref string, id openapi_types.UUID, params *V1GetProjectApiKeyParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/api-keys/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Reveal != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "reveal", *params.Reveal, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateProjectApiKeyRequest calls the generic V1UpdateProjectApiKey builder with application/json body -func NewV1UpdateProjectApiKeyRequest(server string, ref string, id openapi_types.UUID, params *V1UpdateProjectApiKeyParams, body V1UpdateProjectApiKeyJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateProjectApiKeyRequestWithBody(server, ref, id, params, "application/json", bodyReader) -} - -// NewV1UpdateProjectApiKeyRequestWithBody generates requests for V1UpdateProjectApiKey with any type of body -func NewV1UpdateProjectApiKeyRequestWithBody(server string, ref string, id openapi_types.UUID, params *V1UpdateProjectApiKeyParams, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/api-keys/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Reveal != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "reveal", *params.Reveal, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1ListProjectAddonsRequest generates requests for V1ListProjectAddons -func NewV1ListProjectAddonsRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/billing/addons", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ApplyProjectAddonRequest calls the generic V1ApplyProjectAddon builder with application/json body -func NewV1ApplyProjectAddonRequest(server string, ref string, body V1ApplyProjectAddonJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1ApplyProjectAddonRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1ApplyProjectAddonRequestWithBody generates requests for V1ApplyProjectAddon with any type of body -func NewV1ApplyProjectAddonRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/billing/addons", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1RemoveProjectAddonRequest generates requests for V1RemoveProjectAddon -func NewV1RemoveProjectAddonRequest(server string, ref string, addonVariant struct { - union json.RawMessage -}) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "addon_variant", addonVariant, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/billing/addons/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1DisablePreviewBranchingRequest generates requests for V1DisablePreviewBranching -func NewV1DisablePreviewBranchingRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/branches", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ListAllBranchesRequest generates requests for V1ListAllBranches -func NewV1ListAllBranchesRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/branches", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CreateABranchRequest calls the generic V1CreateABranch builder with application/json body -func NewV1CreateABranchRequest(server string, ref string, body V1CreateABranchJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1CreateABranchRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1CreateABranchRequestWithBody generates requests for V1CreateABranch with any type of body -func NewV1CreateABranchRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/branches", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetABranchRequest generates requests for V1GetABranch -func NewV1GetABranchRequest(server string, ref string, name string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "name", name, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/branches/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1DeleteProjectClaimTokenRequest generates requests for V1DeleteProjectClaimToken -func NewV1DeleteProjectClaimTokenRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/claim-token", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetProjectClaimTokenRequest generates requests for V1GetProjectClaimToken -func NewV1GetProjectClaimTokenRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/claim-token", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CreateProjectClaimTokenRequest generates requests for V1CreateProjectClaimToken -func NewV1CreateProjectClaimTokenRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/claim-token", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1DeleteLoginRolesRequest generates requests for V1DeleteLoginRoles -func NewV1DeleteLoginRolesRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/cli/login-role", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CreateLoginRoleRequest calls the generic V1CreateLoginRole builder with application/json body -func NewV1CreateLoginRoleRequest(server string, ref string, body V1CreateLoginRoleJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1CreateLoginRoleRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1CreateLoginRoleRequestWithBody generates requests for V1CreateLoginRole with any type of body -func NewV1CreateLoginRoleRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/cli/login-role", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetAuthServiceConfigRequest generates requests for V1GetAuthServiceConfig -func NewV1GetAuthServiceConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateAuthServiceConfigRequest calls the generic V1UpdateAuthServiceConfig builder with application/json body -func NewV1UpdateAuthServiceConfigRequest(server string, ref string, body V1UpdateAuthServiceConfigJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateAuthServiceConfigRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdateAuthServiceConfigRequestWithBody generates requests for V1UpdateAuthServiceConfig with any type of body -func NewV1UpdateAuthServiceConfigRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetProjectSigningKeysRequest generates requests for V1GetProjectSigningKeys -func NewV1GetProjectSigningKeysRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/signing-keys", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CreateProjectSigningKeyRequest calls the generic V1CreateProjectSigningKey builder with application/json body -func NewV1CreateProjectSigningKeyRequest(server string, ref string, body V1CreateProjectSigningKeyJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1CreateProjectSigningKeyRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1CreateProjectSigningKeyRequestWithBody generates requests for V1CreateProjectSigningKey with any type of body -func NewV1CreateProjectSigningKeyRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/signing-keys", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetLegacySigningKeyRequest generates requests for V1GetLegacySigningKey -func NewV1GetLegacySigningKeyRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/signing-keys/legacy", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CreateLegacySigningKeyRequest generates requests for V1CreateLegacySigningKey -func NewV1CreateLegacySigningKeyRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/signing-keys/legacy", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1RemoveProjectSigningKeyRequest generates requests for V1RemoveProjectSigningKey -func NewV1RemoveProjectSigningKeyRequest(server string, ref string, id openapi_types.UUID) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/signing-keys/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetProjectSigningKeyRequest generates requests for V1GetProjectSigningKey -func NewV1GetProjectSigningKeyRequest(server string, ref string, id openapi_types.UUID) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/signing-keys/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateProjectSigningKeyRequest calls the generic V1UpdateProjectSigningKey builder with application/json body -func NewV1UpdateProjectSigningKeyRequest(server string, ref string, id openapi_types.UUID, body V1UpdateProjectSigningKeyJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateProjectSigningKeyRequestWithBody(server, ref, id, "application/json", bodyReader) -} - -// NewV1UpdateProjectSigningKeyRequestWithBody generates requests for V1UpdateProjectSigningKey with any type of body -func NewV1UpdateProjectSigningKeyRequestWithBody(server string, ref string, id openapi_types.UUID, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/signing-keys/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1ListAllSsoProviderRequest generates requests for V1ListAllSsoProvider -func NewV1ListAllSsoProviderRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/sso/providers", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CreateASsoProviderRequest calls the generic V1CreateASsoProvider builder with application/json body -func NewV1CreateASsoProviderRequest(server string, ref string, body V1CreateASsoProviderJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1CreateASsoProviderRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1CreateASsoProviderRequestWithBody generates requests for V1CreateASsoProvider with any type of body -func NewV1CreateASsoProviderRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/sso/providers", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1DeleteASsoProviderRequest generates requests for V1DeleteASsoProvider -func NewV1DeleteASsoProviderRequest(server string, ref string, providerId openapi_types.UUID) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "provider_id", providerId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/sso/providers/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetASsoProviderRequest generates requests for V1GetASsoProvider -func NewV1GetASsoProviderRequest(server string, ref string, providerId openapi_types.UUID) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "provider_id", providerId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/sso/providers/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateASsoProviderRequest calls the generic V1UpdateASsoProvider builder with application/json body -func NewV1UpdateASsoProviderRequest(server string, ref string, providerId openapi_types.UUID, body V1UpdateASsoProviderJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateASsoProviderRequestWithBody(server, ref, providerId, "application/json", bodyReader) -} - -// NewV1UpdateASsoProviderRequestWithBody generates requests for V1UpdateASsoProvider with any type of body -func NewV1UpdateASsoProviderRequestWithBody(server string, ref string, providerId openapi_types.UUID, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "provider_id", providerId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/sso/providers/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1ListProjectTpaIntegrationsRequest generates requests for V1ListProjectTpaIntegrations -func NewV1ListProjectTpaIntegrationsRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/third-party-auth", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CreateProjectTpaIntegrationRequest calls the generic V1CreateProjectTpaIntegration builder with application/json body -func NewV1CreateProjectTpaIntegrationRequest(server string, ref string, body V1CreateProjectTpaIntegrationJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1CreateProjectTpaIntegrationRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1CreateProjectTpaIntegrationRequestWithBody generates requests for V1CreateProjectTpaIntegration with any type of body -func NewV1CreateProjectTpaIntegrationRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/third-party-auth", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1DeleteProjectTpaIntegrationRequest generates requests for V1DeleteProjectTpaIntegration -func NewV1DeleteProjectTpaIntegrationRequest(server string, ref string, tpaId openapi_types.UUID) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "tpa_id", tpaId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/third-party-auth/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetProjectTpaIntegrationRequest generates requests for V1GetProjectTpaIntegration -func NewV1GetProjectTpaIntegrationRequest(server string, ref string, tpaId openapi_types.UUID) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "tpa_id", tpaId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/auth/third-party-auth/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetProjectPgbouncerConfigRequest generates requests for V1GetProjectPgbouncerConfig -func NewV1GetProjectPgbouncerConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/database/pgbouncer", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetPoolerConfigRequest generates requests for V1GetPoolerConfig -func NewV1GetPoolerConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/database/pooler", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdatePoolerConfigRequest calls the generic V1UpdatePoolerConfig builder with application/json body -func NewV1UpdatePoolerConfigRequest(server string, ref string, body V1UpdatePoolerConfigJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdatePoolerConfigRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdatePoolerConfigRequestWithBody generates requests for V1UpdatePoolerConfig with any type of body -func NewV1UpdatePoolerConfigRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/database/pooler", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetPostgresConfigRequest generates requests for V1GetPostgresConfig -func NewV1GetPostgresConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/database/postgres", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdatePostgresConfigRequest calls the generic V1UpdatePostgresConfig builder with application/json body -func NewV1UpdatePostgresConfigRequest(server string, ref string, body V1UpdatePostgresConfigJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdatePostgresConfigRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdatePostgresConfigRequestWithBody generates requests for V1UpdatePostgresConfig with any type of body -func NewV1UpdatePostgresConfigRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/database/postgres", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetDatabaseDiskRequest generates requests for V1GetDatabaseDisk -func NewV1GetDatabaseDiskRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/disk", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ModifyDatabaseDiskRequest calls the generic V1ModifyDatabaseDisk builder with application/json body -func NewV1ModifyDatabaseDiskRequest(server string, ref string, body V1ModifyDatabaseDiskJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1ModifyDatabaseDiskRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1ModifyDatabaseDiskRequestWithBody generates requests for V1ModifyDatabaseDisk with any type of body -func NewV1ModifyDatabaseDiskRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/disk", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetProjectDiskAutoscaleConfigRequest generates requests for V1GetProjectDiskAutoscaleConfig -func NewV1GetProjectDiskAutoscaleConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/disk/autoscale", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetDiskUtilizationRequest generates requests for V1GetDiskUtilization -func NewV1GetDiskUtilizationRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/disk/util", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetRealtimeConfigRequest generates requests for V1GetRealtimeConfig -func NewV1GetRealtimeConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/realtime", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateRealtimeConfigRequest calls the generic V1UpdateRealtimeConfig builder with application/json body -func NewV1UpdateRealtimeConfigRequest(server string, ref string, body V1UpdateRealtimeConfigJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateRealtimeConfigRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdateRealtimeConfigRequestWithBody generates requests for V1UpdateRealtimeConfig with any type of body -func NewV1UpdateRealtimeConfigRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/realtime", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1ShutdownRealtimeRequest generates requests for V1ShutdownRealtime -func NewV1ShutdownRealtimeRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/realtime/shutdown", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetStorageConfigRequest generates requests for V1GetStorageConfig -func NewV1GetStorageConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/storage", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateStorageConfigRequest calls the generic V1UpdateStorageConfig builder with application/json body -func NewV1UpdateStorageConfigRequest(server string, ref string, body V1UpdateStorageConfigJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateStorageConfigRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdateStorageConfigRequestWithBody generates requests for V1UpdateStorageConfig with any type of body -func NewV1UpdateStorageConfigRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/config/storage", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1DeleteHostnameConfigRequest generates requests for V1DeleteHostnameConfig -func NewV1DeleteHostnameConfigRequest(server string, ref string, params *V1DeleteHostnameConfigParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/custom-hostname", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.RemoveAddon != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "remove_addon", *params.RemoveAddon, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetHostnameConfigRequest generates requests for V1GetHostnameConfig -func NewV1GetHostnameConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/custom-hostname", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ActivateCustomHostnameRequest generates requests for V1ActivateCustomHostname -func NewV1ActivateCustomHostnameRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/custom-hostname/activate", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateHostnameConfigRequest calls the generic V1UpdateHostnameConfig builder with application/json body -func NewV1UpdateHostnameConfigRequest(server string, ref string, body V1UpdateHostnameConfigJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateHostnameConfigRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdateHostnameConfigRequestWithBody generates requests for V1UpdateHostnameConfig with any type of body -func NewV1UpdateHostnameConfigRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/custom-hostname/initialize", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1VerifyDnsConfigRequest generates requests for V1VerifyDnsConfig -func NewV1VerifyDnsConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/custom-hostname/reverify", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ListAllBackupsRequest generates requests for V1ListAllBackups -func NewV1ListAllBackupsRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/backups", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1RestorePhysicalBackupRequest calls the generic V1RestorePhysicalBackup builder with application/json body -func NewV1RestorePhysicalBackupRequest(server string, ref string, body V1RestorePhysicalBackupJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1RestorePhysicalBackupRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1RestorePhysicalBackupRequestWithBody generates requests for V1RestorePhysicalBackup with any type of body -func NewV1RestorePhysicalBackupRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/backups/restore", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1RestorePitrBackupRequest calls the generic V1RestorePitrBackup builder with application/json body -func NewV1RestorePitrBackupRequest(server string, ref string, body V1RestorePitrBackupJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1RestorePitrBackupRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1RestorePitrBackupRequestWithBody generates requests for V1RestorePitrBackup with any type of body -func NewV1RestorePitrBackupRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/backups/restore-pitr", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetRestorePointRequest generates requests for V1GetRestorePoint -func NewV1GetRestorePointRequest(server string, ref string, params *V1GetRestorePointParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/backups/restore-point", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Name != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "name", *params.Name, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CreateRestorePointRequest calls the generic V1CreateRestorePoint builder with application/json body -func NewV1CreateRestorePointRequest(server string, ref string, body V1CreateRestorePointJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1CreateRestorePointRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1CreateRestorePointRequestWithBody generates requests for V1CreateRestorePoint with any type of body -func NewV1CreateRestorePointRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/backups/restore-point", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetBackupScheduleRequest generates requests for V1GetBackupSchedule -func NewV1GetBackupScheduleRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/backups/schedule", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateBackupScheduleRequest calls the generic V1UpdateBackupSchedule builder with application/json body -func NewV1UpdateBackupScheduleRequest(server string, ref string, body V1UpdateBackupScheduleJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateBackupScheduleRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdateBackupScheduleRequestWithBody generates requests for V1UpdateBackupSchedule with any type of body -func NewV1UpdateBackupScheduleRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/backups/schedule", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1UndoRequest calls the generic V1Undo builder with application/json body -func NewV1UndoRequest(server string, ref string, body V1UndoJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UndoRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UndoRequestWithBody generates requests for V1Undo with any type of body -func NewV1UndoRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/backups/undo", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetDatabaseMetadataRequest generates requests for V1GetDatabaseMetadata -func NewV1GetDatabaseMetadataRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/context", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetJitAccessRequest generates requests for V1GetJitAccess -func NewV1GetJitAccessRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/jit", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1AuthorizeJitAccessRequest calls the generic V1AuthorizeJitAccess builder with application/json body -func NewV1AuthorizeJitAccessRequest(server string, ref string, body V1AuthorizeJitAccessJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1AuthorizeJitAccessRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1AuthorizeJitAccessRequestWithBody generates requests for V1AuthorizeJitAccess with any type of body -func NewV1AuthorizeJitAccessRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/jit", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1UpdateJitAccessRequest calls the generic V1UpdateJitAccess builder with application/json body -func NewV1UpdateJitAccessRequest(server string, ref string, body V1UpdateJitAccessJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateJitAccessRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdateJitAccessRequestWithBody generates requests for V1UpdateJitAccess with any type of body -func NewV1UpdateJitAccessRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/jit", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1InviteExternalJitAccessRequest calls the generic V1InviteExternalJitAccess builder with application/json body -func NewV1InviteExternalJitAccessRequest(server string, ref string, body V1InviteExternalJitAccessJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1InviteExternalJitAccessRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1InviteExternalJitAccessRequestWithBody generates requests for V1InviteExternalJitAccess with any type of body -func NewV1InviteExternalJitAccessRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/jit/invite", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1AcceptInviteExternalJitAccessRequest calls the generic V1AcceptInviteExternalJitAccess builder with application/json body -func NewV1AcceptInviteExternalJitAccessRequest(server string, ref string, body V1AcceptInviteExternalJitAccessJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1AcceptInviteExternalJitAccessRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1AcceptInviteExternalJitAccessRequestWithBody generates requests for V1AcceptInviteExternalJitAccess with any type of body -func NewV1AcceptInviteExternalJitAccessRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/jit/invite/accept", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1DeleteInviteExternalJitAccessRequest generates requests for V1DeleteInviteExternalJitAccess -func NewV1DeleteInviteExternalJitAccessRequest(server string, ref string, inviteId openapi_types.UUID) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "invite_id", inviteId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/jit/invite/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ListJitAccessRequest generates requests for V1ListJitAccess -func NewV1ListJitAccessRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/jit/list", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1DeleteJitAccessRequest generates requests for V1DeleteJitAccess -func NewV1DeleteJitAccessRequest(server string, ref string, userId openapi_types.UUID) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "user_id", userId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/jit/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1RollbackMigrationsRequest generates requests for V1RollbackMigrations -func NewV1RollbackMigrationsRequest(server string, ref string, params *V1RollbackMigrationsParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/migrations", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "gte", params.Gte, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ListMigrationHistoryRequest generates requests for V1ListMigrationHistory -func NewV1ListMigrationHistoryRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/migrations", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ApplyAMigrationRequest calls the generic V1ApplyAMigration builder with application/json body -func NewV1ApplyAMigrationRequest(server string, ref string, params *V1ApplyAMigrationParams, body V1ApplyAMigrationJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1ApplyAMigrationRequestWithBody(server, ref, params, "application/json", bodyReader) -} - -// NewV1ApplyAMigrationRequestWithBody generates requests for V1ApplyAMigration with any type of body -func NewV1ApplyAMigrationRequestWithBody(server string, ref string, params *V1ApplyAMigrationParams, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/migrations", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - if params != nil { - - if params.IdempotencyKey != nil { - var headerParam0 string - - headerParam0, err = runtime.StyleParamWithOptions("simple", false, "Idempotency-Key", *params.IdempotencyKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - req.Header.Set("Idempotency-Key", headerParam0) - } - - } - - return req, nil -} - -// NewV1UpsertAMigrationRequest calls the generic V1UpsertAMigration builder with application/json body -func NewV1UpsertAMigrationRequest(server string, ref string, params *V1UpsertAMigrationParams, body V1UpsertAMigrationJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpsertAMigrationRequestWithBody(server, ref, params, "application/json", bodyReader) -} - -// NewV1UpsertAMigrationRequestWithBody generates requests for V1UpsertAMigration with any type of body -func NewV1UpsertAMigrationRequestWithBody(server string, ref string, params *V1UpsertAMigrationParams, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/migrations", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - if params != nil { - - if params.IdempotencyKey != nil { - var headerParam0 string - - headerParam0, err = runtime.StyleParamWithOptions("simple", false, "Idempotency-Key", *params.IdempotencyKey, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - req.Header.Set("Idempotency-Key", headerParam0) - } - - } - - return req, nil -} - -// NewV1GetAMigrationRequest generates requests for V1GetAMigration -func NewV1GetAMigrationRequest(server string, ref string, version string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "version", version, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/migrations/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1PatchAMigrationRequest calls the generic V1PatchAMigration builder with application/json body -func NewV1PatchAMigrationRequest(server string, ref string, version string, body V1PatchAMigrationJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1PatchAMigrationRequestWithBody(server, ref, version, "application/json", bodyReader) -} - -// NewV1PatchAMigrationRequestWithBody generates requests for V1PatchAMigration with any type of body -func NewV1PatchAMigrationRequestWithBody(server string, ref string, version string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "version", version, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/migrations/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetDatabaseOpenapiRequest generates requests for V1GetDatabaseOpenapi -func NewV1GetDatabaseOpenapiRequest(server string, ref string, params *V1GetDatabaseOpenapiParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/openapi", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Schema != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "schema", *params.Schema, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateDatabasePasswordRequest calls the generic V1UpdateDatabasePassword builder with application/json body -func NewV1UpdateDatabasePasswordRequest(server string, ref string, body V1UpdateDatabasePasswordJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateDatabasePasswordRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdateDatabasePasswordRequestWithBody generates requests for V1UpdateDatabasePassword with any type of body -func NewV1UpdateDatabasePasswordRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/password", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1RunAQueryRequest calls the generic V1RunAQuery builder with application/json body -func NewV1RunAQueryRequest(server string, ref string, body V1RunAQueryJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1RunAQueryRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1RunAQueryRequestWithBody generates requests for V1RunAQuery with any type of body -func NewV1RunAQueryRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/query", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1ReadOnlyQueryRequest calls the generic V1ReadOnlyQuery builder with application/json body -func NewV1ReadOnlyQueryRequest(server string, ref string, body V1ReadOnlyQueryJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1ReadOnlyQueryRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1ReadOnlyQueryRequestWithBody generates requests for V1ReadOnlyQuery with any type of body -func NewV1ReadOnlyQueryRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/query/read-only", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1EnableDatabaseWebhookRequest generates requests for V1EnableDatabaseWebhook -func NewV1EnableDatabaseWebhookRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/database/webhooks/enable", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ListAllFunctionsRequest generates requests for V1ListAllFunctions -func NewV1ListAllFunctionsRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/functions", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CreateAFunctionRequest calls the generic V1CreateAFunction builder with application/json body -func NewV1CreateAFunctionRequest(server string, ref string, params *V1CreateAFunctionParams, body V1CreateAFunctionJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1CreateAFunctionRequestWithBody(server, ref, params, "application/json", bodyReader) -} - -// NewV1CreateAFunctionRequestWithBody generates requests for V1CreateAFunction with any type of body -func NewV1CreateAFunctionRequestWithBody(server string, ref string, params *V1CreateAFunctionParams, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/functions", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Slug != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "slug", *params.Slug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.Name != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "name", *params.Name, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.VerifyJwt != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "verify_jwt", *params.VerifyJwt, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.ImportMap != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "import_map", *params.ImportMap, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.EntrypointPath != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "entrypoint_path", *params.EntrypointPath, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.ImportMapPath != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "import_map_path", *params.ImportMapPath, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.EzbrSha256 != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "ezbr_sha256", *params.EzbrSha256, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1BulkUpdateFunctionsRequest calls the generic V1BulkUpdateFunctions builder with application/json body -func NewV1BulkUpdateFunctionsRequest(server string, ref string, body V1BulkUpdateFunctionsJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1BulkUpdateFunctionsRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1BulkUpdateFunctionsRequestWithBody generates requests for V1BulkUpdateFunctions with any type of body -func NewV1BulkUpdateFunctionsRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/functions", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1DeployAFunctionRequestWithBody generates requests for V1DeployAFunction with any type of body -func NewV1DeployAFunctionRequestWithBody(server string, ref string, params *V1DeployAFunctionParams, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/functions/deploy", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Slug != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "slug", *params.Slug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.BundleOnly != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "bundleOnly", *params.BundleOnly, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1DeleteAFunctionRequest generates requests for V1DeleteAFunction -func NewV1DeleteAFunctionRequest(server string, ref string, functionSlug string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "function_slug", functionSlug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/functions/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetAFunctionRequest generates requests for V1GetAFunction -func NewV1GetAFunctionRequest(server string, ref string, functionSlug string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "function_slug", functionSlug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/functions/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateAFunctionRequest calls the generic V1UpdateAFunction builder with application/json body -func NewV1UpdateAFunctionRequest(server string, ref string, functionSlug string, params *V1UpdateAFunctionParams, body V1UpdateAFunctionJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateAFunctionRequestWithBody(server, ref, functionSlug, params, "application/json", bodyReader) -} - -// NewV1UpdateAFunctionRequestWithBody generates requests for V1UpdateAFunction with any type of body -func NewV1UpdateAFunctionRequestWithBody(server string, ref string, functionSlug string, params *V1UpdateAFunctionParams, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "function_slug", functionSlug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/functions/%s", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Slug != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "slug", *params.Slug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.Name != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "name", *params.Name, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.VerifyJwt != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "verify_jwt", *params.VerifyJwt, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.ImportMap != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "import_map", *params.ImportMap, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.EntrypointPath != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "entrypoint_path", *params.EntrypointPath, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.ImportMapPath != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "import_map_path", *params.ImportMapPath, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.EzbrSha256 != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "ezbr_sha256", *params.EzbrSha256, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetAFunctionBodyRequest generates requests for V1GetAFunctionBody -func NewV1GetAFunctionBodyRequest(server string, ref string, functionSlug string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - var pathParam1 string - - pathParam1, err = runtime.StyleParamWithOptions("simple", false, "function_slug", functionSlug, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/functions/%s/body", pathParam0, pathParam1) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetServicesHealthRequest generates requests for V1GetServicesHealth -func NewV1GetServicesHealthRequest(server string, ref string, params *V1GetServicesHealthParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/health", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.Services != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "services", params.Services, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.TimeoutMs != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "timeout_ms", *params.TimeoutMs, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetJitAccessConfigRequest generates requests for V1GetJitAccessConfig -func NewV1GetJitAccessConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/jit-access", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateJitAccessConfigRequest calls the generic V1UpdateJitAccessConfig builder with application/json body -func NewV1UpdateJitAccessConfigRequest(server string, ref string, body V1UpdateJitAccessConfigJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateJitAccessConfigRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdateJitAccessConfigRequestWithBody generates requests for V1UpdateJitAccessConfig with any type of body -func NewV1UpdateJitAccessConfigRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/jit-access", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1DeleteNetworkBansRequest calls the generic V1DeleteNetworkBans builder with application/json body -func NewV1DeleteNetworkBansRequest(server string, ref string, body V1DeleteNetworkBansJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1DeleteNetworkBansRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1DeleteNetworkBansRequestWithBody generates requests for V1DeleteNetworkBans with any type of body -func NewV1DeleteNetworkBansRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/network-bans", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1ListAllNetworkBansRequest generates requests for V1ListAllNetworkBans -func NewV1ListAllNetworkBansRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/network-bans/retrieve", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ListAllNetworkBansEnrichedRequest generates requests for V1ListAllNetworkBansEnriched -func NewV1ListAllNetworkBansEnrichedRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/network-bans/retrieve/enriched", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetNetworkRestrictionsRequest generates requests for V1GetNetworkRestrictions -func NewV1GetNetworkRestrictionsRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/network-restrictions", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1PatchNetworkRestrictionsRequest calls the generic V1PatchNetworkRestrictions builder with application/json body -func NewV1PatchNetworkRestrictionsRequest(server string, ref string, body V1PatchNetworkRestrictionsJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1PatchNetworkRestrictionsRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1PatchNetworkRestrictionsRequestWithBody generates requests for V1PatchNetworkRestrictions with any type of body -func NewV1PatchNetworkRestrictionsRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/network-restrictions", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1UpdateNetworkRestrictionsRequest calls the generic V1UpdateNetworkRestrictions builder with application/json body -func NewV1UpdateNetworkRestrictionsRequest(server string, ref string, body V1UpdateNetworkRestrictionsJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateNetworkRestrictionsRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdateNetworkRestrictionsRequestWithBody generates requests for V1UpdateNetworkRestrictions with any type of body -func NewV1UpdateNetworkRestrictionsRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/network-restrictions/apply", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1PauseAProjectRequest generates requests for V1PauseAProject -func NewV1PauseAProjectRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/pause", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetPgsodiumConfigRequest generates requests for V1GetPgsodiumConfig -func NewV1GetPgsodiumConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/pgsodium", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdatePgsodiumConfigRequest calls the generic V1UpdatePgsodiumConfig builder with application/json body -func NewV1UpdatePgsodiumConfigRequest(server string, ref string, body V1UpdatePgsodiumConfigJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdatePgsodiumConfigRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdatePgsodiumConfigRequestWithBody generates requests for V1UpdatePgsodiumConfig with any type of body -func NewV1UpdatePgsodiumConfigRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/pgsodium", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetPostgrestServiceConfigRequest generates requests for V1GetPostgrestServiceConfig -func NewV1GetPostgrestServiceConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/postgrest", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdatePostgrestServiceConfigRequest calls the generic V1UpdatePostgrestServiceConfig builder with application/json body -func NewV1UpdatePostgrestServiceConfigRequest(server string, ref string, body V1UpdatePostgrestServiceConfigJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdatePostgrestServiceConfigRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdatePostgrestServiceConfigRequestWithBody generates requests for V1UpdatePostgrestServiceConfig with any type of body -func NewV1UpdatePostgrestServiceConfigRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/postgrest", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1RemoveAReadReplicaRequest calls the generic V1RemoveAReadReplica builder with application/json body -func NewV1RemoveAReadReplicaRequest(server string, ref string, body V1RemoveAReadReplicaJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1RemoveAReadReplicaRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1RemoveAReadReplicaRequestWithBody generates requests for V1RemoveAReadReplica with any type of body -func NewV1RemoveAReadReplicaRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/read-replicas/remove", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1SetupAReadReplicaRequest calls the generic V1SetupAReadReplica builder with application/json body -func NewV1SetupAReadReplicaRequest(server string, ref string, body V1SetupAReadReplicaJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1SetupAReadReplicaRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1SetupAReadReplicaRequestWithBody generates requests for V1SetupAReadReplica with any type of body -func NewV1SetupAReadReplicaRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/read-replicas/setup", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetReadonlyModeStatusRequest generates requests for V1GetReadonlyModeStatus -func NewV1GetReadonlyModeStatusRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/readonly", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1DisableReadonlyModeTemporarilyRequest generates requests for V1DisableReadonlyModeTemporarily -func NewV1DisableReadonlyModeTemporarilyRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/readonly/temporary-disable", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1RestartAProjectRequest generates requests for V1RestartAProject -func NewV1RestartAProjectRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/restart", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ListAvailableRestoreVersionsRequest generates requests for V1ListAvailableRestoreVersions -func NewV1ListAvailableRestoreVersionsRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/restore", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1RestoreAProjectRequest generates requests for V1RestoreAProject -func NewV1RestoreAProjectRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/restore", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1CancelAProjectRestorationRequest generates requests for V1CancelAProjectRestoration -func NewV1CancelAProjectRestorationRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/restore/cancel", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1BulkDeleteSecretsRequest calls the generic V1BulkDeleteSecrets builder with application/json body -func NewV1BulkDeleteSecretsRequest(server string, ref string, body V1BulkDeleteSecretsJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1BulkDeleteSecretsRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1BulkDeleteSecretsRequestWithBody generates requests for V1BulkDeleteSecrets with any type of body -func NewV1BulkDeleteSecretsRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/secrets", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1ListAllSecretsRequest generates requests for V1ListAllSecrets -func NewV1ListAllSecretsRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/secrets", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1BulkCreateSecretsRequest calls the generic V1BulkCreateSecrets builder with application/json body -func NewV1BulkCreateSecretsRequest(server string, ref string, body V1BulkCreateSecretsJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1BulkCreateSecretsRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1BulkCreateSecretsRequestWithBody generates requests for V1BulkCreateSecrets with any type of body -func NewV1BulkCreateSecretsRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/secrets", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetSslEnforcementConfigRequest generates requests for V1GetSslEnforcementConfig -func NewV1GetSslEnforcementConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/ssl-enforcement", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpdateSslEnforcementConfigRequest calls the generic V1UpdateSslEnforcementConfig builder with application/json body -func NewV1UpdateSslEnforcementConfigRequest(server string, ref string, body V1UpdateSslEnforcementConfigJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpdateSslEnforcementConfigRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpdateSslEnforcementConfigRequestWithBody generates requests for V1UpdateSslEnforcementConfig with any type of body -func NewV1UpdateSslEnforcementConfigRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/ssl-enforcement", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1ListAllBucketsRequest generates requests for V1ListAllBuckets -func NewV1ListAllBucketsRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/storage/buckets", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GenerateTypescriptTypesRequest generates requests for V1GenerateTypescriptTypes -func NewV1GenerateTypescriptTypesRequest(server string, ref string, params *V1GenerateTypescriptTypesParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/types/typescript", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.IncludedSchemas != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "included_schemas", *params.IncludedSchemas, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1UpgradePostgresVersionRequest calls the generic V1UpgradePostgresVersion builder with application/json body -func NewV1UpgradePostgresVersionRequest(server string, ref string, body V1UpgradePostgresVersionJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1UpgradePostgresVersionRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1UpgradePostgresVersionRequestWithBody generates requests for V1UpgradePostgresVersion with any type of body -func NewV1UpgradePostgresVersionRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/upgrade", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1GetPostgresUpgradeEligibilityRequest generates requests for V1GetPostgresUpgradeEligibility -func NewV1GetPostgresUpgradeEligibilityRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/upgrade/eligibility", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetPostgresUpgradeStatusRequest generates requests for V1GetPostgresUpgradeStatus -func NewV1GetPostgresUpgradeStatusRequest(server string, ref string, params *V1GetPostgresUpgradeStatusParams) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/upgrade/status", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.TrackingId != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "tracking_id", *params.TrackingId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1DeactivateVanitySubdomainConfigRequest generates requests for V1DeactivateVanitySubdomainConfig -func NewV1DeactivateVanitySubdomainConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/vanity-subdomain", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetVanitySubdomainConfigRequest generates requests for V1GetVanitySubdomainConfig -func NewV1GetVanitySubdomainConfigRequest(server string, ref string) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/vanity-subdomain", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1ActivateVanitySubdomainConfigRequest calls the generic V1ActivateVanitySubdomainConfig builder with application/json body -func NewV1ActivateVanitySubdomainConfigRequest(server string, ref string, body V1ActivateVanitySubdomainConfigJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1ActivateVanitySubdomainConfigRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1ActivateVanitySubdomainConfigRequestWithBody generates requests for V1ActivateVanitySubdomainConfig with any type of body -func NewV1ActivateVanitySubdomainConfigRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/vanity-subdomain/activate", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1CheckVanitySubdomainAvailabilityRequest calls the generic V1CheckVanitySubdomainAvailability builder with application/json body -func NewV1CheckVanitySubdomainAvailabilityRequest(server string, ref string, body V1CheckVanitySubdomainAvailabilityJSONRequestBody) (*http.Request, error) { - var bodyReader io.Reader - buf, err := json.Marshal(body) - if err != nil { - return nil, err - } - bodyReader = bytes.NewReader(buf) - return NewV1CheckVanitySubdomainAvailabilityRequestWithBody(server, ref, "application/json", bodyReader) -} - -// NewV1CheckVanitySubdomainAvailabilityRequestWithBody generates requests for V1CheckVanitySubdomainAvailability with any type of body -func NewV1CheckVanitySubdomainAvailabilityRequestWithBody(server string, ref string, contentType string, body io.Reader) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "ref", ref, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/projects/%s/vanity-subdomain/check-availability", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) - if err != nil { - return nil, err - } - - req.Header.Add("Content-Type", contentType) - - return req, nil -} - -// NewV1ListAllSnippetsRequest generates requests for V1ListAllSnippets -func NewV1ListAllSnippetsRequest(server string, params *V1ListAllSnippetsParams) (*http.Request, error) { - var err error - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/snippets") - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - if params != nil { - // queryValues collects non-styled parameters (passthrough, JSON) - // that are safe to round-trip through url.Values.Encode(). - queryValues := queryURL.Query() - // rawQueryFragments collects pre-encoded query fragments from - // styled parameters, preserving literal commas as delimiters - // per the OpenAPI spec (e.g. "color=blue,black,brown"). - var rawQueryFragments []string - - if params.ProjectRef != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "project_ref", *params.ProjectRef, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.Cursor != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cursor", *params.Cursor, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.Limit != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.SortBy != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "sort_by", *params.SortBy, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if params.SortOrder != nil { - - if queryFrag, err := runtime.StyleParamWithOptions("form", true, "sort_order", *params.SortOrder, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { - return nil, err - } else { - for _, qp := range strings.Split(queryFrag, "&") { - rawQueryFragments = append(rawQueryFragments, qp) - } - } - - } - - if encoded := queryValues.Encode(); encoded != "" { - rawQueryFragments = append(rawQueryFragments, encoded) - } - queryURL.RawQuery = strings.Join(rawQueryFragments, "&") - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -// NewV1GetASnippetRequest generates requests for V1GetASnippet -func NewV1GetASnippetRequest(server string, id openapi_types.UUID) (*http.Request, error) { - var err error - - var pathParam0 string - - pathParam0, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: "uuid"}) - if err != nil { - return nil, err - } - - serverURL, err := url.Parse(server) - if err != nil { - return nil, err - } - - operationPath := fmt.Sprintf("/v1/snippets/%s", pathParam0) - if operationPath[0] == '/' { - operationPath = "." + operationPath - } - - queryURL, err := serverURL.Parse(operationPath) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) - if err != nil { - return nil, err - } - - return req, nil -} - -func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { - for _, r := range c.RequestEditors { - if err := r(ctx, req); err != nil { - return err - } - } - for _, r := range additionalEditors { - if err := r(ctx, req); err != nil { - return err - } - } - return nil -} - -// ClientWithResponses builds on ClientInterface to offer response payloads -type ClientWithResponses struct { - ClientInterface -} - -// NewClientWithResponses creates a new ClientWithResponses, which wraps -// Client with return type handling -func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { - client, err := NewClient(server, opts...) - if err != nil { - return nil, err - } - return &ClientWithResponses{client}, nil -} - -// WithBaseURL overrides the baseURL. -func WithBaseURL(baseURL string) ClientOption { - return func(c *Client) error { - newBaseURL, err := url.Parse(baseURL) - if err != nil { - return err - } - c.Server = newBaseURL.String() - return nil - } -} - -// ClientWithResponsesInterface is the interface specification for the client with responses above. -type ClientWithResponsesInterface interface { - // V1DeleteABranchWithResponse request - V1DeleteABranchWithResponse(ctx context.Context, branchIdOrRef string, params *V1DeleteABranchParams, reqEditors ...RequestEditorFn) (*V1DeleteABranchResponse, error) - - // V1GetABranchConfigWithResponse request - V1GetABranchConfigWithResponse(ctx context.Context, branchIdOrRef string, reqEditors ...RequestEditorFn) (*V1GetABranchConfigResponse, error) - - // V1UpdateABranchConfigWithBodyWithResponse request with any body - V1UpdateABranchConfigWithBodyWithResponse(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateABranchConfigResponse, error) - - V1UpdateABranchConfigWithResponse(ctx context.Context, branchIdOrRef string, body V1UpdateABranchConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateABranchConfigResponse, error) - - // V1DiffABranchWithResponse request - V1DiffABranchWithResponse(ctx context.Context, branchIdOrRef string, params *V1DiffABranchParams, reqEditors ...RequestEditorFn) (*V1DiffABranchResponse, error) - - // V1MergeABranchWithBodyWithResponse request with any body - V1MergeABranchWithBodyWithResponse(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1MergeABranchResponse, error) - - V1MergeABranchWithResponse(ctx context.Context, branchIdOrRef string, body V1MergeABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*V1MergeABranchResponse, error) - - // V1PushABranchWithBodyWithResponse request with any body - V1PushABranchWithBodyWithResponse(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1PushABranchResponse, error) - - V1PushABranchWithResponse(ctx context.Context, branchIdOrRef string, body V1PushABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*V1PushABranchResponse, error) - - // V1ResetABranchWithBodyWithResponse request with any body - V1ResetABranchWithBodyWithResponse(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ResetABranchResponse, error) - - V1ResetABranchWithResponse(ctx context.Context, branchIdOrRef string, body V1ResetABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*V1ResetABranchResponse, error) - - // V1RestoreABranchWithResponse request - V1RestoreABranchWithResponse(ctx context.Context, branchIdOrRef string, reqEditors ...RequestEditorFn) (*V1RestoreABranchResponse, error) - - // V1AuthorizeUserWithResponse request - V1AuthorizeUserWithResponse(ctx context.Context, params *V1AuthorizeUserParams, reqEditors ...RequestEditorFn) (*V1AuthorizeUserResponse, error) - - // V1OauthAuthorizeProjectClaimWithResponse request - V1OauthAuthorizeProjectClaimWithResponse(ctx context.Context, params *V1OauthAuthorizeProjectClaimParams, reqEditors ...RequestEditorFn) (*V1OauthAuthorizeProjectClaimResponse, error) - - // V1RevokeTokenWithBodyWithResponse request with any body - V1RevokeTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1RevokeTokenResponse, error) - - V1RevokeTokenWithResponse(ctx context.Context, body V1RevokeTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*V1RevokeTokenResponse, error) - - // V1ExchangeOauthTokenWithBodyWithResponse request with any body - V1ExchangeOauthTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ExchangeOauthTokenResponse, error) - - V1ExchangeOauthTokenWithFormdataBodyWithResponse(ctx context.Context, body V1ExchangeOauthTokenFormdataRequestBody, reqEditors ...RequestEditorFn) (*V1ExchangeOauthTokenResponse, error) - - // V1ListAllOrganizationsWithResponse request - V1ListAllOrganizationsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*V1ListAllOrganizationsResponse, error) - - // V1CreateAnOrganizationWithBodyWithResponse request with any body - V1CreateAnOrganizationWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateAnOrganizationResponse, error) - - V1CreateAnOrganizationWithResponse(ctx context.Context, body V1CreateAnOrganizationJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateAnOrganizationResponse, error) - - // V1GetAnOrganizationWithResponse request - V1GetAnOrganizationWithResponse(ctx context.Context, slug string, reqEditors ...RequestEditorFn) (*V1GetAnOrganizationResponse, error) - - // V1GetOrganizationEntitlementsWithResponse request - V1GetOrganizationEntitlementsWithResponse(ctx context.Context, slug string, reqEditors ...RequestEditorFn) (*V1GetOrganizationEntitlementsResponse, error) - - // V1ListOrganizationMembersWithResponse request - V1ListOrganizationMembersWithResponse(ctx context.Context, slug string, reqEditors ...RequestEditorFn) (*V1ListOrganizationMembersResponse, error) - - // V1GetOrganizationProjectClaimWithResponse request - V1GetOrganizationProjectClaimWithResponse(ctx context.Context, slug string, token string, reqEditors ...RequestEditorFn) (*V1GetOrganizationProjectClaimResponse, error) - - // V1ClaimProjectForOrganizationWithResponse request - V1ClaimProjectForOrganizationWithResponse(ctx context.Context, slug string, token string, reqEditors ...RequestEditorFn) (*V1ClaimProjectForOrganizationResponse, error) - - // V1GetAllProjectsForOrganizationWithResponse request - V1GetAllProjectsForOrganizationWithResponse(ctx context.Context, slug string, params *V1GetAllProjectsForOrganizationParams, reqEditors ...RequestEditorFn) (*V1GetAllProjectsForOrganizationResponse, error) - - // V1GetProfileWithResponse request - V1GetProfileWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*V1GetProfileResponse, error) - - // V1ListAllProjectsWithResponse request - V1ListAllProjectsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*V1ListAllProjectsResponse, error) - - // V1CreateAProjectWithBodyWithResponse request with any body - V1CreateAProjectWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateAProjectResponse, error) - - V1CreateAProjectWithResponse(ctx context.Context, body V1CreateAProjectJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateAProjectResponse, error) - - // V1GetAvailableRegionsWithResponse request - V1GetAvailableRegionsWithResponse(ctx context.Context, params *V1GetAvailableRegionsParams, reqEditors ...RequestEditorFn) (*V1GetAvailableRegionsResponse, error) - - // V1DeleteAProjectWithResponse request - V1DeleteAProjectWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1DeleteAProjectResponse, error) - - // V1GetProjectWithResponse request - V1GetProjectWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectResponse, error) - - // V1UpdateAProjectWithBodyWithResponse request with any body - V1UpdateAProjectWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateAProjectResponse, error) - - V1UpdateAProjectWithResponse(ctx context.Context, ref string, body V1UpdateAProjectJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateAProjectResponse, error) - - // V1ListActionRunsWithResponse request - V1ListActionRunsWithResponse(ctx context.Context, ref string, params *V1ListActionRunsParams, reqEditors ...RequestEditorFn) (*V1ListActionRunsResponse, error) - - // V1CountActionRunsWithResponse request - V1CountActionRunsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1CountActionRunsResponse, error) - - // V1GetActionRunWithResponse request - V1GetActionRunWithResponse(ctx context.Context, ref string, runId string, reqEditors ...RequestEditorFn) (*V1GetActionRunResponse, error) - - // V1GetActionRunLogsWithResponse request - V1GetActionRunLogsWithResponse(ctx context.Context, ref string, runId string, reqEditors ...RequestEditorFn) (*V1GetActionRunLogsResponse, error) - - // V1UpdateActionRunStatusWithBodyWithResponse request with any body - V1UpdateActionRunStatusWithBodyWithResponse(ctx context.Context, ref string, runId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateActionRunStatusResponse, error) - - V1UpdateActionRunStatusWithResponse(ctx context.Context, ref string, runId string, body V1UpdateActionRunStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateActionRunStatusResponse, error) - - // V1GetPerformanceAdvisorsWithResponse request - V1GetPerformanceAdvisorsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetPerformanceAdvisorsResponse, error) - - // V1GetSecurityAdvisorsWithResponse request - V1GetSecurityAdvisorsWithResponse(ctx context.Context, ref string, params *V1GetSecurityAdvisorsParams, reqEditors ...RequestEditorFn) (*V1GetSecurityAdvisorsResponse, error) - - // V1GetProjectFunctionCombinedStatsWithResponse request - V1GetProjectFunctionCombinedStatsWithResponse(ctx context.Context, ref string, params *V1GetProjectFunctionCombinedStatsParams, reqEditors ...RequestEditorFn) (*V1GetProjectFunctionCombinedStatsResponse, error) - - // V1GetProjectLogsWithResponse request - V1GetProjectLogsWithResponse(ctx context.Context, ref string, params *V1GetProjectLogsParams, reqEditors ...RequestEditorFn) (*V1GetProjectLogsResponse, error) - - // V1GetProjectLogsAllWithResponse request - V1GetProjectLogsAllWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectLogsAllResponse, error) - - // V1ScrapeProjectMetricsWithResponse request - V1ScrapeProjectMetricsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ScrapeProjectMetricsResponse, error) - - // V1GetProjectUsageApiCountWithResponse request - V1GetProjectUsageApiCountWithResponse(ctx context.Context, ref string, params *V1GetProjectUsageApiCountParams, reqEditors ...RequestEditorFn) (*V1GetProjectUsageApiCountResponse, error) - - // V1GetProjectUsageRequestCountWithResponse request - V1GetProjectUsageRequestCountWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectUsageRequestCountResponse, error) - - // V1GetProjectApiKeysWithResponse request - V1GetProjectApiKeysWithResponse(ctx context.Context, ref string, params *V1GetProjectApiKeysParams, reqEditors ...RequestEditorFn) (*V1GetProjectApiKeysResponse, error) - - // V1CreateProjectApiKeyWithBodyWithResponse request with any body - V1CreateProjectApiKeyWithBodyWithResponse(ctx context.Context, ref string, params *V1CreateProjectApiKeyParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateProjectApiKeyResponse, error) - - V1CreateProjectApiKeyWithResponse(ctx context.Context, ref string, params *V1CreateProjectApiKeyParams, body V1CreateProjectApiKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateProjectApiKeyResponse, error) - - // V1GetProjectLegacyApiKeysWithResponse request - V1GetProjectLegacyApiKeysWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectLegacyApiKeysResponse, error) - - // V1UpdateProjectLegacyApiKeysWithResponse request - V1UpdateProjectLegacyApiKeysWithResponse(ctx context.Context, ref string, params *V1UpdateProjectLegacyApiKeysParams, reqEditors ...RequestEditorFn) (*V1UpdateProjectLegacyApiKeysResponse, error) - - // V1DeleteProjectApiKeyWithResponse request - V1DeleteProjectApiKeyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, params *V1DeleteProjectApiKeyParams, reqEditors ...RequestEditorFn) (*V1DeleteProjectApiKeyResponse, error) - - // V1GetProjectApiKeyWithResponse request - V1GetProjectApiKeyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, params *V1GetProjectApiKeyParams, reqEditors ...RequestEditorFn) (*V1GetProjectApiKeyResponse, error) - - // V1UpdateProjectApiKeyWithBodyWithResponse request with any body - V1UpdateProjectApiKeyWithBodyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, params *V1UpdateProjectApiKeyParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateProjectApiKeyResponse, error) - - V1UpdateProjectApiKeyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, params *V1UpdateProjectApiKeyParams, body V1UpdateProjectApiKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateProjectApiKeyResponse, error) - - // V1ListProjectAddonsWithResponse request - V1ListProjectAddonsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListProjectAddonsResponse, error) - - // V1ApplyProjectAddonWithBodyWithResponse request with any body - V1ApplyProjectAddonWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ApplyProjectAddonResponse, error) - - V1ApplyProjectAddonWithResponse(ctx context.Context, ref string, body V1ApplyProjectAddonJSONRequestBody, reqEditors ...RequestEditorFn) (*V1ApplyProjectAddonResponse, error) - - // V1RemoveProjectAddonWithResponse request - V1RemoveProjectAddonWithResponse(ctx context.Context, ref string, addonVariant struct { - union json.RawMessage - }, reqEditors ...RequestEditorFn) (*V1RemoveProjectAddonResponse, error) - - // V1DisablePreviewBranchingWithResponse request - V1DisablePreviewBranchingWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1DisablePreviewBranchingResponse, error) - - // V1ListAllBranchesWithResponse request - V1ListAllBranchesWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllBranchesResponse, error) - - // V1CreateABranchWithBodyWithResponse request with any body - V1CreateABranchWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateABranchResponse, error) - - V1CreateABranchWithResponse(ctx context.Context, ref string, body V1CreateABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateABranchResponse, error) - - // V1GetABranchWithResponse request - V1GetABranchWithResponse(ctx context.Context, ref string, name string, reqEditors ...RequestEditorFn) (*V1GetABranchResponse, error) - - // V1DeleteProjectClaimTokenWithResponse request - V1DeleteProjectClaimTokenWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1DeleteProjectClaimTokenResponse, error) - - // V1GetProjectClaimTokenWithResponse request - V1GetProjectClaimTokenWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectClaimTokenResponse, error) - - // V1CreateProjectClaimTokenWithResponse request - V1CreateProjectClaimTokenWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1CreateProjectClaimTokenResponse, error) - - // V1DeleteLoginRolesWithResponse request - V1DeleteLoginRolesWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1DeleteLoginRolesResponse, error) - - // V1CreateLoginRoleWithBodyWithResponse request with any body - V1CreateLoginRoleWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateLoginRoleResponse, error) - - V1CreateLoginRoleWithResponse(ctx context.Context, ref string, body V1CreateLoginRoleJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateLoginRoleResponse, error) - - // V1GetAuthServiceConfigWithResponse request - V1GetAuthServiceConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetAuthServiceConfigResponse, error) - - // V1UpdateAuthServiceConfigWithBodyWithResponse request with any body - V1UpdateAuthServiceConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateAuthServiceConfigResponse, error) - - V1UpdateAuthServiceConfigWithResponse(ctx context.Context, ref string, body V1UpdateAuthServiceConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateAuthServiceConfigResponse, error) - - // V1GetProjectSigningKeysWithResponse request - V1GetProjectSigningKeysWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectSigningKeysResponse, error) - - // V1CreateProjectSigningKeyWithBodyWithResponse request with any body - V1CreateProjectSigningKeyWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateProjectSigningKeyResponse, error) - - V1CreateProjectSigningKeyWithResponse(ctx context.Context, ref string, body V1CreateProjectSigningKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateProjectSigningKeyResponse, error) - - // V1GetLegacySigningKeyWithResponse request - V1GetLegacySigningKeyWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetLegacySigningKeyResponse, error) - - // V1CreateLegacySigningKeyWithResponse request - V1CreateLegacySigningKeyWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1CreateLegacySigningKeyResponse, error) - - // V1RemoveProjectSigningKeyWithResponse request - V1RemoveProjectSigningKeyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1RemoveProjectSigningKeyResponse, error) - - // V1GetProjectSigningKeyWithResponse request - V1GetProjectSigningKeyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1GetProjectSigningKeyResponse, error) - - // V1UpdateProjectSigningKeyWithBodyWithResponse request with any body - V1UpdateProjectSigningKeyWithBodyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateProjectSigningKeyResponse, error) - - V1UpdateProjectSigningKeyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, body V1UpdateProjectSigningKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateProjectSigningKeyResponse, error) - - // V1ListAllSsoProviderWithResponse request - V1ListAllSsoProviderWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllSsoProviderResponse, error) - - // V1CreateASsoProviderWithBodyWithResponse request with any body - V1CreateASsoProviderWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateASsoProviderResponse, error) - - V1CreateASsoProviderWithResponse(ctx context.Context, ref string, body V1CreateASsoProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateASsoProviderResponse, error) - - // V1DeleteASsoProviderWithResponse request - V1DeleteASsoProviderWithResponse(ctx context.Context, ref string, providerId openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1DeleteASsoProviderResponse, error) - - // V1GetASsoProviderWithResponse request - V1GetASsoProviderWithResponse(ctx context.Context, ref string, providerId openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1GetASsoProviderResponse, error) - - // V1UpdateASsoProviderWithBodyWithResponse request with any body - V1UpdateASsoProviderWithBodyWithResponse(ctx context.Context, ref string, providerId openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateASsoProviderResponse, error) - - V1UpdateASsoProviderWithResponse(ctx context.Context, ref string, providerId openapi_types.UUID, body V1UpdateASsoProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateASsoProviderResponse, error) - - // V1ListProjectTpaIntegrationsWithResponse request - V1ListProjectTpaIntegrationsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListProjectTpaIntegrationsResponse, error) - - // V1CreateProjectTpaIntegrationWithBodyWithResponse request with any body - V1CreateProjectTpaIntegrationWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateProjectTpaIntegrationResponse, error) - - V1CreateProjectTpaIntegrationWithResponse(ctx context.Context, ref string, body V1CreateProjectTpaIntegrationJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateProjectTpaIntegrationResponse, error) - - // V1DeleteProjectTpaIntegrationWithResponse request - V1DeleteProjectTpaIntegrationWithResponse(ctx context.Context, ref string, tpaId openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1DeleteProjectTpaIntegrationResponse, error) - - // V1GetProjectTpaIntegrationWithResponse request - V1GetProjectTpaIntegrationWithResponse(ctx context.Context, ref string, tpaId openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1GetProjectTpaIntegrationResponse, error) - - // V1GetProjectPgbouncerConfigWithResponse request - V1GetProjectPgbouncerConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectPgbouncerConfigResponse, error) - - // V1GetPoolerConfigWithResponse request - V1GetPoolerConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetPoolerConfigResponse, error) - - // V1UpdatePoolerConfigWithBodyWithResponse request with any body - V1UpdatePoolerConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdatePoolerConfigResponse, error) - - V1UpdatePoolerConfigWithResponse(ctx context.Context, ref string, body V1UpdatePoolerConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdatePoolerConfigResponse, error) - - // V1GetPostgresConfigWithResponse request - V1GetPostgresConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetPostgresConfigResponse, error) - - // V1UpdatePostgresConfigWithBodyWithResponse request with any body - V1UpdatePostgresConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdatePostgresConfigResponse, error) - - V1UpdatePostgresConfigWithResponse(ctx context.Context, ref string, body V1UpdatePostgresConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdatePostgresConfigResponse, error) - - // V1GetDatabaseDiskWithResponse request - V1GetDatabaseDiskWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetDatabaseDiskResponse, error) - - // V1ModifyDatabaseDiskWithBodyWithResponse request with any body - V1ModifyDatabaseDiskWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ModifyDatabaseDiskResponse, error) - - V1ModifyDatabaseDiskWithResponse(ctx context.Context, ref string, body V1ModifyDatabaseDiskJSONRequestBody, reqEditors ...RequestEditorFn) (*V1ModifyDatabaseDiskResponse, error) - - // V1GetProjectDiskAutoscaleConfigWithResponse request - V1GetProjectDiskAutoscaleConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectDiskAutoscaleConfigResponse, error) - - // V1GetDiskUtilizationWithResponse request - V1GetDiskUtilizationWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetDiskUtilizationResponse, error) - - // V1GetRealtimeConfigWithResponse request - V1GetRealtimeConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetRealtimeConfigResponse, error) - - // V1UpdateRealtimeConfigWithBodyWithResponse request with any body - V1UpdateRealtimeConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateRealtimeConfigResponse, error) - - V1UpdateRealtimeConfigWithResponse(ctx context.Context, ref string, body V1UpdateRealtimeConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateRealtimeConfigResponse, error) - - // V1ShutdownRealtimeWithResponse request - V1ShutdownRealtimeWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ShutdownRealtimeResponse, error) - - // V1GetStorageConfigWithResponse request - V1GetStorageConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetStorageConfigResponse, error) - - // V1UpdateStorageConfigWithBodyWithResponse request with any body - V1UpdateStorageConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateStorageConfigResponse, error) - - V1UpdateStorageConfigWithResponse(ctx context.Context, ref string, body V1UpdateStorageConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateStorageConfigResponse, error) - - // V1DeleteHostnameConfigWithResponse request - V1DeleteHostnameConfigWithResponse(ctx context.Context, ref string, params *V1DeleteHostnameConfigParams, reqEditors ...RequestEditorFn) (*V1DeleteHostnameConfigResponse, error) - - // V1GetHostnameConfigWithResponse request - V1GetHostnameConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetHostnameConfigResponse, error) - - // V1ActivateCustomHostnameWithResponse request - V1ActivateCustomHostnameWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ActivateCustomHostnameResponse, error) - - // V1UpdateHostnameConfigWithBodyWithResponse request with any body - V1UpdateHostnameConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateHostnameConfigResponse, error) - - V1UpdateHostnameConfigWithResponse(ctx context.Context, ref string, body V1UpdateHostnameConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateHostnameConfigResponse, error) - - // V1VerifyDnsConfigWithResponse request - V1VerifyDnsConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1VerifyDnsConfigResponse, error) - - // V1ListAllBackupsWithResponse request - V1ListAllBackupsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllBackupsResponse, error) - - // V1RestorePhysicalBackupWithBodyWithResponse request with any body - V1RestorePhysicalBackupWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1RestorePhysicalBackupResponse, error) - - V1RestorePhysicalBackupWithResponse(ctx context.Context, ref string, body V1RestorePhysicalBackupJSONRequestBody, reqEditors ...RequestEditorFn) (*V1RestorePhysicalBackupResponse, error) - - // V1RestorePitrBackupWithBodyWithResponse request with any body - V1RestorePitrBackupWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1RestorePitrBackupResponse, error) - - V1RestorePitrBackupWithResponse(ctx context.Context, ref string, body V1RestorePitrBackupJSONRequestBody, reqEditors ...RequestEditorFn) (*V1RestorePitrBackupResponse, error) - - // V1GetRestorePointWithResponse request - V1GetRestorePointWithResponse(ctx context.Context, ref string, params *V1GetRestorePointParams, reqEditors ...RequestEditorFn) (*V1GetRestorePointResponse, error) - - // V1CreateRestorePointWithBodyWithResponse request with any body - V1CreateRestorePointWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateRestorePointResponse, error) - - V1CreateRestorePointWithResponse(ctx context.Context, ref string, body V1CreateRestorePointJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateRestorePointResponse, error) - - // V1GetBackupScheduleWithResponse request - V1GetBackupScheduleWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetBackupScheduleResponse, error) - - // V1UpdateBackupScheduleWithBodyWithResponse request with any body - V1UpdateBackupScheduleWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateBackupScheduleResponse, error) - - V1UpdateBackupScheduleWithResponse(ctx context.Context, ref string, body V1UpdateBackupScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateBackupScheduleResponse, error) - - // V1UndoWithBodyWithResponse request with any body - V1UndoWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UndoResponse, error) - - V1UndoWithResponse(ctx context.Context, ref string, body V1UndoJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UndoResponse, error) - - // V1GetDatabaseMetadataWithResponse request - V1GetDatabaseMetadataWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetDatabaseMetadataResponse, error) - - // V1GetJitAccessWithResponse request - V1GetJitAccessWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetJitAccessResponse, error) - - // V1AuthorizeJitAccessWithBodyWithResponse request with any body - V1AuthorizeJitAccessWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1AuthorizeJitAccessResponse, error) - - V1AuthorizeJitAccessWithResponse(ctx context.Context, ref string, body V1AuthorizeJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*V1AuthorizeJitAccessResponse, error) - - // V1UpdateJitAccessWithBodyWithResponse request with any body - V1UpdateJitAccessWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateJitAccessResponse, error) - - V1UpdateJitAccessWithResponse(ctx context.Context, ref string, body V1UpdateJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateJitAccessResponse, error) - - // V1InviteExternalJitAccessWithBodyWithResponse request with any body - V1InviteExternalJitAccessWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1InviteExternalJitAccessResponse, error) - - V1InviteExternalJitAccessWithResponse(ctx context.Context, ref string, body V1InviteExternalJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*V1InviteExternalJitAccessResponse, error) - - // V1AcceptInviteExternalJitAccessWithBodyWithResponse request with any body - V1AcceptInviteExternalJitAccessWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1AcceptInviteExternalJitAccessResponse, error) - - V1AcceptInviteExternalJitAccessWithResponse(ctx context.Context, ref string, body V1AcceptInviteExternalJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*V1AcceptInviteExternalJitAccessResponse, error) - - // V1DeleteInviteExternalJitAccessWithResponse request - V1DeleteInviteExternalJitAccessWithResponse(ctx context.Context, ref string, inviteId openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1DeleteInviteExternalJitAccessResponse, error) - - // V1ListJitAccessWithResponse request - V1ListJitAccessWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListJitAccessResponse, error) - - // V1DeleteJitAccessWithResponse request - V1DeleteJitAccessWithResponse(ctx context.Context, ref string, userId openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1DeleteJitAccessResponse, error) - - // V1RollbackMigrationsWithResponse request - V1RollbackMigrationsWithResponse(ctx context.Context, ref string, params *V1RollbackMigrationsParams, reqEditors ...RequestEditorFn) (*V1RollbackMigrationsResponse, error) - - // V1ListMigrationHistoryWithResponse request - V1ListMigrationHistoryWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListMigrationHistoryResponse, error) - - // V1ApplyAMigrationWithBodyWithResponse request with any body - V1ApplyAMigrationWithBodyWithResponse(ctx context.Context, ref string, params *V1ApplyAMigrationParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ApplyAMigrationResponse, error) - - V1ApplyAMigrationWithResponse(ctx context.Context, ref string, params *V1ApplyAMigrationParams, body V1ApplyAMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*V1ApplyAMigrationResponse, error) - - // V1UpsertAMigrationWithBodyWithResponse request with any body - V1UpsertAMigrationWithBodyWithResponse(ctx context.Context, ref string, params *V1UpsertAMigrationParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpsertAMigrationResponse, error) - - V1UpsertAMigrationWithResponse(ctx context.Context, ref string, params *V1UpsertAMigrationParams, body V1UpsertAMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpsertAMigrationResponse, error) - - // V1GetAMigrationWithResponse request - V1GetAMigrationWithResponse(ctx context.Context, ref string, version string, reqEditors ...RequestEditorFn) (*V1GetAMigrationResponse, error) - - // V1PatchAMigrationWithBodyWithResponse request with any body - V1PatchAMigrationWithBodyWithResponse(ctx context.Context, ref string, version string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1PatchAMigrationResponse, error) - - V1PatchAMigrationWithResponse(ctx context.Context, ref string, version string, body V1PatchAMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*V1PatchAMigrationResponse, error) - - // V1GetDatabaseOpenapiWithResponse request - V1GetDatabaseOpenapiWithResponse(ctx context.Context, ref string, params *V1GetDatabaseOpenapiParams, reqEditors ...RequestEditorFn) (*V1GetDatabaseOpenapiResponse, error) - - // V1UpdateDatabasePasswordWithBodyWithResponse request with any body - V1UpdateDatabasePasswordWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateDatabasePasswordResponse, error) - - V1UpdateDatabasePasswordWithResponse(ctx context.Context, ref string, body V1UpdateDatabasePasswordJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateDatabasePasswordResponse, error) - - // V1RunAQueryWithBodyWithResponse request with any body - V1RunAQueryWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1RunAQueryResponse, error) - - V1RunAQueryWithResponse(ctx context.Context, ref string, body V1RunAQueryJSONRequestBody, reqEditors ...RequestEditorFn) (*V1RunAQueryResponse, error) - - // V1ReadOnlyQueryWithBodyWithResponse request with any body - V1ReadOnlyQueryWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ReadOnlyQueryResponse, error) - - V1ReadOnlyQueryWithResponse(ctx context.Context, ref string, body V1ReadOnlyQueryJSONRequestBody, reqEditors ...RequestEditorFn) (*V1ReadOnlyQueryResponse, error) - - // V1EnableDatabaseWebhookWithResponse request - V1EnableDatabaseWebhookWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1EnableDatabaseWebhookResponse, error) - - // V1ListAllFunctionsWithResponse request - V1ListAllFunctionsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllFunctionsResponse, error) - - // V1CreateAFunctionWithBodyWithResponse request with any body - V1CreateAFunctionWithBodyWithResponse(ctx context.Context, ref string, params *V1CreateAFunctionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateAFunctionResponse, error) - - V1CreateAFunctionWithResponse(ctx context.Context, ref string, params *V1CreateAFunctionParams, body V1CreateAFunctionJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateAFunctionResponse, error) - - // V1BulkUpdateFunctionsWithBodyWithResponse request with any body - V1BulkUpdateFunctionsWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1BulkUpdateFunctionsResponse, error) - - V1BulkUpdateFunctionsWithResponse(ctx context.Context, ref string, body V1BulkUpdateFunctionsJSONRequestBody, reqEditors ...RequestEditorFn) (*V1BulkUpdateFunctionsResponse, error) - - // V1DeployAFunctionWithBodyWithResponse request with any body - V1DeployAFunctionWithBodyWithResponse(ctx context.Context, ref string, params *V1DeployAFunctionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1DeployAFunctionResponse, error) - - // V1DeleteAFunctionWithResponse request - V1DeleteAFunctionWithResponse(ctx context.Context, ref string, functionSlug string, reqEditors ...RequestEditorFn) (*V1DeleteAFunctionResponse, error) - - // V1GetAFunctionWithResponse request - V1GetAFunctionWithResponse(ctx context.Context, ref string, functionSlug string, reqEditors ...RequestEditorFn) (*V1GetAFunctionResponse, error) - - // V1UpdateAFunctionWithBodyWithResponse request with any body - V1UpdateAFunctionWithBodyWithResponse(ctx context.Context, ref string, functionSlug string, params *V1UpdateAFunctionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateAFunctionResponse, error) - - V1UpdateAFunctionWithResponse(ctx context.Context, ref string, functionSlug string, params *V1UpdateAFunctionParams, body V1UpdateAFunctionJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateAFunctionResponse, error) - - // V1GetAFunctionBodyWithResponse request - V1GetAFunctionBodyWithResponse(ctx context.Context, ref string, functionSlug string, reqEditors ...RequestEditorFn) (*V1GetAFunctionBodyResponse, error) - - // V1GetServicesHealthWithResponse request - V1GetServicesHealthWithResponse(ctx context.Context, ref string, params *V1GetServicesHealthParams, reqEditors ...RequestEditorFn) (*V1GetServicesHealthResponse, error) - - // V1GetJitAccessConfigWithResponse request - V1GetJitAccessConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetJitAccessConfigResponse, error) - - // V1UpdateJitAccessConfigWithBodyWithResponse request with any body - V1UpdateJitAccessConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateJitAccessConfigResponse, error) - - V1UpdateJitAccessConfigWithResponse(ctx context.Context, ref string, body V1UpdateJitAccessConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateJitAccessConfigResponse, error) - - // V1DeleteNetworkBansWithBodyWithResponse request with any body - V1DeleteNetworkBansWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1DeleteNetworkBansResponse, error) - - V1DeleteNetworkBansWithResponse(ctx context.Context, ref string, body V1DeleteNetworkBansJSONRequestBody, reqEditors ...RequestEditorFn) (*V1DeleteNetworkBansResponse, error) - - // V1ListAllNetworkBansWithResponse request - V1ListAllNetworkBansWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllNetworkBansResponse, error) - - // V1ListAllNetworkBansEnrichedWithResponse request - V1ListAllNetworkBansEnrichedWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllNetworkBansEnrichedResponse, error) - - // V1GetNetworkRestrictionsWithResponse request - V1GetNetworkRestrictionsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetNetworkRestrictionsResponse, error) - - // V1PatchNetworkRestrictionsWithBodyWithResponse request with any body - V1PatchNetworkRestrictionsWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1PatchNetworkRestrictionsResponse, error) - - V1PatchNetworkRestrictionsWithResponse(ctx context.Context, ref string, body V1PatchNetworkRestrictionsJSONRequestBody, reqEditors ...RequestEditorFn) (*V1PatchNetworkRestrictionsResponse, error) - - // V1UpdateNetworkRestrictionsWithBodyWithResponse request with any body - V1UpdateNetworkRestrictionsWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateNetworkRestrictionsResponse, error) - - V1UpdateNetworkRestrictionsWithResponse(ctx context.Context, ref string, body V1UpdateNetworkRestrictionsJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateNetworkRestrictionsResponse, error) - - // V1PauseAProjectWithResponse request - V1PauseAProjectWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1PauseAProjectResponse, error) - - // V1GetPgsodiumConfigWithResponse request - V1GetPgsodiumConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetPgsodiumConfigResponse, error) - - // V1UpdatePgsodiumConfigWithBodyWithResponse request with any body - V1UpdatePgsodiumConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdatePgsodiumConfigResponse, error) - - V1UpdatePgsodiumConfigWithResponse(ctx context.Context, ref string, body V1UpdatePgsodiumConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdatePgsodiumConfigResponse, error) - - // V1GetPostgrestServiceConfigWithResponse request - V1GetPostgrestServiceConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetPostgrestServiceConfigResponse, error) - - // V1UpdatePostgrestServiceConfigWithBodyWithResponse request with any body - V1UpdatePostgrestServiceConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdatePostgrestServiceConfigResponse, error) - - V1UpdatePostgrestServiceConfigWithResponse(ctx context.Context, ref string, body V1UpdatePostgrestServiceConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdatePostgrestServiceConfigResponse, error) - - // V1RemoveAReadReplicaWithBodyWithResponse request with any body - V1RemoveAReadReplicaWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1RemoveAReadReplicaResponse, error) - - V1RemoveAReadReplicaWithResponse(ctx context.Context, ref string, body V1RemoveAReadReplicaJSONRequestBody, reqEditors ...RequestEditorFn) (*V1RemoveAReadReplicaResponse, error) - - // V1SetupAReadReplicaWithBodyWithResponse request with any body - V1SetupAReadReplicaWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1SetupAReadReplicaResponse, error) - - V1SetupAReadReplicaWithResponse(ctx context.Context, ref string, body V1SetupAReadReplicaJSONRequestBody, reqEditors ...RequestEditorFn) (*V1SetupAReadReplicaResponse, error) - - // V1GetReadonlyModeStatusWithResponse request - V1GetReadonlyModeStatusWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetReadonlyModeStatusResponse, error) - - // V1DisableReadonlyModeTemporarilyWithResponse request - V1DisableReadonlyModeTemporarilyWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1DisableReadonlyModeTemporarilyResponse, error) - - // V1RestartAProjectWithResponse request - V1RestartAProjectWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1RestartAProjectResponse, error) - - // V1ListAvailableRestoreVersionsWithResponse request - V1ListAvailableRestoreVersionsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAvailableRestoreVersionsResponse, error) - - // V1RestoreAProjectWithResponse request - V1RestoreAProjectWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1RestoreAProjectResponse, error) - - // V1CancelAProjectRestorationWithResponse request - V1CancelAProjectRestorationWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1CancelAProjectRestorationResponse, error) - - // V1BulkDeleteSecretsWithBodyWithResponse request with any body - V1BulkDeleteSecretsWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1BulkDeleteSecretsResponse, error) - - V1BulkDeleteSecretsWithResponse(ctx context.Context, ref string, body V1BulkDeleteSecretsJSONRequestBody, reqEditors ...RequestEditorFn) (*V1BulkDeleteSecretsResponse, error) - - // V1ListAllSecretsWithResponse request - V1ListAllSecretsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllSecretsResponse, error) - - // V1BulkCreateSecretsWithBodyWithResponse request with any body - V1BulkCreateSecretsWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1BulkCreateSecretsResponse, error) - - V1BulkCreateSecretsWithResponse(ctx context.Context, ref string, body V1BulkCreateSecretsJSONRequestBody, reqEditors ...RequestEditorFn) (*V1BulkCreateSecretsResponse, error) - - // V1GetSslEnforcementConfigWithResponse request - V1GetSslEnforcementConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetSslEnforcementConfigResponse, error) - - // V1UpdateSslEnforcementConfigWithBodyWithResponse request with any body - V1UpdateSslEnforcementConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateSslEnforcementConfigResponse, error) - - V1UpdateSslEnforcementConfigWithResponse(ctx context.Context, ref string, body V1UpdateSslEnforcementConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateSslEnforcementConfigResponse, error) - - // V1ListAllBucketsWithResponse request - V1ListAllBucketsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllBucketsResponse, error) - - // V1GenerateTypescriptTypesWithResponse request - V1GenerateTypescriptTypesWithResponse(ctx context.Context, ref string, params *V1GenerateTypescriptTypesParams, reqEditors ...RequestEditorFn) (*V1GenerateTypescriptTypesResponse, error) - - // V1UpgradePostgresVersionWithBodyWithResponse request with any body - V1UpgradePostgresVersionWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpgradePostgresVersionResponse, error) - - V1UpgradePostgresVersionWithResponse(ctx context.Context, ref string, body V1UpgradePostgresVersionJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpgradePostgresVersionResponse, error) - - // V1GetPostgresUpgradeEligibilityWithResponse request - V1GetPostgresUpgradeEligibilityWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetPostgresUpgradeEligibilityResponse, error) - - // V1GetPostgresUpgradeStatusWithResponse request - V1GetPostgresUpgradeStatusWithResponse(ctx context.Context, ref string, params *V1GetPostgresUpgradeStatusParams, reqEditors ...RequestEditorFn) (*V1GetPostgresUpgradeStatusResponse, error) - - // V1DeactivateVanitySubdomainConfigWithResponse request - V1DeactivateVanitySubdomainConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1DeactivateVanitySubdomainConfigResponse, error) - - // V1GetVanitySubdomainConfigWithResponse request - V1GetVanitySubdomainConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetVanitySubdomainConfigResponse, error) - - // V1ActivateVanitySubdomainConfigWithBodyWithResponse request with any body - V1ActivateVanitySubdomainConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ActivateVanitySubdomainConfigResponse, error) - - V1ActivateVanitySubdomainConfigWithResponse(ctx context.Context, ref string, body V1ActivateVanitySubdomainConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1ActivateVanitySubdomainConfigResponse, error) - - // V1CheckVanitySubdomainAvailabilityWithBodyWithResponse request with any body - V1CheckVanitySubdomainAvailabilityWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CheckVanitySubdomainAvailabilityResponse, error) - - V1CheckVanitySubdomainAvailabilityWithResponse(ctx context.Context, ref string, body V1CheckVanitySubdomainAvailabilityJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CheckVanitySubdomainAvailabilityResponse, error) - - // V1ListAllSnippetsWithResponse request - V1ListAllSnippetsWithResponse(ctx context.Context, params *V1ListAllSnippetsParams, reqEditors ...RequestEditorFn) (*V1ListAllSnippetsResponse, error) - - // V1GetASnippetWithResponse request - V1GetASnippetWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1GetASnippetResponse, error) -} - -type V1DeleteABranchResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *BranchDeleteResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1DeleteABranchResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeleteABranchResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeleteABranchResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetABranchConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *BranchDetailResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetABranchConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetABranchConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetABranchConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateABranchConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *BranchResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateABranchConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateABranchConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateABranchConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DiffABranchResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1DiffABranchResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DiffABranchResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DiffABranchResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1MergeABranchResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *BranchUpdateResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1MergeABranchResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1MergeABranchResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1MergeABranchResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1PushABranchResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *BranchUpdateResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1PushABranchResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1PushABranchResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1PushABranchResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ResetABranchResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *BranchUpdateResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ResetABranchResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ResetABranchResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ResetABranchResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1RestoreABranchResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *BranchRestoreResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1RestoreABranchResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1RestoreABranchResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1RestoreABranchResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1AuthorizeUserResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1AuthorizeUserResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1AuthorizeUserResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1AuthorizeUserResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1OauthAuthorizeProjectClaimResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1OauthAuthorizeProjectClaimResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1OauthAuthorizeProjectClaimResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1OauthAuthorizeProjectClaimResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1RevokeTokenResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1RevokeTokenResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1RevokeTokenResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1RevokeTokenResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ExchangeOauthTokenResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *OAuthTokenResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ExchangeOauthTokenResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ExchangeOauthTokenResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ExchangeOauthTokenResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListAllOrganizationsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *[]OrganizationResponseV1Output -} - -// Status returns HTTPResponse.Status -func (r V1ListAllOrganizationsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListAllOrganizationsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListAllOrganizationsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CreateAnOrganizationResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *OrganizationResponseV1Output -} - -// Status returns HTTPResponse.Status -func (r V1CreateAnOrganizationResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CreateAnOrganizationResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CreateAnOrganizationResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetAnOrganizationResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1OrganizationSlugResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetAnOrganizationResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetAnOrganizationResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetAnOrganizationResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetOrganizationEntitlementsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1ListEntitlementsResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetOrganizationEntitlementsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetOrganizationEntitlementsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetOrganizationEntitlementsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListOrganizationMembersResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *[]V1OrganizationMemberResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListOrganizationMembersResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListOrganizationMembersResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListOrganizationMembersResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetOrganizationProjectClaimResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *OrganizationProjectClaimResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetOrganizationProjectClaimResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetOrganizationProjectClaimResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetOrganizationProjectClaimResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ClaimProjectForOrganizationResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1ClaimProjectForOrganizationResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ClaimProjectForOrganizationResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ClaimProjectForOrganizationResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetAllProjectsForOrganizationResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *OrganizationProjectsResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetAllProjectsForOrganizationResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetAllProjectsForOrganizationResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetAllProjectsForOrganizationResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProfileResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1ProfileResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProfileResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProfileResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProfileResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListAllProjectsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *[]V1ProjectWithDatabaseResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListAllProjectsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListAllProjectsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListAllProjectsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CreateAProjectResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *V1ProjectResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1CreateAProjectResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CreateAProjectResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CreateAProjectResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetAvailableRegionsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *RegionsInfoOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetAvailableRegionsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetAvailableRegionsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetAvailableRegionsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DeleteAProjectResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1ProjectRefResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1DeleteAProjectResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeleteAProjectResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeleteAProjectResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1ProjectWithDatabaseResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateAProjectResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1ProjectRefResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateAProjectResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateAProjectResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateAProjectResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListActionRunsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *ListActionRunResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListActionRunsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListActionRunsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListActionRunsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CountActionRunsResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1CountActionRunsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CountActionRunsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CountActionRunsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetActionRunResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *ActionRunResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetActionRunResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetActionRunResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetActionRunResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetActionRunLogsResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1GetActionRunLogsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetActionRunLogsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetActionRunLogsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateActionRunStatusResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *UpdateRunStatusResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateActionRunStatusResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateActionRunStatusResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateActionRunStatusResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetPerformanceAdvisorsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1ProjectAdvisorsResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetPerformanceAdvisorsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetPerformanceAdvisorsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetPerformanceAdvisorsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetSecurityAdvisorsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1ProjectAdvisorsResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetSecurityAdvisorsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetSecurityAdvisorsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetSecurityAdvisorsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectFunctionCombinedStatsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *AnalyticsResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectFunctionCombinedStatsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectFunctionCombinedStatsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectFunctionCombinedStatsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectLogsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *AnalyticsResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectLogsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectLogsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectLogsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectLogsAllResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectLogsAllResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectLogsAllResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectLogsAllResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ScrapeProjectMetricsResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1ScrapeProjectMetricsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ScrapeProjectMetricsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ScrapeProjectMetricsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectUsageApiCountResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1GetUsageApiCountResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectUsageApiCountResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectUsageApiCountResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectUsageApiCountResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectUsageRequestCountResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1GetUsageApiRequestsCountResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectUsageRequestCountResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectUsageRequestCountResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectUsageRequestCountResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectApiKeysResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *[]ApiKeyResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectApiKeysResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectApiKeysResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectApiKeysResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CreateProjectApiKeyResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *ApiKeyResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1CreateProjectApiKeyResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CreateProjectApiKeyResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CreateProjectApiKeyResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectLegacyApiKeysResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *LegacyApiKeysResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectLegacyApiKeysResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectLegacyApiKeysResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectLegacyApiKeysResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateProjectLegacyApiKeysResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *LegacyApiKeysResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateProjectLegacyApiKeysResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateProjectLegacyApiKeysResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateProjectLegacyApiKeysResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DeleteProjectApiKeyResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *ApiKeyResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1DeleteProjectApiKeyResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeleteProjectApiKeyResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeleteProjectApiKeyResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectApiKeyResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *ApiKeyResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectApiKeyResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectApiKeyResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectApiKeyResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateProjectApiKeyResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *ApiKeyResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateProjectApiKeyResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateProjectApiKeyResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateProjectApiKeyResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListProjectAddonsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *ListProjectAddonsResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListProjectAddonsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListProjectAddonsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListProjectAddonsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ApplyProjectAddonResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1ApplyProjectAddonResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ApplyProjectAddonResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ApplyProjectAddonResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1RemoveProjectAddonResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1RemoveProjectAddonResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1RemoveProjectAddonResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1RemoveProjectAddonResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DisablePreviewBranchingResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1DisablePreviewBranchingResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DisablePreviewBranchingResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DisablePreviewBranchingResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListAllBranchesResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *[]BranchResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListAllBranchesResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListAllBranchesResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListAllBranchesResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CreateABranchResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *BranchResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1CreateABranchResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CreateABranchResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CreateABranchResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetABranchResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *BranchResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetABranchResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetABranchResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetABranchResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DeleteProjectClaimTokenResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1DeleteProjectClaimTokenResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeleteProjectClaimTokenResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeleteProjectClaimTokenResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectClaimTokenResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *ProjectClaimTokenResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectClaimTokenResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectClaimTokenResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectClaimTokenResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CreateProjectClaimTokenResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *CreateProjectClaimTokenResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1CreateProjectClaimTokenResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CreateProjectClaimTokenResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CreateProjectClaimTokenResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DeleteLoginRolesResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *DeleteRolesResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1DeleteLoginRolesResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeleteLoginRolesResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeleteLoginRolesResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CreateLoginRoleResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *CreateRoleResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1CreateLoginRoleResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CreateLoginRoleResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CreateLoginRoleResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetAuthServiceConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *AuthConfigResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetAuthServiceConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetAuthServiceConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetAuthServiceConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateAuthServiceConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *AuthConfigResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateAuthServiceConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateAuthServiceConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateAuthServiceConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectSigningKeysResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *SigningKeysResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectSigningKeysResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectSigningKeysResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectSigningKeysResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CreateProjectSigningKeyResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *SigningKeyResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1CreateProjectSigningKeyResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CreateProjectSigningKeyResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CreateProjectSigningKeyResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetLegacySigningKeyResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *SigningKeyResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetLegacySigningKeyResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetLegacySigningKeyResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetLegacySigningKeyResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CreateLegacySigningKeyResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *SigningKeyResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1CreateLegacySigningKeyResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CreateLegacySigningKeyResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CreateLegacySigningKeyResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1RemoveProjectSigningKeyResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *SigningKeyResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1RemoveProjectSigningKeyResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1RemoveProjectSigningKeyResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1RemoveProjectSigningKeyResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectSigningKeyResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *SigningKeyResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectSigningKeyResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectSigningKeyResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectSigningKeyResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateProjectSigningKeyResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *SigningKeyResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateProjectSigningKeyResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateProjectSigningKeyResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateProjectSigningKeyResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListAllSsoProviderResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *ListProvidersResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListAllSsoProviderResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListAllSsoProviderResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListAllSsoProviderResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CreateASsoProviderResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *CreateProviderResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1CreateASsoProviderResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CreateASsoProviderResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CreateASsoProviderResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DeleteASsoProviderResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *DeleteProviderResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1DeleteASsoProviderResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeleteASsoProviderResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeleteASsoProviderResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetASsoProviderResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *GetProviderResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetASsoProviderResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetASsoProviderResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetASsoProviderResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateASsoProviderResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *UpdateProviderResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateASsoProviderResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateASsoProviderResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateASsoProviderResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListProjectTpaIntegrationsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *[]ThirdPartyAuthOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListProjectTpaIntegrationsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListProjectTpaIntegrationsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListProjectTpaIntegrationsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CreateProjectTpaIntegrationResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *ThirdPartyAuthOutput -} - -// Status returns HTTPResponse.Status -func (r V1CreateProjectTpaIntegrationResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CreateProjectTpaIntegrationResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CreateProjectTpaIntegrationResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DeleteProjectTpaIntegrationResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *ThirdPartyAuthOutput -} - -// Status returns HTTPResponse.Status -func (r V1DeleteProjectTpaIntegrationResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeleteProjectTpaIntegrationResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeleteProjectTpaIntegrationResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectTpaIntegrationResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *ThirdPartyAuthOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectTpaIntegrationResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectTpaIntegrationResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectTpaIntegrationResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectPgbouncerConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1PgbouncerConfigResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectPgbouncerConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectPgbouncerConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectPgbouncerConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetPoolerConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *[]SupavisorConfigResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetPoolerConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetPoolerConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetPoolerConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdatePoolerConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *UpdateSupavisorConfigResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdatePoolerConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdatePoolerConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdatePoolerConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetPostgresConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *PostgresConfigResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetPostgresConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetPostgresConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetPostgresConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdatePostgresConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *PostgresConfigResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdatePostgresConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdatePostgresConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdatePostgresConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetDatabaseDiskResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *DiskResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetDatabaseDiskResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetDatabaseDiskResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetDatabaseDiskResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ModifyDatabaseDiskResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1ModifyDatabaseDiskResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ModifyDatabaseDiskResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ModifyDatabaseDiskResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetProjectDiskAutoscaleConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *DiskAutoscaleConfigOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetProjectDiskAutoscaleConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetProjectDiskAutoscaleConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetProjectDiskAutoscaleConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetDiskUtilizationResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *DiskUtilMetricsResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetDiskUtilizationResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetDiskUtilizationResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetDiskUtilizationResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetRealtimeConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *RealtimeConfigResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetRealtimeConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetRealtimeConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetRealtimeConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateRealtimeConfigResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1UpdateRealtimeConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateRealtimeConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateRealtimeConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ShutdownRealtimeResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1ShutdownRealtimeResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ShutdownRealtimeResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ShutdownRealtimeResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetStorageConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *StorageConfigResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetStorageConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetStorageConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetStorageConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateStorageConfigResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1UpdateStorageConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateStorageConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateStorageConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DeleteHostnameConfigResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1DeleteHostnameConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeleteHostnameConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeleteHostnameConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetHostnameConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *UpdateCustomHostnameResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetHostnameConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetHostnameConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetHostnameConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ActivateCustomHostnameResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *UpdateCustomHostnameResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ActivateCustomHostnameResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ActivateCustomHostnameResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ActivateCustomHostnameResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateHostnameConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *UpdateCustomHostnameResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateHostnameConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateHostnameConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateHostnameConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1VerifyDnsConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *UpdateCustomHostnameResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1VerifyDnsConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1VerifyDnsConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1VerifyDnsConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListAllBackupsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1BackupsResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListAllBackupsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListAllBackupsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListAllBackupsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1RestorePhysicalBackupResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1RestorePhysicalBackupResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1RestorePhysicalBackupResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1RestorePhysicalBackupResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1RestorePitrBackupResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1RestorePitrBackupResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1RestorePitrBackupResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1RestorePitrBackupResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetRestorePointResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1RestorePointResponse -} - -// Status returns HTTPResponse.Status -func (r V1GetRestorePointResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetRestorePointResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetRestorePointResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CreateRestorePointResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *V1RestorePointResponse -} - -// Status returns HTTPResponse.Status -func (r V1CreateRestorePointResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CreateRestorePointResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CreateRestorePointResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetBackupScheduleResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1BackupScheduleResponseOutput - JSON402 *PlanGateErrorBody -} - -// Status returns HTTPResponse.Status -func (r V1GetBackupScheduleResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetBackupScheduleResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetBackupScheduleResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateBackupScheduleResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1BackupScheduleResponseOutput - JSON402 *PlanGateErrorBody -} - -// Status returns HTTPResponse.Status -func (r V1UpdateBackupScheduleResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateBackupScheduleResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateBackupScheduleResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UndoResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1UndoResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UndoResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UndoResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetDatabaseMetadataResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *GetProjectDbMetadataResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetDatabaseMetadataResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetDatabaseMetadataResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetDatabaseMetadataResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetJitAccessResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *JitAccessResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetJitAccessResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetJitAccessResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetJitAccessResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1AuthorizeJitAccessResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *JitAuthorizeAccessResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1AuthorizeJitAccessResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1AuthorizeJitAccessResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1AuthorizeJitAccessResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateJitAccessResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *JitAccessResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateJitAccessResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateJitAccessResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateJitAccessResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1InviteExternalJitAccessResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *InviteExternalUserJitResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1InviteExternalJitAccessResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1InviteExternalJitAccessResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1InviteExternalJitAccessResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1AcceptInviteExternalJitAccessResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *JitAccessResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1AcceptInviteExternalJitAccessResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1AcceptInviteExternalJitAccessResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1AcceptInviteExternalJitAccessResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DeleteInviteExternalJitAccessResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1DeleteInviteExternalJitAccessResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeleteInviteExternalJitAccessResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeleteInviteExternalJitAccessResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListJitAccessResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *JitListAccessResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListJitAccessResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListJitAccessResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListJitAccessResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DeleteJitAccessResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1DeleteJitAccessResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeleteJitAccessResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeleteJitAccessResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1RollbackMigrationsResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1RollbackMigrationsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1RollbackMigrationsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1RollbackMigrationsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListMigrationHistoryResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1ListMigrationsResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListMigrationHistoryResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListMigrationHistoryResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListMigrationHistoryResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ApplyAMigrationResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1ApplyAMigrationResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ApplyAMigrationResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ApplyAMigrationResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpsertAMigrationResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1UpsertAMigrationResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpsertAMigrationResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpsertAMigrationResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetAMigrationResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1GetMigrationResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetAMigrationResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetAMigrationResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetAMigrationResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1PatchAMigrationResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1PatchAMigrationResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1PatchAMigrationResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1PatchAMigrationResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetDatabaseOpenapiResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *map[string]interface{} -} - -// Status returns HTTPResponse.Status -func (r V1GetDatabaseOpenapiResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetDatabaseOpenapiResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetDatabaseOpenapiResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateDatabasePasswordResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1UpdatePasswordResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateDatabasePasswordResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateDatabasePasswordResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateDatabasePasswordResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1RunAQueryResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1RunAQueryResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1RunAQueryResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1RunAQueryResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ReadOnlyQueryResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1ReadOnlyQueryResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ReadOnlyQueryResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ReadOnlyQueryResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1EnableDatabaseWebhookResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1EnableDatabaseWebhookResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1EnableDatabaseWebhookResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1EnableDatabaseWebhookResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListAllFunctionsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *[]FunctionResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListAllFunctionsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListAllFunctionsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListAllFunctionsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CreateAFunctionResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *FunctionResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1CreateAFunctionResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CreateAFunctionResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CreateAFunctionResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1BulkUpdateFunctionsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *BulkUpdateFunctionResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1BulkUpdateFunctionsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1BulkUpdateFunctionsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1BulkUpdateFunctionsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DeployAFunctionResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *DeployFunctionResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1DeployAFunctionResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeployAFunctionResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeployAFunctionResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DeleteAFunctionResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1DeleteAFunctionResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeleteAFunctionResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeleteAFunctionResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetAFunctionResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *FunctionSlugResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetAFunctionResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetAFunctionResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetAFunctionResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateAFunctionResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *FunctionSlugResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateAFunctionResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateAFunctionResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateAFunctionResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetAFunctionBodyResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *StreamableFile -} - -// Status returns HTTPResponse.Status -func (r V1GetAFunctionBodyResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetAFunctionBodyResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetAFunctionBodyResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetServicesHealthResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *[]V1ServiceHealthResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetServicesHealthResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetServicesHealthResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetServicesHealthResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetJitAccessConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1GetJitAccessConfig200JSONResponseBody -} - -// Status returns HTTPResponse.Status -func (r V1GetJitAccessConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetJitAccessConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetJitAccessConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateJitAccessConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1UpdateJitAccessConfig200JSONResponseBody -} - -// Status returns HTTPResponse.Status -func (r V1UpdateJitAccessConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateJitAccessConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateJitAccessConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DeleteNetworkBansResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1DeleteNetworkBansResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeleteNetworkBansResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeleteNetworkBansResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListAllNetworkBansResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *NetworkBanResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListAllNetworkBansResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListAllNetworkBansResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListAllNetworkBansResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListAllNetworkBansEnrichedResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *NetworkBanResponseEnrichedOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListAllNetworkBansEnrichedResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListAllNetworkBansEnrichedResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListAllNetworkBansEnrichedResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetNetworkRestrictionsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *NetworkRestrictionsResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetNetworkRestrictionsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetNetworkRestrictionsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetNetworkRestrictionsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1PatchNetworkRestrictionsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *NetworkRestrictionsV2ResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1PatchNetworkRestrictionsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1PatchNetworkRestrictionsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1PatchNetworkRestrictionsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateNetworkRestrictionsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *NetworkRestrictionsResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateNetworkRestrictionsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateNetworkRestrictionsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateNetworkRestrictionsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1PauseAProjectResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1PauseAProjectResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1PauseAProjectResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1PauseAProjectResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetPgsodiumConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *PgsodiumConfigResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetPgsodiumConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetPgsodiumConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetPgsodiumConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdatePgsodiumConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *PgsodiumConfigResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdatePgsodiumConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdatePgsodiumConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdatePgsodiumConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetPostgrestServiceConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *PostgrestConfigWithJWTSecretResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetPostgrestServiceConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetPostgrestServiceConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetPostgrestServiceConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdatePostgrestServiceConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *V1PostgrestConfigResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdatePostgrestServiceConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdatePostgrestServiceConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdatePostgrestServiceConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1RemoveAReadReplicaResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1RemoveAReadReplicaResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1RemoveAReadReplicaResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1RemoveAReadReplicaResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1SetupAReadReplicaResponse struct { - Body []byte - HTTPResponse *http.Response - JSON402 *PlanGateErrorBody -} - -// Status returns HTTPResponse.Status -func (r V1SetupAReadReplicaResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1SetupAReadReplicaResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1SetupAReadReplicaResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetReadonlyModeStatusResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *ReadOnlyStatusResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetReadonlyModeStatusResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetReadonlyModeStatusResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetReadonlyModeStatusResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DisableReadonlyModeTemporarilyResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1DisableReadonlyModeTemporarilyResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DisableReadonlyModeTemporarilyResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DisableReadonlyModeTemporarilyResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1RestartAProjectResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1RestartAProjectResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1RestartAProjectResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1RestartAProjectResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListAvailableRestoreVersionsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *GetProjectAvailableRestoreVersionsResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListAvailableRestoreVersionsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListAvailableRestoreVersionsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListAvailableRestoreVersionsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1RestoreAProjectResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1RestoreAProjectResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1RestoreAProjectResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1RestoreAProjectResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CancelAProjectRestorationResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1CancelAProjectRestorationResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CancelAProjectRestorationResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CancelAProjectRestorationResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1BulkDeleteSecretsResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1BulkDeleteSecretsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1BulkDeleteSecretsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1BulkDeleteSecretsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListAllSecretsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *[]SecretResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListAllSecretsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListAllSecretsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListAllSecretsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1BulkCreateSecretsResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1BulkCreateSecretsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1BulkCreateSecretsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1BulkCreateSecretsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetSslEnforcementConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *SslEnforcementResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetSslEnforcementConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetSslEnforcementConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetSslEnforcementConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpdateSslEnforcementConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *SslEnforcementResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpdateSslEnforcementConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpdateSslEnforcementConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpdateSslEnforcementConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListAllBucketsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *[]V1StorageBucketResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListAllBucketsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListAllBucketsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListAllBucketsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GenerateTypescriptTypesResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *TypescriptResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GenerateTypescriptTypesResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GenerateTypescriptTypesResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GenerateTypescriptTypesResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1UpgradePostgresVersionResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *ProjectUpgradeInitiateResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1UpgradePostgresVersionResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1UpgradePostgresVersionResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1UpgradePostgresVersionResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetPostgresUpgradeEligibilityResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *ProjectUpgradeEligibilityResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetPostgresUpgradeEligibilityResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetPostgresUpgradeEligibilityResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetPostgresUpgradeEligibilityResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetPostgresUpgradeStatusResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *DatabaseUpgradeStatusResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetPostgresUpgradeStatusResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetPostgresUpgradeStatusResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetPostgresUpgradeStatusResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1DeactivateVanitySubdomainConfigResponse struct { - Body []byte - HTTPResponse *http.Response -} - -// Status returns HTTPResponse.Status -func (r V1DeactivateVanitySubdomainConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1DeactivateVanitySubdomainConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1DeactivateVanitySubdomainConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetVanitySubdomainConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *VanitySubdomainConfigResponseOutput - JSON400 *PlanGateErrorBody -} - -// Status returns HTTPResponse.Status -func (r V1GetVanitySubdomainConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetVanitySubdomainConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetVanitySubdomainConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ActivateVanitySubdomainConfigResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *ActivateVanitySubdomainResponseOutput - JSON400 *PlanGateErrorBody -} - -// Status returns HTTPResponse.Status -func (r V1ActivateVanitySubdomainConfigResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ActivateVanitySubdomainConfigResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ActivateVanitySubdomainConfigResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1CheckVanitySubdomainAvailabilityResponse struct { - Body []byte - HTTPResponse *http.Response - JSON201 *SubdomainAvailabilityResponseOutput - JSON400 *PlanGateErrorBody -} - -// Status returns HTTPResponse.Status -func (r V1CheckVanitySubdomainAvailabilityResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1CheckVanitySubdomainAvailabilityResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1CheckVanitySubdomainAvailabilityResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1ListAllSnippetsResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *SnippetListOutput -} - -// Status returns HTTPResponse.Status -func (r V1ListAllSnippetsResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1ListAllSnippetsResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1ListAllSnippetsResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -type V1GetASnippetResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *SnippetResponseOutput -} - -// Status returns HTTPResponse.Status -func (r V1GetASnippetResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} - -// StatusCode returns HTTPResponse.StatusCode -func (r V1GetASnippetResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers -func (r V1GetASnippetResponse) ContentType() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Header.Get("Content-Type") - } - return "" -} - -// V1DeleteABranchWithResponse request returning *V1DeleteABranchResponse -func (c *ClientWithResponses) V1DeleteABranchWithResponse(ctx context.Context, branchIdOrRef string, params *V1DeleteABranchParams, reqEditors ...RequestEditorFn) (*V1DeleteABranchResponse, error) { - rsp, err := c.V1DeleteABranch(ctx, branchIdOrRef, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeleteABranchResponse(rsp) -} - -// V1GetABranchConfigWithResponse request returning *V1GetABranchConfigResponse -func (c *ClientWithResponses) V1GetABranchConfigWithResponse(ctx context.Context, branchIdOrRef string, reqEditors ...RequestEditorFn) (*V1GetABranchConfigResponse, error) { - rsp, err := c.V1GetABranchConfig(ctx, branchIdOrRef, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetABranchConfigResponse(rsp) -} - -// V1UpdateABranchConfigWithBodyWithResponse request with arbitrary body returning *V1UpdateABranchConfigResponse -func (c *ClientWithResponses) V1UpdateABranchConfigWithBodyWithResponse(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateABranchConfigResponse, error) { - rsp, err := c.V1UpdateABranchConfigWithBody(ctx, branchIdOrRef, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateABranchConfigResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateABranchConfigWithResponse(ctx context.Context, branchIdOrRef string, body V1UpdateABranchConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateABranchConfigResponse, error) { - rsp, err := c.V1UpdateABranchConfig(ctx, branchIdOrRef, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateABranchConfigResponse(rsp) -} - -// V1DiffABranchWithResponse request returning *V1DiffABranchResponse -func (c *ClientWithResponses) V1DiffABranchWithResponse(ctx context.Context, branchIdOrRef string, params *V1DiffABranchParams, reqEditors ...RequestEditorFn) (*V1DiffABranchResponse, error) { - rsp, err := c.V1DiffABranch(ctx, branchIdOrRef, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DiffABranchResponse(rsp) -} - -// V1MergeABranchWithBodyWithResponse request with arbitrary body returning *V1MergeABranchResponse -func (c *ClientWithResponses) V1MergeABranchWithBodyWithResponse(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1MergeABranchResponse, error) { - rsp, err := c.V1MergeABranchWithBody(ctx, branchIdOrRef, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1MergeABranchResponse(rsp) -} - -func (c *ClientWithResponses) V1MergeABranchWithResponse(ctx context.Context, branchIdOrRef string, body V1MergeABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*V1MergeABranchResponse, error) { - rsp, err := c.V1MergeABranch(ctx, branchIdOrRef, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1MergeABranchResponse(rsp) -} - -// V1PushABranchWithBodyWithResponse request with arbitrary body returning *V1PushABranchResponse -func (c *ClientWithResponses) V1PushABranchWithBodyWithResponse(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1PushABranchResponse, error) { - rsp, err := c.V1PushABranchWithBody(ctx, branchIdOrRef, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1PushABranchResponse(rsp) -} - -func (c *ClientWithResponses) V1PushABranchWithResponse(ctx context.Context, branchIdOrRef string, body V1PushABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*V1PushABranchResponse, error) { - rsp, err := c.V1PushABranch(ctx, branchIdOrRef, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1PushABranchResponse(rsp) -} - -// V1ResetABranchWithBodyWithResponse request with arbitrary body returning *V1ResetABranchResponse -func (c *ClientWithResponses) V1ResetABranchWithBodyWithResponse(ctx context.Context, branchIdOrRef string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ResetABranchResponse, error) { - rsp, err := c.V1ResetABranchWithBody(ctx, branchIdOrRef, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ResetABranchResponse(rsp) -} - -func (c *ClientWithResponses) V1ResetABranchWithResponse(ctx context.Context, branchIdOrRef string, body V1ResetABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*V1ResetABranchResponse, error) { - rsp, err := c.V1ResetABranch(ctx, branchIdOrRef, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ResetABranchResponse(rsp) -} - -// V1RestoreABranchWithResponse request returning *V1RestoreABranchResponse -func (c *ClientWithResponses) V1RestoreABranchWithResponse(ctx context.Context, branchIdOrRef string, reqEditors ...RequestEditorFn) (*V1RestoreABranchResponse, error) { - rsp, err := c.V1RestoreABranch(ctx, branchIdOrRef, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RestoreABranchResponse(rsp) -} - -// V1AuthorizeUserWithResponse request returning *V1AuthorizeUserResponse -func (c *ClientWithResponses) V1AuthorizeUserWithResponse(ctx context.Context, params *V1AuthorizeUserParams, reqEditors ...RequestEditorFn) (*V1AuthorizeUserResponse, error) { - rsp, err := c.V1AuthorizeUser(ctx, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1AuthorizeUserResponse(rsp) -} - -// V1OauthAuthorizeProjectClaimWithResponse request returning *V1OauthAuthorizeProjectClaimResponse -func (c *ClientWithResponses) V1OauthAuthorizeProjectClaimWithResponse(ctx context.Context, params *V1OauthAuthorizeProjectClaimParams, reqEditors ...RequestEditorFn) (*V1OauthAuthorizeProjectClaimResponse, error) { - rsp, err := c.V1OauthAuthorizeProjectClaim(ctx, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1OauthAuthorizeProjectClaimResponse(rsp) -} - -// V1RevokeTokenWithBodyWithResponse request with arbitrary body returning *V1RevokeTokenResponse -func (c *ClientWithResponses) V1RevokeTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1RevokeTokenResponse, error) { - rsp, err := c.V1RevokeTokenWithBody(ctx, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RevokeTokenResponse(rsp) -} - -func (c *ClientWithResponses) V1RevokeTokenWithResponse(ctx context.Context, body V1RevokeTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*V1RevokeTokenResponse, error) { - rsp, err := c.V1RevokeToken(ctx, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RevokeTokenResponse(rsp) -} - -// V1ExchangeOauthTokenWithBodyWithResponse request with arbitrary body returning *V1ExchangeOauthTokenResponse -func (c *ClientWithResponses) V1ExchangeOauthTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ExchangeOauthTokenResponse, error) { - rsp, err := c.V1ExchangeOauthTokenWithBody(ctx, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ExchangeOauthTokenResponse(rsp) -} - -func (c *ClientWithResponses) V1ExchangeOauthTokenWithFormdataBodyWithResponse(ctx context.Context, body V1ExchangeOauthTokenFormdataRequestBody, reqEditors ...RequestEditorFn) (*V1ExchangeOauthTokenResponse, error) { - rsp, err := c.V1ExchangeOauthTokenWithFormdataBody(ctx, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ExchangeOauthTokenResponse(rsp) -} - -// V1ListAllOrganizationsWithResponse request returning *V1ListAllOrganizationsResponse -func (c *ClientWithResponses) V1ListAllOrganizationsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*V1ListAllOrganizationsResponse, error) { - rsp, err := c.V1ListAllOrganizations(ctx, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListAllOrganizationsResponse(rsp) -} - -// V1CreateAnOrganizationWithBodyWithResponse request with arbitrary body returning *V1CreateAnOrganizationResponse -func (c *ClientWithResponses) V1CreateAnOrganizationWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateAnOrganizationResponse, error) { - rsp, err := c.V1CreateAnOrganizationWithBody(ctx, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateAnOrganizationResponse(rsp) -} - -func (c *ClientWithResponses) V1CreateAnOrganizationWithResponse(ctx context.Context, body V1CreateAnOrganizationJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateAnOrganizationResponse, error) { - rsp, err := c.V1CreateAnOrganization(ctx, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateAnOrganizationResponse(rsp) -} - -// V1GetAnOrganizationWithResponse request returning *V1GetAnOrganizationResponse -func (c *ClientWithResponses) V1GetAnOrganizationWithResponse(ctx context.Context, slug string, reqEditors ...RequestEditorFn) (*V1GetAnOrganizationResponse, error) { - rsp, err := c.V1GetAnOrganization(ctx, slug, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetAnOrganizationResponse(rsp) -} - -// V1GetOrganizationEntitlementsWithResponse request returning *V1GetOrganizationEntitlementsResponse -func (c *ClientWithResponses) V1GetOrganizationEntitlementsWithResponse(ctx context.Context, slug string, reqEditors ...RequestEditorFn) (*V1GetOrganizationEntitlementsResponse, error) { - rsp, err := c.V1GetOrganizationEntitlements(ctx, slug, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetOrganizationEntitlementsResponse(rsp) -} - -// V1ListOrganizationMembersWithResponse request returning *V1ListOrganizationMembersResponse -func (c *ClientWithResponses) V1ListOrganizationMembersWithResponse(ctx context.Context, slug string, reqEditors ...RequestEditorFn) (*V1ListOrganizationMembersResponse, error) { - rsp, err := c.V1ListOrganizationMembers(ctx, slug, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListOrganizationMembersResponse(rsp) -} - -// V1GetOrganizationProjectClaimWithResponse request returning *V1GetOrganizationProjectClaimResponse -func (c *ClientWithResponses) V1GetOrganizationProjectClaimWithResponse(ctx context.Context, slug string, token string, reqEditors ...RequestEditorFn) (*V1GetOrganizationProjectClaimResponse, error) { - rsp, err := c.V1GetOrganizationProjectClaim(ctx, slug, token, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetOrganizationProjectClaimResponse(rsp) -} - -// V1ClaimProjectForOrganizationWithResponse request returning *V1ClaimProjectForOrganizationResponse -func (c *ClientWithResponses) V1ClaimProjectForOrganizationWithResponse(ctx context.Context, slug string, token string, reqEditors ...RequestEditorFn) (*V1ClaimProjectForOrganizationResponse, error) { - rsp, err := c.V1ClaimProjectForOrganization(ctx, slug, token, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ClaimProjectForOrganizationResponse(rsp) -} - -// V1GetAllProjectsForOrganizationWithResponse request returning *V1GetAllProjectsForOrganizationResponse -func (c *ClientWithResponses) V1GetAllProjectsForOrganizationWithResponse(ctx context.Context, slug string, params *V1GetAllProjectsForOrganizationParams, reqEditors ...RequestEditorFn) (*V1GetAllProjectsForOrganizationResponse, error) { - rsp, err := c.V1GetAllProjectsForOrganization(ctx, slug, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetAllProjectsForOrganizationResponse(rsp) -} - -// V1GetProfileWithResponse request returning *V1GetProfileResponse -func (c *ClientWithResponses) V1GetProfileWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*V1GetProfileResponse, error) { - rsp, err := c.V1GetProfile(ctx, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProfileResponse(rsp) -} - -// V1ListAllProjectsWithResponse request returning *V1ListAllProjectsResponse -func (c *ClientWithResponses) V1ListAllProjectsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*V1ListAllProjectsResponse, error) { - rsp, err := c.V1ListAllProjects(ctx, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListAllProjectsResponse(rsp) -} - -// V1CreateAProjectWithBodyWithResponse request with arbitrary body returning *V1CreateAProjectResponse -func (c *ClientWithResponses) V1CreateAProjectWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateAProjectResponse, error) { - rsp, err := c.V1CreateAProjectWithBody(ctx, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateAProjectResponse(rsp) -} - -func (c *ClientWithResponses) V1CreateAProjectWithResponse(ctx context.Context, body V1CreateAProjectJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateAProjectResponse, error) { - rsp, err := c.V1CreateAProject(ctx, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateAProjectResponse(rsp) -} - -// V1GetAvailableRegionsWithResponse request returning *V1GetAvailableRegionsResponse -func (c *ClientWithResponses) V1GetAvailableRegionsWithResponse(ctx context.Context, params *V1GetAvailableRegionsParams, reqEditors ...RequestEditorFn) (*V1GetAvailableRegionsResponse, error) { - rsp, err := c.V1GetAvailableRegions(ctx, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetAvailableRegionsResponse(rsp) -} - -// V1DeleteAProjectWithResponse request returning *V1DeleteAProjectResponse -func (c *ClientWithResponses) V1DeleteAProjectWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1DeleteAProjectResponse, error) { - rsp, err := c.V1DeleteAProject(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeleteAProjectResponse(rsp) -} - -// V1GetProjectWithResponse request returning *V1GetProjectResponse -func (c *ClientWithResponses) V1GetProjectWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectResponse, error) { - rsp, err := c.V1GetProject(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectResponse(rsp) -} - -// V1UpdateAProjectWithBodyWithResponse request with arbitrary body returning *V1UpdateAProjectResponse -func (c *ClientWithResponses) V1UpdateAProjectWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateAProjectResponse, error) { - rsp, err := c.V1UpdateAProjectWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateAProjectResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateAProjectWithResponse(ctx context.Context, ref string, body V1UpdateAProjectJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateAProjectResponse, error) { - rsp, err := c.V1UpdateAProject(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateAProjectResponse(rsp) -} - -// V1ListActionRunsWithResponse request returning *V1ListActionRunsResponse -func (c *ClientWithResponses) V1ListActionRunsWithResponse(ctx context.Context, ref string, params *V1ListActionRunsParams, reqEditors ...RequestEditorFn) (*V1ListActionRunsResponse, error) { - rsp, err := c.V1ListActionRuns(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListActionRunsResponse(rsp) -} - -// V1CountActionRunsWithResponse request returning *V1CountActionRunsResponse -func (c *ClientWithResponses) V1CountActionRunsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1CountActionRunsResponse, error) { - rsp, err := c.V1CountActionRuns(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CountActionRunsResponse(rsp) -} - -// V1GetActionRunWithResponse request returning *V1GetActionRunResponse -func (c *ClientWithResponses) V1GetActionRunWithResponse(ctx context.Context, ref string, runId string, reqEditors ...RequestEditorFn) (*V1GetActionRunResponse, error) { - rsp, err := c.V1GetActionRun(ctx, ref, runId, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetActionRunResponse(rsp) -} - -// V1GetActionRunLogsWithResponse request returning *V1GetActionRunLogsResponse -func (c *ClientWithResponses) V1GetActionRunLogsWithResponse(ctx context.Context, ref string, runId string, reqEditors ...RequestEditorFn) (*V1GetActionRunLogsResponse, error) { - rsp, err := c.V1GetActionRunLogs(ctx, ref, runId, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetActionRunLogsResponse(rsp) -} - -// V1UpdateActionRunStatusWithBodyWithResponse request with arbitrary body returning *V1UpdateActionRunStatusResponse -func (c *ClientWithResponses) V1UpdateActionRunStatusWithBodyWithResponse(ctx context.Context, ref string, runId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateActionRunStatusResponse, error) { - rsp, err := c.V1UpdateActionRunStatusWithBody(ctx, ref, runId, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateActionRunStatusResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateActionRunStatusWithResponse(ctx context.Context, ref string, runId string, body V1UpdateActionRunStatusJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateActionRunStatusResponse, error) { - rsp, err := c.V1UpdateActionRunStatus(ctx, ref, runId, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateActionRunStatusResponse(rsp) -} - -// V1GetPerformanceAdvisorsWithResponse request returning *V1GetPerformanceAdvisorsResponse -func (c *ClientWithResponses) V1GetPerformanceAdvisorsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetPerformanceAdvisorsResponse, error) { - rsp, err := c.V1GetPerformanceAdvisors(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetPerformanceAdvisorsResponse(rsp) -} - -// V1GetSecurityAdvisorsWithResponse request returning *V1GetSecurityAdvisorsResponse -func (c *ClientWithResponses) V1GetSecurityAdvisorsWithResponse(ctx context.Context, ref string, params *V1GetSecurityAdvisorsParams, reqEditors ...RequestEditorFn) (*V1GetSecurityAdvisorsResponse, error) { - rsp, err := c.V1GetSecurityAdvisors(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetSecurityAdvisorsResponse(rsp) -} - -// V1GetProjectFunctionCombinedStatsWithResponse request returning *V1GetProjectFunctionCombinedStatsResponse -func (c *ClientWithResponses) V1GetProjectFunctionCombinedStatsWithResponse(ctx context.Context, ref string, params *V1GetProjectFunctionCombinedStatsParams, reqEditors ...RequestEditorFn) (*V1GetProjectFunctionCombinedStatsResponse, error) { - rsp, err := c.V1GetProjectFunctionCombinedStats(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectFunctionCombinedStatsResponse(rsp) -} - -// V1GetProjectLogsWithResponse request returning *V1GetProjectLogsResponse -func (c *ClientWithResponses) V1GetProjectLogsWithResponse(ctx context.Context, ref string, params *V1GetProjectLogsParams, reqEditors ...RequestEditorFn) (*V1GetProjectLogsResponse, error) { - rsp, err := c.V1GetProjectLogs(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectLogsResponse(rsp) -} - -// V1GetProjectLogsAllWithResponse request returning *V1GetProjectLogsAllResponse -func (c *ClientWithResponses) V1GetProjectLogsAllWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectLogsAllResponse, error) { - rsp, err := c.V1GetProjectLogsAll(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectLogsAllResponse(rsp) -} - -// V1ScrapeProjectMetricsWithResponse request returning *V1ScrapeProjectMetricsResponse -func (c *ClientWithResponses) V1ScrapeProjectMetricsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ScrapeProjectMetricsResponse, error) { - rsp, err := c.V1ScrapeProjectMetrics(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ScrapeProjectMetricsResponse(rsp) -} - -// V1GetProjectUsageApiCountWithResponse request returning *V1GetProjectUsageApiCountResponse -func (c *ClientWithResponses) V1GetProjectUsageApiCountWithResponse(ctx context.Context, ref string, params *V1GetProjectUsageApiCountParams, reqEditors ...RequestEditorFn) (*V1GetProjectUsageApiCountResponse, error) { - rsp, err := c.V1GetProjectUsageApiCount(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectUsageApiCountResponse(rsp) -} - -// V1GetProjectUsageRequestCountWithResponse request returning *V1GetProjectUsageRequestCountResponse -func (c *ClientWithResponses) V1GetProjectUsageRequestCountWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectUsageRequestCountResponse, error) { - rsp, err := c.V1GetProjectUsageRequestCount(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectUsageRequestCountResponse(rsp) -} - -// V1GetProjectApiKeysWithResponse request returning *V1GetProjectApiKeysResponse -func (c *ClientWithResponses) V1GetProjectApiKeysWithResponse(ctx context.Context, ref string, params *V1GetProjectApiKeysParams, reqEditors ...RequestEditorFn) (*V1GetProjectApiKeysResponse, error) { - rsp, err := c.V1GetProjectApiKeys(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectApiKeysResponse(rsp) -} - -// V1CreateProjectApiKeyWithBodyWithResponse request with arbitrary body returning *V1CreateProjectApiKeyResponse -func (c *ClientWithResponses) V1CreateProjectApiKeyWithBodyWithResponse(ctx context.Context, ref string, params *V1CreateProjectApiKeyParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateProjectApiKeyResponse, error) { - rsp, err := c.V1CreateProjectApiKeyWithBody(ctx, ref, params, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateProjectApiKeyResponse(rsp) -} - -func (c *ClientWithResponses) V1CreateProjectApiKeyWithResponse(ctx context.Context, ref string, params *V1CreateProjectApiKeyParams, body V1CreateProjectApiKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateProjectApiKeyResponse, error) { - rsp, err := c.V1CreateProjectApiKey(ctx, ref, params, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateProjectApiKeyResponse(rsp) -} - -// V1GetProjectLegacyApiKeysWithResponse request returning *V1GetProjectLegacyApiKeysResponse -func (c *ClientWithResponses) V1GetProjectLegacyApiKeysWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectLegacyApiKeysResponse, error) { - rsp, err := c.V1GetProjectLegacyApiKeys(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectLegacyApiKeysResponse(rsp) -} - -// V1UpdateProjectLegacyApiKeysWithResponse request returning *V1UpdateProjectLegacyApiKeysResponse -func (c *ClientWithResponses) V1UpdateProjectLegacyApiKeysWithResponse(ctx context.Context, ref string, params *V1UpdateProjectLegacyApiKeysParams, reqEditors ...RequestEditorFn) (*V1UpdateProjectLegacyApiKeysResponse, error) { - rsp, err := c.V1UpdateProjectLegacyApiKeys(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateProjectLegacyApiKeysResponse(rsp) -} - -// V1DeleteProjectApiKeyWithResponse request returning *V1DeleteProjectApiKeyResponse -func (c *ClientWithResponses) V1DeleteProjectApiKeyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, params *V1DeleteProjectApiKeyParams, reqEditors ...RequestEditorFn) (*V1DeleteProjectApiKeyResponse, error) { - rsp, err := c.V1DeleteProjectApiKey(ctx, ref, id, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeleteProjectApiKeyResponse(rsp) -} - -// V1GetProjectApiKeyWithResponse request returning *V1GetProjectApiKeyResponse -func (c *ClientWithResponses) V1GetProjectApiKeyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, params *V1GetProjectApiKeyParams, reqEditors ...RequestEditorFn) (*V1GetProjectApiKeyResponse, error) { - rsp, err := c.V1GetProjectApiKey(ctx, ref, id, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectApiKeyResponse(rsp) -} - -// V1UpdateProjectApiKeyWithBodyWithResponse request with arbitrary body returning *V1UpdateProjectApiKeyResponse -func (c *ClientWithResponses) V1UpdateProjectApiKeyWithBodyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, params *V1UpdateProjectApiKeyParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateProjectApiKeyResponse, error) { - rsp, err := c.V1UpdateProjectApiKeyWithBody(ctx, ref, id, params, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateProjectApiKeyResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateProjectApiKeyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, params *V1UpdateProjectApiKeyParams, body V1UpdateProjectApiKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateProjectApiKeyResponse, error) { - rsp, err := c.V1UpdateProjectApiKey(ctx, ref, id, params, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateProjectApiKeyResponse(rsp) -} - -// V1ListProjectAddonsWithResponse request returning *V1ListProjectAddonsResponse -func (c *ClientWithResponses) V1ListProjectAddonsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListProjectAddonsResponse, error) { - rsp, err := c.V1ListProjectAddons(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListProjectAddonsResponse(rsp) -} - -// V1ApplyProjectAddonWithBodyWithResponse request with arbitrary body returning *V1ApplyProjectAddonResponse -func (c *ClientWithResponses) V1ApplyProjectAddonWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ApplyProjectAddonResponse, error) { - rsp, err := c.V1ApplyProjectAddonWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ApplyProjectAddonResponse(rsp) -} - -func (c *ClientWithResponses) V1ApplyProjectAddonWithResponse(ctx context.Context, ref string, body V1ApplyProjectAddonJSONRequestBody, reqEditors ...RequestEditorFn) (*V1ApplyProjectAddonResponse, error) { - rsp, err := c.V1ApplyProjectAddon(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ApplyProjectAddonResponse(rsp) -} - -// V1RemoveProjectAddonWithResponse request returning *V1RemoveProjectAddonResponse -func (c *ClientWithResponses) V1RemoveProjectAddonWithResponse(ctx context.Context, ref string, addonVariant struct { - union json.RawMessage -}, reqEditors ...RequestEditorFn) (*V1RemoveProjectAddonResponse, error) { - rsp, err := c.V1RemoveProjectAddon(ctx, ref, addonVariant, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RemoveProjectAddonResponse(rsp) -} - -// V1DisablePreviewBranchingWithResponse request returning *V1DisablePreviewBranchingResponse -func (c *ClientWithResponses) V1DisablePreviewBranchingWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1DisablePreviewBranchingResponse, error) { - rsp, err := c.V1DisablePreviewBranching(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DisablePreviewBranchingResponse(rsp) -} - -// V1ListAllBranchesWithResponse request returning *V1ListAllBranchesResponse -func (c *ClientWithResponses) V1ListAllBranchesWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllBranchesResponse, error) { - rsp, err := c.V1ListAllBranches(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListAllBranchesResponse(rsp) -} - -// V1CreateABranchWithBodyWithResponse request with arbitrary body returning *V1CreateABranchResponse -func (c *ClientWithResponses) V1CreateABranchWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateABranchResponse, error) { - rsp, err := c.V1CreateABranchWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateABranchResponse(rsp) -} - -func (c *ClientWithResponses) V1CreateABranchWithResponse(ctx context.Context, ref string, body V1CreateABranchJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateABranchResponse, error) { - rsp, err := c.V1CreateABranch(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateABranchResponse(rsp) -} - -// V1GetABranchWithResponse request returning *V1GetABranchResponse -func (c *ClientWithResponses) V1GetABranchWithResponse(ctx context.Context, ref string, name string, reqEditors ...RequestEditorFn) (*V1GetABranchResponse, error) { - rsp, err := c.V1GetABranch(ctx, ref, name, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetABranchResponse(rsp) -} - -// V1DeleteProjectClaimTokenWithResponse request returning *V1DeleteProjectClaimTokenResponse -func (c *ClientWithResponses) V1DeleteProjectClaimTokenWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1DeleteProjectClaimTokenResponse, error) { - rsp, err := c.V1DeleteProjectClaimToken(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeleteProjectClaimTokenResponse(rsp) -} - -// V1GetProjectClaimTokenWithResponse request returning *V1GetProjectClaimTokenResponse -func (c *ClientWithResponses) V1GetProjectClaimTokenWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectClaimTokenResponse, error) { - rsp, err := c.V1GetProjectClaimToken(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectClaimTokenResponse(rsp) -} - -// V1CreateProjectClaimTokenWithResponse request returning *V1CreateProjectClaimTokenResponse -func (c *ClientWithResponses) V1CreateProjectClaimTokenWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1CreateProjectClaimTokenResponse, error) { - rsp, err := c.V1CreateProjectClaimToken(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateProjectClaimTokenResponse(rsp) -} - -// V1DeleteLoginRolesWithResponse request returning *V1DeleteLoginRolesResponse -func (c *ClientWithResponses) V1DeleteLoginRolesWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1DeleteLoginRolesResponse, error) { - rsp, err := c.V1DeleteLoginRoles(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeleteLoginRolesResponse(rsp) -} - -// V1CreateLoginRoleWithBodyWithResponse request with arbitrary body returning *V1CreateLoginRoleResponse -func (c *ClientWithResponses) V1CreateLoginRoleWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateLoginRoleResponse, error) { - rsp, err := c.V1CreateLoginRoleWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateLoginRoleResponse(rsp) -} - -func (c *ClientWithResponses) V1CreateLoginRoleWithResponse(ctx context.Context, ref string, body V1CreateLoginRoleJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateLoginRoleResponse, error) { - rsp, err := c.V1CreateLoginRole(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateLoginRoleResponse(rsp) -} - -// V1GetAuthServiceConfigWithResponse request returning *V1GetAuthServiceConfigResponse -func (c *ClientWithResponses) V1GetAuthServiceConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetAuthServiceConfigResponse, error) { - rsp, err := c.V1GetAuthServiceConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetAuthServiceConfigResponse(rsp) -} - -// V1UpdateAuthServiceConfigWithBodyWithResponse request with arbitrary body returning *V1UpdateAuthServiceConfigResponse -func (c *ClientWithResponses) V1UpdateAuthServiceConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateAuthServiceConfigResponse, error) { - rsp, err := c.V1UpdateAuthServiceConfigWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateAuthServiceConfigResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateAuthServiceConfigWithResponse(ctx context.Context, ref string, body V1UpdateAuthServiceConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateAuthServiceConfigResponse, error) { - rsp, err := c.V1UpdateAuthServiceConfig(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateAuthServiceConfigResponse(rsp) -} - -// V1GetProjectSigningKeysWithResponse request returning *V1GetProjectSigningKeysResponse -func (c *ClientWithResponses) V1GetProjectSigningKeysWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectSigningKeysResponse, error) { - rsp, err := c.V1GetProjectSigningKeys(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectSigningKeysResponse(rsp) -} - -// V1CreateProjectSigningKeyWithBodyWithResponse request with arbitrary body returning *V1CreateProjectSigningKeyResponse -func (c *ClientWithResponses) V1CreateProjectSigningKeyWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateProjectSigningKeyResponse, error) { - rsp, err := c.V1CreateProjectSigningKeyWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateProjectSigningKeyResponse(rsp) -} - -func (c *ClientWithResponses) V1CreateProjectSigningKeyWithResponse(ctx context.Context, ref string, body V1CreateProjectSigningKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateProjectSigningKeyResponse, error) { - rsp, err := c.V1CreateProjectSigningKey(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateProjectSigningKeyResponse(rsp) -} - -// V1GetLegacySigningKeyWithResponse request returning *V1GetLegacySigningKeyResponse -func (c *ClientWithResponses) V1GetLegacySigningKeyWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetLegacySigningKeyResponse, error) { - rsp, err := c.V1GetLegacySigningKey(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetLegacySigningKeyResponse(rsp) -} - -// V1CreateLegacySigningKeyWithResponse request returning *V1CreateLegacySigningKeyResponse -func (c *ClientWithResponses) V1CreateLegacySigningKeyWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1CreateLegacySigningKeyResponse, error) { - rsp, err := c.V1CreateLegacySigningKey(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateLegacySigningKeyResponse(rsp) -} - -// V1RemoveProjectSigningKeyWithResponse request returning *V1RemoveProjectSigningKeyResponse -func (c *ClientWithResponses) V1RemoveProjectSigningKeyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1RemoveProjectSigningKeyResponse, error) { - rsp, err := c.V1RemoveProjectSigningKey(ctx, ref, id, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RemoveProjectSigningKeyResponse(rsp) -} - -// V1GetProjectSigningKeyWithResponse request returning *V1GetProjectSigningKeyResponse -func (c *ClientWithResponses) V1GetProjectSigningKeyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1GetProjectSigningKeyResponse, error) { - rsp, err := c.V1GetProjectSigningKey(ctx, ref, id, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectSigningKeyResponse(rsp) -} - -// V1UpdateProjectSigningKeyWithBodyWithResponse request with arbitrary body returning *V1UpdateProjectSigningKeyResponse -func (c *ClientWithResponses) V1UpdateProjectSigningKeyWithBodyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateProjectSigningKeyResponse, error) { - rsp, err := c.V1UpdateProjectSigningKeyWithBody(ctx, ref, id, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateProjectSigningKeyResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateProjectSigningKeyWithResponse(ctx context.Context, ref string, id openapi_types.UUID, body V1UpdateProjectSigningKeyJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateProjectSigningKeyResponse, error) { - rsp, err := c.V1UpdateProjectSigningKey(ctx, ref, id, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateProjectSigningKeyResponse(rsp) -} - -// V1ListAllSsoProviderWithResponse request returning *V1ListAllSsoProviderResponse -func (c *ClientWithResponses) V1ListAllSsoProviderWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllSsoProviderResponse, error) { - rsp, err := c.V1ListAllSsoProvider(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListAllSsoProviderResponse(rsp) -} - -// V1CreateASsoProviderWithBodyWithResponse request with arbitrary body returning *V1CreateASsoProviderResponse -func (c *ClientWithResponses) V1CreateASsoProviderWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateASsoProviderResponse, error) { - rsp, err := c.V1CreateASsoProviderWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateASsoProviderResponse(rsp) -} - -func (c *ClientWithResponses) V1CreateASsoProviderWithResponse(ctx context.Context, ref string, body V1CreateASsoProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateASsoProviderResponse, error) { - rsp, err := c.V1CreateASsoProvider(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateASsoProviderResponse(rsp) -} - -// V1DeleteASsoProviderWithResponse request returning *V1DeleteASsoProviderResponse -func (c *ClientWithResponses) V1DeleteASsoProviderWithResponse(ctx context.Context, ref string, providerId openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1DeleteASsoProviderResponse, error) { - rsp, err := c.V1DeleteASsoProvider(ctx, ref, providerId, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeleteASsoProviderResponse(rsp) -} - -// V1GetASsoProviderWithResponse request returning *V1GetASsoProviderResponse -func (c *ClientWithResponses) V1GetASsoProviderWithResponse(ctx context.Context, ref string, providerId openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1GetASsoProviderResponse, error) { - rsp, err := c.V1GetASsoProvider(ctx, ref, providerId, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetASsoProviderResponse(rsp) -} - -// V1UpdateASsoProviderWithBodyWithResponse request with arbitrary body returning *V1UpdateASsoProviderResponse -func (c *ClientWithResponses) V1UpdateASsoProviderWithBodyWithResponse(ctx context.Context, ref string, providerId openapi_types.UUID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateASsoProviderResponse, error) { - rsp, err := c.V1UpdateASsoProviderWithBody(ctx, ref, providerId, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateASsoProviderResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateASsoProviderWithResponse(ctx context.Context, ref string, providerId openapi_types.UUID, body V1UpdateASsoProviderJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateASsoProviderResponse, error) { - rsp, err := c.V1UpdateASsoProvider(ctx, ref, providerId, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateASsoProviderResponse(rsp) -} - -// V1ListProjectTpaIntegrationsWithResponse request returning *V1ListProjectTpaIntegrationsResponse -func (c *ClientWithResponses) V1ListProjectTpaIntegrationsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListProjectTpaIntegrationsResponse, error) { - rsp, err := c.V1ListProjectTpaIntegrations(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListProjectTpaIntegrationsResponse(rsp) -} - -// V1CreateProjectTpaIntegrationWithBodyWithResponse request with arbitrary body returning *V1CreateProjectTpaIntegrationResponse -func (c *ClientWithResponses) V1CreateProjectTpaIntegrationWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateProjectTpaIntegrationResponse, error) { - rsp, err := c.V1CreateProjectTpaIntegrationWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateProjectTpaIntegrationResponse(rsp) -} - -func (c *ClientWithResponses) V1CreateProjectTpaIntegrationWithResponse(ctx context.Context, ref string, body V1CreateProjectTpaIntegrationJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateProjectTpaIntegrationResponse, error) { - rsp, err := c.V1CreateProjectTpaIntegration(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateProjectTpaIntegrationResponse(rsp) -} - -// V1DeleteProjectTpaIntegrationWithResponse request returning *V1DeleteProjectTpaIntegrationResponse -func (c *ClientWithResponses) V1DeleteProjectTpaIntegrationWithResponse(ctx context.Context, ref string, tpaId openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1DeleteProjectTpaIntegrationResponse, error) { - rsp, err := c.V1DeleteProjectTpaIntegration(ctx, ref, tpaId, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeleteProjectTpaIntegrationResponse(rsp) -} - -// V1GetProjectTpaIntegrationWithResponse request returning *V1GetProjectTpaIntegrationResponse -func (c *ClientWithResponses) V1GetProjectTpaIntegrationWithResponse(ctx context.Context, ref string, tpaId openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1GetProjectTpaIntegrationResponse, error) { - rsp, err := c.V1GetProjectTpaIntegration(ctx, ref, tpaId, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectTpaIntegrationResponse(rsp) -} - -// V1GetProjectPgbouncerConfigWithResponse request returning *V1GetProjectPgbouncerConfigResponse -func (c *ClientWithResponses) V1GetProjectPgbouncerConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectPgbouncerConfigResponse, error) { - rsp, err := c.V1GetProjectPgbouncerConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectPgbouncerConfigResponse(rsp) -} - -// V1GetPoolerConfigWithResponse request returning *V1GetPoolerConfigResponse -func (c *ClientWithResponses) V1GetPoolerConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetPoolerConfigResponse, error) { - rsp, err := c.V1GetPoolerConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetPoolerConfigResponse(rsp) -} - -// V1UpdatePoolerConfigWithBodyWithResponse request with arbitrary body returning *V1UpdatePoolerConfigResponse -func (c *ClientWithResponses) V1UpdatePoolerConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdatePoolerConfigResponse, error) { - rsp, err := c.V1UpdatePoolerConfigWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdatePoolerConfigResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdatePoolerConfigWithResponse(ctx context.Context, ref string, body V1UpdatePoolerConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdatePoolerConfigResponse, error) { - rsp, err := c.V1UpdatePoolerConfig(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdatePoolerConfigResponse(rsp) -} - -// V1GetPostgresConfigWithResponse request returning *V1GetPostgresConfigResponse -func (c *ClientWithResponses) V1GetPostgresConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetPostgresConfigResponse, error) { - rsp, err := c.V1GetPostgresConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetPostgresConfigResponse(rsp) -} - -// V1UpdatePostgresConfigWithBodyWithResponse request with arbitrary body returning *V1UpdatePostgresConfigResponse -func (c *ClientWithResponses) V1UpdatePostgresConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdatePostgresConfigResponse, error) { - rsp, err := c.V1UpdatePostgresConfigWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdatePostgresConfigResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdatePostgresConfigWithResponse(ctx context.Context, ref string, body V1UpdatePostgresConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdatePostgresConfigResponse, error) { - rsp, err := c.V1UpdatePostgresConfig(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdatePostgresConfigResponse(rsp) -} - -// V1GetDatabaseDiskWithResponse request returning *V1GetDatabaseDiskResponse -func (c *ClientWithResponses) V1GetDatabaseDiskWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetDatabaseDiskResponse, error) { - rsp, err := c.V1GetDatabaseDisk(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetDatabaseDiskResponse(rsp) -} - -// V1ModifyDatabaseDiskWithBodyWithResponse request with arbitrary body returning *V1ModifyDatabaseDiskResponse -func (c *ClientWithResponses) V1ModifyDatabaseDiskWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ModifyDatabaseDiskResponse, error) { - rsp, err := c.V1ModifyDatabaseDiskWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ModifyDatabaseDiskResponse(rsp) -} - -func (c *ClientWithResponses) V1ModifyDatabaseDiskWithResponse(ctx context.Context, ref string, body V1ModifyDatabaseDiskJSONRequestBody, reqEditors ...RequestEditorFn) (*V1ModifyDatabaseDiskResponse, error) { - rsp, err := c.V1ModifyDatabaseDisk(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ModifyDatabaseDiskResponse(rsp) -} - -// V1GetProjectDiskAutoscaleConfigWithResponse request returning *V1GetProjectDiskAutoscaleConfigResponse -func (c *ClientWithResponses) V1GetProjectDiskAutoscaleConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetProjectDiskAutoscaleConfigResponse, error) { - rsp, err := c.V1GetProjectDiskAutoscaleConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetProjectDiskAutoscaleConfigResponse(rsp) -} - -// V1GetDiskUtilizationWithResponse request returning *V1GetDiskUtilizationResponse -func (c *ClientWithResponses) V1GetDiskUtilizationWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetDiskUtilizationResponse, error) { - rsp, err := c.V1GetDiskUtilization(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetDiskUtilizationResponse(rsp) -} - -// V1GetRealtimeConfigWithResponse request returning *V1GetRealtimeConfigResponse -func (c *ClientWithResponses) V1GetRealtimeConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetRealtimeConfigResponse, error) { - rsp, err := c.V1GetRealtimeConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetRealtimeConfigResponse(rsp) -} - -// V1UpdateRealtimeConfigWithBodyWithResponse request with arbitrary body returning *V1UpdateRealtimeConfigResponse -func (c *ClientWithResponses) V1UpdateRealtimeConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateRealtimeConfigResponse, error) { - rsp, err := c.V1UpdateRealtimeConfigWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateRealtimeConfigResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateRealtimeConfigWithResponse(ctx context.Context, ref string, body V1UpdateRealtimeConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateRealtimeConfigResponse, error) { - rsp, err := c.V1UpdateRealtimeConfig(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateRealtimeConfigResponse(rsp) -} - -// V1ShutdownRealtimeWithResponse request returning *V1ShutdownRealtimeResponse -func (c *ClientWithResponses) V1ShutdownRealtimeWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ShutdownRealtimeResponse, error) { - rsp, err := c.V1ShutdownRealtime(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ShutdownRealtimeResponse(rsp) -} - -// V1GetStorageConfigWithResponse request returning *V1GetStorageConfigResponse -func (c *ClientWithResponses) V1GetStorageConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetStorageConfigResponse, error) { - rsp, err := c.V1GetStorageConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetStorageConfigResponse(rsp) -} - -// V1UpdateStorageConfigWithBodyWithResponse request with arbitrary body returning *V1UpdateStorageConfigResponse -func (c *ClientWithResponses) V1UpdateStorageConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateStorageConfigResponse, error) { - rsp, err := c.V1UpdateStorageConfigWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateStorageConfigResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateStorageConfigWithResponse(ctx context.Context, ref string, body V1UpdateStorageConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateStorageConfigResponse, error) { - rsp, err := c.V1UpdateStorageConfig(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateStorageConfigResponse(rsp) -} - -// V1DeleteHostnameConfigWithResponse request returning *V1DeleteHostnameConfigResponse -func (c *ClientWithResponses) V1DeleteHostnameConfigWithResponse(ctx context.Context, ref string, params *V1DeleteHostnameConfigParams, reqEditors ...RequestEditorFn) (*V1DeleteHostnameConfigResponse, error) { - rsp, err := c.V1DeleteHostnameConfig(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeleteHostnameConfigResponse(rsp) -} - -// V1GetHostnameConfigWithResponse request returning *V1GetHostnameConfigResponse -func (c *ClientWithResponses) V1GetHostnameConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetHostnameConfigResponse, error) { - rsp, err := c.V1GetHostnameConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetHostnameConfigResponse(rsp) -} - -// V1ActivateCustomHostnameWithResponse request returning *V1ActivateCustomHostnameResponse -func (c *ClientWithResponses) V1ActivateCustomHostnameWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ActivateCustomHostnameResponse, error) { - rsp, err := c.V1ActivateCustomHostname(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ActivateCustomHostnameResponse(rsp) -} - -// V1UpdateHostnameConfigWithBodyWithResponse request with arbitrary body returning *V1UpdateHostnameConfigResponse -func (c *ClientWithResponses) V1UpdateHostnameConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateHostnameConfigResponse, error) { - rsp, err := c.V1UpdateHostnameConfigWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateHostnameConfigResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateHostnameConfigWithResponse(ctx context.Context, ref string, body V1UpdateHostnameConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateHostnameConfigResponse, error) { - rsp, err := c.V1UpdateHostnameConfig(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateHostnameConfigResponse(rsp) -} - -// V1VerifyDnsConfigWithResponse request returning *V1VerifyDnsConfigResponse -func (c *ClientWithResponses) V1VerifyDnsConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1VerifyDnsConfigResponse, error) { - rsp, err := c.V1VerifyDnsConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1VerifyDnsConfigResponse(rsp) -} - -// V1ListAllBackupsWithResponse request returning *V1ListAllBackupsResponse -func (c *ClientWithResponses) V1ListAllBackupsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllBackupsResponse, error) { - rsp, err := c.V1ListAllBackups(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListAllBackupsResponse(rsp) -} - -// V1RestorePhysicalBackupWithBodyWithResponse request with arbitrary body returning *V1RestorePhysicalBackupResponse -func (c *ClientWithResponses) V1RestorePhysicalBackupWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1RestorePhysicalBackupResponse, error) { - rsp, err := c.V1RestorePhysicalBackupWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RestorePhysicalBackupResponse(rsp) -} - -func (c *ClientWithResponses) V1RestorePhysicalBackupWithResponse(ctx context.Context, ref string, body V1RestorePhysicalBackupJSONRequestBody, reqEditors ...RequestEditorFn) (*V1RestorePhysicalBackupResponse, error) { - rsp, err := c.V1RestorePhysicalBackup(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RestorePhysicalBackupResponse(rsp) -} - -// V1RestorePitrBackupWithBodyWithResponse request with arbitrary body returning *V1RestorePitrBackupResponse -func (c *ClientWithResponses) V1RestorePitrBackupWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1RestorePitrBackupResponse, error) { - rsp, err := c.V1RestorePitrBackupWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RestorePitrBackupResponse(rsp) -} - -func (c *ClientWithResponses) V1RestorePitrBackupWithResponse(ctx context.Context, ref string, body V1RestorePitrBackupJSONRequestBody, reqEditors ...RequestEditorFn) (*V1RestorePitrBackupResponse, error) { - rsp, err := c.V1RestorePitrBackup(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RestorePitrBackupResponse(rsp) -} - -// V1GetRestorePointWithResponse request returning *V1GetRestorePointResponse -func (c *ClientWithResponses) V1GetRestorePointWithResponse(ctx context.Context, ref string, params *V1GetRestorePointParams, reqEditors ...RequestEditorFn) (*V1GetRestorePointResponse, error) { - rsp, err := c.V1GetRestorePoint(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetRestorePointResponse(rsp) -} - -// V1CreateRestorePointWithBodyWithResponse request with arbitrary body returning *V1CreateRestorePointResponse -func (c *ClientWithResponses) V1CreateRestorePointWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateRestorePointResponse, error) { - rsp, err := c.V1CreateRestorePointWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateRestorePointResponse(rsp) -} - -func (c *ClientWithResponses) V1CreateRestorePointWithResponse(ctx context.Context, ref string, body V1CreateRestorePointJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateRestorePointResponse, error) { - rsp, err := c.V1CreateRestorePoint(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateRestorePointResponse(rsp) -} - -// V1GetBackupScheduleWithResponse request returning *V1GetBackupScheduleResponse -func (c *ClientWithResponses) V1GetBackupScheduleWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetBackupScheduleResponse, error) { - rsp, err := c.V1GetBackupSchedule(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetBackupScheduleResponse(rsp) -} - -// V1UpdateBackupScheduleWithBodyWithResponse request with arbitrary body returning *V1UpdateBackupScheduleResponse -func (c *ClientWithResponses) V1UpdateBackupScheduleWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateBackupScheduleResponse, error) { - rsp, err := c.V1UpdateBackupScheduleWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateBackupScheduleResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateBackupScheduleWithResponse(ctx context.Context, ref string, body V1UpdateBackupScheduleJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateBackupScheduleResponse, error) { - rsp, err := c.V1UpdateBackupSchedule(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateBackupScheduleResponse(rsp) -} - -// V1UndoWithBodyWithResponse request with arbitrary body returning *V1UndoResponse -func (c *ClientWithResponses) V1UndoWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UndoResponse, error) { - rsp, err := c.V1UndoWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UndoResponse(rsp) -} - -func (c *ClientWithResponses) V1UndoWithResponse(ctx context.Context, ref string, body V1UndoJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UndoResponse, error) { - rsp, err := c.V1Undo(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UndoResponse(rsp) -} - -// V1GetDatabaseMetadataWithResponse request returning *V1GetDatabaseMetadataResponse -func (c *ClientWithResponses) V1GetDatabaseMetadataWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetDatabaseMetadataResponse, error) { - rsp, err := c.V1GetDatabaseMetadata(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetDatabaseMetadataResponse(rsp) -} - -// V1GetJitAccessWithResponse request returning *V1GetJitAccessResponse -func (c *ClientWithResponses) V1GetJitAccessWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetJitAccessResponse, error) { - rsp, err := c.V1GetJitAccess(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetJitAccessResponse(rsp) -} - -// V1AuthorizeJitAccessWithBodyWithResponse request with arbitrary body returning *V1AuthorizeJitAccessResponse -func (c *ClientWithResponses) V1AuthorizeJitAccessWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1AuthorizeJitAccessResponse, error) { - rsp, err := c.V1AuthorizeJitAccessWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1AuthorizeJitAccessResponse(rsp) -} - -func (c *ClientWithResponses) V1AuthorizeJitAccessWithResponse(ctx context.Context, ref string, body V1AuthorizeJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*V1AuthorizeJitAccessResponse, error) { - rsp, err := c.V1AuthorizeJitAccess(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1AuthorizeJitAccessResponse(rsp) -} - -// V1UpdateJitAccessWithBodyWithResponse request with arbitrary body returning *V1UpdateJitAccessResponse -func (c *ClientWithResponses) V1UpdateJitAccessWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateJitAccessResponse, error) { - rsp, err := c.V1UpdateJitAccessWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateJitAccessResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateJitAccessWithResponse(ctx context.Context, ref string, body V1UpdateJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateJitAccessResponse, error) { - rsp, err := c.V1UpdateJitAccess(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateJitAccessResponse(rsp) -} - -// V1InviteExternalJitAccessWithBodyWithResponse request with arbitrary body returning *V1InviteExternalJitAccessResponse -func (c *ClientWithResponses) V1InviteExternalJitAccessWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1InviteExternalJitAccessResponse, error) { - rsp, err := c.V1InviteExternalJitAccessWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1InviteExternalJitAccessResponse(rsp) -} - -func (c *ClientWithResponses) V1InviteExternalJitAccessWithResponse(ctx context.Context, ref string, body V1InviteExternalJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*V1InviteExternalJitAccessResponse, error) { - rsp, err := c.V1InviteExternalJitAccess(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1InviteExternalJitAccessResponse(rsp) -} - -// V1AcceptInviteExternalJitAccessWithBodyWithResponse request with arbitrary body returning *V1AcceptInviteExternalJitAccessResponse -func (c *ClientWithResponses) V1AcceptInviteExternalJitAccessWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1AcceptInviteExternalJitAccessResponse, error) { - rsp, err := c.V1AcceptInviteExternalJitAccessWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1AcceptInviteExternalJitAccessResponse(rsp) -} - -func (c *ClientWithResponses) V1AcceptInviteExternalJitAccessWithResponse(ctx context.Context, ref string, body V1AcceptInviteExternalJitAccessJSONRequestBody, reqEditors ...RequestEditorFn) (*V1AcceptInviteExternalJitAccessResponse, error) { - rsp, err := c.V1AcceptInviteExternalJitAccess(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1AcceptInviteExternalJitAccessResponse(rsp) -} - -// V1DeleteInviteExternalJitAccessWithResponse request returning *V1DeleteInviteExternalJitAccessResponse -func (c *ClientWithResponses) V1DeleteInviteExternalJitAccessWithResponse(ctx context.Context, ref string, inviteId openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1DeleteInviteExternalJitAccessResponse, error) { - rsp, err := c.V1DeleteInviteExternalJitAccess(ctx, ref, inviteId, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeleteInviteExternalJitAccessResponse(rsp) -} - -// V1ListJitAccessWithResponse request returning *V1ListJitAccessResponse -func (c *ClientWithResponses) V1ListJitAccessWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListJitAccessResponse, error) { - rsp, err := c.V1ListJitAccess(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListJitAccessResponse(rsp) -} - -// V1DeleteJitAccessWithResponse request returning *V1DeleteJitAccessResponse -func (c *ClientWithResponses) V1DeleteJitAccessWithResponse(ctx context.Context, ref string, userId openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1DeleteJitAccessResponse, error) { - rsp, err := c.V1DeleteJitAccess(ctx, ref, userId, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeleteJitAccessResponse(rsp) -} - -// V1RollbackMigrationsWithResponse request returning *V1RollbackMigrationsResponse -func (c *ClientWithResponses) V1RollbackMigrationsWithResponse(ctx context.Context, ref string, params *V1RollbackMigrationsParams, reqEditors ...RequestEditorFn) (*V1RollbackMigrationsResponse, error) { - rsp, err := c.V1RollbackMigrations(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RollbackMigrationsResponse(rsp) -} - -// V1ListMigrationHistoryWithResponse request returning *V1ListMigrationHistoryResponse -func (c *ClientWithResponses) V1ListMigrationHistoryWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListMigrationHistoryResponse, error) { - rsp, err := c.V1ListMigrationHistory(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListMigrationHistoryResponse(rsp) -} - -// V1ApplyAMigrationWithBodyWithResponse request with arbitrary body returning *V1ApplyAMigrationResponse -func (c *ClientWithResponses) V1ApplyAMigrationWithBodyWithResponse(ctx context.Context, ref string, params *V1ApplyAMigrationParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ApplyAMigrationResponse, error) { - rsp, err := c.V1ApplyAMigrationWithBody(ctx, ref, params, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ApplyAMigrationResponse(rsp) -} - -func (c *ClientWithResponses) V1ApplyAMigrationWithResponse(ctx context.Context, ref string, params *V1ApplyAMigrationParams, body V1ApplyAMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*V1ApplyAMigrationResponse, error) { - rsp, err := c.V1ApplyAMigration(ctx, ref, params, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ApplyAMigrationResponse(rsp) -} - -// V1UpsertAMigrationWithBodyWithResponse request with arbitrary body returning *V1UpsertAMigrationResponse -func (c *ClientWithResponses) V1UpsertAMigrationWithBodyWithResponse(ctx context.Context, ref string, params *V1UpsertAMigrationParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpsertAMigrationResponse, error) { - rsp, err := c.V1UpsertAMigrationWithBody(ctx, ref, params, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpsertAMigrationResponse(rsp) -} - -func (c *ClientWithResponses) V1UpsertAMigrationWithResponse(ctx context.Context, ref string, params *V1UpsertAMigrationParams, body V1UpsertAMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpsertAMigrationResponse, error) { - rsp, err := c.V1UpsertAMigration(ctx, ref, params, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpsertAMigrationResponse(rsp) -} - -// V1GetAMigrationWithResponse request returning *V1GetAMigrationResponse -func (c *ClientWithResponses) V1GetAMigrationWithResponse(ctx context.Context, ref string, version string, reqEditors ...RequestEditorFn) (*V1GetAMigrationResponse, error) { - rsp, err := c.V1GetAMigration(ctx, ref, version, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetAMigrationResponse(rsp) -} - -// V1PatchAMigrationWithBodyWithResponse request with arbitrary body returning *V1PatchAMigrationResponse -func (c *ClientWithResponses) V1PatchAMigrationWithBodyWithResponse(ctx context.Context, ref string, version string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1PatchAMigrationResponse, error) { - rsp, err := c.V1PatchAMigrationWithBody(ctx, ref, version, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1PatchAMigrationResponse(rsp) -} - -func (c *ClientWithResponses) V1PatchAMigrationWithResponse(ctx context.Context, ref string, version string, body V1PatchAMigrationJSONRequestBody, reqEditors ...RequestEditorFn) (*V1PatchAMigrationResponse, error) { - rsp, err := c.V1PatchAMigration(ctx, ref, version, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1PatchAMigrationResponse(rsp) -} - -// V1GetDatabaseOpenapiWithResponse request returning *V1GetDatabaseOpenapiResponse -func (c *ClientWithResponses) V1GetDatabaseOpenapiWithResponse(ctx context.Context, ref string, params *V1GetDatabaseOpenapiParams, reqEditors ...RequestEditorFn) (*V1GetDatabaseOpenapiResponse, error) { - rsp, err := c.V1GetDatabaseOpenapi(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetDatabaseOpenapiResponse(rsp) -} - -// V1UpdateDatabasePasswordWithBodyWithResponse request with arbitrary body returning *V1UpdateDatabasePasswordResponse -func (c *ClientWithResponses) V1UpdateDatabasePasswordWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateDatabasePasswordResponse, error) { - rsp, err := c.V1UpdateDatabasePasswordWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateDatabasePasswordResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateDatabasePasswordWithResponse(ctx context.Context, ref string, body V1UpdateDatabasePasswordJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateDatabasePasswordResponse, error) { - rsp, err := c.V1UpdateDatabasePassword(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateDatabasePasswordResponse(rsp) -} - -// V1RunAQueryWithBodyWithResponse request with arbitrary body returning *V1RunAQueryResponse -func (c *ClientWithResponses) V1RunAQueryWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1RunAQueryResponse, error) { - rsp, err := c.V1RunAQueryWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RunAQueryResponse(rsp) -} - -func (c *ClientWithResponses) V1RunAQueryWithResponse(ctx context.Context, ref string, body V1RunAQueryJSONRequestBody, reqEditors ...RequestEditorFn) (*V1RunAQueryResponse, error) { - rsp, err := c.V1RunAQuery(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RunAQueryResponse(rsp) -} - -// V1ReadOnlyQueryWithBodyWithResponse request with arbitrary body returning *V1ReadOnlyQueryResponse -func (c *ClientWithResponses) V1ReadOnlyQueryWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ReadOnlyQueryResponse, error) { - rsp, err := c.V1ReadOnlyQueryWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ReadOnlyQueryResponse(rsp) -} - -func (c *ClientWithResponses) V1ReadOnlyQueryWithResponse(ctx context.Context, ref string, body V1ReadOnlyQueryJSONRequestBody, reqEditors ...RequestEditorFn) (*V1ReadOnlyQueryResponse, error) { - rsp, err := c.V1ReadOnlyQuery(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ReadOnlyQueryResponse(rsp) -} - -// V1EnableDatabaseWebhookWithResponse request returning *V1EnableDatabaseWebhookResponse -func (c *ClientWithResponses) V1EnableDatabaseWebhookWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1EnableDatabaseWebhookResponse, error) { - rsp, err := c.V1EnableDatabaseWebhook(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1EnableDatabaseWebhookResponse(rsp) -} - -// V1ListAllFunctionsWithResponse request returning *V1ListAllFunctionsResponse -func (c *ClientWithResponses) V1ListAllFunctionsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllFunctionsResponse, error) { - rsp, err := c.V1ListAllFunctions(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListAllFunctionsResponse(rsp) -} - -// V1CreateAFunctionWithBodyWithResponse request with arbitrary body returning *V1CreateAFunctionResponse -func (c *ClientWithResponses) V1CreateAFunctionWithBodyWithResponse(ctx context.Context, ref string, params *V1CreateAFunctionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CreateAFunctionResponse, error) { - rsp, err := c.V1CreateAFunctionWithBody(ctx, ref, params, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateAFunctionResponse(rsp) -} - -func (c *ClientWithResponses) V1CreateAFunctionWithResponse(ctx context.Context, ref string, params *V1CreateAFunctionParams, body V1CreateAFunctionJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CreateAFunctionResponse, error) { - rsp, err := c.V1CreateAFunction(ctx, ref, params, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CreateAFunctionResponse(rsp) -} - -// V1BulkUpdateFunctionsWithBodyWithResponse request with arbitrary body returning *V1BulkUpdateFunctionsResponse -func (c *ClientWithResponses) V1BulkUpdateFunctionsWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1BulkUpdateFunctionsResponse, error) { - rsp, err := c.V1BulkUpdateFunctionsWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1BulkUpdateFunctionsResponse(rsp) -} - -func (c *ClientWithResponses) V1BulkUpdateFunctionsWithResponse(ctx context.Context, ref string, body V1BulkUpdateFunctionsJSONRequestBody, reqEditors ...RequestEditorFn) (*V1BulkUpdateFunctionsResponse, error) { - rsp, err := c.V1BulkUpdateFunctions(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1BulkUpdateFunctionsResponse(rsp) -} - -// V1DeployAFunctionWithBodyWithResponse request with arbitrary body returning *V1DeployAFunctionResponse -func (c *ClientWithResponses) V1DeployAFunctionWithBodyWithResponse(ctx context.Context, ref string, params *V1DeployAFunctionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1DeployAFunctionResponse, error) { - rsp, err := c.V1DeployAFunctionWithBody(ctx, ref, params, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeployAFunctionResponse(rsp) -} - -// V1DeleteAFunctionWithResponse request returning *V1DeleteAFunctionResponse -func (c *ClientWithResponses) V1DeleteAFunctionWithResponse(ctx context.Context, ref string, functionSlug string, reqEditors ...RequestEditorFn) (*V1DeleteAFunctionResponse, error) { - rsp, err := c.V1DeleteAFunction(ctx, ref, functionSlug, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeleteAFunctionResponse(rsp) -} - -// V1GetAFunctionWithResponse request returning *V1GetAFunctionResponse -func (c *ClientWithResponses) V1GetAFunctionWithResponse(ctx context.Context, ref string, functionSlug string, reqEditors ...RequestEditorFn) (*V1GetAFunctionResponse, error) { - rsp, err := c.V1GetAFunction(ctx, ref, functionSlug, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetAFunctionResponse(rsp) -} - -// V1UpdateAFunctionWithBodyWithResponse request with arbitrary body returning *V1UpdateAFunctionResponse -func (c *ClientWithResponses) V1UpdateAFunctionWithBodyWithResponse(ctx context.Context, ref string, functionSlug string, params *V1UpdateAFunctionParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateAFunctionResponse, error) { - rsp, err := c.V1UpdateAFunctionWithBody(ctx, ref, functionSlug, params, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateAFunctionResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateAFunctionWithResponse(ctx context.Context, ref string, functionSlug string, params *V1UpdateAFunctionParams, body V1UpdateAFunctionJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateAFunctionResponse, error) { - rsp, err := c.V1UpdateAFunction(ctx, ref, functionSlug, params, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateAFunctionResponse(rsp) -} - -// V1GetAFunctionBodyWithResponse request returning *V1GetAFunctionBodyResponse -func (c *ClientWithResponses) V1GetAFunctionBodyWithResponse(ctx context.Context, ref string, functionSlug string, reqEditors ...RequestEditorFn) (*V1GetAFunctionBodyResponse, error) { - rsp, err := c.V1GetAFunctionBody(ctx, ref, functionSlug, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetAFunctionBodyResponse(rsp) -} - -// V1GetServicesHealthWithResponse request returning *V1GetServicesHealthResponse -func (c *ClientWithResponses) V1GetServicesHealthWithResponse(ctx context.Context, ref string, params *V1GetServicesHealthParams, reqEditors ...RequestEditorFn) (*V1GetServicesHealthResponse, error) { - rsp, err := c.V1GetServicesHealth(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetServicesHealthResponse(rsp) -} - -// V1GetJitAccessConfigWithResponse request returning *V1GetJitAccessConfigResponse -func (c *ClientWithResponses) V1GetJitAccessConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetJitAccessConfigResponse, error) { - rsp, err := c.V1GetJitAccessConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetJitAccessConfigResponse(rsp) -} - -// V1UpdateJitAccessConfigWithBodyWithResponse request with arbitrary body returning *V1UpdateJitAccessConfigResponse -func (c *ClientWithResponses) V1UpdateJitAccessConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateJitAccessConfigResponse, error) { - rsp, err := c.V1UpdateJitAccessConfigWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateJitAccessConfigResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateJitAccessConfigWithResponse(ctx context.Context, ref string, body V1UpdateJitAccessConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateJitAccessConfigResponse, error) { - rsp, err := c.V1UpdateJitAccessConfig(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateJitAccessConfigResponse(rsp) -} - -// V1DeleteNetworkBansWithBodyWithResponse request with arbitrary body returning *V1DeleteNetworkBansResponse -func (c *ClientWithResponses) V1DeleteNetworkBansWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1DeleteNetworkBansResponse, error) { - rsp, err := c.V1DeleteNetworkBansWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeleteNetworkBansResponse(rsp) -} - -func (c *ClientWithResponses) V1DeleteNetworkBansWithResponse(ctx context.Context, ref string, body V1DeleteNetworkBansJSONRequestBody, reqEditors ...RequestEditorFn) (*V1DeleteNetworkBansResponse, error) { - rsp, err := c.V1DeleteNetworkBans(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeleteNetworkBansResponse(rsp) -} - -// V1ListAllNetworkBansWithResponse request returning *V1ListAllNetworkBansResponse -func (c *ClientWithResponses) V1ListAllNetworkBansWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllNetworkBansResponse, error) { - rsp, err := c.V1ListAllNetworkBans(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListAllNetworkBansResponse(rsp) -} - -// V1ListAllNetworkBansEnrichedWithResponse request returning *V1ListAllNetworkBansEnrichedResponse -func (c *ClientWithResponses) V1ListAllNetworkBansEnrichedWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllNetworkBansEnrichedResponse, error) { - rsp, err := c.V1ListAllNetworkBansEnriched(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListAllNetworkBansEnrichedResponse(rsp) -} - -// V1GetNetworkRestrictionsWithResponse request returning *V1GetNetworkRestrictionsResponse -func (c *ClientWithResponses) V1GetNetworkRestrictionsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetNetworkRestrictionsResponse, error) { - rsp, err := c.V1GetNetworkRestrictions(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetNetworkRestrictionsResponse(rsp) -} - -// V1PatchNetworkRestrictionsWithBodyWithResponse request with arbitrary body returning *V1PatchNetworkRestrictionsResponse -func (c *ClientWithResponses) V1PatchNetworkRestrictionsWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1PatchNetworkRestrictionsResponse, error) { - rsp, err := c.V1PatchNetworkRestrictionsWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1PatchNetworkRestrictionsResponse(rsp) -} - -func (c *ClientWithResponses) V1PatchNetworkRestrictionsWithResponse(ctx context.Context, ref string, body V1PatchNetworkRestrictionsJSONRequestBody, reqEditors ...RequestEditorFn) (*V1PatchNetworkRestrictionsResponse, error) { - rsp, err := c.V1PatchNetworkRestrictions(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1PatchNetworkRestrictionsResponse(rsp) -} - -// V1UpdateNetworkRestrictionsWithBodyWithResponse request with arbitrary body returning *V1UpdateNetworkRestrictionsResponse -func (c *ClientWithResponses) V1UpdateNetworkRestrictionsWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateNetworkRestrictionsResponse, error) { - rsp, err := c.V1UpdateNetworkRestrictionsWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateNetworkRestrictionsResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateNetworkRestrictionsWithResponse(ctx context.Context, ref string, body V1UpdateNetworkRestrictionsJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateNetworkRestrictionsResponse, error) { - rsp, err := c.V1UpdateNetworkRestrictions(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateNetworkRestrictionsResponse(rsp) -} - -// V1PauseAProjectWithResponse request returning *V1PauseAProjectResponse -func (c *ClientWithResponses) V1PauseAProjectWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1PauseAProjectResponse, error) { - rsp, err := c.V1PauseAProject(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1PauseAProjectResponse(rsp) -} - -// V1GetPgsodiumConfigWithResponse request returning *V1GetPgsodiumConfigResponse -func (c *ClientWithResponses) V1GetPgsodiumConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetPgsodiumConfigResponse, error) { - rsp, err := c.V1GetPgsodiumConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetPgsodiumConfigResponse(rsp) -} - -// V1UpdatePgsodiumConfigWithBodyWithResponse request with arbitrary body returning *V1UpdatePgsodiumConfigResponse -func (c *ClientWithResponses) V1UpdatePgsodiumConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdatePgsodiumConfigResponse, error) { - rsp, err := c.V1UpdatePgsodiumConfigWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdatePgsodiumConfigResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdatePgsodiumConfigWithResponse(ctx context.Context, ref string, body V1UpdatePgsodiumConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdatePgsodiumConfigResponse, error) { - rsp, err := c.V1UpdatePgsodiumConfig(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdatePgsodiumConfigResponse(rsp) -} - -// V1GetPostgrestServiceConfigWithResponse request returning *V1GetPostgrestServiceConfigResponse -func (c *ClientWithResponses) V1GetPostgrestServiceConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetPostgrestServiceConfigResponse, error) { - rsp, err := c.V1GetPostgrestServiceConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetPostgrestServiceConfigResponse(rsp) -} - -// V1UpdatePostgrestServiceConfigWithBodyWithResponse request with arbitrary body returning *V1UpdatePostgrestServiceConfigResponse -func (c *ClientWithResponses) V1UpdatePostgrestServiceConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdatePostgrestServiceConfigResponse, error) { - rsp, err := c.V1UpdatePostgrestServiceConfigWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdatePostgrestServiceConfigResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdatePostgrestServiceConfigWithResponse(ctx context.Context, ref string, body V1UpdatePostgrestServiceConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdatePostgrestServiceConfigResponse, error) { - rsp, err := c.V1UpdatePostgrestServiceConfig(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdatePostgrestServiceConfigResponse(rsp) -} - -// V1RemoveAReadReplicaWithBodyWithResponse request with arbitrary body returning *V1RemoveAReadReplicaResponse -func (c *ClientWithResponses) V1RemoveAReadReplicaWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1RemoveAReadReplicaResponse, error) { - rsp, err := c.V1RemoveAReadReplicaWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RemoveAReadReplicaResponse(rsp) -} - -func (c *ClientWithResponses) V1RemoveAReadReplicaWithResponse(ctx context.Context, ref string, body V1RemoveAReadReplicaJSONRequestBody, reqEditors ...RequestEditorFn) (*V1RemoveAReadReplicaResponse, error) { - rsp, err := c.V1RemoveAReadReplica(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RemoveAReadReplicaResponse(rsp) -} - -// V1SetupAReadReplicaWithBodyWithResponse request with arbitrary body returning *V1SetupAReadReplicaResponse -func (c *ClientWithResponses) V1SetupAReadReplicaWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1SetupAReadReplicaResponse, error) { - rsp, err := c.V1SetupAReadReplicaWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1SetupAReadReplicaResponse(rsp) -} - -func (c *ClientWithResponses) V1SetupAReadReplicaWithResponse(ctx context.Context, ref string, body V1SetupAReadReplicaJSONRequestBody, reqEditors ...RequestEditorFn) (*V1SetupAReadReplicaResponse, error) { - rsp, err := c.V1SetupAReadReplica(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1SetupAReadReplicaResponse(rsp) -} - -// V1GetReadonlyModeStatusWithResponse request returning *V1GetReadonlyModeStatusResponse -func (c *ClientWithResponses) V1GetReadonlyModeStatusWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetReadonlyModeStatusResponse, error) { - rsp, err := c.V1GetReadonlyModeStatus(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetReadonlyModeStatusResponse(rsp) -} - -// V1DisableReadonlyModeTemporarilyWithResponse request returning *V1DisableReadonlyModeTemporarilyResponse -func (c *ClientWithResponses) V1DisableReadonlyModeTemporarilyWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1DisableReadonlyModeTemporarilyResponse, error) { - rsp, err := c.V1DisableReadonlyModeTemporarily(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DisableReadonlyModeTemporarilyResponse(rsp) -} - -// V1RestartAProjectWithResponse request returning *V1RestartAProjectResponse -func (c *ClientWithResponses) V1RestartAProjectWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1RestartAProjectResponse, error) { - rsp, err := c.V1RestartAProject(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RestartAProjectResponse(rsp) -} - -// V1ListAvailableRestoreVersionsWithResponse request returning *V1ListAvailableRestoreVersionsResponse -func (c *ClientWithResponses) V1ListAvailableRestoreVersionsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAvailableRestoreVersionsResponse, error) { - rsp, err := c.V1ListAvailableRestoreVersions(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListAvailableRestoreVersionsResponse(rsp) -} - -// V1RestoreAProjectWithResponse request returning *V1RestoreAProjectResponse -func (c *ClientWithResponses) V1RestoreAProjectWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1RestoreAProjectResponse, error) { - rsp, err := c.V1RestoreAProject(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1RestoreAProjectResponse(rsp) -} - -// V1CancelAProjectRestorationWithResponse request returning *V1CancelAProjectRestorationResponse -func (c *ClientWithResponses) V1CancelAProjectRestorationWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1CancelAProjectRestorationResponse, error) { - rsp, err := c.V1CancelAProjectRestoration(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CancelAProjectRestorationResponse(rsp) -} - -// V1BulkDeleteSecretsWithBodyWithResponse request with arbitrary body returning *V1BulkDeleteSecretsResponse -func (c *ClientWithResponses) V1BulkDeleteSecretsWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1BulkDeleteSecretsResponse, error) { - rsp, err := c.V1BulkDeleteSecretsWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1BulkDeleteSecretsResponse(rsp) -} - -func (c *ClientWithResponses) V1BulkDeleteSecretsWithResponse(ctx context.Context, ref string, body V1BulkDeleteSecretsJSONRequestBody, reqEditors ...RequestEditorFn) (*V1BulkDeleteSecretsResponse, error) { - rsp, err := c.V1BulkDeleteSecrets(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1BulkDeleteSecretsResponse(rsp) -} - -// V1ListAllSecretsWithResponse request returning *V1ListAllSecretsResponse -func (c *ClientWithResponses) V1ListAllSecretsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllSecretsResponse, error) { - rsp, err := c.V1ListAllSecrets(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListAllSecretsResponse(rsp) -} - -// V1BulkCreateSecretsWithBodyWithResponse request with arbitrary body returning *V1BulkCreateSecretsResponse -func (c *ClientWithResponses) V1BulkCreateSecretsWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1BulkCreateSecretsResponse, error) { - rsp, err := c.V1BulkCreateSecretsWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1BulkCreateSecretsResponse(rsp) -} - -func (c *ClientWithResponses) V1BulkCreateSecretsWithResponse(ctx context.Context, ref string, body V1BulkCreateSecretsJSONRequestBody, reqEditors ...RequestEditorFn) (*V1BulkCreateSecretsResponse, error) { - rsp, err := c.V1BulkCreateSecrets(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1BulkCreateSecretsResponse(rsp) -} - -// V1GetSslEnforcementConfigWithResponse request returning *V1GetSslEnforcementConfigResponse -func (c *ClientWithResponses) V1GetSslEnforcementConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetSslEnforcementConfigResponse, error) { - rsp, err := c.V1GetSslEnforcementConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetSslEnforcementConfigResponse(rsp) -} - -// V1UpdateSslEnforcementConfigWithBodyWithResponse request with arbitrary body returning *V1UpdateSslEnforcementConfigResponse -func (c *ClientWithResponses) V1UpdateSslEnforcementConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpdateSslEnforcementConfigResponse, error) { - rsp, err := c.V1UpdateSslEnforcementConfigWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateSslEnforcementConfigResponse(rsp) -} - -func (c *ClientWithResponses) V1UpdateSslEnforcementConfigWithResponse(ctx context.Context, ref string, body V1UpdateSslEnforcementConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpdateSslEnforcementConfigResponse, error) { - rsp, err := c.V1UpdateSslEnforcementConfig(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpdateSslEnforcementConfigResponse(rsp) -} - -// V1ListAllBucketsWithResponse request returning *V1ListAllBucketsResponse -func (c *ClientWithResponses) V1ListAllBucketsWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1ListAllBucketsResponse, error) { - rsp, err := c.V1ListAllBuckets(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListAllBucketsResponse(rsp) -} - -// V1GenerateTypescriptTypesWithResponse request returning *V1GenerateTypescriptTypesResponse -func (c *ClientWithResponses) V1GenerateTypescriptTypesWithResponse(ctx context.Context, ref string, params *V1GenerateTypescriptTypesParams, reqEditors ...RequestEditorFn) (*V1GenerateTypescriptTypesResponse, error) { - rsp, err := c.V1GenerateTypescriptTypes(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GenerateTypescriptTypesResponse(rsp) -} - -// V1UpgradePostgresVersionWithBodyWithResponse request with arbitrary body returning *V1UpgradePostgresVersionResponse -func (c *ClientWithResponses) V1UpgradePostgresVersionWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1UpgradePostgresVersionResponse, error) { - rsp, err := c.V1UpgradePostgresVersionWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpgradePostgresVersionResponse(rsp) -} - -func (c *ClientWithResponses) V1UpgradePostgresVersionWithResponse(ctx context.Context, ref string, body V1UpgradePostgresVersionJSONRequestBody, reqEditors ...RequestEditorFn) (*V1UpgradePostgresVersionResponse, error) { - rsp, err := c.V1UpgradePostgresVersion(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1UpgradePostgresVersionResponse(rsp) -} - -// V1GetPostgresUpgradeEligibilityWithResponse request returning *V1GetPostgresUpgradeEligibilityResponse -func (c *ClientWithResponses) V1GetPostgresUpgradeEligibilityWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetPostgresUpgradeEligibilityResponse, error) { - rsp, err := c.V1GetPostgresUpgradeEligibility(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetPostgresUpgradeEligibilityResponse(rsp) -} - -// V1GetPostgresUpgradeStatusWithResponse request returning *V1GetPostgresUpgradeStatusResponse -func (c *ClientWithResponses) V1GetPostgresUpgradeStatusWithResponse(ctx context.Context, ref string, params *V1GetPostgresUpgradeStatusParams, reqEditors ...RequestEditorFn) (*V1GetPostgresUpgradeStatusResponse, error) { - rsp, err := c.V1GetPostgresUpgradeStatus(ctx, ref, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetPostgresUpgradeStatusResponse(rsp) -} - -// V1DeactivateVanitySubdomainConfigWithResponse request returning *V1DeactivateVanitySubdomainConfigResponse -func (c *ClientWithResponses) V1DeactivateVanitySubdomainConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1DeactivateVanitySubdomainConfigResponse, error) { - rsp, err := c.V1DeactivateVanitySubdomainConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1DeactivateVanitySubdomainConfigResponse(rsp) -} - -// V1GetVanitySubdomainConfigWithResponse request returning *V1GetVanitySubdomainConfigResponse -func (c *ClientWithResponses) V1GetVanitySubdomainConfigWithResponse(ctx context.Context, ref string, reqEditors ...RequestEditorFn) (*V1GetVanitySubdomainConfigResponse, error) { - rsp, err := c.V1GetVanitySubdomainConfig(ctx, ref, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetVanitySubdomainConfigResponse(rsp) -} - -// V1ActivateVanitySubdomainConfigWithBodyWithResponse request with arbitrary body returning *V1ActivateVanitySubdomainConfigResponse -func (c *ClientWithResponses) V1ActivateVanitySubdomainConfigWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1ActivateVanitySubdomainConfigResponse, error) { - rsp, err := c.V1ActivateVanitySubdomainConfigWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ActivateVanitySubdomainConfigResponse(rsp) -} - -func (c *ClientWithResponses) V1ActivateVanitySubdomainConfigWithResponse(ctx context.Context, ref string, body V1ActivateVanitySubdomainConfigJSONRequestBody, reqEditors ...RequestEditorFn) (*V1ActivateVanitySubdomainConfigResponse, error) { - rsp, err := c.V1ActivateVanitySubdomainConfig(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ActivateVanitySubdomainConfigResponse(rsp) -} - -// V1CheckVanitySubdomainAvailabilityWithBodyWithResponse request with arbitrary body returning *V1CheckVanitySubdomainAvailabilityResponse -func (c *ClientWithResponses) V1CheckVanitySubdomainAvailabilityWithBodyWithResponse(ctx context.Context, ref string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*V1CheckVanitySubdomainAvailabilityResponse, error) { - rsp, err := c.V1CheckVanitySubdomainAvailabilityWithBody(ctx, ref, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CheckVanitySubdomainAvailabilityResponse(rsp) -} - -func (c *ClientWithResponses) V1CheckVanitySubdomainAvailabilityWithResponse(ctx context.Context, ref string, body V1CheckVanitySubdomainAvailabilityJSONRequestBody, reqEditors ...RequestEditorFn) (*V1CheckVanitySubdomainAvailabilityResponse, error) { - rsp, err := c.V1CheckVanitySubdomainAvailability(ctx, ref, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1CheckVanitySubdomainAvailabilityResponse(rsp) -} - -// V1ListAllSnippetsWithResponse request returning *V1ListAllSnippetsResponse -func (c *ClientWithResponses) V1ListAllSnippetsWithResponse(ctx context.Context, params *V1ListAllSnippetsParams, reqEditors ...RequestEditorFn) (*V1ListAllSnippetsResponse, error) { - rsp, err := c.V1ListAllSnippets(ctx, params, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1ListAllSnippetsResponse(rsp) -} - -// V1GetASnippetWithResponse request returning *V1GetASnippetResponse -func (c *ClientWithResponses) V1GetASnippetWithResponse(ctx context.Context, id openapi_types.UUID, reqEditors ...RequestEditorFn) (*V1GetASnippetResponse, error) { - rsp, err := c.V1GetASnippet(ctx, id, reqEditors...) - if err != nil { - return nil, err - } - return ParseV1GetASnippetResponse(rsp) -} - -// ParseV1DeleteABranchResponse parses an HTTP response from a V1DeleteABranchWithResponse call -func ParseV1DeleteABranchResponse(rsp *http.Response) (*V1DeleteABranchResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeleteABranchResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest BranchDeleteResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetABranchConfigResponse parses an HTTP response from a V1GetABranchConfigWithResponse call -func ParseV1GetABranchConfigResponse(rsp *http.Response) (*V1GetABranchConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetABranchConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest BranchDetailResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateABranchConfigResponse parses an HTTP response from a V1UpdateABranchConfigWithResponse call -func ParseV1UpdateABranchConfigResponse(rsp *http.Response) (*V1UpdateABranchConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateABranchConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest BranchResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1DiffABranchResponse parses an HTTP response from a V1DiffABranchWithResponse call -func ParseV1DiffABranchResponse(rsp *http.Response) (*V1DiffABranchResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DiffABranchResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1MergeABranchResponse parses an HTTP response from a V1MergeABranchWithResponse call -func ParseV1MergeABranchResponse(rsp *http.Response) (*V1MergeABranchResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1MergeABranchResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest BranchUpdateResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1PushABranchResponse parses an HTTP response from a V1PushABranchWithResponse call -func ParseV1PushABranchResponse(rsp *http.Response) (*V1PushABranchResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1PushABranchResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest BranchUpdateResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1ResetABranchResponse parses an HTTP response from a V1ResetABranchWithResponse call -func ParseV1ResetABranchResponse(rsp *http.Response) (*V1ResetABranchResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ResetABranchResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest BranchUpdateResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1RestoreABranchResponse parses an HTTP response from a V1RestoreABranchWithResponse call -func ParseV1RestoreABranchResponse(rsp *http.Response) (*V1RestoreABranchResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1RestoreABranchResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest BranchRestoreResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1AuthorizeUserResponse parses an HTTP response from a V1AuthorizeUserWithResponse call -func ParseV1AuthorizeUserResponse(rsp *http.Response) (*V1AuthorizeUserResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1AuthorizeUserResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1OauthAuthorizeProjectClaimResponse parses an HTTP response from a V1OauthAuthorizeProjectClaimWithResponse call -func ParseV1OauthAuthorizeProjectClaimResponse(rsp *http.Response) (*V1OauthAuthorizeProjectClaimResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1OauthAuthorizeProjectClaimResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1RevokeTokenResponse parses an HTTP response from a V1RevokeTokenWithResponse call -func ParseV1RevokeTokenResponse(rsp *http.Response) (*V1RevokeTokenResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1RevokeTokenResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1ExchangeOauthTokenResponse parses an HTTP response from a V1ExchangeOauthTokenWithResponse call -func ParseV1ExchangeOauthTokenResponse(rsp *http.Response) (*V1ExchangeOauthTokenResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ExchangeOauthTokenResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest OAuthTokenResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1ListAllOrganizationsResponse parses an HTTP response from a V1ListAllOrganizationsWithResponse call -func ParseV1ListAllOrganizationsResponse(rsp *http.Response) (*V1ListAllOrganizationsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListAllOrganizationsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest []OrganizationResponseV1Output - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1CreateAnOrganizationResponse parses an HTTP response from a V1CreateAnOrganizationWithResponse call -func ParseV1CreateAnOrganizationResponse(rsp *http.Response) (*V1CreateAnOrganizationResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CreateAnOrganizationResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest OrganizationResponseV1Output - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1GetAnOrganizationResponse parses an HTTP response from a V1GetAnOrganizationWithResponse call -func ParseV1GetAnOrganizationResponse(rsp *http.Response) (*V1GetAnOrganizationResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetAnOrganizationResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1OrganizationSlugResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetOrganizationEntitlementsResponse parses an HTTP response from a V1GetOrganizationEntitlementsWithResponse call -func ParseV1GetOrganizationEntitlementsResponse(rsp *http.Response) (*V1GetOrganizationEntitlementsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetOrganizationEntitlementsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1ListEntitlementsResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1ListOrganizationMembersResponse parses an HTTP response from a V1ListOrganizationMembersWithResponse call -func ParseV1ListOrganizationMembersResponse(rsp *http.Response) (*V1ListOrganizationMembersResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListOrganizationMembersResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest []V1OrganizationMemberResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetOrganizationProjectClaimResponse parses an HTTP response from a V1GetOrganizationProjectClaimWithResponse call -func ParseV1GetOrganizationProjectClaimResponse(rsp *http.Response) (*V1GetOrganizationProjectClaimResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetOrganizationProjectClaimResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest OrganizationProjectClaimResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1ClaimProjectForOrganizationResponse parses an HTTP response from a V1ClaimProjectForOrganizationWithResponse call -func ParseV1ClaimProjectForOrganizationResponse(rsp *http.Response) (*V1ClaimProjectForOrganizationResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ClaimProjectForOrganizationResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetAllProjectsForOrganizationResponse parses an HTTP response from a V1GetAllProjectsForOrganizationWithResponse call -func ParseV1GetAllProjectsForOrganizationResponse(rsp *http.Response) (*V1GetAllProjectsForOrganizationResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetAllProjectsForOrganizationResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest OrganizationProjectsResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetProfileResponse parses an HTTP response from a V1GetProfileWithResponse call -func ParseV1GetProfileResponse(rsp *http.Response) (*V1GetProfileResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProfileResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1ProfileResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1ListAllProjectsResponse parses an HTTP response from a V1ListAllProjectsWithResponse call -func ParseV1ListAllProjectsResponse(rsp *http.Response) (*V1ListAllProjectsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListAllProjectsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest []V1ProjectWithDatabaseResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1CreateAProjectResponse parses an HTTP response from a V1CreateAProjectWithResponse call -func ParseV1CreateAProjectResponse(rsp *http.Response) (*V1CreateAProjectResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CreateAProjectResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest V1ProjectResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1GetAvailableRegionsResponse parses an HTTP response from a V1GetAvailableRegionsWithResponse call -func ParseV1GetAvailableRegionsResponse(rsp *http.Response) (*V1GetAvailableRegionsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetAvailableRegionsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest RegionsInfoOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1DeleteAProjectResponse parses an HTTP response from a V1DeleteAProjectWithResponse call -func ParseV1DeleteAProjectResponse(rsp *http.Response) (*V1DeleteAProjectResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeleteAProjectResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1ProjectRefResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetProjectResponse parses an HTTP response from a V1GetProjectWithResponse call -func ParseV1GetProjectResponse(rsp *http.Response) (*V1GetProjectResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1ProjectWithDatabaseResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateAProjectResponse parses an HTTP response from a V1UpdateAProjectWithResponse call -func ParseV1UpdateAProjectResponse(rsp *http.Response) (*V1UpdateAProjectResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateAProjectResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1ProjectRefResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1ListActionRunsResponse parses an HTTP response from a V1ListActionRunsWithResponse call -func ParseV1ListActionRunsResponse(rsp *http.Response) (*V1ListActionRunsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListActionRunsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListActionRunResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1CountActionRunsResponse parses an HTTP response from a V1CountActionRunsWithResponse call -func ParseV1CountActionRunsResponse(rsp *http.Response) (*V1CountActionRunsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CountActionRunsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetActionRunResponse parses an HTTP response from a V1GetActionRunWithResponse call -func ParseV1GetActionRunResponse(rsp *http.Response) (*V1GetActionRunResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetActionRunResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ActionRunResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetActionRunLogsResponse parses an HTTP response from a V1GetActionRunLogsWithResponse call -func ParseV1GetActionRunLogsResponse(rsp *http.Response) (*V1GetActionRunLogsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetActionRunLogsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1UpdateActionRunStatusResponse parses an HTTP response from a V1UpdateActionRunStatusWithResponse call -func ParseV1UpdateActionRunStatusResponse(rsp *http.Response) (*V1UpdateActionRunStatusResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateActionRunStatusResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateRunStatusResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetPerformanceAdvisorsResponse parses an HTTP response from a V1GetPerformanceAdvisorsWithResponse call -func ParseV1GetPerformanceAdvisorsResponse(rsp *http.Response) (*V1GetPerformanceAdvisorsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetPerformanceAdvisorsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1ProjectAdvisorsResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetSecurityAdvisorsResponse parses an HTTP response from a V1GetSecurityAdvisorsWithResponse call -func ParseV1GetSecurityAdvisorsResponse(rsp *http.Response) (*V1GetSecurityAdvisorsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetSecurityAdvisorsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1ProjectAdvisorsResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetProjectFunctionCombinedStatsResponse parses an HTTP response from a V1GetProjectFunctionCombinedStatsWithResponse call -func ParseV1GetProjectFunctionCombinedStatsResponse(rsp *http.Response) (*V1GetProjectFunctionCombinedStatsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectFunctionCombinedStatsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest AnalyticsResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetProjectLogsResponse parses an HTTP response from a V1GetProjectLogsWithResponse call -func ParseV1GetProjectLogsResponse(rsp *http.Response) (*V1GetProjectLogsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectLogsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest AnalyticsResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetProjectLogsAllResponse parses an HTTP response from a V1GetProjectLogsAllWithResponse call -func ParseV1GetProjectLogsAllResponse(rsp *http.Response) (*V1GetProjectLogsAllResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectLogsAllResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1ScrapeProjectMetricsResponse parses an HTTP response from a V1ScrapeProjectMetricsWithResponse call -func ParseV1ScrapeProjectMetricsResponse(rsp *http.Response) (*V1ScrapeProjectMetricsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ScrapeProjectMetricsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetProjectUsageApiCountResponse parses an HTTP response from a V1GetProjectUsageApiCountWithResponse call -func ParseV1GetProjectUsageApiCountResponse(rsp *http.Response) (*V1GetProjectUsageApiCountResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectUsageApiCountResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1GetUsageApiCountResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetProjectUsageRequestCountResponse parses an HTTP response from a V1GetProjectUsageRequestCountWithResponse call -func ParseV1GetProjectUsageRequestCountResponse(rsp *http.Response) (*V1GetProjectUsageRequestCountResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectUsageRequestCountResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1GetUsageApiRequestsCountResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetProjectApiKeysResponse parses an HTTP response from a V1GetProjectApiKeysWithResponse call -func ParseV1GetProjectApiKeysResponse(rsp *http.Response) (*V1GetProjectApiKeysResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectApiKeysResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest []ApiKeyResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1CreateProjectApiKeyResponse parses an HTTP response from a V1CreateProjectApiKeyWithResponse call -func ParseV1CreateProjectApiKeyResponse(rsp *http.Response) (*V1CreateProjectApiKeyResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CreateProjectApiKeyResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest ApiKeyResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1GetProjectLegacyApiKeysResponse parses an HTTP response from a V1GetProjectLegacyApiKeysWithResponse call -func ParseV1GetProjectLegacyApiKeysResponse(rsp *http.Response) (*V1GetProjectLegacyApiKeysResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectLegacyApiKeysResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest LegacyApiKeysResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateProjectLegacyApiKeysResponse parses an HTTP response from a V1UpdateProjectLegacyApiKeysWithResponse call -func ParseV1UpdateProjectLegacyApiKeysResponse(rsp *http.Response) (*V1UpdateProjectLegacyApiKeysResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateProjectLegacyApiKeysResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest LegacyApiKeysResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1DeleteProjectApiKeyResponse parses an HTTP response from a V1DeleteProjectApiKeyWithResponse call -func ParseV1DeleteProjectApiKeyResponse(rsp *http.Response) (*V1DeleteProjectApiKeyResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeleteProjectApiKeyResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ApiKeyResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetProjectApiKeyResponse parses an HTTP response from a V1GetProjectApiKeyWithResponse call -func ParseV1GetProjectApiKeyResponse(rsp *http.Response) (*V1GetProjectApiKeyResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectApiKeyResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ApiKeyResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateProjectApiKeyResponse parses an HTTP response from a V1UpdateProjectApiKeyWithResponse call -func ParseV1UpdateProjectApiKeyResponse(rsp *http.Response) (*V1UpdateProjectApiKeyResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateProjectApiKeyResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ApiKeyResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1ListProjectAddonsResponse parses an HTTP response from a V1ListProjectAddonsWithResponse call -func ParseV1ListProjectAddonsResponse(rsp *http.Response) (*V1ListProjectAddonsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListProjectAddonsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListProjectAddonsResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1ApplyProjectAddonResponse parses an HTTP response from a V1ApplyProjectAddonWithResponse call -func ParseV1ApplyProjectAddonResponse(rsp *http.Response) (*V1ApplyProjectAddonResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ApplyProjectAddonResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1RemoveProjectAddonResponse parses an HTTP response from a V1RemoveProjectAddonWithResponse call -func ParseV1RemoveProjectAddonResponse(rsp *http.Response) (*V1RemoveProjectAddonResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1RemoveProjectAddonResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1DisablePreviewBranchingResponse parses an HTTP response from a V1DisablePreviewBranchingWithResponse call -func ParseV1DisablePreviewBranchingResponse(rsp *http.Response) (*V1DisablePreviewBranchingResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DisablePreviewBranchingResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1ListAllBranchesResponse parses an HTTP response from a V1ListAllBranchesWithResponse call -func ParseV1ListAllBranchesResponse(rsp *http.Response) (*V1ListAllBranchesResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListAllBranchesResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest []BranchResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1CreateABranchResponse parses an HTTP response from a V1CreateABranchWithResponse call -func ParseV1CreateABranchResponse(rsp *http.Response) (*V1CreateABranchResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CreateABranchResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest BranchResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1GetABranchResponse parses an HTTP response from a V1GetABranchWithResponse call -func ParseV1GetABranchResponse(rsp *http.Response) (*V1GetABranchResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetABranchResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest BranchResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1DeleteProjectClaimTokenResponse parses an HTTP response from a V1DeleteProjectClaimTokenWithResponse call -func ParseV1DeleteProjectClaimTokenResponse(rsp *http.Response) (*V1DeleteProjectClaimTokenResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeleteProjectClaimTokenResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetProjectClaimTokenResponse parses an HTTP response from a V1GetProjectClaimTokenWithResponse call -func ParseV1GetProjectClaimTokenResponse(rsp *http.Response) (*V1GetProjectClaimTokenResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectClaimTokenResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ProjectClaimTokenResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1CreateProjectClaimTokenResponse parses an HTTP response from a V1CreateProjectClaimTokenWithResponse call -func ParseV1CreateProjectClaimTokenResponse(rsp *http.Response) (*V1CreateProjectClaimTokenResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CreateProjectClaimTokenResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest CreateProjectClaimTokenResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1DeleteLoginRolesResponse parses an HTTP response from a V1DeleteLoginRolesWithResponse call -func ParseV1DeleteLoginRolesResponse(rsp *http.Response) (*V1DeleteLoginRolesResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeleteLoginRolesResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest DeleteRolesResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1CreateLoginRoleResponse parses an HTTP response from a V1CreateLoginRoleWithResponse call -func ParseV1CreateLoginRoleResponse(rsp *http.Response) (*V1CreateLoginRoleResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CreateLoginRoleResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CreateRoleResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1GetAuthServiceConfigResponse parses an HTTP response from a V1GetAuthServiceConfigWithResponse call -func ParseV1GetAuthServiceConfigResponse(rsp *http.Response) (*V1GetAuthServiceConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetAuthServiceConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest AuthConfigResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateAuthServiceConfigResponse parses an HTTP response from a V1UpdateAuthServiceConfigWithResponse call -func ParseV1UpdateAuthServiceConfigResponse(rsp *http.Response) (*V1UpdateAuthServiceConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateAuthServiceConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest AuthConfigResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetProjectSigningKeysResponse parses an HTTP response from a V1GetProjectSigningKeysWithResponse call -func ParseV1GetProjectSigningKeysResponse(rsp *http.Response) (*V1GetProjectSigningKeysResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectSigningKeysResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest SigningKeysResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1CreateProjectSigningKeyResponse parses an HTTP response from a V1CreateProjectSigningKeyWithResponse call -func ParseV1CreateProjectSigningKeyResponse(rsp *http.Response) (*V1CreateProjectSigningKeyResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CreateProjectSigningKeyResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest SigningKeyResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1GetLegacySigningKeyResponse parses an HTTP response from a V1GetLegacySigningKeyWithResponse call -func ParseV1GetLegacySigningKeyResponse(rsp *http.Response) (*V1GetLegacySigningKeyResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetLegacySigningKeyResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest SigningKeyResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1CreateLegacySigningKeyResponse parses an HTTP response from a V1CreateLegacySigningKeyWithResponse call -func ParseV1CreateLegacySigningKeyResponse(rsp *http.Response) (*V1CreateLegacySigningKeyResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CreateLegacySigningKeyResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest SigningKeyResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1RemoveProjectSigningKeyResponse parses an HTTP response from a V1RemoveProjectSigningKeyWithResponse call -func ParseV1RemoveProjectSigningKeyResponse(rsp *http.Response) (*V1RemoveProjectSigningKeyResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1RemoveProjectSigningKeyResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest SigningKeyResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetProjectSigningKeyResponse parses an HTTP response from a V1GetProjectSigningKeyWithResponse call -func ParseV1GetProjectSigningKeyResponse(rsp *http.Response) (*V1GetProjectSigningKeyResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectSigningKeyResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest SigningKeyResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateProjectSigningKeyResponse parses an HTTP response from a V1UpdateProjectSigningKeyWithResponse call -func ParseV1UpdateProjectSigningKeyResponse(rsp *http.Response) (*V1UpdateProjectSigningKeyResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateProjectSigningKeyResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest SigningKeyResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1ListAllSsoProviderResponse parses an HTTP response from a V1ListAllSsoProviderWithResponse call -func ParseV1ListAllSsoProviderResponse(rsp *http.Response) (*V1ListAllSsoProviderResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListAllSsoProviderResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ListProvidersResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1CreateASsoProviderResponse parses an HTTP response from a V1CreateASsoProviderWithResponse call -func ParseV1CreateASsoProviderResponse(rsp *http.Response) (*V1CreateASsoProviderResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CreateASsoProviderResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest CreateProviderResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1DeleteASsoProviderResponse parses an HTTP response from a V1DeleteASsoProviderWithResponse call -func ParseV1DeleteASsoProviderResponse(rsp *http.Response) (*V1DeleteASsoProviderResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeleteASsoProviderResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest DeleteProviderResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetASsoProviderResponse parses an HTTP response from a V1GetASsoProviderWithResponse call -func ParseV1GetASsoProviderResponse(rsp *http.Response) (*V1GetASsoProviderResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetASsoProviderResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetProviderResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateASsoProviderResponse parses an HTTP response from a V1UpdateASsoProviderWithResponse call -func ParseV1UpdateASsoProviderResponse(rsp *http.Response) (*V1UpdateASsoProviderResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateASsoProviderResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateProviderResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1ListProjectTpaIntegrationsResponse parses an HTTP response from a V1ListProjectTpaIntegrationsWithResponse call -func ParseV1ListProjectTpaIntegrationsResponse(rsp *http.Response) (*V1ListProjectTpaIntegrationsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListProjectTpaIntegrationsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest []ThirdPartyAuthOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1CreateProjectTpaIntegrationResponse parses an HTTP response from a V1CreateProjectTpaIntegrationWithResponse call -func ParseV1CreateProjectTpaIntegrationResponse(rsp *http.Response) (*V1CreateProjectTpaIntegrationResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CreateProjectTpaIntegrationResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest ThirdPartyAuthOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1DeleteProjectTpaIntegrationResponse parses an HTTP response from a V1DeleteProjectTpaIntegrationWithResponse call -func ParseV1DeleteProjectTpaIntegrationResponse(rsp *http.Response) (*V1DeleteProjectTpaIntegrationResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeleteProjectTpaIntegrationResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ThirdPartyAuthOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetProjectTpaIntegrationResponse parses an HTTP response from a V1GetProjectTpaIntegrationWithResponse call -func ParseV1GetProjectTpaIntegrationResponse(rsp *http.Response) (*V1GetProjectTpaIntegrationResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectTpaIntegrationResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ThirdPartyAuthOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetProjectPgbouncerConfigResponse parses an HTTP response from a V1GetProjectPgbouncerConfigWithResponse call -func ParseV1GetProjectPgbouncerConfigResponse(rsp *http.Response) (*V1GetProjectPgbouncerConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectPgbouncerConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1PgbouncerConfigResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetPoolerConfigResponse parses an HTTP response from a V1GetPoolerConfigWithResponse call -func ParseV1GetPoolerConfigResponse(rsp *http.Response) (*V1GetPoolerConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetPoolerConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest []SupavisorConfigResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdatePoolerConfigResponse parses an HTTP response from a V1UpdatePoolerConfigWithResponse call -func ParseV1UpdatePoolerConfigResponse(rsp *http.Response) (*V1UpdatePoolerConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdatePoolerConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateSupavisorConfigResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetPostgresConfigResponse parses an HTTP response from a V1GetPostgresConfigWithResponse call -func ParseV1GetPostgresConfigResponse(rsp *http.Response) (*V1GetPostgresConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetPostgresConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest PostgresConfigResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdatePostgresConfigResponse parses an HTTP response from a V1UpdatePostgresConfigWithResponse call -func ParseV1UpdatePostgresConfigResponse(rsp *http.Response) (*V1UpdatePostgresConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdatePostgresConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest PostgresConfigResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetDatabaseDiskResponse parses an HTTP response from a V1GetDatabaseDiskWithResponse call -func ParseV1GetDatabaseDiskResponse(rsp *http.Response) (*V1GetDatabaseDiskResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetDatabaseDiskResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest DiskResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1ModifyDatabaseDiskResponse parses an HTTP response from a V1ModifyDatabaseDiskWithResponse call -func ParseV1ModifyDatabaseDiskResponse(rsp *http.Response) (*V1ModifyDatabaseDiskResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ModifyDatabaseDiskResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetProjectDiskAutoscaleConfigResponse parses an HTTP response from a V1GetProjectDiskAutoscaleConfigWithResponse call -func ParseV1GetProjectDiskAutoscaleConfigResponse(rsp *http.Response) (*V1GetProjectDiskAutoscaleConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetProjectDiskAutoscaleConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest DiskAutoscaleConfigOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetDiskUtilizationResponse parses an HTTP response from a V1GetDiskUtilizationWithResponse call -func ParseV1GetDiskUtilizationResponse(rsp *http.Response) (*V1GetDiskUtilizationResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetDiskUtilizationResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest DiskUtilMetricsResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetRealtimeConfigResponse parses an HTTP response from a V1GetRealtimeConfigWithResponse call -func ParseV1GetRealtimeConfigResponse(rsp *http.Response) (*V1GetRealtimeConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetRealtimeConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest RealtimeConfigResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateRealtimeConfigResponse parses an HTTP response from a V1UpdateRealtimeConfigWithResponse call -func ParseV1UpdateRealtimeConfigResponse(rsp *http.Response) (*V1UpdateRealtimeConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateRealtimeConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1ShutdownRealtimeResponse parses an HTTP response from a V1ShutdownRealtimeWithResponse call -func ParseV1ShutdownRealtimeResponse(rsp *http.Response) (*V1ShutdownRealtimeResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ShutdownRealtimeResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetStorageConfigResponse parses an HTTP response from a V1GetStorageConfigWithResponse call -func ParseV1GetStorageConfigResponse(rsp *http.Response) (*V1GetStorageConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetStorageConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest StorageConfigResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateStorageConfigResponse parses an HTTP response from a V1UpdateStorageConfigWithResponse call -func ParseV1UpdateStorageConfigResponse(rsp *http.Response) (*V1UpdateStorageConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateStorageConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1DeleteHostnameConfigResponse parses an HTTP response from a V1DeleteHostnameConfigWithResponse call -func ParseV1DeleteHostnameConfigResponse(rsp *http.Response) (*V1DeleteHostnameConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeleteHostnameConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetHostnameConfigResponse parses an HTTP response from a V1GetHostnameConfigWithResponse call -func ParseV1GetHostnameConfigResponse(rsp *http.Response) (*V1GetHostnameConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetHostnameConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest UpdateCustomHostnameResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1ActivateCustomHostnameResponse parses an HTTP response from a V1ActivateCustomHostnameWithResponse call -func ParseV1ActivateCustomHostnameResponse(rsp *http.Response) (*V1ActivateCustomHostnameResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ActivateCustomHostnameResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest UpdateCustomHostnameResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1UpdateHostnameConfigResponse parses an HTTP response from a V1UpdateHostnameConfigWithResponse call -func ParseV1UpdateHostnameConfigResponse(rsp *http.Response) (*V1UpdateHostnameConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateHostnameConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest UpdateCustomHostnameResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1VerifyDnsConfigResponse parses an HTTP response from a V1VerifyDnsConfigWithResponse call -func ParseV1VerifyDnsConfigResponse(rsp *http.Response) (*V1VerifyDnsConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1VerifyDnsConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest UpdateCustomHostnameResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1ListAllBackupsResponse parses an HTTP response from a V1ListAllBackupsWithResponse call -func ParseV1ListAllBackupsResponse(rsp *http.Response) (*V1ListAllBackupsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListAllBackupsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1BackupsResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1RestorePhysicalBackupResponse parses an HTTP response from a V1RestorePhysicalBackupWithResponse call -func ParseV1RestorePhysicalBackupResponse(rsp *http.Response) (*V1RestorePhysicalBackupResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1RestorePhysicalBackupResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1RestorePitrBackupResponse parses an HTTP response from a V1RestorePitrBackupWithResponse call -func ParseV1RestorePitrBackupResponse(rsp *http.Response) (*V1RestorePitrBackupResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1RestorePitrBackupResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetRestorePointResponse parses an HTTP response from a V1GetRestorePointWithResponse call -func ParseV1GetRestorePointResponse(rsp *http.Response) (*V1GetRestorePointResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetRestorePointResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1RestorePointResponse - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1CreateRestorePointResponse parses an HTTP response from a V1CreateRestorePointWithResponse call -func ParseV1CreateRestorePointResponse(rsp *http.Response) (*V1CreateRestorePointResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CreateRestorePointResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest V1RestorePointResponse - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1GetBackupScheduleResponse parses an HTTP response from a V1GetBackupScheduleWithResponse call -func ParseV1GetBackupScheduleResponse(rsp *http.Response) (*V1GetBackupScheduleResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetBackupScheduleResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1BackupScheduleResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 402: - var dest PlanGateErrorBody - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON402 = &dest - - } - - return response, nil -} - -// ParseV1UpdateBackupScheduleResponse parses an HTTP response from a V1UpdateBackupScheduleWithResponse call -func ParseV1UpdateBackupScheduleResponse(rsp *http.Response) (*V1UpdateBackupScheduleResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateBackupScheduleResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1BackupScheduleResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 402: - var dest PlanGateErrorBody - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON402 = &dest - - } - - return response, nil -} - -// ParseV1UndoResponse parses an HTTP response from a V1UndoWithResponse call -func ParseV1UndoResponse(rsp *http.Response) (*V1UndoResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UndoResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetDatabaseMetadataResponse parses an HTTP response from a V1GetDatabaseMetadataWithResponse call -func ParseV1GetDatabaseMetadataResponse(rsp *http.Response) (*V1GetDatabaseMetadataResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetDatabaseMetadataResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetProjectDbMetadataResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetJitAccessResponse parses an HTTP response from a V1GetJitAccessWithResponse call -func ParseV1GetJitAccessResponse(rsp *http.Response) (*V1GetJitAccessResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetJitAccessResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest JitAccessResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1AuthorizeJitAccessResponse parses an HTTP response from a V1AuthorizeJitAccessWithResponse call -func ParseV1AuthorizeJitAccessResponse(rsp *http.Response) (*V1AuthorizeJitAccessResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1AuthorizeJitAccessResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest JitAuthorizeAccessResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateJitAccessResponse parses an HTTP response from a V1UpdateJitAccessWithResponse call -func ParseV1UpdateJitAccessResponse(rsp *http.Response) (*V1UpdateJitAccessResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateJitAccessResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest JitAccessResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1InviteExternalJitAccessResponse parses an HTTP response from a V1InviteExternalJitAccessWithResponse call -func ParseV1InviteExternalJitAccessResponse(rsp *http.Response) (*V1InviteExternalJitAccessResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1InviteExternalJitAccessResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest InviteExternalUserJitResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1AcceptInviteExternalJitAccessResponse parses an HTTP response from a V1AcceptInviteExternalJitAccessWithResponse call -func ParseV1AcceptInviteExternalJitAccessResponse(rsp *http.Response) (*V1AcceptInviteExternalJitAccessResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1AcceptInviteExternalJitAccessResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest JitAccessResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1DeleteInviteExternalJitAccessResponse parses an HTTP response from a V1DeleteInviteExternalJitAccessWithResponse call -func ParseV1DeleteInviteExternalJitAccessResponse(rsp *http.Response) (*V1DeleteInviteExternalJitAccessResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeleteInviteExternalJitAccessResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1ListJitAccessResponse parses an HTTP response from a V1ListJitAccessWithResponse call -func ParseV1ListJitAccessResponse(rsp *http.Response) (*V1ListJitAccessResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListJitAccessResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest JitListAccessResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1DeleteJitAccessResponse parses an HTTP response from a V1DeleteJitAccessWithResponse call -func ParseV1DeleteJitAccessResponse(rsp *http.Response) (*V1DeleteJitAccessResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeleteJitAccessResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1RollbackMigrationsResponse parses an HTTP response from a V1RollbackMigrationsWithResponse call -func ParseV1RollbackMigrationsResponse(rsp *http.Response) (*V1RollbackMigrationsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1RollbackMigrationsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1ListMigrationHistoryResponse parses an HTTP response from a V1ListMigrationHistoryWithResponse call -func ParseV1ListMigrationHistoryResponse(rsp *http.Response) (*V1ListMigrationHistoryResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListMigrationHistoryResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1ListMigrationsResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1ApplyAMigrationResponse parses an HTTP response from a V1ApplyAMigrationWithResponse call -func ParseV1ApplyAMigrationResponse(rsp *http.Response) (*V1ApplyAMigrationResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ApplyAMigrationResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1UpsertAMigrationResponse parses an HTTP response from a V1UpsertAMigrationWithResponse call -func ParseV1UpsertAMigrationResponse(rsp *http.Response) (*V1UpsertAMigrationResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpsertAMigrationResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetAMigrationResponse parses an HTTP response from a V1GetAMigrationWithResponse call -func ParseV1GetAMigrationResponse(rsp *http.Response) (*V1GetAMigrationResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetAMigrationResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1GetMigrationResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1PatchAMigrationResponse parses an HTTP response from a V1PatchAMigrationWithResponse call -func ParseV1PatchAMigrationResponse(rsp *http.Response) (*V1PatchAMigrationResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1PatchAMigrationResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetDatabaseOpenapiResponse parses an HTTP response from a V1GetDatabaseOpenapiWithResponse call -func ParseV1GetDatabaseOpenapiResponse(rsp *http.Response) (*V1GetDatabaseOpenapiResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetDatabaseOpenapiResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest map[string]interface{} - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateDatabasePasswordResponse parses an HTTP response from a V1UpdateDatabasePasswordWithResponse call -func ParseV1UpdateDatabasePasswordResponse(rsp *http.Response) (*V1UpdateDatabasePasswordResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateDatabasePasswordResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1UpdatePasswordResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1RunAQueryResponse parses an HTTP response from a V1RunAQueryWithResponse call -func ParseV1RunAQueryResponse(rsp *http.Response) (*V1RunAQueryResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1RunAQueryResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1ReadOnlyQueryResponse parses an HTTP response from a V1ReadOnlyQueryWithResponse call -func ParseV1ReadOnlyQueryResponse(rsp *http.Response) (*V1ReadOnlyQueryResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ReadOnlyQueryResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1EnableDatabaseWebhookResponse parses an HTTP response from a V1EnableDatabaseWebhookWithResponse call -func ParseV1EnableDatabaseWebhookResponse(rsp *http.Response) (*V1EnableDatabaseWebhookResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1EnableDatabaseWebhookResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1ListAllFunctionsResponse parses an HTTP response from a V1ListAllFunctionsWithResponse call -func ParseV1ListAllFunctionsResponse(rsp *http.Response) (*V1ListAllFunctionsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListAllFunctionsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest []FunctionResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1CreateAFunctionResponse parses an HTTP response from a V1CreateAFunctionWithResponse call -func ParseV1CreateAFunctionResponse(rsp *http.Response) (*V1CreateAFunctionResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CreateAFunctionResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest FunctionResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1BulkUpdateFunctionsResponse parses an HTTP response from a V1BulkUpdateFunctionsWithResponse call -func ParseV1BulkUpdateFunctionsResponse(rsp *http.Response) (*V1BulkUpdateFunctionsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1BulkUpdateFunctionsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest BulkUpdateFunctionResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1DeployAFunctionResponse parses an HTTP response from a V1DeployAFunctionWithResponse call -func ParseV1DeployAFunctionResponse(rsp *http.Response) (*V1DeployAFunctionResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeployAFunctionResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest DeployFunctionResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1DeleteAFunctionResponse parses an HTTP response from a V1DeleteAFunctionWithResponse call -func ParseV1DeleteAFunctionResponse(rsp *http.Response) (*V1DeleteAFunctionResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeleteAFunctionResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetAFunctionResponse parses an HTTP response from a V1GetAFunctionWithResponse call -func ParseV1GetAFunctionResponse(rsp *http.Response) (*V1GetAFunctionResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetAFunctionResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest FunctionSlugResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateAFunctionResponse parses an HTTP response from a V1UpdateAFunctionWithResponse call -func ParseV1UpdateAFunctionResponse(rsp *http.Response) (*V1UpdateAFunctionResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateAFunctionResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest FunctionSlugResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetAFunctionBodyResponse parses an HTTP response from a V1GetAFunctionBodyWithResponse call -func ParseV1GetAFunctionBodyResponse(rsp *http.Response) (*V1GetAFunctionBodyResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetAFunctionBodyResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest StreamableFile - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetServicesHealthResponse parses an HTTP response from a V1GetServicesHealthWithResponse call -func ParseV1GetServicesHealthResponse(rsp *http.Response) (*V1GetServicesHealthResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetServicesHealthResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest []V1ServiceHealthResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetJitAccessConfigResponse parses an HTTP response from a V1GetJitAccessConfigWithResponse call -func ParseV1GetJitAccessConfigResponse(rsp *http.Response) (*V1GetJitAccessConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetJitAccessConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1GetJitAccessConfig200JSONResponseBody - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateJitAccessConfigResponse parses an HTTP response from a V1UpdateJitAccessConfigWithResponse call -func ParseV1UpdateJitAccessConfigResponse(rsp *http.Response) (*V1UpdateJitAccessConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateJitAccessConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1UpdateJitAccessConfig200JSONResponseBody - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1DeleteNetworkBansResponse parses an HTTP response from a V1DeleteNetworkBansWithResponse call -func ParseV1DeleteNetworkBansResponse(rsp *http.Response) (*V1DeleteNetworkBansResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeleteNetworkBansResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1ListAllNetworkBansResponse parses an HTTP response from a V1ListAllNetworkBansWithResponse call -func ParseV1ListAllNetworkBansResponse(rsp *http.Response) (*V1ListAllNetworkBansResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListAllNetworkBansResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest NetworkBanResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1ListAllNetworkBansEnrichedResponse parses an HTTP response from a V1ListAllNetworkBansEnrichedWithResponse call -func ParseV1ListAllNetworkBansEnrichedResponse(rsp *http.Response) (*V1ListAllNetworkBansEnrichedResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListAllNetworkBansEnrichedResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest NetworkBanResponseEnrichedOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1GetNetworkRestrictionsResponse parses an HTTP response from a V1GetNetworkRestrictionsWithResponse call -func ParseV1GetNetworkRestrictionsResponse(rsp *http.Response) (*V1GetNetworkRestrictionsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetNetworkRestrictionsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest NetworkRestrictionsResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1PatchNetworkRestrictionsResponse parses an HTTP response from a V1PatchNetworkRestrictionsWithResponse call -func ParseV1PatchNetworkRestrictionsResponse(rsp *http.Response) (*V1PatchNetworkRestrictionsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1PatchNetworkRestrictionsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest NetworkRestrictionsV2ResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateNetworkRestrictionsResponse parses an HTTP response from a V1UpdateNetworkRestrictionsWithResponse call -func ParseV1UpdateNetworkRestrictionsResponse(rsp *http.Response) (*V1UpdateNetworkRestrictionsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateNetworkRestrictionsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest NetworkRestrictionsResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1PauseAProjectResponse parses an HTTP response from a V1PauseAProjectWithResponse call -func ParseV1PauseAProjectResponse(rsp *http.Response) (*V1PauseAProjectResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1PauseAProjectResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetPgsodiumConfigResponse parses an HTTP response from a V1GetPgsodiumConfigWithResponse call -func ParseV1GetPgsodiumConfigResponse(rsp *http.Response) (*V1GetPgsodiumConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetPgsodiumConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest PgsodiumConfigResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdatePgsodiumConfigResponse parses an HTTP response from a V1UpdatePgsodiumConfigWithResponse call -func ParseV1UpdatePgsodiumConfigResponse(rsp *http.Response) (*V1UpdatePgsodiumConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdatePgsodiumConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest PgsodiumConfigResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetPostgrestServiceConfigResponse parses an HTTP response from a V1GetPostgrestServiceConfigWithResponse call -func ParseV1GetPostgrestServiceConfigResponse(rsp *http.Response) (*V1GetPostgrestServiceConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetPostgrestServiceConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest PostgrestConfigWithJWTSecretResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdatePostgrestServiceConfigResponse parses an HTTP response from a V1UpdatePostgrestServiceConfigWithResponse call -func ParseV1UpdatePostgrestServiceConfigResponse(rsp *http.Response) (*V1UpdatePostgrestServiceConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdatePostgrestServiceConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest V1PostgrestConfigResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1RemoveAReadReplicaResponse parses an HTTP response from a V1RemoveAReadReplicaWithResponse call -func ParseV1RemoveAReadReplicaResponse(rsp *http.Response) (*V1RemoveAReadReplicaResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1RemoveAReadReplicaResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1SetupAReadReplicaResponse parses an HTTP response from a V1SetupAReadReplicaWithResponse call -func ParseV1SetupAReadReplicaResponse(rsp *http.Response) (*V1SetupAReadReplicaResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1SetupAReadReplicaResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 402: - var dest PlanGateErrorBody - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON402 = &dest - - } - - return response, nil -} - -// ParseV1GetReadonlyModeStatusResponse parses an HTTP response from a V1GetReadonlyModeStatusWithResponse call -func ParseV1GetReadonlyModeStatusResponse(rsp *http.Response) (*V1GetReadonlyModeStatusResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetReadonlyModeStatusResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ReadOnlyStatusResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1DisableReadonlyModeTemporarilyResponse parses an HTTP response from a V1DisableReadonlyModeTemporarilyWithResponse call -func ParseV1DisableReadonlyModeTemporarilyResponse(rsp *http.Response) (*V1DisableReadonlyModeTemporarilyResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DisableReadonlyModeTemporarilyResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1RestartAProjectResponse parses an HTTP response from a V1RestartAProjectWithResponse call -func ParseV1RestartAProjectResponse(rsp *http.Response) (*V1RestartAProjectResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1RestartAProjectResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1ListAvailableRestoreVersionsResponse parses an HTTP response from a V1ListAvailableRestoreVersionsWithResponse call -func ParseV1ListAvailableRestoreVersionsResponse(rsp *http.Response) (*V1ListAvailableRestoreVersionsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListAvailableRestoreVersionsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest GetProjectAvailableRestoreVersionsResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1RestoreAProjectResponse parses an HTTP response from a V1RestoreAProjectWithResponse call -func ParseV1RestoreAProjectResponse(rsp *http.Response) (*V1RestoreAProjectResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1RestoreAProjectResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1CancelAProjectRestorationResponse parses an HTTP response from a V1CancelAProjectRestorationWithResponse call -func ParseV1CancelAProjectRestorationResponse(rsp *http.Response) (*V1CancelAProjectRestorationResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CancelAProjectRestorationResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1BulkDeleteSecretsResponse parses an HTTP response from a V1BulkDeleteSecretsWithResponse call -func ParseV1BulkDeleteSecretsResponse(rsp *http.Response) (*V1BulkDeleteSecretsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1BulkDeleteSecretsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1ListAllSecretsResponse parses an HTTP response from a V1ListAllSecretsWithResponse call -func ParseV1ListAllSecretsResponse(rsp *http.Response) (*V1ListAllSecretsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListAllSecretsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest []SecretResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1BulkCreateSecretsResponse parses an HTTP response from a V1BulkCreateSecretsWithResponse call -func ParseV1BulkCreateSecretsResponse(rsp *http.Response) (*V1BulkCreateSecretsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1BulkCreateSecretsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetSslEnforcementConfigResponse parses an HTTP response from a V1GetSslEnforcementConfigWithResponse call -func ParseV1GetSslEnforcementConfigResponse(rsp *http.Response) (*V1GetSslEnforcementConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetSslEnforcementConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest SslEnforcementResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpdateSslEnforcementConfigResponse parses an HTTP response from a V1UpdateSslEnforcementConfigWithResponse call -func ParseV1UpdateSslEnforcementConfigResponse(rsp *http.Response) (*V1UpdateSslEnforcementConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpdateSslEnforcementConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest SslEnforcementResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1ListAllBucketsResponse parses an HTTP response from a V1ListAllBucketsWithResponse call -func ParseV1ListAllBucketsResponse(rsp *http.Response) (*V1ListAllBucketsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListAllBucketsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest []V1StorageBucketResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GenerateTypescriptTypesResponse parses an HTTP response from a V1GenerateTypescriptTypesWithResponse call -func ParseV1GenerateTypescriptTypesResponse(rsp *http.Response) (*V1GenerateTypescriptTypesResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GenerateTypescriptTypesResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest TypescriptResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1UpgradePostgresVersionResponse parses an HTTP response from a V1UpgradePostgresVersionWithResponse call -func ParseV1UpgradePostgresVersionResponse(rsp *http.Response) (*V1UpgradePostgresVersionResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1UpgradePostgresVersionResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest ProjectUpgradeInitiateResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - } - - return response, nil -} - -// ParseV1GetPostgresUpgradeEligibilityResponse parses an HTTP response from a V1GetPostgresUpgradeEligibilityWithResponse call -func ParseV1GetPostgresUpgradeEligibilityResponse(rsp *http.Response) (*V1GetPostgresUpgradeEligibilityResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetPostgresUpgradeEligibilityResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest ProjectUpgradeEligibilityResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetPostgresUpgradeStatusResponse parses an HTTP response from a V1GetPostgresUpgradeStatusWithResponse call -func ParseV1GetPostgresUpgradeStatusResponse(rsp *http.Response) (*V1GetPostgresUpgradeStatusResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetPostgresUpgradeStatusResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest DatabaseUpgradeStatusResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1DeactivateVanitySubdomainConfigResponse parses an HTTP response from a V1DeactivateVanitySubdomainConfigWithResponse call -func ParseV1DeactivateVanitySubdomainConfigResponse(rsp *http.Response) (*V1DeactivateVanitySubdomainConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1DeactivateVanitySubdomainConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - return response, nil -} - -// ParseV1GetVanitySubdomainConfigResponse parses an HTTP response from a V1GetVanitySubdomainConfigWithResponse call -func ParseV1GetVanitySubdomainConfigResponse(rsp *http.Response) (*V1GetVanitySubdomainConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetVanitySubdomainConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest VanitySubdomainConfigResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest PlanGateErrorBody - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON400 = &dest - - } - - return response, nil -} - -// ParseV1ActivateVanitySubdomainConfigResponse parses an HTTP response from a V1ActivateVanitySubdomainConfigWithResponse call -func ParseV1ActivateVanitySubdomainConfigResponse(rsp *http.Response) (*V1ActivateVanitySubdomainConfigResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ActivateVanitySubdomainConfigResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest ActivateVanitySubdomainResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest PlanGateErrorBody - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON400 = &dest - - } - - return response, nil -} - -// ParseV1CheckVanitySubdomainAvailabilityResponse parses an HTTP response from a V1CheckVanitySubdomainAvailabilityWithResponse call -func ParseV1CheckVanitySubdomainAvailabilityResponse(rsp *http.Response) (*V1CheckVanitySubdomainAvailabilityResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1CheckVanitySubdomainAvailabilityResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: - var dest SubdomainAvailabilityResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON201 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest PlanGateErrorBody - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON400 = &dest - - } - - return response, nil -} - -// ParseV1ListAllSnippetsResponse parses an HTTP response from a V1ListAllSnippetsWithResponse call -func ParseV1ListAllSnippetsResponse(rsp *http.Response) (*V1ListAllSnippetsResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1ListAllSnippetsResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest SnippetListOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} - -// ParseV1GetASnippetResponse parses an HTTP response from a V1GetASnippetWithResponse call -func ParseV1GetASnippetResponse(rsp *http.Response) (*V1GetASnippetResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } - - response := &V1GetASnippetResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } - - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest SnippetResponseOutput - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest - - } - - return response, nil -} diff --git a/apps/cli-go/pkg/api/types.cfg.yaml b/apps/cli-go/pkg/api/types.cfg.yaml deleted file mode 100755 index 128f4a387b..0000000000 --- a/apps/cli-go/pkg/api/types.cfg.yaml +++ /dev/null @@ -1,8 +0,0 @@ -package: api -generate: - models: true -output-options: - nullable-type: true - overlay: - path: api/overlay.yaml -output: pkg/api/types.gen.go diff --git a/apps/cli-go/pkg/api/types.gen.go b/apps/cli-go/pkg/api/types.gen.go deleted file mode 100644 index a22dbfa07c..0000000000 --- a/apps/cli-go/pkg/api/types.gen.go +++ /dev/null @@ -1,11659 +0,0 @@ -// Package api provides primitives to interact with the openapi HTTP API. -// -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.7.1 DO NOT EDIT. -package api - -import ( - "encoding/json" - "time" - - "github.com/oapi-codegen/nullable" - "github.com/oapi-codegen/runtime" - openapi_types "github.com/oapi-codegen/runtime/types" -) - -const ( - BearerScopes bearerContextKey = "bearer.Scopes" -) - -// Defines values for ActionRunResponseOutputRunStepsName. -const ( - ActionRunResponseOutputRunStepsNameClone ActionRunResponseOutputRunStepsName = "clone" - ActionRunResponseOutputRunStepsNameConfigure ActionRunResponseOutputRunStepsName = "configure" - ActionRunResponseOutputRunStepsNameDeploy ActionRunResponseOutputRunStepsName = "deploy" - ActionRunResponseOutputRunStepsNameHealth ActionRunResponseOutputRunStepsName = "health" - ActionRunResponseOutputRunStepsNameMigrate ActionRunResponseOutputRunStepsName = "migrate" - ActionRunResponseOutputRunStepsNamePull ActionRunResponseOutputRunStepsName = "pull" - ActionRunResponseOutputRunStepsNameSeed ActionRunResponseOutputRunStepsName = "seed" -) - -// Valid indicates whether the value is a known member of the ActionRunResponseOutputRunStepsName enum. -func (e ActionRunResponseOutputRunStepsName) Valid() bool { - switch e { - case ActionRunResponseOutputRunStepsNameClone: - return true - case ActionRunResponseOutputRunStepsNameConfigure: - return true - case ActionRunResponseOutputRunStepsNameDeploy: - return true - case ActionRunResponseOutputRunStepsNameHealth: - return true - case ActionRunResponseOutputRunStepsNameMigrate: - return true - case ActionRunResponseOutputRunStepsNamePull: - return true - case ActionRunResponseOutputRunStepsNameSeed: - return true - default: - return false - } -} - -// Defines values for ActionRunResponseOutputRunStepsStatus. -const ( - ActionRunResponseOutputRunStepsStatusCREATED ActionRunResponseOutputRunStepsStatus = "CREATED" - ActionRunResponseOutputRunStepsStatusDEAD ActionRunResponseOutputRunStepsStatus = "DEAD" - ActionRunResponseOutputRunStepsStatusEXITED ActionRunResponseOutputRunStepsStatus = "EXITED" - ActionRunResponseOutputRunStepsStatusPAUSED ActionRunResponseOutputRunStepsStatus = "PAUSED" - ActionRunResponseOutputRunStepsStatusREMOVING ActionRunResponseOutputRunStepsStatus = "REMOVING" - ActionRunResponseOutputRunStepsStatusRESTARTING ActionRunResponseOutputRunStepsStatus = "RESTARTING" - ActionRunResponseOutputRunStepsStatusRUNNING ActionRunResponseOutputRunStepsStatus = "RUNNING" -) - -// Valid indicates whether the value is a known member of the ActionRunResponseOutputRunStepsStatus enum. -func (e ActionRunResponseOutputRunStepsStatus) Valid() bool { - switch e { - case ActionRunResponseOutputRunStepsStatusCREATED: - return true - case ActionRunResponseOutputRunStepsStatusDEAD: - return true - case ActionRunResponseOutputRunStepsStatusEXITED: - return true - case ActionRunResponseOutputRunStepsStatusPAUSED: - return true - case ActionRunResponseOutputRunStepsStatusREMOVING: - return true - case ActionRunResponseOutputRunStepsStatusRESTARTING: - return true - case ActionRunResponseOutputRunStepsStatusRUNNING: - return true - default: - return false - } -} - -// Defines values for ApiKeyResponseOutputType. -const ( - ApiKeyResponseOutputTypeLegacy ApiKeyResponseOutputType = "legacy" - ApiKeyResponseOutputTypeLessThannil ApiKeyResponseOutputType = "" - ApiKeyResponseOutputTypePublishable ApiKeyResponseOutputType = "publishable" - ApiKeyResponseOutputTypeSecret ApiKeyResponseOutputType = "secret" -) - -// Valid indicates whether the value is a known member of the ApiKeyResponseOutputType enum. -func (e ApiKeyResponseOutputType) Valid() bool { - switch e { - case ApiKeyResponseOutputTypeLegacy: - return true - case ApiKeyResponseOutputTypeLessThannil: - return true - case ApiKeyResponseOutputTypePublishable: - return true - case ApiKeyResponseOutputTypeSecret: - return true - default: - return false - } -} - -// Defines values for ApplyProjectAddonBodyAddonType. -const ( - ApplyProjectAddonBodyAddonTypeAuthMfaPhone ApplyProjectAddonBodyAddonType = "auth_mfa_phone" - ApplyProjectAddonBodyAddonTypeAuthMfaWebAuthn ApplyProjectAddonBodyAddonType = "auth_mfa_web_authn" - ApplyProjectAddonBodyAddonTypeComputeInstance ApplyProjectAddonBodyAddonType = "compute_instance" - ApplyProjectAddonBodyAddonTypeCustomDomain ApplyProjectAddonBodyAddonType = "custom_domain" - ApplyProjectAddonBodyAddonTypeEtlPipeline ApplyProjectAddonBodyAddonType = "etl_pipeline" - ApplyProjectAddonBodyAddonTypeIpv4 ApplyProjectAddonBodyAddonType = "ipv4" - ApplyProjectAddonBodyAddonTypeLogDrain ApplyProjectAddonBodyAddonType = "log_drain" - ApplyProjectAddonBodyAddonTypePitr ApplyProjectAddonBodyAddonType = "pitr" -) - -// Valid indicates whether the value is a known member of the ApplyProjectAddonBodyAddonType enum. -func (e ApplyProjectAddonBodyAddonType) Valid() bool { - switch e { - case ApplyProjectAddonBodyAddonTypeAuthMfaPhone: - return true - case ApplyProjectAddonBodyAddonTypeAuthMfaWebAuthn: - return true - case ApplyProjectAddonBodyAddonTypeComputeInstance: - return true - case ApplyProjectAddonBodyAddonTypeCustomDomain: - return true - case ApplyProjectAddonBodyAddonTypeEtlPipeline: - return true - case ApplyProjectAddonBodyAddonTypeIpv4: - return true - case ApplyProjectAddonBodyAddonTypeLogDrain: - return true - case ApplyProjectAddonBodyAddonTypePitr: - return true - default: - return false - } -} - -// Defines values for ApplyProjectAddonBodyAddonVariant0. -const ( - ApplyProjectAddonBodyAddonVariant0Ci12xlarge ApplyProjectAddonBodyAddonVariant0 = "ci_12xlarge" - ApplyProjectAddonBodyAddonVariant0Ci16xlarge ApplyProjectAddonBodyAddonVariant0 = "ci_16xlarge" - ApplyProjectAddonBodyAddonVariant0Ci24xlarge ApplyProjectAddonBodyAddonVariant0 = "ci_24xlarge" - ApplyProjectAddonBodyAddonVariant0Ci24xlargeHighMemory ApplyProjectAddonBodyAddonVariant0 = "ci_24xlarge_high_memory" - ApplyProjectAddonBodyAddonVariant0Ci24xlargeOptimizedCpu ApplyProjectAddonBodyAddonVariant0 = "ci_24xlarge_optimized_cpu" - ApplyProjectAddonBodyAddonVariant0Ci24xlargeOptimizedMemory ApplyProjectAddonBodyAddonVariant0 = "ci_24xlarge_optimized_memory" - ApplyProjectAddonBodyAddonVariant0Ci2xlarge ApplyProjectAddonBodyAddonVariant0 = "ci_2xlarge" - ApplyProjectAddonBodyAddonVariant0Ci48xlarge ApplyProjectAddonBodyAddonVariant0 = "ci_48xlarge" - ApplyProjectAddonBodyAddonVariant0Ci48xlargeHighMemory ApplyProjectAddonBodyAddonVariant0 = "ci_48xlarge_high_memory" - ApplyProjectAddonBodyAddonVariant0Ci48xlargeOptimizedCpu ApplyProjectAddonBodyAddonVariant0 = "ci_48xlarge_optimized_cpu" - ApplyProjectAddonBodyAddonVariant0Ci48xlargeOptimizedMemory ApplyProjectAddonBodyAddonVariant0 = "ci_48xlarge_optimized_memory" - ApplyProjectAddonBodyAddonVariant0Ci4xlarge ApplyProjectAddonBodyAddonVariant0 = "ci_4xlarge" - ApplyProjectAddonBodyAddonVariant0Ci8xlarge ApplyProjectAddonBodyAddonVariant0 = "ci_8xlarge" - ApplyProjectAddonBodyAddonVariant0CiLarge ApplyProjectAddonBodyAddonVariant0 = "ci_large" - ApplyProjectAddonBodyAddonVariant0CiMedium ApplyProjectAddonBodyAddonVariant0 = "ci_medium" - ApplyProjectAddonBodyAddonVariant0CiMicro ApplyProjectAddonBodyAddonVariant0 = "ci_micro" - ApplyProjectAddonBodyAddonVariant0CiSmall ApplyProjectAddonBodyAddonVariant0 = "ci_small" - ApplyProjectAddonBodyAddonVariant0CiXlarge ApplyProjectAddonBodyAddonVariant0 = "ci_xlarge" -) - -// Valid indicates whether the value is a known member of the ApplyProjectAddonBodyAddonVariant0 enum. -func (e ApplyProjectAddonBodyAddonVariant0) Valid() bool { - switch e { - case ApplyProjectAddonBodyAddonVariant0Ci12xlarge: - return true - case ApplyProjectAddonBodyAddonVariant0Ci16xlarge: - return true - case ApplyProjectAddonBodyAddonVariant0Ci24xlarge: - return true - case ApplyProjectAddonBodyAddonVariant0Ci24xlargeHighMemory: - return true - case ApplyProjectAddonBodyAddonVariant0Ci24xlargeOptimizedCpu: - return true - case ApplyProjectAddonBodyAddonVariant0Ci24xlargeOptimizedMemory: - return true - case ApplyProjectAddonBodyAddonVariant0Ci2xlarge: - return true - case ApplyProjectAddonBodyAddonVariant0Ci48xlarge: - return true - case ApplyProjectAddonBodyAddonVariant0Ci48xlargeHighMemory: - return true - case ApplyProjectAddonBodyAddonVariant0Ci48xlargeOptimizedCpu: - return true - case ApplyProjectAddonBodyAddonVariant0Ci48xlargeOptimizedMemory: - return true - case ApplyProjectAddonBodyAddonVariant0Ci4xlarge: - return true - case ApplyProjectAddonBodyAddonVariant0Ci8xlarge: - return true - case ApplyProjectAddonBodyAddonVariant0CiLarge: - return true - case ApplyProjectAddonBodyAddonVariant0CiMedium: - return true - case ApplyProjectAddonBodyAddonVariant0CiMicro: - return true - case ApplyProjectAddonBodyAddonVariant0CiSmall: - return true - case ApplyProjectAddonBodyAddonVariant0CiXlarge: - return true - default: - return false - } -} - -// Defines values for ApplyProjectAddonBodyAddonVariant1. -const ( - ApplyProjectAddonBodyAddonVariant1CdDefault ApplyProjectAddonBodyAddonVariant1 = "cd_default" -) - -// Valid indicates whether the value is a known member of the ApplyProjectAddonBodyAddonVariant1 enum. -func (e ApplyProjectAddonBodyAddonVariant1) Valid() bool { - switch e { - case ApplyProjectAddonBodyAddonVariant1CdDefault: - return true - default: - return false - } -} - -// Defines values for ApplyProjectAddonBodyAddonVariant2. -const ( - ApplyProjectAddonBodyAddonVariant2Pitr14 ApplyProjectAddonBodyAddonVariant2 = "pitr_14" - ApplyProjectAddonBodyAddonVariant2Pitr28 ApplyProjectAddonBodyAddonVariant2 = "pitr_28" - ApplyProjectAddonBodyAddonVariant2Pitr7 ApplyProjectAddonBodyAddonVariant2 = "pitr_7" -) - -// Valid indicates whether the value is a known member of the ApplyProjectAddonBodyAddonVariant2 enum. -func (e ApplyProjectAddonBodyAddonVariant2) Valid() bool { - switch e { - case ApplyProjectAddonBodyAddonVariant2Pitr14: - return true - case ApplyProjectAddonBodyAddonVariant2Pitr28: - return true - case ApplyProjectAddonBodyAddonVariant2Pitr7: - return true - default: - return false - } -} - -// Defines values for ApplyProjectAddonBodyAddonVariant3. -const ( - ApplyProjectAddonBodyAddonVariant3Ipv4Default ApplyProjectAddonBodyAddonVariant3 = "ipv4_default" -) - -// Valid indicates whether the value is a known member of the ApplyProjectAddonBodyAddonVariant3 enum. -func (e ApplyProjectAddonBodyAddonVariant3) Valid() bool { - switch e { - case ApplyProjectAddonBodyAddonVariant3Ipv4Default: - return true - default: - return false - } -} - -// Defines values for AuthConfigResponseOutputDbMaxPoolSizeUnit. -const ( - AuthConfigResponseOutputDbMaxPoolSizeUnitConnections AuthConfigResponseOutputDbMaxPoolSizeUnit = "connections" - AuthConfigResponseOutputDbMaxPoolSizeUnitLessThannil AuthConfigResponseOutputDbMaxPoolSizeUnit = "" - AuthConfigResponseOutputDbMaxPoolSizeUnitPercent AuthConfigResponseOutputDbMaxPoolSizeUnit = "percent" -) - -// Valid indicates whether the value is a known member of the AuthConfigResponseOutputDbMaxPoolSizeUnit enum. -func (e AuthConfigResponseOutputDbMaxPoolSizeUnit) Valid() bool { - switch e { - case AuthConfigResponseOutputDbMaxPoolSizeUnitConnections: - return true - case AuthConfigResponseOutputDbMaxPoolSizeUnitLessThannil: - return true - case AuthConfigResponseOutputDbMaxPoolSizeUnitPercent: - return true - default: - return false - } -} - -// Defines values for AuthConfigResponseOutputPasswordRequiredCharacters. -const ( - AuthConfigResponseOutputPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 AuthConfigResponseOutputPasswordRequiredCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789" - AuthConfigResponseOutputPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567891 AuthConfigResponseOutputPasswordRequiredCharacters = "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789" - AuthConfigResponseOutputPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567892 AuthConfigResponseOutputPasswordRequiredCharacters = "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\\\:\"|<>?,./`~" - AuthConfigResponseOutputPasswordRequiredCharactersEmpty AuthConfigResponseOutputPasswordRequiredCharacters = "" -) - -// Valid indicates whether the value is a known member of the AuthConfigResponseOutputPasswordRequiredCharacters enum. -func (e AuthConfigResponseOutputPasswordRequiredCharacters) Valid() bool { - switch e { - case AuthConfigResponseOutputPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789: - return true - case AuthConfigResponseOutputPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567891: - return true - case AuthConfigResponseOutputPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567892: - return true - case AuthConfigResponseOutputPasswordRequiredCharactersEmpty: - return true - default: - return false - } -} - -// Defines values for AuthConfigResponseOutputSecurityCaptchaProvider. -const ( - AuthConfigResponseOutputSecurityCaptchaProviderHcaptcha AuthConfigResponseOutputSecurityCaptchaProvider = "hcaptcha" - AuthConfigResponseOutputSecurityCaptchaProviderLessThannil AuthConfigResponseOutputSecurityCaptchaProvider = "" - AuthConfigResponseOutputSecurityCaptchaProviderTurnstile AuthConfigResponseOutputSecurityCaptchaProvider = "turnstile" -) - -// Valid indicates whether the value is a known member of the AuthConfigResponseOutputSecurityCaptchaProvider enum. -func (e AuthConfigResponseOutputSecurityCaptchaProvider) Valid() bool { - switch e { - case AuthConfigResponseOutputSecurityCaptchaProviderHcaptcha: - return true - case AuthConfigResponseOutputSecurityCaptchaProviderLessThannil: - return true - case AuthConfigResponseOutputSecurityCaptchaProviderTurnstile: - return true - default: - return false - } -} - -// Defines values for AuthConfigResponseOutputSmsProvider. -const ( - AuthConfigResponseOutputSmsProviderLessThannil AuthConfigResponseOutputSmsProvider = "" - AuthConfigResponseOutputSmsProviderMessagebird AuthConfigResponseOutputSmsProvider = "messagebird" - AuthConfigResponseOutputSmsProviderTextlocal AuthConfigResponseOutputSmsProvider = "textlocal" - AuthConfigResponseOutputSmsProviderTwilio AuthConfigResponseOutputSmsProvider = "twilio" - AuthConfigResponseOutputSmsProviderTwilioVerify AuthConfigResponseOutputSmsProvider = "twilio_verify" - AuthConfigResponseOutputSmsProviderVonage AuthConfigResponseOutputSmsProvider = "vonage" -) - -// Valid indicates whether the value is a known member of the AuthConfigResponseOutputSmsProvider enum. -func (e AuthConfigResponseOutputSmsProvider) Valid() bool { - switch e { - case AuthConfigResponseOutputSmsProviderLessThannil: - return true - case AuthConfigResponseOutputSmsProviderMessagebird: - return true - case AuthConfigResponseOutputSmsProviderTextlocal: - return true - case AuthConfigResponseOutputSmsProviderTwilio: - return true - case AuthConfigResponseOutputSmsProviderTwilioVerify: - return true - case AuthConfigResponseOutputSmsProviderVonage: - return true - default: - return false - } -} - -// Defines values for BranchDeleteResponseOutputMessage. -const ( - BranchDeleteResponseOutputMessageOk BranchDeleteResponseOutputMessage = "ok" -) - -// Valid indicates whether the value is a known member of the BranchDeleteResponseOutputMessage enum. -func (e BranchDeleteResponseOutputMessage) Valid() bool { - switch e { - case BranchDeleteResponseOutputMessageOk: - return true - default: - return false - } -} - -// Defines values for BranchDetailResponseOutputStatus. -const ( - BranchDetailResponseOutputStatusACTIVEHEALTHY BranchDetailResponseOutputStatus = "ACTIVE_HEALTHY" - BranchDetailResponseOutputStatusACTIVEUNHEALTHY BranchDetailResponseOutputStatus = "ACTIVE_UNHEALTHY" - BranchDetailResponseOutputStatusCOMINGUP BranchDetailResponseOutputStatus = "COMING_UP" - BranchDetailResponseOutputStatusGOINGDOWN BranchDetailResponseOutputStatus = "GOING_DOWN" - BranchDetailResponseOutputStatusINACTIVE BranchDetailResponseOutputStatus = "INACTIVE" - BranchDetailResponseOutputStatusINITFAILED BranchDetailResponseOutputStatus = "INIT_FAILED" - BranchDetailResponseOutputStatusPAUSEFAILED BranchDetailResponseOutputStatus = "PAUSE_FAILED" - BranchDetailResponseOutputStatusPAUSING BranchDetailResponseOutputStatus = "PAUSING" - BranchDetailResponseOutputStatusREMOVED BranchDetailResponseOutputStatus = "REMOVED" - BranchDetailResponseOutputStatusRESIZING BranchDetailResponseOutputStatus = "RESIZING" - BranchDetailResponseOutputStatusRESTARTING BranchDetailResponseOutputStatus = "RESTARTING" - BranchDetailResponseOutputStatusRESTOREFAILED BranchDetailResponseOutputStatus = "RESTORE_FAILED" - BranchDetailResponseOutputStatusRESTORING BranchDetailResponseOutputStatus = "RESTORING" - BranchDetailResponseOutputStatusUNKNOWN BranchDetailResponseOutputStatus = "UNKNOWN" - BranchDetailResponseOutputStatusUPGRADING BranchDetailResponseOutputStatus = "UPGRADING" -) - -// Valid indicates whether the value is a known member of the BranchDetailResponseOutputStatus enum. -func (e BranchDetailResponseOutputStatus) Valid() bool { - switch e { - case BranchDetailResponseOutputStatusACTIVEHEALTHY: - return true - case BranchDetailResponseOutputStatusACTIVEUNHEALTHY: - return true - case BranchDetailResponseOutputStatusCOMINGUP: - return true - case BranchDetailResponseOutputStatusGOINGDOWN: - return true - case BranchDetailResponseOutputStatusINACTIVE: - return true - case BranchDetailResponseOutputStatusINITFAILED: - return true - case BranchDetailResponseOutputStatusPAUSEFAILED: - return true - case BranchDetailResponseOutputStatusPAUSING: - return true - case BranchDetailResponseOutputStatusREMOVED: - return true - case BranchDetailResponseOutputStatusRESIZING: - return true - case BranchDetailResponseOutputStatusRESTARTING: - return true - case BranchDetailResponseOutputStatusRESTOREFAILED: - return true - case BranchDetailResponseOutputStatusRESTORING: - return true - case BranchDetailResponseOutputStatusUNKNOWN: - return true - case BranchDetailResponseOutputStatusUPGRADING: - return true - default: - return false - } -} - -// Defines values for BranchResponseOutputPreviewProjectStatus. -const ( - BranchResponseOutputPreviewProjectStatusACTIVEHEALTHY BranchResponseOutputPreviewProjectStatus = "ACTIVE_HEALTHY" - BranchResponseOutputPreviewProjectStatusACTIVEUNHEALTHY BranchResponseOutputPreviewProjectStatus = "ACTIVE_UNHEALTHY" - BranchResponseOutputPreviewProjectStatusCOMINGUP BranchResponseOutputPreviewProjectStatus = "COMING_UP" - BranchResponseOutputPreviewProjectStatusGOINGDOWN BranchResponseOutputPreviewProjectStatus = "GOING_DOWN" - BranchResponseOutputPreviewProjectStatusINACTIVE BranchResponseOutputPreviewProjectStatus = "INACTIVE" - BranchResponseOutputPreviewProjectStatusINITFAILED BranchResponseOutputPreviewProjectStatus = "INIT_FAILED" - BranchResponseOutputPreviewProjectStatusPAUSEFAILED BranchResponseOutputPreviewProjectStatus = "PAUSE_FAILED" - BranchResponseOutputPreviewProjectStatusPAUSING BranchResponseOutputPreviewProjectStatus = "PAUSING" - BranchResponseOutputPreviewProjectStatusREMOVED BranchResponseOutputPreviewProjectStatus = "REMOVED" - BranchResponseOutputPreviewProjectStatusRESIZING BranchResponseOutputPreviewProjectStatus = "RESIZING" - BranchResponseOutputPreviewProjectStatusRESTARTING BranchResponseOutputPreviewProjectStatus = "RESTARTING" - BranchResponseOutputPreviewProjectStatusRESTOREFAILED BranchResponseOutputPreviewProjectStatus = "RESTORE_FAILED" - BranchResponseOutputPreviewProjectStatusRESTORING BranchResponseOutputPreviewProjectStatus = "RESTORING" - BranchResponseOutputPreviewProjectStatusUNKNOWN BranchResponseOutputPreviewProjectStatus = "UNKNOWN" - BranchResponseOutputPreviewProjectStatusUPGRADING BranchResponseOutputPreviewProjectStatus = "UPGRADING" -) - -// Valid indicates whether the value is a known member of the BranchResponseOutputPreviewProjectStatus enum. -func (e BranchResponseOutputPreviewProjectStatus) Valid() bool { - switch e { - case BranchResponseOutputPreviewProjectStatusACTIVEHEALTHY: - return true - case BranchResponseOutputPreviewProjectStatusACTIVEUNHEALTHY: - return true - case BranchResponseOutputPreviewProjectStatusCOMINGUP: - return true - case BranchResponseOutputPreviewProjectStatusGOINGDOWN: - return true - case BranchResponseOutputPreviewProjectStatusINACTIVE: - return true - case BranchResponseOutputPreviewProjectStatusINITFAILED: - return true - case BranchResponseOutputPreviewProjectStatusPAUSEFAILED: - return true - case BranchResponseOutputPreviewProjectStatusPAUSING: - return true - case BranchResponseOutputPreviewProjectStatusREMOVED: - return true - case BranchResponseOutputPreviewProjectStatusRESIZING: - return true - case BranchResponseOutputPreviewProjectStatusRESTARTING: - return true - case BranchResponseOutputPreviewProjectStatusRESTOREFAILED: - return true - case BranchResponseOutputPreviewProjectStatusRESTORING: - return true - case BranchResponseOutputPreviewProjectStatusUNKNOWN: - return true - case BranchResponseOutputPreviewProjectStatusUPGRADING: - return true - default: - return false - } -} - -// Defines values for BranchResponseOutputStatus. -const ( - BranchResponseOutputStatusCREATINGPROJECT BranchResponseOutputStatus = "CREATING_PROJECT" - BranchResponseOutputStatusFUNCTIONSDEPLOYED BranchResponseOutputStatus = "FUNCTIONS_DEPLOYED" - BranchResponseOutputStatusFUNCTIONSFAILED BranchResponseOutputStatus = "FUNCTIONS_FAILED" - BranchResponseOutputStatusMIGRATIONSFAILED BranchResponseOutputStatus = "MIGRATIONS_FAILED" - BranchResponseOutputStatusMIGRATIONSPASSED BranchResponseOutputStatus = "MIGRATIONS_PASSED" - BranchResponseOutputStatusRUNNINGMIGRATIONS BranchResponseOutputStatus = "RUNNING_MIGRATIONS" -) - -// Valid indicates whether the value is a known member of the BranchResponseOutputStatus enum. -func (e BranchResponseOutputStatus) Valid() bool { - switch e { - case BranchResponseOutputStatusCREATINGPROJECT: - return true - case BranchResponseOutputStatusFUNCTIONSDEPLOYED: - return true - case BranchResponseOutputStatusFUNCTIONSFAILED: - return true - case BranchResponseOutputStatusMIGRATIONSFAILED: - return true - case BranchResponseOutputStatusMIGRATIONSPASSED: - return true - case BranchResponseOutputStatusRUNNINGMIGRATIONS: - return true - default: - return false - } -} - -// Defines values for BranchRestoreResponseOutputMessage. -const ( - BranchRestorationInitiated BranchRestoreResponseOutputMessage = "Branch restoration initiated" -) - -// Valid indicates whether the value is a known member of the BranchRestoreResponseOutputMessage enum. -func (e BranchRestoreResponseOutputMessage) Valid() bool { - switch e { - case BranchRestorationInitiated: - return true - default: - return false - } -} - -// Defines values for BranchUpdateResponseOutputMessage. -const ( - BranchUpdateResponseOutputMessageOk BranchUpdateResponseOutputMessage = "ok" -) - -// Valid indicates whether the value is a known member of the BranchUpdateResponseOutputMessage enum. -func (e BranchUpdateResponseOutputMessage) Valid() bool { - switch e { - case BranchUpdateResponseOutputMessageOk: - return true - default: - return false - } -} - -// Defines values for BulkUpdateFunctionBodyStatus. -const ( - BulkUpdateFunctionBodyStatusACTIVE BulkUpdateFunctionBodyStatus = "ACTIVE" - BulkUpdateFunctionBodyStatusREMOVED BulkUpdateFunctionBodyStatus = "REMOVED" - BulkUpdateFunctionBodyStatusTHROTTLED BulkUpdateFunctionBodyStatus = "THROTTLED" -) - -// Valid indicates whether the value is a known member of the BulkUpdateFunctionBodyStatus enum. -func (e BulkUpdateFunctionBodyStatus) Valid() bool { - switch e { - case BulkUpdateFunctionBodyStatusACTIVE: - return true - case BulkUpdateFunctionBodyStatusREMOVED: - return true - case BulkUpdateFunctionBodyStatusTHROTTLED: - return true - default: - return false - } -} - -// Defines values for BulkUpdateFunctionResponseOutputFunctionsStatus. -const ( - BulkUpdateFunctionResponseOutputFunctionsStatusACTIVE BulkUpdateFunctionResponseOutputFunctionsStatus = "ACTIVE" - BulkUpdateFunctionResponseOutputFunctionsStatusREMOVED BulkUpdateFunctionResponseOutputFunctionsStatus = "REMOVED" - BulkUpdateFunctionResponseOutputFunctionsStatusTHROTTLED BulkUpdateFunctionResponseOutputFunctionsStatus = "THROTTLED" -) - -// Valid indicates whether the value is a known member of the BulkUpdateFunctionResponseOutputFunctionsStatus enum. -func (e BulkUpdateFunctionResponseOutputFunctionsStatus) Valid() bool { - switch e { - case BulkUpdateFunctionResponseOutputFunctionsStatusACTIVE: - return true - case BulkUpdateFunctionResponseOutputFunctionsStatusREMOVED: - return true - case BulkUpdateFunctionResponseOutputFunctionsStatusTHROTTLED: - return true - default: - return false - } -} - -// Defines values for CreateApiKeyBodyType. -const ( - Publishable CreateApiKeyBodyType = "publishable" - Secret CreateApiKeyBodyType = "secret" -) - -// Valid indicates whether the value is a known member of the CreateApiKeyBodyType enum. -func (e CreateApiKeyBodyType) Valid() bool { - switch e { - case Publishable: - return true - case Secret: - return true - default: - return false - } -} - -// Defines values for CreateBranchBodyDesiredInstanceSize. -const ( - CreateBranchBodyDesiredInstanceSizeLarge CreateBranchBodyDesiredInstanceSize = "large" - CreateBranchBodyDesiredInstanceSizeMedium CreateBranchBodyDesiredInstanceSize = "medium" - CreateBranchBodyDesiredInstanceSizeMicro CreateBranchBodyDesiredInstanceSize = "micro" - CreateBranchBodyDesiredInstanceSizeN12xlarge CreateBranchBodyDesiredInstanceSize = "12xlarge" - CreateBranchBodyDesiredInstanceSizeN16xlarge CreateBranchBodyDesiredInstanceSize = "16xlarge" - CreateBranchBodyDesiredInstanceSizeN24xlarge CreateBranchBodyDesiredInstanceSize = "24xlarge" - CreateBranchBodyDesiredInstanceSizeN24xlargeHighMemory CreateBranchBodyDesiredInstanceSize = "24xlarge_high_memory" - CreateBranchBodyDesiredInstanceSizeN24xlargeOptimizedCpu CreateBranchBodyDesiredInstanceSize = "24xlarge_optimized_cpu" - CreateBranchBodyDesiredInstanceSizeN24xlargeOptimizedMemory CreateBranchBodyDesiredInstanceSize = "24xlarge_optimized_memory" - CreateBranchBodyDesiredInstanceSizeN2xlarge CreateBranchBodyDesiredInstanceSize = "2xlarge" - CreateBranchBodyDesiredInstanceSizeN48xlarge CreateBranchBodyDesiredInstanceSize = "48xlarge" - CreateBranchBodyDesiredInstanceSizeN48xlargeHighMemory CreateBranchBodyDesiredInstanceSize = "48xlarge_high_memory" - CreateBranchBodyDesiredInstanceSizeN48xlargeOptimizedCpu CreateBranchBodyDesiredInstanceSize = "48xlarge_optimized_cpu" - CreateBranchBodyDesiredInstanceSizeN48xlargeOptimizedMemory CreateBranchBodyDesiredInstanceSize = "48xlarge_optimized_memory" - CreateBranchBodyDesiredInstanceSizeN4xlarge CreateBranchBodyDesiredInstanceSize = "4xlarge" - CreateBranchBodyDesiredInstanceSizeN8xlarge CreateBranchBodyDesiredInstanceSize = "8xlarge" - CreateBranchBodyDesiredInstanceSizeNano CreateBranchBodyDesiredInstanceSize = "nano" - CreateBranchBodyDesiredInstanceSizePico CreateBranchBodyDesiredInstanceSize = "pico" - CreateBranchBodyDesiredInstanceSizeSmall CreateBranchBodyDesiredInstanceSize = "small" - CreateBranchBodyDesiredInstanceSizeXlarge CreateBranchBodyDesiredInstanceSize = "xlarge" -) - -// Valid indicates whether the value is a known member of the CreateBranchBodyDesiredInstanceSize enum. -func (e CreateBranchBodyDesiredInstanceSize) Valid() bool { - switch e { - case CreateBranchBodyDesiredInstanceSizeLarge: - return true - case CreateBranchBodyDesiredInstanceSizeMedium: - return true - case CreateBranchBodyDesiredInstanceSizeMicro: - return true - case CreateBranchBodyDesiredInstanceSizeN12xlarge: - return true - case CreateBranchBodyDesiredInstanceSizeN16xlarge: - return true - case CreateBranchBodyDesiredInstanceSizeN24xlarge: - return true - case CreateBranchBodyDesiredInstanceSizeN24xlargeHighMemory: - return true - case CreateBranchBodyDesiredInstanceSizeN24xlargeOptimizedCpu: - return true - case CreateBranchBodyDesiredInstanceSizeN24xlargeOptimizedMemory: - return true - case CreateBranchBodyDesiredInstanceSizeN2xlarge: - return true - case CreateBranchBodyDesiredInstanceSizeN48xlarge: - return true - case CreateBranchBodyDesiredInstanceSizeN48xlargeHighMemory: - return true - case CreateBranchBodyDesiredInstanceSizeN48xlargeOptimizedCpu: - return true - case CreateBranchBodyDesiredInstanceSizeN48xlargeOptimizedMemory: - return true - case CreateBranchBodyDesiredInstanceSizeN4xlarge: - return true - case CreateBranchBodyDesiredInstanceSizeN8xlarge: - return true - case CreateBranchBodyDesiredInstanceSizeNano: - return true - case CreateBranchBodyDesiredInstanceSizePico: - return true - case CreateBranchBodyDesiredInstanceSizeSmall: - return true - case CreateBranchBodyDesiredInstanceSizeXlarge: - return true - default: - return false - } -} - -// Defines values for CreateBranchBodyPostgresEngine. -const ( - CreateBranchBodyPostgresEngineN15 CreateBranchBodyPostgresEngine = "15" - CreateBranchBodyPostgresEngineN17 CreateBranchBodyPostgresEngine = "17" - CreateBranchBodyPostgresEngineN17Oriole CreateBranchBodyPostgresEngine = "17-oriole" -) - -// Valid indicates whether the value is a known member of the CreateBranchBodyPostgresEngine enum. -func (e CreateBranchBodyPostgresEngine) Valid() bool { - switch e { - case CreateBranchBodyPostgresEngineN15: - return true - case CreateBranchBodyPostgresEngineN17: - return true - case CreateBranchBodyPostgresEngineN17Oriole: - return true - default: - return false - } -} - -// Defines values for CreateBranchBodyReleaseChannel. -const ( - CreateBranchBodyReleaseChannelAlpha CreateBranchBodyReleaseChannel = "alpha" - CreateBranchBodyReleaseChannelBeta CreateBranchBodyReleaseChannel = "beta" - CreateBranchBodyReleaseChannelGa CreateBranchBodyReleaseChannel = "ga" - CreateBranchBodyReleaseChannelInternal CreateBranchBodyReleaseChannel = "internal" - CreateBranchBodyReleaseChannelPreview CreateBranchBodyReleaseChannel = "preview" - CreateBranchBodyReleaseChannelWithdrawn CreateBranchBodyReleaseChannel = "withdrawn" -) - -// Valid indicates whether the value is a known member of the CreateBranchBodyReleaseChannel enum. -func (e CreateBranchBodyReleaseChannel) Valid() bool { - switch e { - case CreateBranchBodyReleaseChannelAlpha: - return true - case CreateBranchBodyReleaseChannelBeta: - return true - case CreateBranchBodyReleaseChannelGa: - return true - case CreateBranchBodyReleaseChannelInternal: - return true - case CreateBranchBodyReleaseChannelPreview: - return true - case CreateBranchBodyReleaseChannelWithdrawn: - return true - default: - return false - } -} - -// Defines values for CreateProviderBodyNameIdFormat. -const ( - CreateProviderBodyNameIdFormatUrnOasisNamesTcSAML11NameidFormatEmailAddress CreateProviderBodyNameIdFormat = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" - CreateProviderBodyNameIdFormatUrnOasisNamesTcSAML11NameidFormatUnspecified CreateProviderBodyNameIdFormat = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" - CreateProviderBodyNameIdFormatUrnOasisNamesTcSAML20NameidFormatPersistent CreateProviderBodyNameIdFormat = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" - CreateProviderBodyNameIdFormatUrnOasisNamesTcSAML20NameidFormatTransient CreateProviderBodyNameIdFormat = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" -) - -// Valid indicates whether the value is a known member of the CreateProviderBodyNameIdFormat enum. -func (e CreateProviderBodyNameIdFormat) Valid() bool { - switch e { - case CreateProviderBodyNameIdFormatUrnOasisNamesTcSAML11NameidFormatEmailAddress: - return true - case CreateProviderBodyNameIdFormatUrnOasisNamesTcSAML11NameidFormatUnspecified: - return true - case CreateProviderBodyNameIdFormatUrnOasisNamesTcSAML20NameidFormatPersistent: - return true - case CreateProviderBodyNameIdFormatUrnOasisNamesTcSAML20NameidFormatTransient: - return true - default: - return false - } -} - -// Defines values for CreateProviderBodyType. -const ( - Saml CreateProviderBodyType = "saml" -) - -// Valid indicates whether the value is a known member of the CreateProviderBodyType enum. -func (e CreateProviderBodyType) Valid() bool { - switch e { - case Saml: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyAlgorithm. -const ( - CreateSigningKeyBodyAlgorithmES256 CreateSigningKeyBodyAlgorithm = "ES256" - CreateSigningKeyBodyAlgorithmEdDSA CreateSigningKeyBodyAlgorithm = "EdDSA" - CreateSigningKeyBodyAlgorithmHS256 CreateSigningKeyBodyAlgorithm = "HS256" - CreateSigningKeyBodyAlgorithmRS256 CreateSigningKeyBodyAlgorithm = "RS256" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyAlgorithm enum. -func (e CreateSigningKeyBodyAlgorithm) Valid() bool { - switch e { - case CreateSigningKeyBodyAlgorithmES256: - return true - case CreateSigningKeyBodyAlgorithmEdDSA: - return true - case CreateSigningKeyBodyAlgorithmHS256: - return true - case CreateSigningKeyBodyAlgorithmRS256: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk0Alg. -const ( - CreateSigningKeyBodyPrivateJwk0AlgRS256 CreateSigningKeyBodyPrivateJwk0Alg = "RS256" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk0Alg enum. -func (e CreateSigningKeyBodyPrivateJwk0Alg) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk0AlgRS256: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk0E. -const ( - AQAB CreateSigningKeyBodyPrivateJwk0E = "AQAB" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk0E enum. -func (e CreateSigningKeyBodyPrivateJwk0E) Valid() bool { - switch e { - case AQAB: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk0Ext. -const ( - CreateSigningKeyBodyPrivateJwk0ExtTrue CreateSigningKeyBodyPrivateJwk0Ext = true -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk0Ext enum. -func (e CreateSigningKeyBodyPrivateJwk0Ext) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk0ExtTrue: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk0KeyOps. -const ( - CreateSigningKeyBodyPrivateJwk0KeyOpsSign CreateSigningKeyBodyPrivateJwk0KeyOps = "sign" - CreateSigningKeyBodyPrivateJwk0KeyOpsVerify CreateSigningKeyBodyPrivateJwk0KeyOps = "verify" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk0KeyOps enum. -func (e CreateSigningKeyBodyPrivateJwk0KeyOps) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk0KeyOpsSign: - return true - case CreateSigningKeyBodyPrivateJwk0KeyOpsVerify: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk0Kty. -const ( - RSA CreateSigningKeyBodyPrivateJwk0Kty = "RSA" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk0Kty enum. -func (e CreateSigningKeyBodyPrivateJwk0Kty) Valid() bool { - switch e { - case RSA: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk0Use. -const ( - CreateSigningKeyBodyPrivateJwk0UseSig CreateSigningKeyBodyPrivateJwk0Use = "sig" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk0Use enum. -func (e CreateSigningKeyBodyPrivateJwk0Use) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk0UseSig: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk1Alg. -const ( - CreateSigningKeyBodyPrivateJwk1AlgES256 CreateSigningKeyBodyPrivateJwk1Alg = "ES256" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk1Alg enum. -func (e CreateSigningKeyBodyPrivateJwk1Alg) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk1AlgES256: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk1Crv. -const ( - P256 CreateSigningKeyBodyPrivateJwk1Crv = "P-256" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk1Crv enum. -func (e CreateSigningKeyBodyPrivateJwk1Crv) Valid() bool { - switch e { - case P256: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk1Ext. -const ( - CreateSigningKeyBodyPrivateJwk1ExtTrue CreateSigningKeyBodyPrivateJwk1Ext = true -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk1Ext enum. -func (e CreateSigningKeyBodyPrivateJwk1Ext) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk1ExtTrue: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk1KeyOps. -const ( - CreateSigningKeyBodyPrivateJwk1KeyOpsSign CreateSigningKeyBodyPrivateJwk1KeyOps = "sign" - CreateSigningKeyBodyPrivateJwk1KeyOpsVerify CreateSigningKeyBodyPrivateJwk1KeyOps = "verify" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk1KeyOps enum. -func (e CreateSigningKeyBodyPrivateJwk1KeyOps) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk1KeyOpsSign: - return true - case CreateSigningKeyBodyPrivateJwk1KeyOpsVerify: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk1Kty. -const ( - EC CreateSigningKeyBodyPrivateJwk1Kty = "EC" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk1Kty enum. -func (e CreateSigningKeyBodyPrivateJwk1Kty) Valid() bool { - switch e { - case EC: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk1Use. -const ( - CreateSigningKeyBodyPrivateJwk1UseSig CreateSigningKeyBodyPrivateJwk1Use = "sig" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk1Use enum. -func (e CreateSigningKeyBodyPrivateJwk1Use) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk1UseSig: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk2Alg. -const ( - CreateSigningKeyBodyPrivateJwk2AlgEdDSA CreateSigningKeyBodyPrivateJwk2Alg = "EdDSA" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk2Alg enum. -func (e CreateSigningKeyBodyPrivateJwk2Alg) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk2AlgEdDSA: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk2Crv. -const ( - Ed25519 CreateSigningKeyBodyPrivateJwk2Crv = "Ed25519" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk2Crv enum. -func (e CreateSigningKeyBodyPrivateJwk2Crv) Valid() bool { - switch e { - case Ed25519: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk2Ext. -const ( - CreateSigningKeyBodyPrivateJwk2ExtTrue CreateSigningKeyBodyPrivateJwk2Ext = true -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk2Ext enum. -func (e CreateSigningKeyBodyPrivateJwk2Ext) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk2ExtTrue: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk2KeyOps. -const ( - CreateSigningKeyBodyPrivateJwk2KeyOpsSign CreateSigningKeyBodyPrivateJwk2KeyOps = "sign" - CreateSigningKeyBodyPrivateJwk2KeyOpsVerify CreateSigningKeyBodyPrivateJwk2KeyOps = "verify" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk2KeyOps enum. -func (e CreateSigningKeyBodyPrivateJwk2KeyOps) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk2KeyOpsSign: - return true - case CreateSigningKeyBodyPrivateJwk2KeyOpsVerify: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk2Kty. -const ( - OKP CreateSigningKeyBodyPrivateJwk2Kty = "OKP" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk2Kty enum. -func (e CreateSigningKeyBodyPrivateJwk2Kty) Valid() bool { - switch e { - case OKP: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk2Use. -const ( - CreateSigningKeyBodyPrivateJwk2UseSig CreateSigningKeyBodyPrivateJwk2Use = "sig" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk2Use enum. -func (e CreateSigningKeyBodyPrivateJwk2Use) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk2UseSig: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk3Alg. -const ( - HS256 CreateSigningKeyBodyPrivateJwk3Alg = "HS256" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk3Alg enum. -func (e CreateSigningKeyBodyPrivateJwk3Alg) Valid() bool { - switch e { - case HS256: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk3Ext. -const ( - CreateSigningKeyBodyPrivateJwk3ExtTrue CreateSigningKeyBodyPrivateJwk3Ext = true -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk3Ext enum. -func (e CreateSigningKeyBodyPrivateJwk3Ext) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk3ExtTrue: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk3KeyOps. -const ( - CreateSigningKeyBodyPrivateJwk3KeyOpsSign CreateSigningKeyBodyPrivateJwk3KeyOps = "sign" - CreateSigningKeyBodyPrivateJwk3KeyOpsVerify CreateSigningKeyBodyPrivateJwk3KeyOps = "verify" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk3KeyOps enum. -func (e CreateSigningKeyBodyPrivateJwk3KeyOps) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk3KeyOpsSign: - return true - case CreateSigningKeyBodyPrivateJwk3KeyOpsVerify: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk3Kty. -const ( - Oct CreateSigningKeyBodyPrivateJwk3Kty = "oct" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk3Kty enum. -func (e CreateSigningKeyBodyPrivateJwk3Kty) Valid() bool { - switch e { - case Oct: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyPrivateJwk3Use. -const ( - CreateSigningKeyBodyPrivateJwk3UseSig CreateSigningKeyBodyPrivateJwk3Use = "sig" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyPrivateJwk3Use enum. -func (e CreateSigningKeyBodyPrivateJwk3Use) Valid() bool { - switch e { - case CreateSigningKeyBodyPrivateJwk3UseSig: - return true - default: - return false - } -} - -// Defines values for CreateSigningKeyBodyStatus. -const ( - CreateSigningKeyBodyStatusInUse CreateSigningKeyBodyStatus = "in_use" - CreateSigningKeyBodyStatusStandby CreateSigningKeyBodyStatus = "standby" -) - -// Valid indicates whether the value is a known member of the CreateSigningKeyBodyStatus enum. -func (e CreateSigningKeyBodyStatus) Valid() bool { - switch e { - case CreateSigningKeyBodyStatusInUse: - return true - case CreateSigningKeyBodyStatusStandby: - return true - default: - return false - } -} - -// Defines values for DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError. -const ( - N1UpgradedInstanceLaunchFailed DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError = "1_upgraded_instance_launch_failed" - N2VolumeDetachchmentFromUpgradedInstanceFailed DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError = "2_volume_detachchment_from_upgraded_instance_failed" - N3VolumeAttachmentToOriginalInstanceFailed DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError = "3_volume_attachment_to_original_instance_failed" - N4DataUpgradeInitiationFailed DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError = "4_data_upgrade_initiation_failed" - N5DataUpgradeCompletionFailed DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError = "5_data_upgrade_completion_failed" - N6VolumeDetachchmentFromOriginalInstanceFailed DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError = "6_volume_detachchment_from_original_instance_failed" - N7VolumeAttachmentToUpgradedInstanceFailed DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError = "7_volume_attachment_to_upgraded_instance_failed" - N8UpgradeCompletionFailed DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError = "8_upgrade_completion_failed" - N9PostPhysicalBackupFailed DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError = "9_post_physical_backup_failed" -) - -// Valid indicates whether the value is a known member of the DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError enum. -func (e DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError) Valid() bool { - switch e { - case N1UpgradedInstanceLaunchFailed: - return true - case N2VolumeDetachchmentFromUpgradedInstanceFailed: - return true - case N3VolumeAttachmentToOriginalInstanceFailed: - return true - case N4DataUpgradeInitiationFailed: - return true - case N5DataUpgradeCompletionFailed: - return true - case N6VolumeDetachchmentFromOriginalInstanceFailed: - return true - case N7VolumeAttachmentToUpgradedInstanceFailed: - return true - case N8UpgradeCompletionFailed: - return true - case N9PostPhysicalBackupFailed: - return true - default: - return false - } -} - -// Defines values for DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress. -const ( - N0Requested DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress = "0_requested" - N10CompletedPostPhysicalBackup DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress = "10_completed_post_physical_backup" - N1Started DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress = "1_started" - N2LaunchedUpgradedInstance DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress = "2_launched_upgraded_instance" - N3DetachedVolumeFromUpgradedInstance DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress = "3_detached_volume_from_upgraded_instance" - N4AttachedVolumeToOriginalInstance DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress = "4_attached_volume_to_original_instance" - N5InitiatedDataUpgrade DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress = "5_initiated_data_upgrade" - N6CompletedDataUpgrade DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress = "6_completed_data_upgrade" - N7DetachedVolumeFromOriginalInstance DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress = "7_detached_volume_from_original_instance" - N8AttachedVolumeToUpgradedInstance DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress = "8_attached_volume_to_upgraded_instance" - N9CompletedUpgrade DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress = "9_completed_upgrade" -) - -// Valid indicates whether the value is a known member of the DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress enum. -func (e DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress) Valid() bool { - switch e { - case N0Requested: - return true - case N10CompletedPostPhysicalBackup: - return true - case N1Started: - return true - case N2LaunchedUpgradedInstance: - return true - case N3DetachedVolumeFromUpgradedInstance: - return true - case N4AttachedVolumeToOriginalInstance: - return true - case N5InitiatedDataUpgrade: - return true - case N6CompletedDataUpgrade: - return true - case N7DetachedVolumeFromOriginalInstance: - return true - case N8AttachedVolumeToUpgradedInstance: - return true - case N9CompletedUpgrade: - return true - default: - return false - } -} - -// Defines values for DeleteRolesResponseOutputMessage. -const ( - DeleteRolesResponseOutputMessageOk DeleteRolesResponseOutputMessage = "ok" -) - -// Valid indicates whether the value is a known member of the DeleteRolesResponseOutputMessage enum. -func (e DeleteRolesResponseOutputMessage) Valid() bool { - switch e { - case DeleteRolesResponseOutputMessageOk: - return true - default: - return false - } -} - -// Defines values for DeployFunctionResponseOutputStatus. -const ( - DeployFunctionResponseOutputStatusACTIVE DeployFunctionResponseOutputStatus = "ACTIVE" - DeployFunctionResponseOutputStatusREMOVED DeployFunctionResponseOutputStatus = "REMOVED" - DeployFunctionResponseOutputStatusTHROTTLED DeployFunctionResponseOutputStatus = "THROTTLED" -) - -// Valid indicates whether the value is a known member of the DeployFunctionResponseOutputStatus enum. -func (e DeployFunctionResponseOutputStatus) Valid() bool { - switch e { - case DeployFunctionResponseOutputStatusACTIVE: - return true - case DeployFunctionResponseOutputStatusREMOVED: - return true - case DeployFunctionResponseOutputStatusTHROTTLED: - return true - default: - return false - } -} - -// Defines values for DiskRequestBodyAttributes0Type. -const ( - DiskRequestBodyAttributes0TypeGp3 DiskRequestBodyAttributes0Type = "gp3" -) - -// Valid indicates whether the value is a known member of the DiskRequestBodyAttributes0Type enum. -func (e DiskRequestBodyAttributes0Type) Valid() bool { - switch e { - case DiskRequestBodyAttributes0TypeGp3: - return true - default: - return false - } -} - -// Defines values for DiskRequestBodyAttributes1Type. -const ( - DiskRequestBodyAttributes1TypeIo2 DiskRequestBodyAttributes1Type = "io2" -) - -// Valid indicates whether the value is a known member of the DiskRequestBodyAttributes1Type enum. -func (e DiskRequestBodyAttributes1Type) Valid() bool { - switch e { - case DiskRequestBodyAttributes1TypeIo2: - return true - default: - return false - } -} - -// Defines values for DiskResponseOutputAttributes0Type. -const ( - DiskResponseOutputAttributes0TypeGp3 DiskResponseOutputAttributes0Type = "gp3" -) - -// Valid indicates whether the value is a known member of the DiskResponseOutputAttributes0Type enum. -func (e DiskResponseOutputAttributes0Type) Valid() bool { - switch e { - case DiskResponseOutputAttributes0TypeGp3: - return true - default: - return false - } -} - -// Defines values for DiskResponseOutputAttributes1Type. -const ( - DiskResponseOutputAttributes1TypeIo2 DiskResponseOutputAttributes1Type = "io2" -) - -// Valid indicates whether the value is a known member of the DiskResponseOutputAttributes1Type enum. -func (e DiskResponseOutputAttributes1Type) Valid() bool { - switch e { - case DiskResponseOutputAttributes1TypeIo2: - return true - default: - return false - } -} - -// Defines values for FunctionResponseOutputStatus. -const ( - FunctionResponseOutputStatusACTIVE FunctionResponseOutputStatus = "ACTIVE" - FunctionResponseOutputStatusREMOVED FunctionResponseOutputStatus = "REMOVED" - FunctionResponseOutputStatusTHROTTLED FunctionResponseOutputStatus = "THROTTLED" -) - -// Valid indicates whether the value is a known member of the FunctionResponseOutputStatus enum. -func (e FunctionResponseOutputStatus) Valid() bool { - switch e { - case FunctionResponseOutputStatusACTIVE: - return true - case FunctionResponseOutputStatusREMOVED: - return true - case FunctionResponseOutputStatusTHROTTLED: - return true - default: - return false - } -} - -// Defines values for FunctionSlugResponseOutputStatus. -const ( - FunctionSlugResponseOutputStatusACTIVE FunctionSlugResponseOutputStatus = "ACTIVE" - FunctionSlugResponseOutputStatusREMOVED FunctionSlugResponseOutputStatus = "REMOVED" - FunctionSlugResponseOutputStatusTHROTTLED FunctionSlugResponseOutputStatus = "THROTTLED" -) - -// Valid indicates whether the value is a known member of the FunctionSlugResponseOutputStatus enum. -func (e FunctionSlugResponseOutputStatus) Valid() bool { - switch e { - case FunctionSlugResponseOutputStatusACTIVE: - return true - case FunctionSlugResponseOutputStatusREMOVED: - return true - case FunctionSlugResponseOutputStatusTHROTTLED: - return true - default: - return false - } -} - -// Defines values for GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngine. -const ( - GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngineN13 GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngine = "13" - GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngineN14 GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngine = "14" - GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngineN15 GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngine = "15" - GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngineN17 GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngine = "17" - GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngineN17Oriole GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngine = "17-oriole" -) - -// Valid indicates whether the value is a known member of the GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngine enum. -func (e GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngine) Valid() bool { - switch e { - case GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngineN13: - return true - case GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngineN14: - return true - case GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngineN15: - return true - case GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngineN17: - return true - case GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngineN17Oriole: - return true - default: - return false - } -} - -// Defines values for GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannel. -const ( - GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannelAlpha GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannel = "alpha" - GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannelBeta GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannel = "beta" - GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannelGa GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannel = "ga" - GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannelInternal GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannel = "internal" - GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannelPreview GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannel = "preview" - GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannelWithdrawn GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannel = "withdrawn" -) - -// Valid indicates whether the value is a known member of the GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannel enum. -func (e GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannel) Valid() bool { - switch e { - case GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannelAlpha: - return true - case GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannelBeta: - return true - case GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannelGa: - return true - case GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannelInternal: - return true - case GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannelPreview: - return true - case GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannelWithdrawn: - return true - default: - return false - } -} - -// Defines values for JitAccessRequestRequestState. -const ( - JitAccessRequestRequestStateDisabled JitAccessRequestRequestState = "disabled" - JitAccessRequestRequestStateEnabled JitAccessRequestRequestState = "enabled" -) - -// Valid indicates whether the value is a known member of the JitAccessRequestRequestState enum. -func (e JitAccessRequestRequestState) Valid() bool { - switch e { - case JitAccessRequestRequestStateDisabled: - return true - case JitAccessRequestRequestStateEnabled: - return true - default: - return false - } -} - -// Defines values for ListActionRunResponseOutputRunStepsName. -const ( - ListActionRunResponseOutputRunStepsNameClone ListActionRunResponseOutputRunStepsName = "clone" - ListActionRunResponseOutputRunStepsNameConfigure ListActionRunResponseOutputRunStepsName = "configure" - ListActionRunResponseOutputRunStepsNameDeploy ListActionRunResponseOutputRunStepsName = "deploy" - ListActionRunResponseOutputRunStepsNameHealth ListActionRunResponseOutputRunStepsName = "health" - ListActionRunResponseOutputRunStepsNameMigrate ListActionRunResponseOutputRunStepsName = "migrate" - ListActionRunResponseOutputRunStepsNamePull ListActionRunResponseOutputRunStepsName = "pull" - ListActionRunResponseOutputRunStepsNameSeed ListActionRunResponseOutputRunStepsName = "seed" -) - -// Valid indicates whether the value is a known member of the ListActionRunResponseOutputRunStepsName enum. -func (e ListActionRunResponseOutputRunStepsName) Valid() bool { - switch e { - case ListActionRunResponseOutputRunStepsNameClone: - return true - case ListActionRunResponseOutputRunStepsNameConfigure: - return true - case ListActionRunResponseOutputRunStepsNameDeploy: - return true - case ListActionRunResponseOutputRunStepsNameHealth: - return true - case ListActionRunResponseOutputRunStepsNameMigrate: - return true - case ListActionRunResponseOutputRunStepsNamePull: - return true - case ListActionRunResponseOutputRunStepsNameSeed: - return true - default: - return false - } -} - -// Defines values for ListActionRunResponseOutputRunStepsStatus. -const ( - ListActionRunResponseOutputRunStepsStatusCREATED ListActionRunResponseOutputRunStepsStatus = "CREATED" - ListActionRunResponseOutputRunStepsStatusDEAD ListActionRunResponseOutputRunStepsStatus = "DEAD" - ListActionRunResponseOutputRunStepsStatusEXITED ListActionRunResponseOutputRunStepsStatus = "EXITED" - ListActionRunResponseOutputRunStepsStatusPAUSED ListActionRunResponseOutputRunStepsStatus = "PAUSED" - ListActionRunResponseOutputRunStepsStatusREMOVING ListActionRunResponseOutputRunStepsStatus = "REMOVING" - ListActionRunResponseOutputRunStepsStatusRESTARTING ListActionRunResponseOutputRunStepsStatus = "RESTARTING" - ListActionRunResponseOutputRunStepsStatusRUNNING ListActionRunResponseOutputRunStepsStatus = "RUNNING" -) - -// Valid indicates whether the value is a known member of the ListActionRunResponseOutputRunStepsStatus enum. -func (e ListActionRunResponseOutputRunStepsStatus) Valid() bool { - switch e { - case ListActionRunResponseOutputRunStepsStatusCREATED: - return true - case ListActionRunResponseOutputRunStepsStatusDEAD: - return true - case ListActionRunResponseOutputRunStepsStatusEXITED: - return true - case ListActionRunResponseOutputRunStepsStatusPAUSED: - return true - case ListActionRunResponseOutputRunStepsStatusREMOVING: - return true - case ListActionRunResponseOutputRunStepsStatusRESTARTING: - return true - case ListActionRunResponseOutputRunStepsStatusRUNNING: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputAvailableAddonsType. -const ( - ListProjectAddonsResponseOutputAvailableAddonsTypeAuthMfaPhone ListProjectAddonsResponseOutputAvailableAddonsType = "auth_mfa_phone" - ListProjectAddonsResponseOutputAvailableAddonsTypeAuthMfaWebAuthn ListProjectAddonsResponseOutputAvailableAddonsType = "auth_mfa_web_authn" - ListProjectAddonsResponseOutputAvailableAddonsTypeComputeInstance ListProjectAddonsResponseOutputAvailableAddonsType = "compute_instance" - ListProjectAddonsResponseOutputAvailableAddonsTypeCustomDomain ListProjectAddonsResponseOutputAvailableAddonsType = "custom_domain" - ListProjectAddonsResponseOutputAvailableAddonsTypeEtlPipeline ListProjectAddonsResponseOutputAvailableAddonsType = "etl_pipeline" - ListProjectAddonsResponseOutputAvailableAddonsTypeIpv4 ListProjectAddonsResponseOutputAvailableAddonsType = "ipv4" - ListProjectAddonsResponseOutputAvailableAddonsTypeLogDrain ListProjectAddonsResponseOutputAvailableAddonsType = "log_drain" - ListProjectAddonsResponseOutputAvailableAddonsTypePitr ListProjectAddonsResponseOutputAvailableAddonsType = "pitr" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputAvailableAddonsType enum. -func (e ListProjectAddonsResponseOutputAvailableAddonsType) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputAvailableAddonsTypeAuthMfaPhone: - return true - case ListProjectAddonsResponseOutputAvailableAddonsTypeAuthMfaWebAuthn: - return true - case ListProjectAddonsResponseOutputAvailableAddonsTypeComputeInstance: - return true - case ListProjectAddonsResponseOutputAvailableAddonsTypeCustomDomain: - return true - case ListProjectAddonsResponseOutputAvailableAddonsTypeEtlPipeline: - return true - case ListProjectAddonsResponseOutputAvailableAddonsTypeIpv4: - return true - case ListProjectAddonsResponseOutputAvailableAddonsTypeLogDrain: - return true - case ListProjectAddonsResponseOutputAvailableAddonsTypePitr: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputAvailableAddonsVariantsId0. -const ( - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci12xlarge ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_12xlarge" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci16xlarge ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_16xlarge" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci24xlarge ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_24xlarge" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci24xlargeHighMemory ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_24xlarge_high_memory" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci24xlargeOptimizedCpu ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_24xlarge_optimized_cpu" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci24xlargeOptimizedMemory ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_24xlarge_optimized_memory" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci2xlarge ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_2xlarge" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci48xlarge ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_48xlarge" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci48xlargeHighMemory ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_48xlarge_high_memory" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci48xlargeOptimizedCpu ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_48xlarge_optimized_cpu" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci48xlargeOptimizedMemory ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_48xlarge_optimized_memory" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci4xlarge ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_4xlarge" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci8xlarge ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_8xlarge" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0CiLarge ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_large" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0CiMedium ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_medium" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0CiMicro ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_micro" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0CiSmall ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_small" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId0CiXlarge ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 = "ci_xlarge" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 enum. -func (e ListProjectAddonsResponseOutputAvailableAddonsVariantsId0) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci12xlarge: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci16xlarge: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci24xlarge: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci24xlargeHighMemory: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci24xlargeOptimizedCpu: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci24xlargeOptimizedMemory: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci2xlarge: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci48xlarge: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci48xlargeHighMemory: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci48xlargeOptimizedCpu: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci48xlargeOptimizedMemory: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci4xlarge: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0Ci8xlarge: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0CiLarge: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0CiMedium: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0CiMicro: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0CiSmall: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId0CiXlarge: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputAvailableAddonsVariantsId1. -const ( - ListProjectAddonsResponseOutputAvailableAddonsVariantsId1CdDefault ListProjectAddonsResponseOutputAvailableAddonsVariantsId1 = "cd_default" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputAvailableAddonsVariantsId1 enum. -func (e ListProjectAddonsResponseOutputAvailableAddonsVariantsId1) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId1CdDefault: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputAvailableAddonsVariantsId2. -const ( - ListProjectAddonsResponseOutputAvailableAddonsVariantsId2Pitr14 ListProjectAddonsResponseOutputAvailableAddonsVariantsId2 = "pitr_14" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId2Pitr28 ListProjectAddonsResponseOutputAvailableAddonsVariantsId2 = "pitr_28" - ListProjectAddonsResponseOutputAvailableAddonsVariantsId2Pitr7 ListProjectAddonsResponseOutputAvailableAddonsVariantsId2 = "pitr_7" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputAvailableAddonsVariantsId2 enum. -func (e ListProjectAddonsResponseOutputAvailableAddonsVariantsId2) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId2Pitr14: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId2Pitr28: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId2Pitr7: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputAvailableAddonsVariantsId3. -const ( - ListProjectAddonsResponseOutputAvailableAddonsVariantsId3Ipv4Default ListProjectAddonsResponseOutputAvailableAddonsVariantsId3 = "ipv4_default" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputAvailableAddonsVariantsId3 enum. -func (e ListProjectAddonsResponseOutputAvailableAddonsVariantsId3) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId3Ipv4Default: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputAvailableAddonsVariantsId4. -const ( - ListProjectAddonsResponseOutputAvailableAddonsVariantsId4AuthMfaPhoneDefault ListProjectAddonsResponseOutputAvailableAddonsVariantsId4 = "auth_mfa_phone_default" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputAvailableAddonsVariantsId4 enum. -func (e ListProjectAddonsResponseOutputAvailableAddonsVariantsId4) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId4AuthMfaPhoneDefault: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputAvailableAddonsVariantsId5. -const ( - ListProjectAddonsResponseOutputAvailableAddonsVariantsId5AuthMfaWebAuthnDefault ListProjectAddonsResponseOutputAvailableAddonsVariantsId5 = "auth_mfa_web_authn_default" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputAvailableAddonsVariantsId5 enum. -func (e ListProjectAddonsResponseOutputAvailableAddonsVariantsId5) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId5AuthMfaWebAuthnDefault: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputAvailableAddonsVariantsId6. -const ( - ListProjectAddonsResponseOutputAvailableAddonsVariantsId6LogDrainDefault ListProjectAddonsResponseOutputAvailableAddonsVariantsId6 = "log_drain_default" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputAvailableAddonsVariantsId6 enum. -func (e ListProjectAddonsResponseOutputAvailableAddonsVariantsId6) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId6LogDrainDefault: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputAvailableAddonsVariantsId7. -const ( - ListProjectAddonsResponseOutputAvailableAddonsVariantsId7EtlPipelineDefault ListProjectAddonsResponseOutputAvailableAddonsVariantsId7 = "etl_pipeline_default" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputAvailableAddonsVariantsId7 enum. -func (e ListProjectAddonsResponseOutputAvailableAddonsVariantsId7) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputAvailableAddonsVariantsId7EtlPipelineDefault: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceInterval. -const ( - ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceIntervalHourly ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceInterval = "hourly" - ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceIntervalMonthly ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceInterval = "monthly" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceInterval enum. -func (e ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceInterval) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceIntervalHourly: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceIntervalMonthly: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceType. -const ( - ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceTypeFixed ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceType = "fixed" - ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceTypeUsage ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceType = "usage" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceType enum. -func (e ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceType) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceTypeFixed: - return true - case ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceTypeUsage: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputSelectedAddonsType. -const ( - ListProjectAddonsResponseOutputSelectedAddonsTypeAuthMfaPhone ListProjectAddonsResponseOutputSelectedAddonsType = "auth_mfa_phone" - ListProjectAddonsResponseOutputSelectedAddonsTypeAuthMfaWebAuthn ListProjectAddonsResponseOutputSelectedAddonsType = "auth_mfa_web_authn" - ListProjectAddonsResponseOutputSelectedAddonsTypeComputeInstance ListProjectAddonsResponseOutputSelectedAddonsType = "compute_instance" - ListProjectAddonsResponseOutputSelectedAddonsTypeCustomDomain ListProjectAddonsResponseOutputSelectedAddonsType = "custom_domain" - ListProjectAddonsResponseOutputSelectedAddonsTypeEtlPipeline ListProjectAddonsResponseOutputSelectedAddonsType = "etl_pipeline" - ListProjectAddonsResponseOutputSelectedAddonsTypeIpv4 ListProjectAddonsResponseOutputSelectedAddonsType = "ipv4" - ListProjectAddonsResponseOutputSelectedAddonsTypeLogDrain ListProjectAddonsResponseOutputSelectedAddonsType = "log_drain" - ListProjectAddonsResponseOutputSelectedAddonsTypePitr ListProjectAddonsResponseOutputSelectedAddonsType = "pitr" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputSelectedAddonsType enum. -func (e ListProjectAddonsResponseOutputSelectedAddonsType) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputSelectedAddonsTypeAuthMfaPhone: - return true - case ListProjectAddonsResponseOutputSelectedAddonsTypeAuthMfaWebAuthn: - return true - case ListProjectAddonsResponseOutputSelectedAddonsTypeComputeInstance: - return true - case ListProjectAddonsResponseOutputSelectedAddonsTypeCustomDomain: - return true - case ListProjectAddonsResponseOutputSelectedAddonsTypeEtlPipeline: - return true - case ListProjectAddonsResponseOutputSelectedAddonsTypeIpv4: - return true - case ListProjectAddonsResponseOutputSelectedAddonsTypeLogDrain: - return true - case ListProjectAddonsResponseOutputSelectedAddonsTypePitr: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputSelectedAddonsVariantId0. -const ( - ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci12xlarge ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_12xlarge" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci16xlarge ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_16xlarge" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci24xlarge ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_24xlarge" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci24xlargeHighMemory ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_24xlarge_high_memory" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci24xlargeOptimizedCpu ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_24xlarge_optimized_cpu" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci24xlargeOptimizedMemory ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_24xlarge_optimized_memory" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci2xlarge ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_2xlarge" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci48xlarge ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_48xlarge" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci48xlargeHighMemory ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_48xlarge_high_memory" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci48xlargeOptimizedCpu ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_48xlarge_optimized_cpu" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci48xlargeOptimizedMemory ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_48xlarge_optimized_memory" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci4xlarge ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_4xlarge" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci8xlarge ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_8xlarge" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0CiLarge ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_large" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0CiMedium ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_medium" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0CiMicro ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_micro" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0CiSmall ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_small" - ListProjectAddonsResponseOutputSelectedAddonsVariantId0CiXlarge ListProjectAddonsResponseOutputSelectedAddonsVariantId0 = "ci_xlarge" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputSelectedAddonsVariantId0 enum. -func (e ListProjectAddonsResponseOutputSelectedAddonsVariantId0) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci12xlarge: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci16xlarge: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci24xlarge: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci24xlargeHighMemory: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci24xlargeOptimizedCpu: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci24xlargeOptimizedMemory: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci2xlarge: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci48xlarge: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci48xlargeHighMemory: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci48xlargeOptimizedCpu: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci48xlargeOptimizedMemory: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci4xlarge: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0Ci8xlarge: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0CiLarge: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0CiMedium: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0CiMicro: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0CiSmall: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId0CiXlarge: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputSelectedAddonsVariantId1. -const ( - ListProjectAddonsResponseOutputSelectedAddonsVariantId1CdDefault ListProjectAddonsResponseOutputSelectedAddonsVariantId1 = "cd_default" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputSelectedAddonsVariantId1 enum. -func (e ListProjectAddonsResponseOutputSelectedAddonsVariantId1) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputSelectedAddonsVariantId1CdDefault: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputSelectedAddonsVariantId2. -const ( - ListProjectAddonsResponseOutputSelectedAddonsVariantId2Pitr14 ListProjectAddonsResponseOutputSelectedAddonsVariantId2 = "pitr_14" - ListProjectAddonsResponseOutputSelectedAddonsVariantId2Pitr28 ListProjectAddonsResponseOutputSelectedAddonsVariantId2 = "pitr_28" - ListProjectAddonsResponseOutputSelectedAddonsVariantId2Pitr7 ListProjectAddonsResponseOutputSelectedAddonsVariantId2 = "pitr_7" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputSelectedAddonsVariantId2 enum. -func (e ListProjectAddonsResponseOutputSelectedAddonsVariantId2) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputSelectedAddonsVariantId2Pitr14: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId2Pitr28: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantId2Pitr7: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputSelectedAddonsVariantId3. -const ( - ListProjectAddonsResponseOutputSelectedAddonsVariantId3Ipv4Default ListProjectAddonsResponseOutputSelectedAddonsVariantId3 = "ipv4_default" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputSelectedAddonsVariantId3 enum. -func (e ListProjectAddonsResponseOutputSelectedAddonsVariantId3) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputSelectedAddonsVariantId3Ipv4Default: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputSelectedAddonsVariantId4. -const ( - ListProjectAddonsResponseOutputSelectedAddonsVariantId4AuthMfaPhoneDefault ListProjectAddonsResponseOutputSelectedAddonsVariantId4 = "auth_mfa_phone_default" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputSelectedAddonsVariantId4 enum. -func (e ListProjectAddonsResponseOutputSelectedAddonsVariantId4) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputSelectedAddonsVariantId4AuthMfaPhoneDefault: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputSelectedAddonsVariantId5. -const ( - ListProjectAddonsResponseOutputSelectedAddonsVariantId5AuthMfaWebAuthnDefault ListProjectAddonsResponseOutputSelectedAddonsVariantId5 = "auth_mfa_web_authn_default" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputSelectedAddonsVariantId5 enum. -func (e ListProjectAddonsResponseOutputSelectedAddonsVariantId5) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputSelectedAddonsVariantId5AuthMfaWebAuthnDefault: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputSelectedAddonsVariantId6. -const ( - ListProjectAddonsResponseOutputSelectedAddonsVariantId6LogDrainDefault ListProjectAddonsResponseOutputSelectedAddonsVariantId6 = "log_drain_default" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputSelectedAddonsVariantId6 enum. -func (e ListProjectAddonsResponseOutputSelectedAddonsVariantId6) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputSelectedAddonsVariantId6LogDrainDefault: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputSelectedAddonsVariantId7. -const ( - ListProjectAddonsResponseOutputSelectedAddonsVariantId7EtlPipelineDefault ListProjectAddonsResponseOutputSelectedAddonsVariantId7 = "etl_pipeline_default" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputSelectedAddonsVariantId7 enum. -func (e ListProjectAddonsResponseOutputSelectedAddonsVariantId7) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputSelectedAddonsVariantId7EtlPipelineDefault: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputSelectedAddonsVariantPriceInterval. -const ( - ListProjectAddonsResponseOutputSelectedAddonsVariantPriceIntervalHourly ListProjectAddonsResponseOutputSelectedAddonsVariantPriceInterval = "hourly" - ListProjectAddonsResponseOutputSelectedAddonsVariantPriceIntervalMonthly ListProjectAddonsResponseOutputSelectedAddonsVariantPriceInterval = "monthly" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputSelectedAddonsVariantPriceInterval enum. -func (e ListProjectAddonsResponseOutputSelectedAddonsVariantPriceInterval) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputSelectedAddonsVariantPriceIntervalHourly: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantPriceIntervalMonthly: - return true - default: - return false - } -} - -// Defines values for ListProjectAddonsResponseOutputSelectedAddonsVariantPriceType. -const ( - ListProjectAddonsResponseOutputSelectedAddonsVariantPriceTypeFixed ListProjectAddonsResponseOutputSelectedAddonsVariantPriceType = "fixed" - ListProjectAddonsResponseOutputSelectedAddonsVariantPriceTypeUsage ListProjectAddonsResponseOutputSelectedAddonsVariantPriceType = "usage" -) - -// Valid indicates whether the value is a known member of the ListProjectAddonsResponseOutputSelectedAddonsVariantPriceType enum. -func (e ListProjectAddonsResponseOutputSelectedAddonsVariantPriceType) Valid() bool { - switch e { - case ListProjectAddonsResponseOutputSelectedAddonsVariantPriceTypeFixed: - return true - case ListProjectAddonsResponseOutputSelectedAddonsVariantPriceTypeUsage: - return true - default: - return false - } -} - -// Defines values for NetworkRestrictionsResponseOutputEntitlement. -const ( - NetworkRestrictionsResponseOutputEntitlementAllowed NetworkRestrictionsResponseOutputEntitlement = "allowed" - NetworkRestrictionsResponseOutputEntitlementDisallowed NetworkRestrictionsResponseOutputEntitlement = "disallowed" -) - -// Valid indicates whether the value is a known member of the NetworkRestrictionsResponseOutputEntitlement enum. -func (e NetworkRestrictionsResponseOutputEntitlement) Valid() bool { - switch e { - case NetworkRestrictionsResponseOutputEntitlementAllowed: - return true - case NetworkRestrictionsResponseOutputEntitlementDisallowed: - return true - default: - return false - } -} - -// Defines values for NetworkRestrictionsResponseOutputStatus. -const ( - NetworkRestrictionsResponseOutputStatusApplied NetworkRestrictionsResponseOutputStatus = "applied" - NetworkRestrictionsResponseOutputStatusStored NetworkRestrictionsResponseOutputStatus = "stored" -) - -// Valid indicates whether the value is a known member of the NetworkRestrictionsResponseOutputStatus enum. -func (e NetworkRestrictionsResponseOutputStatus) Valid() bool { - switch e { - case NetworkRestrictionsResponseOutputStatusApplied: - return true - case NetworkRestrictionsResponseOutputStatusStored: - return true - default: - return false - } -} - -// Defines values for NetworkRestrictionsV2ResponseOutputConfigDbAllowedCidrsType. -const ( - NetworkRestrictionsV2ResponseOutputConfigDbAllowedCidrsTypeV4 NetworkRestrictionsV2ResponseOutputConfigDbAllowedCidrsType = "v4" - NetworkRestrictionsV2ResponseOutputConfigDbAllowedCidrsTypeV6 NetworkRestrictionsV2ResponseOutputConfigDbAllowedCidrsType = "v6" -) - -// Valid indicates whether the value is a known member of the NetworkRestrictionsV2ResponseOutputConfigDbAllowedCidrsType enum. -func (e NetworkRestrictionsV2ResponseOutputConfigDbAllowedCidrsType) Valid() bool { - switch e { - case NetworkRestrictionsV2ResponseOutputConfigDbAllowedCidrsTypeV4: - return true - case NetworkRestrictionsV2ResponseOutputConfigDbAllowedCidrsTypeV6: - return true - default: - return false - } -} - -// Defines values for NetworkRestrictionsV2ResponseOutputEntitlement. -const ( - NetworkRestrictionsV2ResponseOutputEntitlementAllowed NetworkRestrictionsV2ResponseOutputEntitlement = "allowed" - NetworkRestrictionsV2ResponseOutputEntitlementDisallowed NetworkRestrictionsV2ResponseOutputEntitlement = "disallowed" -) - -// Valid indicates whether the value is a known member of the NetworkRestrictionsV2ResponseOutputEntitlement enum. -func (e NetworkRestrictionsV2ResponseOutputEntitlement) Valid() bool { - switch e { - case NetworkRestrictionsV2ResponseOutputEntitlementAllowed: - return true - case NetworkRestrictionsV2ResponseOutputEntitlementDisallowed: - return true - default: - return false - } -} - -// Defines values for NetworkRestrictionsV2ResponseOutputOldConfigDbAllowedCidrsType. -const ( - NetworkRestrictionsV2ResponseOutputOldConfigDbAllowedCidrsTypeV4 NetworkRestrictionsV2ResponseOutputOldConfigDbAllowedCidrsType = "v4" - NetworkRestrictionsV2ResponseOutputOldConfigDbAllowedCidrsTypeV6 NetworkRestrictionsV2ResponseOutputOldConfigDbAllowedCidrsType = "v6" -) - -// Valid indicates whether the value is a known member of the NetworkRestrictionsV2ResponseOutputOldConfigDbAllowedCidrsType enum. -func (e NetworkRestrictionsV2ResponseOutputOldConfigDbAllowedCidrsType) Valid() bool { - switch e { - case NetworkRestrictionsV2ResponseOutputOldConfigDbAllowedCidrsTypeV4: - return true - case NetworkRestrictionsV2ResponseOutputOldConfigDbAllowedCidrsTypeV6: - return true - default: - return false - } -} - -// Defines values for NetworkRestrictionsV2ResponseOutputStatus. -const ( - NetworkRestrictionsV2ResponseOutputStatusApplied NetworkRestrictionsV2ResponseOutputStatus = "applied" - NetworkRestrictionsV2ResponseOutputStatusStored NetworkRestrictionsV2ResponseOutputStatus = "stored" -) - -// Valid indicates whether the value is a known member of the NetworkRestrictionsV2ResponseOutputStatus enum. -func (e NetworkRestrictionsV2ResponseOutputStatus) Valid() bool { - switch e { - case NetworkRestrictionsV2ResponseOutputStatusApplied: - return true - case NetworkRestrictionsV2ResponseOutputStatusStored: - return true - default: - return false - } -} - -// Defines values for OAuthTokenBodyGrantType. -const ( - AuthorizationCode OAuthTokenBodyGrantType = "authorization_code" - RefreshToken OAuthTokenBodyGrantType = "refresh_token" - UrnIetfParamsOauthGrantTypeJwtBearer OAuthTokenBodyGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer" -) - -// Valid indicates whether the value is a known member of the OAuthTokenBodyGrantType enum. -func (e OAuthTokenBodyGrantType) Valid() bool { - switch e { - case AuthorizationCode: - return true - case RefreshToken: - return true - case UrnIetfParamsOauthGrantTypeJwtBearer: - return true - default: - return false - } -} - -// Defines values for OAuthTokenResponseOutputTokenType. -const ( - Bearer OAuthTokenResponseOutputTokenType = "Bearer" -) - -// Valid indicates whether the value is a known member of the OAuthTokenResponseOutputTokenType enum. -func (e OAuthTokenResponseOutputTokenType) Valid() bool { - switch e { - case Bearer: - return true - default: - return false - } -} - -// Defines values for OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlan. -const ( - OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlanEnterprise OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlan = "enterprise" - OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlanFree OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlan = "free" - OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlanPlatform OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlan = "platform" - OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlanPro OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlan = "pro" - OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlanTeam OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlan = "team" -) - -// Valid indicates whether the value is a known member of the OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlan enum. -func (e OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlan) Valid() bool { - switch e { - case OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlanEnterprise: - return true - case OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlanFree: - return true - case OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlanPlatform: - return true - case OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlanPro: - return true - case OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlanTeam: - return true - default: - return false - } -} - -// Defines values for OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlan. -const ( - OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlanEnterprise OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlan = "enterprise" - OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlanFree OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlan = "free" - OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlanLessThannil OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlan = "" - OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlanPlatform OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlan = "platform" - OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlanPro OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlan = "pro" - OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlanTeam OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlan = "team" -) - -// Valid indicates whether the value is a known member of the OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlan enum. -func (e OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlan) Valid() bool { - switch e { - case OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlanEnterprise: - return true - case OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlanFree: - return true - case OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlanLessThannil: - return true - case OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlanPlatform: - return true - case OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlanPro: - return true - case OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlanTeam: - return true - default: - return false - } -} - -// Defines values for OrganizationProjectsResponseOutputProjectsDatabasesDiskType. -const ( - Gp3 OrganizationProjectsResponseOutputProjectsDatabasesDiskType = "gp3" - Io2 OrganizationProjectsResponseOutputProjectsDatabasesDiskType = "io2" -) - -// Valid indicates whether the value is a known member of the OrganizationProjectsResponseOutputProjectsDatabasesDiskType enum. -func (e OrganizationProjectsResponseOutputProjectsDatabasesDiskType) Valid() bool { - switch e { - case Gp3: - return true - case Io2: - return true - default: - return false - } -} - -// Defines values for OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize. -const ( - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeLarge OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "large" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeMedium OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "medium" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeMicro OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "micro" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN12xlarge OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "12xlarge" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN16xlarge OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "16xlarge" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN24xlarge OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "24xlarge" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN24xlargeHighMemory OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "24xlarge_high_memory" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN24xlargeOptimizedCpu OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "24xlarge_optimized_cpu" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN24xlargeOptimizedMemory OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "24xlarge_optimized_memory" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN2xlarge OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "2xlarge" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN48xlarge OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "48xlarge" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN48xlargeHighMemory OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "48xlarge_high_memory" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN48xlargeOptimizedCpu OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "48xlarge_optimized_cpu" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN48xlargeOptimizedMemory OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "48xlarge_optimized_memory" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN4xlarge OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "4xlarge" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN8xlarge OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "8xlarge" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeNano OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "nano" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizePico OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "pico" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeSmall OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "small" - OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeXlarge OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize = "xlarge" -) - -// Valid indicates whether the value is a known member of the OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize enum. -func (e OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize) Valid() bool { - switch e { - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeLarge: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeMedium: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeMicro: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN12xlarge: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN16xlarge: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN24xlarge: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN24xlargeHighMemory: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN24xlargeOptimizedCpu: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN24xlargeOptimizedMemory: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN2xlarge: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN48xlarge: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN48xlargeHighMemory: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN48xlargeOptimizedCpu: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN48xlargeOptimizedMemory: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN4xlarge: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeN8xlarge: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeNano: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizePico: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeSmall: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSizeXlarge: - return true - default: - return false - } -} - -// Defines values for OrganizationProjectsResponseOutputProjectsDatabasesStatus. -const ( - OrganizationProjectsResponseOutputProjectsDatabasesStatusACTIVEHEALTHY OrganizationProjectsResponseOutputProjectsDatabasesStatus = "ACTIVE_HEALTHY" - OrganizationProjectsResponseOutputProjectsDatabasesStatusACTIVEUNHEALTHY OrganizationProjectsResponseOutputProjectsDatabasesStatus = "ACTIVE_UNHEALTHY" - OrganizationProjectsResponseOutputProjectsDatabasesStatusCOMINGUP OrganizationProjectsResponseOutputProjectsDatabasesStatus = "COMING_UP" - OrganizationProjectsResponseOutputProjectsDatabasesStatusGOINGDOWN OrganizationProjectsResponseOutputProjectsDatabasesStatus = "GOING_DOWN" - OrganizationProjectsResponseOutputProjectsDatabasesStatusINITFAILED OrganizationProjectsResponseOutputProjectsDatabasesStatus = "INIT_FAILED" - OrganizationProjectsResponseOutputProjectsDatabasesStatusINITREADREPLICA OrganizationProjectsResponseOutputProjectsDatabasesStatus = "INIT_READ_REPLICA" - OrganizationProjectsResponseOutputProjectsDatabasesStatusINITREADREPLICAFAILED OrganizationProjectsResponseOutputProjectsDatabasesStatus = "INIT_READ_REPLICA_FAILED" - OrganizationProjectsResponseOutputProjectsDatabasesStatusREMOVED OrganizationProjectsResponseOutputProjectsDatabasesStatus = "REMOVED" - OrganizationProjectsResponseOutputProjectsDatabasesStatusRESIZING OrganizationProjectsResponseOutputProjectsDatabasesStatus = "RESIZING" - OrganizationProjectsResponseOutputProjectsDatabasesStatusRESTARTING OrganizationProjectsResponseOutputProjectsDatabasesStatus = "RESTARTING" - OrganizationProjectsResponseOutputProjectsDatabasesStatusRESTORING OrganizationProjectsResponseOutputProjectsDatabasesStatus = "RESTORING" - OrganizationProjectsResponseOutputProjectsDatabasesStatusUNKNOWN OrganizationProjectsResponseOutputProjectsDatabasesStatus = "UNKNOWN" -) - -// Valid indicates whether the value is a known member of the OrganizationProjectsResponseOutputProjectsDatabasesStatus enum. -func (e OrganizationProjectsResponseOutputProjectsDatabasesStatus) Valid() bool { - switch e { - case OrganizationProjectsResponseOutputProjectsDatabasesStatusACTIVEHEALTHY: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesStatusACTIVEUNHEALTHY: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesStatusCOMINGUP: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesStatusGOINGDOWN: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesStatusINITFAILED: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesStatusINITREADREPLICA: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesStatusINITREADREPLICAFAILED: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesStatusREMOVED: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesStatusRESIZING: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesStatusRESTARTING: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesStatusRESTORING: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesStatusUNKNOWN: - return true - default: - return false - } -} - -// Defines values for OrganizationProjectsResponseOutputProjectsDatabasesType. -const ( - OrganizationProjectsResponseOutputProjectsDatabasesTypePRIMARY OrganizationProjectsResponseOutputProjectsDatabasesType = "PRIMARY" - OrganizationProjectsResponseOutputProjectsDatabasesTypeREADREPLICA OrganizationProjectsResponseOutputProjectsDatabasesType = "READ_REPLICA" -) - -// Valid indicates whether the value is a known member of the OrganizationProjectsResponseOutputProjectsDatabasesType enum. -func (e OrganizationProjectsResponseOutputProjectsDatabasesType) Valid() bool { - switch e { - case OrganizationProjectsResponseOutputProjectsDatabasesTypePRIMARY: - return true - case OrganizationProjectsResponseOutputProjectsDatabasesTypeREADREPLICA: - return true - default: - return false - } -} - -// Defines values for OrganizationProjectsResponseOutputProjectsStatus. -const ( - OrganizationProjectsResponseOutputProjectsStatusACTIVEHEALTHY OrganizationProjectsResponseOutputProjectsStatus = "ACTIVE_HEALTHY" - OrganizationProjectsResponseOutputProjectsStatusACTIVEUNHEALTHY OrganizationProjectsResponseOutputProjectsStatus = "ACTIVE_UNHEALTHY" - OrganizationProjectsResponseOutputProjectsStatusCOMINGUP OrganizationProjectsResponseOutputProjectsStatus = "COMING_UP" - OrganizationProjectsResponseOutputProjectsStatusGOINGDOWN OrganizationProjectsResponseOutputProjectsStatus = "GOING_DOWN" - OrganizationProjectsResponseOutputProjectsStatusINACTIVE OrganizationProjectsResponseOutputProjectsStatus = "INACTIVE" - OrganizationProjectsResponseOutputProjectsStatusINITFAILED OrganizationProjectsResponseOutputProjectsStatus = "INIT_FAILED" - OrganizationProjectsResponseOutputProjectsStatusPAUSEFAILED OrganizationProjectsResponseOutputProjectsStatus = "PAUSE_FAILED" - OrganizationProjectsResponseOutputProjectsStatusPAUSING OrganizationProjectsResponseOutputProjectsStatus = "PAUSING" - OrganizationProjectsResponseOutputProjectsStatusREMOVED OrganizationProjectsResponseOutputProjectsStatus = "REMOVED" - OrganizationProjectsResponseOutputProjectsStatusRESIZING OrganizationProjectsResponseOutputProjectsStatus = "RESIZING" - OrganizationProjectsResponseOutputProjectsStatusRESTARTING OrganizationProjectsResponseOutputProjectsStatus = "RESTARTING" - OrganizationProjectsResponseOutputProjectsStatusRESTOREFAILED OrganizationProjectsResponseOutputProjectsStatus = "RESTORE_FAILED" - OrganizationProjectsResponseOutputProjectsStatusRESTORING OrganizationProjectsResponseOutputProjectsStatus = "RESTORING" - OrganizationProjectsResponseOutputProjectsStatusUNKNOWN OrganizationProjectsResponseOutputProjectsStatus = "UNKNOWN" - OrganizationProjectsResponseOutputProjectsStatusUPGRADING OrganizationProjectsResponseOutputProjectsStatus = "UPGRADING" -) - -// Valid indicates whether the value is a known member of the OrganizationProjectsResponseOutputProjectsStatus enum. -func (e OrganizationProjectsResponseOutputProjectsStatus) Valid() bool { - switch e { - case OrganizationProjectsResponseOutputProjectsStatusACTIVEHEALTHY: - return true - case OrganizationProjectsResponseOutputProjectsStatusACTIVEUNHEALTHY: - return true - case OrganizationProjectsResponseOutputProjectsStatusCOMINGUP: - return true - case OrganizationProjectsResponseOutputProjectsStatusGOINGDOWN: - return true - case OrganizationProjectsResponseOutputProjectsStatusINACTIVE: - return true - case OrganizationProjectsResponseOutputProjectsStatusINITFAILED: - return true - case OrganizationProjectsResponseOutputProjectsStatusPAUSEFAILED: - return true - case OrganizationProjectsResponseOutputProjectsStatusPAUSING: - return true - case OrganizationProjectsResponseOutputProjectsStatusREMOVED: - return true - case OrganizationProjectsResponseOutputProjectsStatusRESIZING: - return true - case OrganizationProjectsResponseOutputProjectsStatusRESTARTING: - return true - case OrganizationProjectsResponseOutputProjectsStatusRESTOREFAILED: - return true - case OrganizationProjectsResponseOutputProjectsStatusRESTORING: - return true - case OrganizationProjectsResponseOutputProjectsStatusUNKNOWN: - return true - case OrganizationProjectsResponseOutputProjectsStatusUPGRADING: - return true - default: - return false - } -} - -// Defines values for PlanGateErrorBodyErrorCode. -const ( - EntitlementRequired PlanGateErrorBodyErrorCode = "entitlement_required" -) - -// Valid indicates whether the value is a known member of the PlanGateErrorBodyErrorCode enum. -func (e PlanGateErrorBodyErrorCode) Valid() bool { - switch e { - case EntitlementRequired: - return true - default: - return false - } -} - -// Defines values for PostgresConfigResponseOutputSessionReplicationRole. -const ( - PostgresConfigResponseOutputSessionReplicationRoleLocal PostgresConfigResponseOutputSessionReplicationRole = "local" - PostgresConfigResponseOutputSessionReplicationRoleOrigin PostgresConfigResponseOutputSessionReplicationRole = "origin" - PostgresConfigResponseOutputSessionReplicationRoleReplica PostgresConfigResponseOutputSessionReplicationRole = "replica" -) - -// Valid indicates whether the value is a known member of the PostgresConfigResponseOutputSessionReplicationRole enum. -func (e PostgresConfigResponseOutputSessionReplicationRole) Valid() bool { - switch e { - case PostgresConfigResponseOutputSessionReplicationRoleLocal: - return true - case PostgresConfigResponseOutputSessionReplicationRoleOrigin: - return true - case PostgresConfigResponseOutputSessionReplicationRoleReplica: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannel. -const ( - ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannelAlpha ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannel = "alpha" - ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannelBeta ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannel = "beta" - ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannelGa ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannel = "ga" - ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannelInternal ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannel = "internal" - ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannelPreview ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannel = "preview" - ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannelWithdrawn ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannel = "withdrawn" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannel enum. -func (e ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannel) Valid() bool { - switch e { - case ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannelAlpha: - return true - case ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannelBeta: - return true - case ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannelGa: - return true - case ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannelInternal: - return true - case ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannelPreview: - return true - case ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannelWithdrawn: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsPostgresVersion. -const ( - N13 ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsPostgresVersion = "13" - N14 ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsPostgresVersion = "14" - N15 ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsPostgresVersion = "15" - N17 ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsPostgresVersion = "17" - N17Oriole ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsPostgresVersion = "17-oriole" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsPostgresVersion enum. -func (e ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsPostgresVersion) Valid() bool { - switch e { - case N13: - return true - case N14: - return true - case N15: - return true - case N17: - return true - case N17Oriole: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannel. -const ( - ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannelAlpha ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannel = "alpha" - ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannelBeta ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannel = "beta" - ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannelGa ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannel = "ga" - ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannelInternal ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannel = "internal" - ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannelPreview ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannel = "preview" - ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannelWithdrawn ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannel = "withdrawn" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannel enum. -func (e ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannel) Valid() bool { - switch e { - case ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannelAlpha: - return true - case ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannelBeta: - return true - case ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannelGa: - return true - case ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannelInternal: - return true - case ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannelPreview: - return true - case ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannelWithdrawn: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputValidationErrors0Type. -const ( - ObjectsDependingOnPgCron ProjectUpgradeEligibilityResponseOutputValidationErrors0Type = "objects_depending_on_pg_cron" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputValidationErrors0Type enum. -func (e ProjectUpgradeEligibilityResponseOutputValidationErrors0Type) Valid() bool { - switch e { - case ObjectsDependingOnPgCron: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputValidationErrors1Type. -const ( - IndexesReferencingLlToEarth ProjectUpgradeEligibilityResponseOutputValidationErrors1Type = "indexes_referencing_ll_to_earth" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputValidationErrors1Type enum. -func (e ProjectUpgradeEligibilityResponseOutputValidationErrors1Type) Valid() bool { - switch e { - case IndexesReferencingLlToEarth: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputValidationErrors2Type. -const ( - FunctionUsingObsoleteLang ProjectUpgradeEligibilityResponseOutputValidationErrors2Type = "function_using_obsolete_lang" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputValidationErrors2Type enum. -func (e ProjectUpgradeEligibilityResponseOutputValidationErrors2Type) Valid() bool { - switch e { - case FunctionUsingObsoleteLang: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputValidationErrors3Type. -const ( - UnsupportedExtension ProjectUpgradeEligibilityResponseOutputValidationErrors3Type = "unsupported_extension" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputValidationErrors3Type enum. -func (e ProjectUpgradeEligibilityResponseOutputValidationErrors3Type) Valid() bool { - switch e { - case UnsupportedExtension: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputValidationErrors4Type. -const ( - UnsupportedFdwHandler ProjectUpgradeEligibilityResponseOutputValidationErrors4Type = "unsupported_fdw_handler" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputValidationErrors4Type enum. -func (e ProjectUpgradeEligibilityResponseOutputValidationErrors4Type) Valid() bool { - switch e { - case UnsupportedFdwHandler: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputValidationErrors5Type. -const ( - UnloggedTableWithPersistentSequence ProjectUpgradeEligibilityResponseOutputValidationErrors5Type = "unlogged_table_with_persistent_sequence" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputValidationErrors5Type enum. -func (e ProjectUpgradeEligibilityResponseOutputValidationErrors5Type) Valid() bool { - switch e { - case UnloggedTableWithPersistentSequence: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0. -const ( - ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0Table ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0 = "table" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0 enum. -func (e ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0) Valid() bool { - switch e { - case ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0Table: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1. -const ( - Function ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1 = "function" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1 enum. -func (e ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1) Valid() bool { - switch e { - case Function: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputValidationErrors6Type. -const ( - UserDefinedObjectsInInternalSchemas ProjectUpgradeEligibilityResponseOutputValidationErrors6Type = "user_defined_objects_in_internal_schemas" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputValidationErrors6Type enum. -func (e ProjectUpgradeEligibilityResponseOutputValidationErrors6Type) Valid() bool { - switch e { - case UserDefinedObjectsInInternalSchemas: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputValidationErrors7Type. -const ( - ActiveReplicationSlot ProjectUpgradeEligibilityResponseOutputValidationErrors7Type = "active_replication_slot" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputValidationErrors7Type enum. -func (e ProjectUpgradeEligibilityResponseOutputValidationErrors7Type) Valid() bool { - switch e { - case ActiveReplicationSlot: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputValidationErrors8Type. -const ( - X86Architecture ProjectUpgradeEligibilityResponseOutputValidationErrors8Type = "x86_architecture" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputValidationErrors8Type enum. -func (e ProjectUpgradeEligibilityResponseOutputValidationErrors8Type) Valid() bool { - switch e { - case X86Architecture: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputValidationErrors9Type. -const ( - ProjectHibernating ProjectUpgradeEligibilityResponseOutputValidationErrors9Type = "project_hibernating" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputValidationErrors9Type enum. -func (e ProjectUpgradeEligibilityResponseOutputValidationErrors9Type) Valid() bool { - switch e { - case ProjectHibernating: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputWarnings0Type. -const ( - PgGraphqlIntrospectionChange ProjectUpgradeEligibilityResponseOutputWarnings0Type = "pg_graphql_introspection_change" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputWarnings0Type enum. -func (e ProjectUpgradeEligibilityResponseOutputWarnings0Type) Valid() bool { - switch e { - case PgGraphqlIntrospectionChange: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputWarnings1Type. -const ( - LtreeReindexRequired ProjectUpgradeEligibilityResponseOutputWarnings1Type = "ltree_reindex_required" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputWarnings1Type enum. -func (e ProjectUpgradeEligibilityResponseOutputWarnings1Type) Valid() bool { - switch e { - case LtreeReindexRequired: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputWarnings2Type. -const ( - OperatorEstimatorGate ProjectUpgradeEligibilityResponseOutputWarnings2Type = "operator_estimator_gate" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputWarnings2Type enum. -func (e ProjectUpgradeEligibilityResponseOutputWarnings2Type) Valid() bool { - switch e { - case OperatorEstimatorGate: - return true - default: - return false - } -} - -// Defines values for ProjectUpgradeEligibilityResponseOutputWarnings3Type. -const ( - BtreeGistNanReindex ProjectUpgradeEligibilityResponseOutputWarnings3Type = "btree_gist_nan_reindex" -) - -// Valid indicates whether the value is a known member of the ProjectUpgradeEligibilityResponseOutputWarnings3Type enum. -func (e ProjectUpgradeEligibilityResponseOutputWarnings3Type) Valid() bool { - switch e { - case BtreeGistNanReindex: - return true - default: - return false - } -} - -// Defines values for RegionsInfoOutputAllSmartGroupCode. -const ( - RegionsInfoOutputAllSmartGroupCodeAmericas RegionsInfoOutputAllSmartGroupCode = "americas" - RegionsInfoOutputAllSmartGroupCodeApac RegionsInfoOutputAllSmartGroupCode = "apac" - RegionsInfoOutputAllSmartGroupCodeEmea RegionsInfoOutputAllSmartGroupCode = "emea" -) - -// Valid indicates whether the value is a known member of the RegionsInfoOutputAllSmartGroupCode enum. -func (e RegionsInfoOutputAllSmartGroupCode) Valid() bool { - switch e { - case RegionsInfoOutputAllSmartGroupCodeAmericas: - return true - case RegionsInfoOutputAllSmartGroupCodeApac: - return true - case RegionsInfoOutputAllSmartGroupCodeEmea: - return true - default: - return false - } -} - -// Defines values for RegionsInfoOutputAllSmartGroupType. -const ( - RegionsInfoOutputAllSmartGroupTypeSmartGroup RegionsInfoOutputAllSmartGroupType = "smartGroup" -) - -// Valid indicates whether the value is a known member of the RegionsInfoOutputAllSmartGroupType enum. -func (e RegionsInfoOutputAllSmartGroupType) Valid() bool { - switch e { - case RegionsInfoOutputAllSmartGroupTypeSmartGroup: - return true - default: - return false - } -} - -// Defines values for RegionsInfoOutputAllSpecificCode. -const ( - RegionsInfoOutputAllSpecificCodeApEast1 RegionsInfoOutputAllSpecificCode = "ap-east-1" - RegionsInfoOutputAllSpecificCodeApNortheast1 RegionsInfoOutputAllSpecificCode = "ap-northeast-1" - RegionsInfoOutputAllSpecificCodeApNortheast2 RegionsInfoOutputAllSpecificCode = "ap-northeast-2" - RegionsInfoOutputAllSpecificCodeApSouth1 RegionsInfoOutputAllSpecificCode = "ap-south-1" - RegionsInfoOutputAllSpecificCodeApSoutheast1 RegionsInfoOutputAllSpecificCode = "ap-southeast-1" - RegionsInfoOutputAllSpecificCodeApSoutheast2 RegionsInfoOutputAllSpecificCode = "ap-southeast-2" - RegionsInfoOutputAllSpecificCodeCaCentral1 RegionsInfoOutputAllSpecificCode = "ca-central-1" - RegionsInfoOutputAllSpecificCodeEuCentral1 RegionsInfoOutputAllSpecificCode = "eu-central-1" - RegionsInfoOutputAllSpecificCodeEuCentral2 RegionsInfoOutputAllSpecificCode = "eu-central-2" - RegionsInfoOutputAllSpecificCodeEuNorth1 RegionsInfoOutputAllSpecificCode = "eu-north-1" - RegionsInfoOutputAllSpecificCodeEuWest1 RegionsInfoOutputAllSpecificCode = "eu-west-1" - RegionsInfoOutputAllSpecificCodeEuWest2 RegionsInfoOutputAllSpecificCode = "eu-west-2" - RegionsInfoOutputAllSpecificCodeEuWest3 RegionsInfoOutputAllSpecificCode = "eu-west-3" - RegionsInfoOutputAllSpecificCodeSaEast1 RegionsInfoOutputAllSpecificCode = "sa-east-1" - RegionsInfoOutputAllSpecificCodeUsEast1 RegionsInfoOutputAllSpecificCode = "us-east-1" - RegionsInfoOutputAllSpecificCodeUsEast2 RegionsInfoOutputAllSpecificCode = "us-east-2" - RegionsInfoOutputAllSpecificCodeUsWest1 RegionsInfoOutputAllSpecificCode = "us-west-1" - RegionsInfoOutputAllSpecificCodeUsWest2 RegionsInfoOutputAllSpecificCode = "us-west-2" -) - -// Valid indicates whether the value is a known member of the RegionsInfoOutputAllSpecificCode enum. -func (e RegionsInfoOutputAllSpecificCode) Valid() bool { - switch e { - case RegionsInfoOutputAllSpecificCodeApEast1: - return true - case RegionsInfoOutputAllSpecificCodeApNortheast1: - return true - case RegionsInfoOutputAllSpecificCodeApNortheast2: - return true - case RegionsInfoOutputAllSpecificCodeApSouth1: - return true - case RegionsInfoOutputAllSpecificCodeApSoutheast1: - return true - case RegionsInfoOutputAllSpecificCodeApSoutheast2: - return true - case RegionsInfoOutputAllSpecificCodeCaCentral1: - return true - case RegionsInfoOutputAllSpecificCodeEuCentral1: - return true - case RegionsInfoOutputAllSpecificCodeEuCentral2: - return true - case RegionsInfoOutputAllSpecificCodeEuNorth1: - return true - case RegionsInfoOutputAllSpecificCodeEuWest1: - return true - case RegionsInfoOutputAllSpecificCodeEuWest2: - return true - case RegionsInfoOutputAllSpecificCodeEuWest3: - return true - case RegionsInfoOutputAllSpecificCodeSaEast1: - return true - case RegionsInfoOutputAllSpecificCodeUsEast1: - return true - case RegionsInfoOutputAllSpecificCodeUsEast2: - return true - case RegionsInfoOutputAllSpecificCodeUsWest1: - return true - case RegionsInfoOutputAllSpecificCodeUsWest2: - return true - default: - return false - } -} - -// Defines values for RegionsInfoOutputAllSpecificProvider. -const ( - RegionsInfoOutputAllSpecificProviderAWS RegionsInfoOutputAllSpecificProvider = "AWS" - RegionsInfoOutputAllSpecificProviderAWSK8S RegionsInfoOutputAllSpecificProvider = "AWS_K8S" - RegionsInfoOutputAllSpecificProviderAWSNIMBUS RegionsInfoOutputAllSpecificProvider = "AWS_NIMBUS" -) - -// Valid indicates whether the value is a known member of the RegionsInfoOutputAllSpecificProvider enum. -func (e RegionsInfoOutputAllSpecificProvider) Valid() bool { - switch e { - case RegionsInfoOutputAllSpecificProviderAWS: - return true - case RegionsInfoOutputAllSpecificProviderAWSK8S: - return true - case RegionsInfoOutputAllSpecificProviderAWSNIMBUS: - return true - default: - return false - } -} - -// Defines values for RegionsInfoOutputAllSpecificStatus. -const ( - RegionsInfoOutputAllSpecificStatusCapacity RegionsInfoOutputAllSpecificStatus = "capacity" - RegionsInfoOutputAllSpecificStatusOther RegionsInfoOutputAllSpecificStatus = "other" -) - -// Valid indicates whether the value is a known member of the RegionsInfoOutputAllSpecificStatus enum. -func (e RegionsInfoOutputAllSpecificStatus) Valid() bool { - switch e { - case RegionsInfoOutputAllSpecificStatusCapacity: - return true - case RegionsInfoOutputAllSpecificStatusOther: - return true - default: - return false - } -} - -// Defines values for RegionsInfoOutputAllSpecificType. -const ( - RegionsInfoOutputAllSpecificTypeSpecific RegionsInfoOutputAllSpecificType = "specific" -) - -// Valid indicates whether the value is a known member of the RegionsInfoOutputAllSpecificType enum. -func (e RegionsInfoOutputAllSpecificType) Valid() bool { - switch e { - case RegionsInfoOutputAllSpecificTypeSpecific: - return true - default: - return false - } -} - -// Defines values for RegionsInfoOutputRecommendationsSmartGroupCode. -const ( - RegionsInfoOutputRecommendationsSmartGroupCodeAmericas RegionsInfoOutputRecommendationsSmartGroupCode = "americas" - RegionsInfoOutputRecommendationsSmartGroupCodeApac RegionsInfoOutputRecommendationsSmartGroupCode = "apac" - RegionsInfoOutputRecommendationsSmartGroupCodeEmea RegionsInfoOutputRecommendationsSmartGroupCode = "emea" -) - -// Valid indicates whether the value is a known member of the RegionsInfoOutputRecommendationsSmartGroupCode enum. -func (e RegionsInfoOutputRecommendationsSmartGroupCode) Valid() bool { - switch e { - case RegionsInfoOutputRecommendationsSmartGroupCodeAmericas: - return true - case RegionsInfoOutputRecommendationsSmartGroupCodeApac: - return true - case RegionsInfoOutputRecommendationsSmartGroupCodeEmea: - return true - default: - return false - } -} - -// Defines values for RegionsInfoOutputRecommendationsSmartGroupType. -const ( - RegionsInfoOutputRecommendationsSmartGroupTypeSmartGroup RegionsInfoOutputRecommendationsSmartGroupType = "smartGroup" -) - -// Valid indicates whether the value is a known member of the RegionsInfoOutputRecommendationsSmartGroupType enum. -func (e RegionsInfoOutputRecommendationsSmartGroupType) Valid() bool { - switch e { - case RegionsInfoOutputRecommendationsSmartGroupTypeSmartGroup: - return true - default: - return false - } -} - -// Defines values for RegionsInfoOutputRecommendationsSpecificCode. -const ( - RegionsInfoOutputRecommendationsSpecificCodeApEast1 RegionsInfoOutputRecommendationsSpecificCode = "ap-east-1" - RegionsInfoOutputRecommendationsSpecificCodeApNortheast1 RegionsInfoOutputRecommendationsSpecificCode = "ap-northeast-1" - RegionsInfoOutputRecommendationsSpecificCodeApNortheast2 RegionsInfoOutputRecommendationsSpecificCode = "ap-northeast-2" - RegionsInfoOutputRecommendationsSpecificCodeApSouth1 RegionsInfoOutputRecommendationsSpecificCode = "ap-south-1" - RegionsInfoOutputRecommendationsSpecificCodeApSoutheast1 RegionsInfoOutputRecommendationsSpecificCode = "ap-southeast-1" - RegionsInfoOutputRecommendationsSpecificCodeApSoutheast2 RegionsInfoOutputRecommendationsSpecificCode = "ap-southeast-2" - RegionsInfoOutputRecommendationsSpecificCodeCaCentral1 RegionsInfoOutputRecommendationsSpecificCode = "ca-central-1" - RegionsInfoOutputRecommendationsSpecificCodeEuCentral1 RegionsInfoOutputRecommendationsSpecificCode = "eu-central-1" - RegionsInfoOutputRecommendationsSpecificCodeEuCentral2 RegionsInfoOutputRecommendationsSpecificCode = "eu-central-2" - RegionsInfoOutputRecommendationsSpecificCodeEuNorth1 RegionsInfoOutputRecommendationsSpecificCode = "eu-north-1" - RegionsInfoOutputRecommendationsSpecificCodeEuWest1 RegionsInfoOutputRecommendationsSpecificCode = "eu-west-1" - RegionsInfoOutputRecommendationsSpecificCodeEuWest2 RegionsInfoOutputRecommendationsSpecificCode = "eu-west-2" - RegionsInfoOutputRecommendationsSpecificCodeEuWest3 RegionsInfoOutputRecommendationsSpecificCode = "eu-west-3" - RegionsInfoOutputRecommendationsSpecificCodeSaEast1 RegionsInfoOutputRecommendationsSpecificCode = "sa-east-1" - RegionsInfoOutputRecommendationsSpecificCodeUsEast1 RegionsInfoOutputRecommendationsSpecificCode = "us-east-1" - RegionsInfoOutputRecommendationsSpecificCodeUsEast2 RegionsInfoOutputRecommendationsSpecificCode = "us-east-2" - RegionsInfoOutputRecommendationsSpecificCodeUsWest1 RegionsInfoOutputRecommendationsSpecificCode = "us-west-1" - RegionsInfoOutputRecommendationsSpecificCodeUsWest2 RegionsInfoOutputRecommendationsSpecificCode = "us-west-2" -) - -// Valid indicates whether the value is a known member of the RegionsInfoOutputRecommendationsSpecificCode enum. -func (e RegionsInfoOutputRecommendationsSpecificCode) Valid() bool { - switch e { - case RegionsInfoOutputRecommendationsSpecificCodeApEast1: - return true - case RegionsInfoOutputRecommendationsSpecificCodeApNortheast1: - return true - case RegionsInfoOutputRecommendationsSpecificCodeApNortheast2: - return true - case RegionsInfoOutputRecommendationsSpecificCodeApSouth1: - return true - case RegionsInfoOutputRecommendationsSpecificCodeApSoutheast1: - return true - case RegionsInfoOutputRecommendationsSpecificCodeApSoutheast2: - return true - case RegionsInfoOutputRecommendationsSpecificCodeCaCentral1: - return true - case RegionsInfoOutputRecommendationsSpecificCodeEuCentral1: - return true - case RegionsInfoOutputRecommendationsSpecificCodeEuCentral2: - return true - case RegionsInfoOutputRecommendationsSpecificCodeEuNorth1: - return true - case RegionsInfoOutputRecommendationsSpecificCodeEuWest1: - return true - case RegionsInfoOutputRecommendationsSpecificCodeEuWest2: - return true - case RegionsInfoOutputRecommendationsSpecificCodeEuWest3: - return true - case RegionsInfoOutputRecommendationsSpecificCodeSaEast1: - return true - case RegionsInfoOutputRecommendationsSpecificCodeUsEast1: - return true - case RegionsInfoOutputRecommendationsSpecificCodeUsEast2: - return true - case RegionsInfoOutputRecommendationsSpecificCodeUsWest1: - return true - case RegionsInfoOutputRecommendationsSpecificCodeUsWest2: - return true - default: - return false - } -} - -// Defines values for RegionsInfoOutputRecommendationsSpecificProvider. -const ( - RegionsInfoOutputRecommendationsSpecificProviderAWS RegionsInfoOutputRecommendationsSpecificProvider = "AWS" - RegionsInfoOutputRecommendationsSpecificProviderAWSK8S RegionsInfoOutputRecommendationsSpecificProvider = "AWS_K8S" - RegionsInfoOutputRecommendationsSpecificProviderAWSNIMBUS RegionsInfoOutputRecommendationsSpecificProvider = "AWS_NIMBUS" -) - -// Valid indicates whether the value is a known member of the RegionsInfoOutputRecommendationsSpecificProvider enum. -func (e RegionsInfoOutputRecommendationsSpecificProvider) Valid() bool { - switch e { - case RegionsInfoOutputRecommendationsSpecificProviderAWS: - return true - case RegionsInfoOutputRecommendationsSpecificProviderAWSK8S: - return true - case RegionsInfoOutputRecommendationsSpecificProviderAWSNIMBUS: - return true - default: - return false - } -} - -// Defines values for RegionsInfoOutputRecommendationsSpecificStatus. -const ( - RegionsInfoOutputRecommendationsSpecificStatusCapacity RegionsInfoOutputRecommendationsSpecificStatus = "capacity" - RegionsInfoOutputRecommendationsSpecificStatusOther RegionsInfoOutputRecommendationsSpecificStatus = "other" -) - -// Valid indicates whether the value is a known member of the RegionsInfoOutputRecommendationsSpecificStatus enum. -func (e RegionsInfoOutputRecommendationsSpecificStatus) Valid() bool { - switch e { - case RegionsInfoOutputRecommendationsSpecificStatusCapacity: - return true - case RegionsInfoOutputRecommendationsSpecificStatusOther: - return true - default: - return false - } -} - -// Defines values for RegionsInfoOutputRecommendationsSpecificType. -const ( - RegionsInfoOutputRecommendationsSpecificTypeSpecific RegionsInfoOutputRecommendationsSpecificType = "specific" -) - -// Valid indicates whether the value is a known member of the RegionsInfoOutputRecommendationsSpecificType enum. -func (e RegionsInfoOutputRecommendationsSpecificType) Valid() bool { - switch e { - case RegionsInfoOutputRecommendationsSpecificTypeSpecific: - return true - default: - return false - } -} - -// Defines values for SetUpReadReplicaBodyReadReplicaRegion. -const ( - SetUpReadReplicaBodyReadReplicaRegionApEast1 SetUpReadReplicaBodyReadReplicaRegion = "ap-east-1" - SetUpReadReplicaBodyReadReplicaRegionApNortheast1 SetUpReadReplicaBodyReadReplicaRegion = "ap-northeast-1" - SetUpReadReplicaBodyReadReplicaRegionApNortheast2 SetUpReadReplicaBodyReadReplicaRegion = "ap-northeast-2" - SetUpReadReplicaBodyReadReplicaRegionApSouth1 SetUpReadReplicaBodyReadReplicaRegion = "ap-south-1" - SetUpReadReplicaBodyReadReplicaRegionApSoutheast1 SetUpReadReplicaBodyReadReplicaRegion = "ap-southeast-1" - SetUpReadReplicaBodyReadReplicaRegionApSoutheast2 SetUpReadReplicaBodyReadReplicaRegion = "ap-southeast-2" - SetUpReadReplicaBodyReadReplicaRegionCaCentral1 SetUpReadReplicaBodyReadReplicaRegion = "ca-central-1" - SetUpReadReplicaBodyReadReplicaRegionEuCentral1 SetUpReadReplicaBodyReadReplicaRegion = "eu-central-1" - SetUpReadReplicaBodyReadReplicaRegionEuCentral2 SetUpReadReplicaBodyReadReplicaRegion = "eu-central-2" - SetUpReadReplicaBodyReadReplicaRegionEuNorth1 SetUpReadReplicaBodyReadReplicaRegion = "eu-north-1" - SetUpReadReplicaBodyReadReplicaRegionEuWest1 SetUpReadReplicaBodyReadReplicaRegion = "eu-west-1" - SetUpReadReplicaBodyReadReplicaRegionEuWest2 SetUpReadReplicaBodyReadReplicaRegion = "eu-west-2" - SetUpReadReplicaBodyReadReplicaRegionEuWest3 SetUpReadReplicaBodyReadReplicaRegion = "eu-west-3" - SetUpReadReplicaBodyReadReplicaRegionSaEast1 SetUpReadReplicaBodyReadReplicaRegion = "sa-east-1" - SetUpReadReplicaBodyReadReplicaRegionUsEast1 SetUpReadReplicaBodyReadReplicaRegion = "us-east-1" - SetUpReadReplicaBodyReadReplicaRegionUsEast2 SetUpReadReplicaBodyReadReplicaRegion = "us-east-2" - SetUpReadReplicaBodyReadReplicaRegionUsWest1 SetUpReadReplicaBodyReadReplicaRegion = "us-west-1" - SetUpReadReplicaBodyReadReplicaRegionUsWest2 SetUpReadReplicaBodyReadReplicaRegion = "us-west-2" -) - -// Valid indicates whether the value is a known member of the SetUpReadReplicaBodyReadReplicaRegion enum. -func (e SetUpReadReplicaBodyReadReplicaRegion) Valid() bool { - switch e { - case SetUpReadReplicaBodyReadReplicaRegionApEast1: - return true - case SetUpReadReplicaBodyReadReplicaRegionApNortheast1: - return true - case SetUpReadReplicaBodyReadReplicaRegionApNortheast2: - return true - case SetUpReadReplicaBodyReadReplicaRegionApSouth1: - return true - case SetUpReadReplicaBodyReadReplicaRegionApSoutheast1: - return true - case SetUpReadReplicaBodyReadReplicaRegionApSoutheast2: - return true - case SetUpReadReplicaBodyReadReplicaRegionCaCentral1: - return true - case SetUpReadReplicaBodyReadReplicaRegionEuCentral1: - return true - case SetUpReadReplicaBodyReadReplicaRegionEuCentral2: - return true - case SetUpReadReplicaBodyReadReplicaRegionEuNorth1: - return true - case SetUpReadReplicaBodyReadReplicaRegionEuWest1: - return true - case SetUpReadReplicaBodyReadReplicaRegionEuWest2: - return true - case SetUpReadReplicaBodyReadReplicaRegionEuWest3: - return true - case SetUpReadReplicaBodyReadReplicaRegionSaEast1: - return true - case SetUpReadReplicaBodyReadReplicaRegionUsEast1: - return true - case SetUpReadReplicaBodyReadReplicaRegionUsEast2: - return true - case SetUpReadReplicaBodyReadReplicaRegionUsWest1: - return true - case SetUpReadReplicaBodyReadReplicaRegionUsWest2: - return true - default: - return false - } -} - -// Defines values for SigningKeyResponseOutputAlgorithm. -const ( - SigningKeyResponseOutputAlgorithmES256 SigningKeyResponseOutputAlgorithm = "ES256" - SigningKeyResponseOutputAlgorithmEdDSA SigningKeyResponseOutputAlgorithm = "EdDSA" - SigningKeyResponseOutputAlgorithmHS256 SigningKeyResponseOutputAlgorithm = "HS256" - SigningKeyResponseOutputAlgorithmRS256 SigningKeyResponseOutputAlgorithm = "RS256" -) - -// Valid indicates whether the value is a known member of the SigningKeyResponseOutputAlgorithm enum. -func (e SigningKeyResponseOutputAlgorithm) Valid() bool { - switch e { - case SigningKeyResponseOutputAlgorithmES256: - return true - case SigningKeyResponseOutputAlgorithmEdDSA: - return true - case SigningKeyResponseOutputAlgorithmHS256: - return true - case SigningKeyResponseOutputAlgorithmRS256: - return true - default: - return false - } -} - -// Defines values for SigningKeyResponseOutputStatus. -const ( - SigningKeyResponseOutputStatusInUse SigningKeyResponseOutputStatus = "in_use" - SigningKeyResponseOutputStatusPreviouslyUsed SigningKeyResponseOutputStatus = "previously_used" - SigningKeyResponseOutputStatusRevoked SigningKeyResponseOutputStatus = "revoked" - SigningKeyResponseOutputStatusStandby SigningKeyResponseOutputStatus = "standby" -) - -// Valid indicates whether the value is a known member of the SigningKeyResponseOutputStatus enum. -func (e SigningKeyResponseOutputStatus) Valid() bool { - switch e { - case SigningKeyResponseOutputStatusInUse: - return true - case SigningKeyResponseOutputStatusPreviouslyUsed: - return true - case SigningKeyResponseOutputStatusRevoked: - return true - case SigningKeyResponseOutputStatusStandby: - return true - default: - return false - } -} - -// Defines values for SigningKeysResponseOutputKeysAlgorithm. -const ( - SigningKeysResponseOutputKeysAlgorithmES256 SigningKeysResponseOutputKeysAlgorithm = "ES256" - SigningKeysResponseOutputKeysAlgorithmEdDSA SigningKeysResponseOutputKeysAlgorithm = "EdDSA" - SigningKeysResponseOutputKeysAlgorithmHS256 SigningKeysResponseOutputKeysAlgorithm = "HS256" - SigningKeysResponseOutputKeysAlgorithmRS256 SigningKeysResponseOutputKeysAlgorithm = "RS256" -) - -// Valid indicates whether the value is a known member of the SigningKeysResponseOutputKeysAlgorithm enum. -func (e SigningKeysResponseOutputKeysAlgorithm) Valid() bool { - switch e { - case SigningKeysResponseOutputKeysAlgorithmES256: - return true - case SigningKeysResponseOutputKeysAlgorithmEdDSA: - return true - case SigningKeysResponseOutputKeysAlgorithmHS256: - return true - case SigningKeysResponseOutputKeysAlgorithmRS256: - return true - default: - return false - } -} - -// Defines values for SigningKeysResponseOutputKeysStatus. -const ( - SigningKeysResponseOutputKeysStatusInUse SigningKeysResponseOutputKeysStatus = "in_use" - SigningKeysResponseOutputKeysStatusPreviouslyUsed SigningKeysResponseOutputKeysStatus = "previously_used" - SigningKeysResponseOutputKeysStatusRevoked SigningKeysResponseOutputKeysStatus = "revoked" - SigningKeysResponseOutputKeysStatusStandby SigningKeysResponseOutputKeysStatus = "standby" -) - -// Valid indicates whether the value is a known member of the SigningKeysResponseOutputKeysStatus enum. -func (e SigningKeysResponseOutputKeysStatus) Valid() bool { - switch e { - case SigningKeysResponseOutputKeysStatusInUse: - return true - case SigningKeysResponseOutputKeysStatusPreviouslyUsed: - return true - case SigningKeysResponseOutputKeysStatusRevoked: - return true - case SigningKeysResponseOutputKeysStatusStandby: - return true - default: - return false - } -} - -// Defines values for SnippetListOutputDataType. -const ( - SnippetListOutputDataTypeSql SnippetListOutputDataType = "sql" -) - -// Valid indicates whether the value is a known member of the SnippetListOutputDataType enum. -func (e SnippetListOutputDataType) Valid() bool { - switch e { - case SnippetListOutputDataTypeSql: - return true - default: - return false - } -} - -// Defines values for SnippetListOutputDataVisibility. -const ( - SnippetListOutputDataVisibilityOrg SnippetListOutputDataVisibility = "org" - SnippetListOutputDataVisibilityProject SnippetListOutputDataVisibility = "project" - SnippetListOutputDataVisibilityPublic SnippetListOutputDataVisibility = "public" - SnippetListOutputDataVisibilityUser SnippetListOutputDataVisibility = "user" -) - -// Valid indicates whether the value is a known member of the SnippetListOutputDataVisibility enum. -func (e SnippetListOutputDataVisibility) Valid() bool { - switch e { - case SnippetListOutputDataVisibilityOrg: - return true - case SnippetListOutputDataVisibilityProject: - return true - case SnippetListOutputDataVisibilityPublic: - return true - case SnippetListOutputDataVisibilityUser: - return true - default: - return false - } -} - -// Defines values for SnippetResponseOutputType. -const ( - SnippetResponseOutputTypeSql SnippetResponseOutputType = "sql" -) - -// Valid indicates whether the value is a known member of the SnippetResponseOutputType enum. -func (e SnippetResponseOutputType) Valid() bool { - switch e { - case SnippetResponseOutputTypeSql: - return true - default: - return false - } -} - -// Defines values for SnippetResponseOutputVisibility. -const ( - SnippetResponseOutputVisibilityOrg SnippetResponseOutputVisibility = "org" - SnippetResponseOutputVisibilityProject SnippetResponseOutputVisibility = "project" - SnippetResponseOutputVisibilityPublic SnippetResponseOutputVisibility = "public" - SnippetResponseOutputVisibilityUser SnippetResponseOutputVisibility = "user" -) - -// Valid indicates whether the value is a known member of the SnippetResponseOutputVisibility enum. -func (e SnippetResponseOutputVisibility) Valid() bool { - switch e { - case SnippetResponseOutputVisibilityOrg: - return true - case SnippetResponseOutputVisibilityProject: - return true - case SnippetResponseOutputVisibilityPublic: - return true - case SnippetResponseOutputVisibilityUser: - return true - default: - return false - } -} - -// Defines values for StorageConfigResponseOutputExternalUpstreamTarget. -const ( - StorageConfigResponseOutputExternalUpstreamTargetCanary StorageConfigResponseOutputExternalUpstreamTarget = "canary" - StorageConfigResponseOutputExternalUpstreamTargetMain StorageConfigResponseOutputExternalUpstreamTarget = "main" -) - -// Valid indicates whether the value is a known member of the StorageConfigResponseOutputExternalUpstreamTarget enum. -func (e StorageConfigResponseOutputExternalUpstreamTarget) Valid() bool { - switch e { - case StorageConfigResponseOutputExternalUpstreamTargetCanary: - return true - case StorageConfigResponseOutputExternalUpstreamTargetMain: - return true - default: - return false - } -} - -// Defines values for SupavisorConfigResponseOutputDatabaseType. -const ( - SupavisorConfigResponseOutputDatabaseTypePRIMARY SupavisorConfigResponseOutputDatabaseType = "PRIMARY" - SupavisorConfigResponseOutputDatabaseTypeREADREPLICA SupavisorConfigResponseOutputDatabaseType = "READ_REPLICA" -) - -// Valid indicates whether the value is a known member of the SupavisorConfigResponseOutputDatabaseType enum. -func (e SupavisorConfigResponseOutputDatabaseType) Valid() bool { - switch e { - case SupavisorConfigResponseOutputDatabaseTypePRIMARY: - return true - case SupavisorConfigResponseOutputDatabaseTypeREADREPLICA: - return true - default: - return false - } -} - -// Defines values for SupavisorConfigResponseOutputPoolMode. -const ( - SupavisorConfigResponseOutputPoolModeSession SupavisorConfigResponseOutputPoolMode = "session" - SupavisorConfigResponseOutputPoolModeTransaction SupavisorConfigResponseOutputPoolMode = "transaction" -) - -// Valid indicates whether the value is a known member of the SupavisorConfigResponseOutputPoolMode enum. -func (e SupavisorConfigResponseOutputPoolMode) Valid() bool { - switch e { - case SupavisorConfigResponseOutputPoolModeSession: - return true - case SupavisorConfigResponseOutputPoolModeTransaction: - return true - default: - return false - } -} - -// Defines values for UpdateAuthConfigBodyDbMaxPoolSizeUnit. -const ( - UpdateAuthConfigBodyDbMaxPoolSizeUnitConnections UpdateAuthConfigBodyDbMaxPoolSizeUnit = "connections" - UpdateAuthConfigBodyDbMaxPoolSizeUnitLessThannil UpdateAuthConfigBodyDbMaxPoolSizeUnit = "" - UpdateAuthConfigBodyDbMaxPoolSizeUnitPercent UpdateAuthConfigBodyDbMaxPoolSizeUnit = "percent" -) - -// Valid indicates whether the value is a known member of the UpdateAuthConfigBodyDbMaxPoolSizeUnit enum. -func (e UpdateAuthConfigBodyDbMaxPoolSizeUnit) Valid() bool { - switch e { - case UpdateAuthConfigBodyDbMaxPoolSizeUnitConnections: - return true - case UpdateAuthConfigBodyDbMaxPoolSizeUnitLessThannil: - return true - case UpdateAuthConfigBodyDbMaxPoolSizeUnitPercent: - return true - default: - return false - } -} - -// Defines values for UpdateAuthConfigBodyPasswordRequiredCharacters. -const ( - UpdateAuthConfigBodyPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 UpdateAuthConfigBodyPasswordRequiredCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789" - UpdateAuthConfigBodyPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567891 UpdateAuthConfigBodyPasswordRequiredCharacters = "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789" - UpdateAuthConfigBodyPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567892 UpdateAuthConfigBodyPasswordRequiredCharacters = "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\\\:\"|<>?,./`~" - UpdateAuthConfigBodyPasswordRequiredCharactersEmpty UpdateAuthConfigBodyPasswordRequiredCharacters = "" -) - -// Valid indicates whether the value is a known member of the UpdateAuthConfigBodyPasswordRequiredCharacters enum. -func (e UpdateAuthConfigBodyPasswordRequiredCharacters) Valid() bool { - switch e { - case UpdateAuthConfigBodyPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789: - return true - case UpdateAuthConfigBodyPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567891: - return true - case UpdateAuthConfigBodyPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567892: - return true - case UpdateAuthConfigBodyPasswordRequiredCharactersEmpty: - return true - default: - return false - } -} - -// Defines values for UpdateAuthConfigBodySecurityCaptchaProvider. -const ( - UpdateAuthConfigBodySecurityCaptchaProviderHcaptcha UpdateAuthConfigBodySecurityCaptchaProvider = "hcaptcha" - UpdateAuthConfigBodySecurityCaptchaProviderLessThannil UpdateAuthConfigBodySecurityCaptchaProvider = "" - UpdateAuthConfigBodySecurityCaptchaProviderTurnstile UpdateAuthConfigBodySecurityCaptchaProvider = "turnstile" -) - -// Valid indicates whether the value is a known member of the UpdateAuthConfigBodySecurityCaptchaProvider enum. -func (e UpdateAuthConfigBodySecurityCaptchaProvider) Valid() bool { - switch e { - case UpdateAuthConfigBodySecurityCaptchaProviderHcaptcha: - return true - case UpdateAuthConfigBodySecurityCaptchaProviderLessThannil: - return true - case UpdateAuthConfigBodySecurityCaptchaProviderTurnstile: - return true - default: - return false - } -} - -// Defines values for UpdateAuthConfigBodySmsProvider. -const ( - LessThannil UpdateAuthConfigBodySmsProvider = "" - Messagebird UpdateAuthConfigBodySmsProvider = "messagebird" - Textlocal UpdateAuthConfigBodySmsProvider = "textlocal" - Twilio UpdateAuthConfigBodySmsProvider = "twilio" - TwilioVerify UpdateAuthConfigBodySmsProvider = "twilio_verify" - Vonage UpdateAuthConfigBodySmsProvider = "vonage" -) - -// Valid indicates whether the value is a known member of the UpdateAuthConfigBodySmsProvider enum. -func (e UpdateAuthConfigBodySmsProvider) Valid() bool { - switch e { - case LessThannil: - return true - case Messagebird: - return true - case Textlocal: - return true - case Twilio: - return true - case TwilioVerify: - return true - case Vonage: - return true - default: - return false - } -} - -// Defines values for UpdateBranchBodyStatus. -const ( - UpdateBranchBodyStatusCREATINGPROJECT UpdateBranchBodyStatus = "CREATING_PROJECT" - UpdateBranchBodyStatusFUNCTIONSDEPLOYED UpdateBranchBodyStatus = "FUNCTIONS_DEPLOYED" - UpdateBranchBodyStatusFUNCTIONSFAILED UpdateBranchBodyStatus = "FUNCTIONS_FAILED" - UpdateBranchBodyStatusMIGRATIONSFAILED UpdateBranchBodyStatus = "MIGRATIONS_FAILED" - UpdateBranchBodyStatusMIGRATIONSPASSED UpdateBranchBodyStatus = "MIGRATIONS_PASSED" - UpdateBranchBodyStatusRUNNINGMIGRATIONS UpdateBranchBodyStatus = "RUNNING_MIGRATIONS" -) - -// Valid indicates whether the value is a known member of the UpdateBranchBodyStatus enum. -func (e UpdateBranchBodyStatus) Valid() bool { - switch e { - case UpdateBranchBodyStatusCREATINGPROJECT: - return true - case UpdateBranchBodyStatusFUNCTIONSDEPLOYED: - return true - case UpdateBranchBodyStatusFUNCTIONSFAILED: - return true - case UpdateBranchBodyStatusMIGRATIONSFAILED: - return true - case UpdateBranchBodyStatusMIGRATIONSPASSED: - return true - case UpdateBranchBodyStatusRUNNINGMIGRATIONS: - return true - default: - return false - } -} - -// Defines values for UpdateCustomHostnameResponseOutputStatus. -const ( - N1NotStarted UpdateCustomHostnameResponseOutputStatus = "1_not_started" - N2Initiated UpdateCustomHostnameResponseOutputStatus = "2_initiated" - N3ChallengeVerified UpdateCustomHostnameResponseOutputStatus = "3_challenge_verified" - N4OriginSetupCompleted UpdateCustomHostnameResponseOutputStatus = "4_origin_setup_completed" - N5ServicesReconfigured UpdateCustomHostnameResponseOutputStatus = "5_services_reconfigured" -) - -// Valid indicates whether the value is a known member of the UpdateCustomHostnameResponseOutputStatus enum. -func (e UpdateCustomHostnameResponseOutputStatus) Valid() bool { - switch e { - case N1NotStarted: - return true - case N2Initiated: - return true - case N3ChallengeVerified: - return true - case N4OriginSetupCompleted: - return true - case N5ServicesReconfigured: - return true - default: - return false - } -} - -// Defines values for UpdatePostgresConfigBodySessionReplicationRole. -const ( - UpdatePostgresConfigBodySessionReplicationRoleLocal UpdatePostgresConfigBodySessionReplicationRole = "local" - UpdatePostgresConfigBodySessionReplicationRoleOrigin UpdatePostgresConfigBodySessionReplicationRole = "origin" - UpdatePostgresConfigBodySessionReplicationRoleReplica UpdatePostgresConfigBodySessionReplicationRole = "replica" -) - -// Valid indicates whether the value is a known member of the UpdatePostgresConfigBodySessionReplicationRole enum. -func (e UpdatePostgresConfigBodySessionReplicationRole) Valid() bool { - switch e { - case UpdatePostgresConfigBodySessionReplicationRoleLocal: - return true - case UpdatePostgresConfigBodySessionReplicationRoleOrigin: - return true - case UpdatePostgresConfigBodySessionReplicationRoleReplica: - return true - default: - return false - } -} - -// Defines values for UpdateProviderBodyNameIdFormat. -const ( - UpdateProviderBodyNameIdFormatUrnOasisNamesTcSAML11NameidFormatEmailAddress UpdateProviderBodyNameIdFormat = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" - UpdateProviderBodyNameIdFormatUrnOasisNamesTcSAML11NameidFormatUnspecified UpdateProviderBodyNameIdFormat = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" - UpdateProviderBodyNameIdFormatUrnOasisNamesTcSAML20NameidFormatPersistent UpdateProviderBodyNameIdFormat = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" - UpdateProviderBodyNameIdFormatUrnOasisNamesTcSAML20NameidFormatTransient UpdateProviderBodyNameIdFormat = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" -) - -// Valid indicates whether the value is a known member of the UpdateProviderBodyNameIdFormat enum. -func (e UpdateProviderBodyNameIdFormat) Valid() bool { - switch e { - case UpdateProviderBodyNameIdFormatUrnOasisNamesTcSAML11NameidFormatEmailAddress: - return true - case UpdateProviderBodyNameIdFormatUrnOasisNamesTcSAML11NameidFormatUnspecified: - return true - case UpdateProviderBodyNameIdFormatUrnOasisNamesTcSAML20NameidFormatPersistent: - return true - case UpdateProviderBodyNameIdFormatUrnOasisNamesTcSAML20NameidFormatTransient: - return true - default: - return false - } -} - -// Defines values for UpdateRunStatusBodyClone. -const ( - UpdateRunStatusBodyCloneCREATED UpdateRunStatusBodyClone = "CREATED" - UpdateRunStatusBodyCloneDEAD UpdateRunStatusBodyClone = "DEAD" - UpdateRunStatusBodyCloneEXITED UpdateRunStatusBodyClone = "EXITED" - UpdateRunStatusBodyClonePAUSED UpdateRunStatusBodyClone = "PAUSED" - UpdateRunStatusBodyCloneREMOVING UpdateRunStatusBodyClone = "REMOVING" - UpdateRunStatusBodyCloneRESTARTING UpdateRunStatusBodyClone = "RESTARTING" - UpdateRunStatusBodyCloneRUNNING UpdateRunStatusBodyClone = "RUNNING" -) - -// Valid indicates whether the value is a known member of the UpdateRunStatusBodyClone enum. -func (e UpdateRunStatusBodyClone) Valid() bool { - switch e { - case UpdateRunStatusBodyCloneCREATED: - return true - case UpdateRunStatusBodyCloneDEAD: - return true - case UpdateRunStatusBodyCloneEXITED: - return true - case UpdateRunStatusBodyClonePAUSED: - return true - case UpdateRunStatusBodyCloneREMOVING: - return true - case UpdateRunStatusBodyCloneRESTARTING: - return true - case UpdateRunStatusBodyCloneRUNNING: - return true - default: - return false - } -} - -// Defines values for UpdateRunStatusBodyConfigure. -const ( - UpdateRunStatusBodyConfigureCREATED UpdateRunStatusBodyConfigure = "CREATED" - UpdateRunStatusBodyConfigureDEAD UpdateRunStatusBodyConfigure = "DEAD" - UpdateRunStatusBodyConfigureEXITED UpdateRunStatusBodyConfigure = "EXITED" - UpdateRunStatusBodyConfigurePAUSED UpdateRunStatusBodyConfigure = "PAUSED" - UpdateRunStatusBodyConfigureREMOVING UpdateRunStatusBodyConfigure = "REMOVING" - UpdateRunStatusBodyConfigureRESTARTING UpdateRunStatusBodyConfigure = "RESTARTING" - UpdateRunStatusBodyConfigureRUNNING UpdateRunStatusBodyConfigure = "RUNNING" -) - -// Valid indicates whether the value is a known member of the UpdateRunStatusBodyConfigure enum. -func (e UpdateRunStatusBodyConfigure) Valid() bool { - switch e { - case UpdateRunStatusBodyConfigureCREATED: - return true - case UpdateRunStatusBodyConfigureDEAD: - return true - case UpdateRunStatusBodyConfigureEXITED: - return true - case UpdateRunStatusBodyConfigurePAUSED: - return true - case UpdateRunStatusBodyConfigureREMOVING: - return true - case UpdateRunStatusBodyConfigureRESTARTING: - return true - case UpdateRunStatusBodyConfigureRUNNING: - return true - default: - return false - } -} - -// Defines values for UpdateRunStatusBodyDeploy. -const ( - UpdateRunStatusBodyDeployCREATED UpdateRunStatusBodyDeploy = "CREATED" - UpdateRunStatusBodyDeployDEAD UpdateRunStatusBodyDeploy = "DEAD" - UpdateRunStatusBodyDeployEXITED UpdateRunStatusBodyDeploy = "EXITED" - UpdateRunStatusBodyDeployPAUSED UpdateRunStatusBodyDeploy = "PAUSED" - UpdateRunStatusBodyDeployREMOVING UpdateRunStatusBodyDeploy = "REMOVING" - UpdateRunStatusBodyDeployRESTARTING UpdateRunStatusBodyDeploy = "RESTARTING" - UpdateRunStatusBodyDeployRUNNING UpdateRunStatusBodyDeploy = "RUNNING" -) - -// Valid indicates whether the value is a known member of the UpdateRunStatusBodyDeploy enum. -func (e UpdateRunStatusBodyDeploy) Valid() bool { - switch e { - case UpdateRunStatusBodyDeployCREATED: - return true - case UpdateRunStatusBodyDeployDEAD: - return true - case UpdateRunStatusBodyDeployEXITED: - return true - case UpdateRunStatusBodyDeployPAUSED: - return true - case UpdateRunStatusBodyDeployREMOVING: - return true - case UpdateRunStatusBodyDeployRESTARTING: - return true - case UpdateRunStatusBodyDeployRUNNING: - return true - default: - return false - } -} - -// Defines values for UpdateRunStatusBodyHealth. -const ( - UpdateRunStatusBodyHealthCREATED UpdateRunStatusBodyHealth = "CREATED" - UpdateRunStatusBodyHealthDEAD UpdateRunStatusBodyHealth = "DEAD" - UpdateRunStatusBodyHealthEXITED UpdateRunStatusBodyHealth = "EXITED" - UpdateRunStatusBodyHealthPAUSED UpdateRunStatusBodyHealth = "PAUSED" - UpdateRunStatusBodyHealthREMOVING UpdateRunStatusBodyHealth = "REMOVING" - UpdateRunStatusBodyHealthRESTARTING UpdateRunStatusBodyHealth = "RESTARTING" - UpdateRunStatusBodyHealthRUNNING UpdateRunStatusBodyHealth = "RUNNING" -) - -// Valid indicates whether the value is a known member of the UpdateRunStatusBodyHealth enum. -func (e UpdateRunStatusBodyHealth) Valid() bool { - switch e { - case UpdateRunStatusBodyHealthCREATED: - return true - case UpdateRunStatusBodyHealthDEAD: - return true - case UpdateRunStatusBodyHealthEXITED: - return true - case UpdateRunStatusBodyHealthPAUSED: - return true - case UpdateRunStatusBodyHealthREMOVING: - return true - case UpdateRunStatusBodyHealthRESTARTING: - return true - case UpdateRunStatusBodyHealthRUNNING: - return true - default: - return false - } -} - -// Defines values for UpdateRunStatusBodyMigrate. -const ( - UpdateRunStatusBodyMigrateCREATED UpdateRunStatusBodyMigrate = "CREATED" - UpdateRunStatusBodyMigrateDEAD UpdateRunStatusBodyMigrate = "DEAD" - UpdateRunStatusBodyMigrateEXITED UpdateRunStatusBodyMigrate = "EXITED" - UpdateRunStatusBodyMigratePAUSED UpdateRunStatusBodyMigrate = "PAUSED" - UpdateRunStatusBodyMigrateREMOVING UpdateRunStatusBodyMigrate = "REMOVING" - UpdateRunStatusBodyMigrateRESTARTING UpdateRunStatusBodyMigrate = "RESTARTING" - UpdateRunStatusBodyMigrateRUNNING UpdateRunStatusBodyMigrate = "RUNNING" -) - -// Valid indicates whether the value is a known member of the UpdateRunStatusBodyMigrate enum. -func (e UpdateRunStatusBodyMigrate) Valid() bool { - switch e { - case UpdateRunStatusBodyMigrateCREATED: - return true - case UpdateRunStatusBodyMigrateDEAD: - return true - case UpdateRunStatusBodyMigrateEXITED: - return true - case UpdateRunStatusBodyMigratePAUSED: - return true - case UpdateRunStatusBodyMigrateREMOVING: - return true - case UpdateRunStatusBodyMigrateRESTARTING: - return true - case UpdateRunStatusBodyMigrateRUNNING: - return true - default: - return false - } -} - -// Defines values for UpdateRunStatusBodyPull. -const ( - UpdateRunStatusBodyPullCREATED UpdateRunStatusBodyPull = "CREATED" - UpdateRunStatusBodyPullDEAD UpdateRunStatusBodyPull = "DEAD" - UpdateRunStatusBodyPullEXITED UpdateRunStatusBodyPull = "EXITED" - UpdateRunStatusBodyPullPAUSED UpdateRunStatusBodyPull = "PAUSED" - UpdateRunStatusBodyPullREMOVING UpdateRunStatusBodyPull = "REMOVING" - UpdateRunStatusBodyPullRESTARTING UpdateRunStatusBodyPull = "RESTARTING" - UpdateRunStatusBodyPullRUNNING UpdateRunStatusBodyPull = "RUNNING" -) - -// Valid indicates whether the value is a known member of the UpdateRunStatusBodyPull enum. -func (e UpdateRunStatusBodyPull) Valid() bool { - switch e { - case UpdateRunStatusBodyPullCREATED: - return true - case UpdateRunStatusBodyPullDEAD: - return true - case UpdateRunStatusBodyPullEXITED: - return true - case UpdateRunStatusBodyPullPAUSED: - return true - case UpdateRunStatusBodyPullREMOVING: - return true - case UpdateRunStatusBodyPullRESTARTING: - return true - case UpdateRunStatusBodyPullRUNNING: - return true - default: - return false - } -} - -// Defines values for UpdateRunStatusBodySeed. -const ( - UpdateRunStatusBodySeedCREATED UpdateRunStatusBodySeed = "CREATED" - UpdateRunStatusBodySeedDEAD UpdateRunStatusBodySeed = "DEAD" - UpdateRunStatusBodySeedEXITED UpdateRunStatusBodySeed = "EXITED" - UpdateRunStatusBodySeedPAUSED UpdateRunStatusBodySeed = "PAUSED" - UpdateRunStatusBodySeedREMOVING UpdateRunStatusBodySeed = "REMOVING" - UpdateRunStatusBodySeedRESTARTING UpdateRunStatusBodySeed = "RESTARTING" - UpdateRunStatusBodySeedRUNNING UpdateRunStatusBodySeed = "RUNNING" -) - -// Valid indicates whether the value is a known member of the UpdateRunStatusBodySeed enum. -func (e UpdateRunStatusBodySeed) Valid() bool { - switch e { - case UpdateRunStatusBodySeedCREATED: - return true - case UpdateRunStatusBodySeedDEAD: - return true - case UpdateRunStatusBodySeedEXITED: - return true - case UpdateRunStatusBodySeedPAUSED: - return true - case UpdateRunStatusBodySeedREMOVING: - return true - case UpdateRunStatusBodySeedRESTARTING: - return true - case UpdateRunStatusBodySeedRUNNING: - return true - default: - return false - } -} - -// Defines values for UpdateRunStatusResponseOutputMessage. -const ( - UpdateRunStatusResponseOutputMessageOk UpdateRunStatusResponseOutputMessage = "ok" -) - -// Valid indicates whether the value is a known member of the UpdateRunStatusResponseOutputMessage enum. -func (e UpdateRunStatusResponseOutputMessage) Valid() bool { - switch e { - case UpdateRunStatusResponseOutputMessageOk: - return true - default: - return false - } -} - -// Defines values for UpdateSigningKeyBodyStatus. -const ( - UpdateSigningKeyBodyStatusInUse UpdateSigningKeyBodyStatus = "in_use" - UpdateSigningKeyBodyStatusPreviouslyUsed UpdateSigningKeyBodyStatus = "previously_used" - UpdateSigningKeyBodyStatusRevoked UpdateSigningKeyBodyStatus = "revoked" - UpdateSigningKeyBodyStatusStandby UpdateSigningKeyBodyStatus = "standby" -) - -// Valid indicates whether the value is a known member of the UpdateSigningKeyBodyStatus enum. -func (e UpdateSigningKeyBodyStatus) Valid() bool { - switch e { - case UpdateSigningKeyBodyStatusInUse: - return true - case UpdateSigningKeyBodyStatusPreviouslyUsed: - return true - case UpdateSigningKeyBodyStatusRevoked: - return true - case UpdateSigningKeyBodyStatusStandby: - return true - default: - return false - } -} - -// Defines values for UpdateStorageConfigBodyExternalUpstreamTarget. -const ( - UpdateStorageConfigBodyExternalUpstreamTargetCanary UpdateStorageConfigBodyExternalUpstreamTarget = "canary" - UpdateStorageConfigBodyExternalUpstreamTargetMain UpdateStorageConfigBodyExternalUpstreamTarget = "main" -) - -// Valid indicates whether the value is a known member of the UpdateStorageConfigBodyExternalUpstreamTarget enum. -func (e UpdateStorageConfigBodyExternalUpstreamTarget) Valid() bool { - switch e { - case UpdateStorageConfigBodyExternalUpstreamTargetCanary: - return true - case UpdateStorageConfigBodyExternalUpstreamTargetMain: - return true - default: - return false - } -} - -// Defines values for UpdateSupavisorConfigBodyPoolMode. -const ( - UpdateSupavisorConfigBodyPoolModeSession UpdateSupavisorConfigBodyPoolMode = "session" - UpdateSupavisorConfigBodyPoolModeTransaction UpdateSupavisorConfigBodyPoolMode = "transaction" -) - -// Valid indicates whether the value is a known member of the UpdateSupavisorConfigBodyPoolMode enum. -func (e UpdateSupavisorConfigBodyPoolMode) Valid() bool { - switch e { - case UpdateSupavisorConfigBodyPoolModeSession: - return true - case UpdateSupavisorConfigBodyPoolModeTransaction: - return true - default: - return false - } -} - -// Defines values for UpgradeDatabaseBodyReleaseChannel. -const ( - UpgradeDatabaseBodyReleaseChannelAlpha UpgradeDatabaseBodyReleaseChannel = "alpha" - UpgradeDatabaseBodyReleaseChannelBeta UpgradeDatabaseBodyReleaseChannel = "beta" - UpgradeDatabaseBodyReleaseChannelGa UpgradeDatabaseBodyReleaseChannel = "ga" - UpgradeDatabaseBodyReleaseChannelInternal UpgradeDatabaseBodyReleaseChannel = "internal" - UpgradeDatabaseBodyReleaseChannelPreview UpgradeDatabaseBodyReleaseChannel = "preview" - UpgradeDatabaseBodyReleaseChannelWithdrawn UpgradeDatabaseBodyReleaseChannel = "withdrawn" -) - -// Valid indicates whether the value is a known member of the UpgradeDatabaseBodyReleaseChannel enum. -func (e UpgradeDatabaseBodyReleaseChannel) Valid() bool { - switch e { - case UpgradeDatabaseBodyReleaseChannelAlpha: - return true - case UpgradeDatabaseBodyReleaseChannelBeta: - return true - case UpgradeDatabaseBodyReleaseChannelGa: - return true - case UpgradeDatabaseBodyReleaseChannelInternal: - return true - case UpgradeDatabaseBodyReleaseChannelPreview: - return true - case UpgradeDatabaseBodyReleaseChannelWithdrawn: - return true - default: - return false - } -} - -// Defines values for V1BackupsResponseOutputBackupsStatus. -const ( - V1BackupsResponseOutputBackupsStatusARCHIVED V1BackupsResponseOutputBackupsStatus = "ARCHIVED" - V1BackupsResponseOutputBackupsStatusCANCELLED V1BackupsResponseOutputBackupsStatus = "CANCELLED" - V1BackupsResponseOutputBackupsStatusCOMPLETED V1BackupsResponseOutputBackupsStatus = "COMPLETED" - V1BackupsResponseOutputBackupsStatusFAILED V1BackupsResponseOutputBackupsStatus = "FAILED" - V1BackupsResponseOutputBackupsStatusPENDING V1BackupsResponseOutputBackupsStatus = "PENDING" - V1BackupsResponseOutputBackupsStatusREMOVED V1BackupsResponseOutputBackupsStatus = "REMOVED" -) - -// Valid indicates whether the value is a known member of the V1BackupsResponseOutputBackupsStatus enum. -func (e V1BackupsResponseOutputBackupsStatus) Valid() bool { - switch e { - case V1BackupsResponseOutputBackupsStatusARCHIVED: - return true - case V1BackupsResponseOutputBackupsStatusCANCELLED: - return true - case V1BackupsResponseOutputBackupsStatusCOMPLETED: - return true - case V1BackupsResponseOutputBackupsStatusFAILED: - return true - case V1BackupsResponseOutputBackupsStatusPENDING: - return true - case V1BackupsResponseOutputBackupsStatusREMOVED: - return true - default: - return false - } -} - -// Defines values for V1CreateProjectBodyDesiredInstanceSize. -const ( - V1CreateProjectBodyDesiredInstanceSizeLarge V1CreateProjectBodyDesiredInstanceSize = "large" - V1CreateProjectBodyDesiredInstanceSizeMedium V1CreateProjectBodyDesiredInstanceSize = "medium" - V1CreateProjectBodyDesiredInstanceSizeMicro V1CreateProjectBodyDesiredInstanceSize = "micro" - V1CreateProjectBodyDesiredInstanceSizeN12xlarge V1CreateProjectBodyDesiredInstanceSize = "12xlarge" - V1CreateProjectBodyDesiredInstanceSizeN16xlarge V1CreateProjectBodyDesiredInstanceSize = "16xlarge" - V1CreateProjectBodyDesiredInstanceSizeN24xlarge V1CreateProjectBodyDesiredInstanceSize = "24xlarge" - V1CreateProjectBodyDesiredInstanceSizeN24xlargeHighMemory V1CreateProjectBodyDesiredInstanceSize = "24xlarge_high_memory" - V1CreateProjectBodyDesiredInstanceSizeN24xlargeOptimizedCpu V1CreateProjectBodyDesiredInstanceSize = "24xlarge_optimized_cpu" - V1CreateProjectBodyDesiredInstanceSizeN24xlargeOptimizedMemory V1CreateProjectBodyDesiredInstanceSize = "24xlarge_optimized_memory" - V1CreateProjectBodyDesiredInstanceSizeN2xlarge V1CreateProjectBodyDesiredInstanceSize = "2xlarge" - V1CreateProjectBodyDesiredInstanceSizeN48xlarge V1CreateProjectBodyDesiredInstanceSize = "48xlarge" - V1CreateProjectBodyDesiredInstanceSizeN48xlargeHighMemory V1CreateProjectBodyDesiredInstanceSize = "48xlarge_high_memory" - V1CreateProjectBodyDesiredInstanceSizeN48xlargeOptimizedCpu V1CreateProjectBodyDesiredInstanceSize = "48xlarge_optimized_cpu" - V1CreateProjectBodyDesiredInstanceSizeN48xlargeOptimizedMemory V1CreateProjectBodyDesiredInstanceSize = "48xlarge_optimized_memory" - V1CreateProjectBodyDesiredInstanceSizeN4xlarge V1CreateProjectBodyDesiredInstanceSize = "4xlarge" - V1CreateProjectBodyDesiredInstanceSizeN8xlarge V1CreateProjectBodyDesiredInstanceSize = "8xlarge" - V1CreateProjectBodyDesiredInstanceSizeNano V1CreateProjectBodyDesiredInstanceSize = "nano" - V1CreateProjectBodyDesiredInstanceSizeSmall V1CreateProjectBodyDesiredInstanceSize = "small" - V1CreateProjectBodyDesiredInstanceSizeXlarge V1CreateProjectBodyDesiredInstanceSize = "xlarge" -) - -// Valid indicates whether the value is a known member of the V1CreateProjectBodyDesiredInstanceSize enum. -func (e V1CreateProjectBodyDesiredInstanceSize) Valid() bool { - switch e { - case V1CreateProjectBodyDesiredInstanceSizeLarge: - return true - case V1CreateProjectBodyDesiredInstanceSizeMedium: - return true - case V1CreateProjectBodyDesiredInstanceSizeMicro: - return true - case V1CreateProjectBodyDesiredInstanceSizeN12xlarge: - return true - case V1CreateProjectBodyDesiredInstanceSizeN16xlarge: - return true - case V1CreateProjectBodyDesiredInstanceSizeN24xlarge: - return true - case V1CreateProjectBodyDesiredInstanceSizeN24xlargeHighMemory: - return true - case V1CreateProjectBodyDesiredInstanceSizeN24xlargeOptimizedCpu: - return true - case V1CreateProjectBodyDesiredInstanceSizeN24xlargeOptimizedMemory: - return true - case V1CreateProjectBodyDesiredInstanceSizeN2xlarge: - return true - case V1CreateProjectBodyDesiredInstanceSizeN48xlarge: - return true - case V1CreateProjectBodyDesiredInstanceSizeN48xlargeHighMemory: - return true - case V1CreateProjectBodyDesiredInstanceSizeN48xlargeOptimizedCpu: - return true - case V1CreateProjectBodyDesiredInstanceSizeN48xlargeOptimizedMemory: - return true - case V1CreateProjectBodyDesiredInstanceSizeN4xlarge: - return true - case V1CreateProjectBodyDesiredInstanceSizeN8xlarge: - return true - case V1CreateProjectBodyDesiredInstanceSizeNano: - return true - case V1CreateProjectBodyDesiredInstanceSizeSmall: - return true - case V1CreateProjectBodyDesiredInstanceSizeXlarge: - return true - default: - return false - } -} - -// Defines values for V1CreateProjectBodyPlan. -const ( - V1CreateProjectBodyPlanFree V1CreateProjectBodyPlan = "free" - V1CreateProjectBodyPlanPro V1CreateProjectBodyPlan = "pro" -) - -// Valid indicates whether the value is a known member of the V1CreateProjectBodyPlan enum. -func (e V1CreateProjectBodyPlan) Valid() bool { - switch e { - case V1CreateProjectBodyPlanFree: - return true - case V1CreateProjectBodyPlanPro: - return true - default: - return false - } -} - -// Defines values for V1CreateProjectBodyRegion. -const ( - V1CreateProjectBodyRegionApEast1 V1CreateProjectBodyRegion = "ap-east-1" - V1CreateProjectBodyRegionApNortheast1 V1CreateProjectBodyRegion = "ap-northeast-1" - V1CreateProjectBodyRegionApNortheast2 V1CreateProjectBodyRegion = "ap-northeast-2" - V1CreateProjectBodyRegionApSouth1 V1CreateProjectBodyRegion = "ap-south-1" - V1CreateProjectBodyRegionApSoutheast1 V1CreateProjectBodyRegion = "ap-southeast-1" - V1CreateProjectBodyRegionApSoutheast2 V1CreateProjectBodyRegion = "ap-southeast-2" - V1CreateProjectBodyRegionCaCentral1 V1CreateProjectBodyRegion = "ca-central-1" - V1CreateProjectBodyRegionEuCentral1 V1CreateProjectBodyRegion = "eu-central-1" - V1CreateProjectBodyRegionEuCentral2 V1CreateProjectBodyRegion = "eu-central-2" - V1CreateProjectBodyRegionEuNorth1 V1CreateProjectBodyRegion = "eu-north-1" - V1CreateProjectBodyRegionEuWest1 V1CreateProjectBodyRegion = "eu-west-1" - V1CreateProjectBodyRegionEuWest2 V1CreateProjectBodyRegion = "eu-west-2" - V1CreateProjectBodyRegionEuWest3 V1CreateProjectBodyRegion = "eu-west-3" - V1CreateProjectBodyRegionSaEast1 V1CreateProjectBodyRegion = "sa-east-1" - V1CreateProjectBodyRegionUsEast1 V1CreateProjectBodyRegion = "us-east-1" - V1CreateProjectBodyRegionUsEast2 V1CreateProjectBodyRegion = "us-east-2" - V1CreateProjectBodyRegionUsWest1 V1CreateProjectBodyRegion = "us-west-1" - V1CreateProjectBodyRegionUsWest2 V1CreateProjectBodyRegion = "us-west-2" -) - -// Valid indicates whether the value is a known member of the V1CreateProjectBodyRegion enum. -func (e V1CreateProjectBodyRegion) Valid() bool { - switch e { - case V1CreateProjectBodyRegionApEast1: - return true - case V1CreateProjectBodyRegionApNortheast1: - return true - case V1CreateProjectBodyRegionApNortheast2: - return true - case V1CreateProjectBodyRegionApSouth1: - return true - case V1CreateProjectBodyRegionApSoutheast1: - return true - case V1CreateProjectBodyRegionApSoutheast2: - return true - case V1CreateProjectBodyRegionCaCentral1: - return true - case V1CreateProjectBodyRegionEuCentral1: - return true - case V1CreateProjectBodyRegionEuCentral2: - return true - case V1CreateProjectBodyRegionEuNorth1: - return true - case V1CreateProjectBodyRegionEuWest1: - return true - case V1CreateProjectBodyRegionEuWest2: - return true - case V1CreateProjectBodyRegionEuWest3: - return true - case V1CreateProjectBodyRegionSaEast1: - return true - case V1CreateProjectBodyRegionUsEast1: - return true - case V1CreateProjectBodyRegionUsEast2: - return true - case V1CreateProjectBodyRegionUsWest1: - return true - case V1CreateProjectBodyRegionUsWest2: - return true - default: - return false - } -} - -// Defines values for V1CreateProjectBodyRegionSelection0Code. -const ( - ApEast1 V1CreateProjectBodyRegionSelection0Code = "ap-east-1" - ApNortheast1 V1CreateProjectBodyRegionSelection0Code = "ap-northeast-1" - ApNortheast2 V1CreateProjectBodyRegionSelection0Code = "ap-northeast-2" - ApSouth1 V1CreateProjectBodyRegionSelection0Code = "ap-south-1" - ApSoutheast1 V1CreateProjectBodyRegionSelection0Code = "ap-southeast-1" - ApSoutheast2 V1CreateProjectBodyRegionSelection0Code = "ap-southeast-2" - CaCentral1 V1CreateProjectBodyRegionSelection0Code = "ca-central-1" - EuCentral1 V1CreateProjectBodyRegionSelection0Code = "eu-central-1" - EuCentral2 V1CreateProjectBodyRegionSelection0Code = "eu-central-2" - EuNorth1 V1CreateProjectBodyRegionSelection0Code = "eu-north-1" - EuWest1 V1CreateProjectBodyRegionSelection0Code = "eu-west-1" - EuWest2 V1CreateProjectBodyRegionSelection0Code = "eu-west-2" - EuWest3 V1CreateProjectBodyRegionSelection0Code = "eu-west-3" - SaEast1 V1CreateProjectBodyRegionSelection0Code = "sa-east-1" - UsEast1 V1CreateProjectBodyRegionSelection0Code = "us-east-1" - UsEast2 V1CreateProjectBodyRegionSelection0Code = "us-east-2" - UsWest1 V1CreateProjectBodyRegionSelection0Code = "us-west-1" - UsWest2 V1CreateProjectBodyRegionSelection0Code = "us-west-2" -) - -// Valid indicates whether the value is a known member of the V1CreateProjectBodyRegionSelection0Code enum. -func (e V1CreateProjectBodyRegionSelection0Code) Valid() bool { - switch e { - case ApEast1: - return true - case ApNortheast1: - return true - case ApNortheast2: - return true - case ApSouth1: - return true - case ApSoutheast1: - return true - case ApSoutheast2: - return true - case CaCentral1: - return true - case EuCentral1: - return true - case EuCentral2: - return true - case EuNorth1: - return true - case EuWest1: - return true - case EuWest2: - return true - case EuWest3: - return true - case SaEast1: - return true - case UsEast1: - return true - case UsEast2: - return true - case UsWest1: - return true - case UsWest2: - return true - default: - return false - } -} - -// Defines values for V1CreateProjectBodyRegionSelection0Type. -const ( - Specific V1CreateProjectBodyRegionSelection0Type = "specific" -) - -// Valid indicates whether the value is a known member of the V1CreateProjectBodyRegionSelection0Type enum. -func (e V1CreateProjectBodyRegionSelection0Type) Valid() bool { - switch e { - case Specific: - return true - default: - return false - } -} - -// Defines values for V1CreateProjectBodyRegionSelection1Code. -const ( - Americas V1CreateProjectBodyRegionSelection1Code = "americas" - Apac V1CreateProjectBodyRegionSelection1Code = "apac" - Emea V1CreateProjectBodyRegionSelection1Code = "emea" -) - -// Valid indicates whether the value is a known member of the V1CreateProjectBodyRegionSelection1Code enum. -func (e V1CreateProjectBodyRegionSelection1Code) Valid() bool { - switch e { - case Americas: - return true - case Apac: - return true - case Emea: - return true - default: - return false - } -} - -// Defines values for V1CreateProjectBodyRegionSelection1Type. -const ( - SmartGroup V1CreateProjectBodyRegionSelection1Type = "smartGroup" -) - -// Valid indicates whether the value is a known member of the V1CreateProjectBodyRegionSelection1Type enum. -func (e V1CreateProjectBodyRegionSelection1Type) Valid() bool { - switch e { - case SmartGroup: - return true - default: - return false - } -} - -// Defines values for V1ListEntitlementsResponseOutputEntitlementsFeatureKey. -const ( - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyApiMembersInvitations V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "api.members.invitations" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyApiMembersRoles V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "api.members.roles" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAssistantAdvanceModel V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "assistant.advance_model" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuditLogDrains V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "audit_log_drains" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthAdvancedAuthSettings V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "auth.advanced_auth_settings" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthCustomJwtTemplate V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "auth.custom_jwt_template" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthCustomOauthMaxProviders V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "auth.custom_oauth.max_providers" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthHooks V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "auth.hooks" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthLeakedPasswordProtection V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "auth.leaked_password_protection" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthMfaEnhancedSecurity V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "auth.mfa_enhanced_security" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthMfaPhone V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "auth.mfa_phone" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthMfaWebAuthn V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "auth.mfa_web_authn" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthPasswordHibp V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "auth.password_hibp" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthPerformanceSettings V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "auth.performance_settings" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthPlatformSso V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "auth.platform.sso" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthSaml2 V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "auth.saml_2" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthUserSessions V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "auth.user_sessions" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyBackupRestoreToNewProject V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "backup.restore_to_new_project" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyBackupRetentionDays V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "backup.retention_days" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyBackupSchedule V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "backup.schedule" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyBranchingLimit V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "branching_limit" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyBranchingPersistent V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "branching_persistent" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyCustomDomain V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "custom_domain" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyDedicatedPooler V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "dedicated_pooler" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyFunctionMaxCount V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "function.max_count" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyFunctionSizeLimitMb V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "function.size_limit_mb" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyInstancesComputeUpdateAvailableSizes V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "instances.compute_update_available_sizes" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyInstancesDiskModifications V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "instances.disk_modifications" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyInstancesHighAvailability V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "instances.high_availability" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyInstancesOrioledb V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "instances.orioledb" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyInstancesReadReplicas V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "instances.read_replicas" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyIntegrationsGithubConnections V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "integrations.github_connections" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyIntegrationsGithubPushWebhooksLimit V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "integrations.github_push_webhooks_limit" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyIpv4 V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "ipv4" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyLogDrains V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "log_drains" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyLogRetentionDays V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "log.retention_days" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyObservabilityDashboardAdvancedMetrics V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "observability.dashboard_advanced_metrics" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyPitrAvailableVariants V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "pitr.available_variants" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyProjectCloning V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "project_cloning" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyProjectPausing V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "project_pausing" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyProjectRestoreAfterExpiry V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "project_restore_after_expiry" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyProjectScopedRoles V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "project_scoped_roles" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxBytesPerSecond V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "realtime.max_bytes_per_second" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxChannelsPerClient V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "realtime.max_channels_per_client" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxConcurrentUsers V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "realtime.max_concurrent_users" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxEventsPerSecond V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "realtime.max_events_per_second" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxJoinsPerSecond V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "realtime.max_joins_per_second" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxPayloadSizeInKb V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "realtime.max_payload_size_in_kb" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxPresenceEventsPerSecond V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "realtime.max_presence_events_per_second" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyReplicationEtl V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "replication.etl" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecurityAuditLogsDays V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "security.audit_logs_days" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecurityEnforceMfa V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "security.enforce_mfa" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecurityIso27001Certificate V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "security.iso27001_certificate" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecurityMemberRoles V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "security.member_roles" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecurityPrivateLink V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "security.private_link" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecurityQuestionnaire V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "security.questionnaire" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecuritySoc2Report V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "security.soc2_report" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyStorageIcebergCatalog V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "storage.iceberg_catalog" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyStorageImageTransformations V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "storage.image_transformations" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyStorageMaxFileSize V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "storage.max_file_size" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyStorageMaxFileSizeConfigurable V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "storage.max_file_size.configurable" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyStoragePurgeCache V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "storage.purge_cache" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyStorageVectorBuckets V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "storage.vector_buckets" - V1ListEntitlementsResponseOutputEntitlementsFeatureKeyVanitySubdomain V1ListEntitlementsResponseOutputEntitlementsFeatureKey = "vanity_subdomain" -) - -// Valid indicates whether the value is a known member of the V1ListEntitlementsResponseOutputEntitlementsFeatureKey enum. -func (e V1ListEntitlementsResponseOutputEntitlementsFeatureKey) Valid() bool { - switch e { - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyApiMembersInvitations: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyApiMembersRoles: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAssistantAdvanceModel: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuditLogDrains: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthAdvancedAuthSettings: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthCustomJwtTemplate: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthCustomOauthMaxProviders: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthHooks: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthLeakedPasswordProtection: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthMfaEnhancedSecurity: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthMfaPhone: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthMfaWebAuthn: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthPasswordHibp: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthPerformanceSettings: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthPlatformSso: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthSaml2: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyAuthUserSessions: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyBackupRestoreToNewProject: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyBackupRetentionDays: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyBackupSchedule: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyBranchingLimit: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyBranchingPersistent: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyCustomDomain: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyDedicatedPooler: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyFunctionMaxCount: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyFunctionSizeLimitMb: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyInstancesComputeUpdateAvailableSizes: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyInstancesDiskModifications: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyInstancesHighAvailability: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyInstancesOrioledb: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyInstancesReadReplicas: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyIntegrationsGithubConnections: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyIntegrationsGithubPushWebhooksLimit: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyIpv4: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyLogDrains: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyLogRetentionDays: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyObservabilityDashboardAdvancedMetrics: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyPitrAvailableVariants: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyProjectCloning: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyProjectPausing: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyProjectRestoreAfterExpiry: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyProjectScopedRoles: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxBytesPerSecond: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxChannelsPerClient: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxConcurrentUsers: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxEventsPerSecond: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxJoinsPerSecond: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxPayloadSizeInKb: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyRealtimeMaxPresenceEventsPerSecond: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyReplicationEtl: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecurityAuditLogsDays: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecurityEnforceMfa: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecurityIso27001Certificate: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecurityMemberRoles: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecurityPrivateLink: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecurityQuestionnaire: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeySecuritySoc2Report: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyStorageIcebergCatalog: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyStorageImageTransformations: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyStorageMaxFileSize: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyStorageMaxFileSizeConfigurable: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyStoragePurgeCache: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyStorageVectorBuckets: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureKeyVanitySubdomain: - return true - default: - return false - } -} - -// Defines values for V1ListEntitlementsResponseOutputEntitlementsFeatureType. -const ( - V1ListEntitlementsResponseOutputEntitlementsFeatureTypeBoolean V1ListEntitlementsResponseOutputEntitlementsFeatureType = "boolean" - V1ListEntitlementsResponseOutputEntitlementsFeatureTypeNumeric V1ListEntitlementsResponseOutputEntitlementsFeatureType = "numeric" - V1ListEntitlementsResponseOutputEntitlementsFeatureTypeSet V1ListEntitlementsResponseOutputEntitlementsFeatureType = "set" -) - -// Valid indicates whether the value is a known member of the V1ListEntitlementsResponseOutputEntitlementsFeatureType enum. -func (e V1ListEntitlementsResponseOutputEntitlementsFeatureType) Valid() bool { - switch e { - case V1ListEntitlementsResponseOutputEntitlementsFeatureTypeBoolean: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureTypeNumeric: - return true - case V1ListEntitlementsResponseOutputEntitlementsFeatureTypeSet: - return true - default: - return false - } -} - -// Defines values for V1ListEntitlementsResponseOutputEntitlementsType. -const ( - V1ListEntitlementsResponseOutputEntitlementsTypeBoolean V1ListEntitlementsResponseOutputEntitlementsType = "boolean" - V1ListEntitlementsResponseOutputEntitlementsTypeNumeric V1ListEntitlementsResponseOutputEntitlementsType = "numeric" - V1ListEntitlementsResponseOutputEntitlementsTypeSet V1ListEntitlementsResponseOutputEntitlementsType = "set" -) - -// Valid indicates whether the value is a known member of the V1ListEntitlementsResponseOutputEntitlementsType enum. -func (e V1ListEntitlementsResponseOutputEntitlementsType) Valid() bool { - switch e { - case V1ListEntitlementsResponseOutputEntitlementsTypeBoolean: - return true - case V1ListEntitlementsResponseOutputEntitlementsTypeNumeric: - return true - case V1ListEntitlementsResponseOutputEntitlementsTypeSet: - return true - default: - return false - } -} - -// Defines values for V1OrganizationSlugResponseOutputAllowedReleaseChannels. -const ( - V1OrganizationSlugResponseOutputAllowedReleaseChannelsAlpha V1OrganizationSlugResponseOutputAllowedReleaseChannels = "alpha" - V1OrganizationSlugResponseOutputAllowedReleaseChannelsBeta V1OrganizationSlugResponseOutputAllowedReleaseChannels = "beta" - V1OrganizationSlugResponseOutputAllowedReleaseChannelsGa V1OrganizationSlugResponseOutputAllowedReleaseChannels = "ga" - V1OrganizationSlugResponseOutputAllowedReleaseChannelsInternal V1OrganizationSlugResponseOutputAllowedReleaseChannels = "internal" - V1OrganizationSlugResponseOutputAllowedReleaseChannelsPreview V1OrganizationSlugResponseOutputAllowedReleaseChannels = "preview" - V1OrganizationSlugResponseOutputAllowedReleaseChannelsWithdrawn V1OrganizationSlugResponseOutputAllowedReleaseChannels = "withdrawn" -) - -// Valid indicates whether the value is a known member of the V1OrganizationSlugResponseOutputAllowedReleaseChannels enum. -func (e V1OrganizationSlugResponseOutputAllowedReleaseChannels) Valid() bool { - switch e { - case V1OrganizationSlugResponseOutputAllowedReleaseChannelsAlpha: - return true - case V1OrganizationSlugResponseOutputAllowedReleaseChannelsBeta: - return true - case V1OrganizationSlugResponseOutputAllowedReleaseChannelsGa: - return true - case V1OrganizationSlugResponseOutputAllowedReleaseChannelsInternal: - return true - case V1OrganizationSlugResponseOutputAllowedReleaseChannelsPreview: - return true - case V1OrganizationSlugResponseOutputAllowedReleaseChannelsWithdrawn: - return true - default: - return false - } -} - -// Defines values for V1OrganizationSlugResponseOutputPlan. -const ( - V1OrganizationSlugResponseOutputPlanEnterprise V1OrganizationSlugResponseOutputPlan = "enterprise" - V1OrganizationSlugResponseOutputPlanFree V1OrganizationSlugResponseOutputPlan = "free" - V1OrganizationSlugResponseOutputPlanPlatform V1OrganizationSlugResponseOutputPlan = "platform" - V1OrganizationSlugResponseOutputPlanPro V1OrganizationSlugResponseOutputPlan = "pro" - V1OrganizationSlugResponseOutputPlanTeam V1OrganizationSlugResponseOutputPlan = "team" -) - -// Valid indicates whether the value is a known member of the V1OrganizationSlugResponseOutputPlan enum. -func (e V1OrganizationSlugResponseOutputPlan) Valid() bool { - switch e { - case V1OrganizationSlugResponseOutputPlanEnterprise: - return true - case V1OrganizationSlugResponseOutputPlanFree: - return true - case V1OrganizationSlugResponseOutputPlanPlatform: - return true - case V1OrganizationSlugResponseOutputPlanPro: - return true - case V1OrganizationSlugResponseOutputPlanTeam: - return true - default: - return false - } -} - -// Defines values for V1PgbouncerConfigResponseOutputPoolMode. -const ( - Session V1PgbouncerConfigResponseOutputPoolMode = "session" - Statement V1PgbouncerConfigResponseOutputPoolMode = "statement" - Transaction V1PgbouncerConfigResponseOutputPoolMode = "transaction" -) - -// Valid indicates whether the value is a known member of the V1PgbouncerConfigResponseOutputPoolMode enum. -func (e V1PgbouncerConfigResponseOutputPoolMode) Valid() bool { - switch e { - case Session: - return true - case Statement: - return true - case Transaction: - return true - default: - return false - } -} - -// Defines values for V1ProjectAdvisorsResponseOutputLintsCategories. -const ( - HEALTH V1ProjectAdvisorsResponseOutputLintsCategories = "HEALTH" - PERFORMANCE V1ProjectAdvisorsResponseOutputLintsCategories = "PERFORMANCE" - SECURITY V1ProjectAdvisorsResponseOutputLintsCategories = "SECURITY" -) - -// Valid indicates whether the value is a known member of the V1ProjectAdvisorsResponseOutputLintsCategories enum. -func (e V1ProjectAdvisorsResponseOutputLintsCategories) Valid() bool { - switch e { - case HEALTH: - return true - case PERFORMANCE: - return true - case SECURITY: - return true - default: - return false - } -} - -// Defines values for V1ProjectAdvisorsResponseOutputLintsFacing. -const ( - EXTERNAL V1ProjectAdvisorsResponseOutputLintsFacing = "EXTERNAL" -) - -// Valid indicates whether the value is a known member of the V1ProjectAdvisorsResponseOutputLintsFacing enum. -func (e V1ProjectAdvisorsResponseOutputLintsFacing) Valid() bool { - switch e { - case EXTERNAL: - return true - default: - return false - } -} - -// Defines values for V1ProjectAdvisorsResponseOutputLintsLevel. -const ( - ERROR V1ProjectAdvisorsResponseOutputLintsLevel = "ERROR" - INFO V1ProjectAdvisorsResponseOutputLintsLevel = "INFO" - WARN V1ProjectAdvisorsResponseOutputLintsLevel = "WARN" -) - -// Valid indicates whether the value is a known member of the V1ProjectAdvisorsResponseOutputLintsLevel enum. -func (e V1ProjectAdvisorsResponseOutputLintsLevel) Valid() bool { - switch e { - case ERROR: - return true - case INFO: - return true - case WARN: - return true - default: - return false - } -} - -// Defines values for V1ProjectAdvisorsResponseOutputLintsMetadataType. -const ( - V1ProjectAdvisorsResponseOutputLintsMetadataTypeAuth V1ProjectAdvisorsResponseOutputLintsMetadataType = "auth" - V1ProjectAdvisorsResponseOutputLintsMetadataTypeCompliance V1ProjectAdvisorsResponseOutputLintsMetadataType = "compliance" - V1ProjectAdvisorsResponseOutputLintsMetadataTypeExtension V1ProjectAdvisorsResponseOutputLintsMetadataType = "extension" - V1ProjectAdvisorsResponseOutputLintsMetadataTypeForeignTable V1ProjectAdvisorsResponseOutputLintsMetadataType = "foreign table" - V1ProjectAdvisorsResponseOutputLintsMetadataTypeFunction V1ProjectAdvisorsResponseOutputLintsMetadataType = "function" - V1ProjectAdvisorsResponseOutputLintsMetadataTypeHealth V1ProjectAdvisorsResponseOutputLintsMetadataType = "health" - V1ProjectAdvisorsResponseOutputLintsMetadataTypeMaterializedView V1ProjectAdvisorsResponseOutputLintsMetadataType = "materialized view" - V1ProjectAdvisorsResponseOutputLintsMetadataTypeTable V1ProjectAdvisorsResponseOutputLintsMetadataType = "table" - V1ProjectAdvisorsResponseOutputLintsMetadataTypeView V1ProjectAdvisorsResponseOutputLintsMetadataType = "view" -) - -// Valid indicates whether the value is a known member of the V1ProjectAdvisorsResponseOutputLintsMetadataType enum. -func (e V1ProjectAdvisorsResponseOutputLintsMetadataType) Valid() bool { - switch e { - case V1ProjectAdvisorsResponseOutputLintsMetadataTypeAuth: - return true - case V1ProjectAdvisorsResponseOutputLintsMetadataTypeCompliance: - return true - case V1ProjectAdvisorsResponseOutputLintsMetadataTypeExtension: - return true - case V1ProjectAdvisorsResponseOutputLintsMetadataTypeForeignTable: - return true - case V1ProjectAdvisorsResponseOutputLintsMetadataTypeFunction: - return true - case V1ProjectAdvisorsResponseOutputLintsMetadataTypeHealth: - return true - case V1ProjectAdvisorsResponseOutputLintsMetadataTypeMaterializedView: - return true - case V1ProjectAdvisorsResponseOutputLintsMetadataTypeTable: - return true - case V1ProjectAdvisorsResponseOutputLintsMetadataTypeView: - return true - default: - return false - } -} - -// Defines values for V1ProjectAdvisorsResponseOutputLintsName. -const ( - AdvisorCheckUnavailable V1ProjectAdvisorsResponseOutputLintsName = "advisor_check_unavailable" - AuthInsufficientMfaOptions V1ProjectAdvisorsResponseOutputLintsName = "auth_insufficient_mfa_options" - AuthLeakedPasswordProtection V1ProjectAdvisorsResponseOutputLintsName = "auth_leaked_password_protection" - AuthOtpLongExpiry V1ProjectAdvisorsResponseOutputLintsName = "auth_otp_long_expiry" - AuthOtpShortLength V1ProjectAdvisorsResponseOutputLintsName = "auth_otp_short_length" - AuthPasswordPolicyMissing V1ProjectAdvisorsResponseOutputLintsName = "auth_password_policy_missing" - AuthRlsInitplan V1ProjectAdvisorsResponseOutputLintsName = "auth_rls_initplan" - AuthUsersExposed V1ProjectAdvisorsResponseOutputLintsName = "auth_users_exposed" - DbConnectionFailing V1ProjectAdvisorsResponseOutputLintsName = "db_connection_failing" - DbConnectionLimitReached V1ProjectAdvisorsResponseOutputLintsName = "db_connection_limit_reached" - DbNotReachable V1ProjectAdvisorsResponseOutputLintsName = "db_not_reachable" - DuplicateIndex V1ProjectAdvisorsResponseOutputLintsName = "duplicate_index" - ExtensionInPublic V1ProjectAdvisorsResponseOutputLintsName = "extension_in_public" - ForeignTableInApi V1ProjectAdvisorsResponseOutputLintsName = "foreign_table_in_api" - FunctionSearchPathMutable V1ProjectAdvisorsResponseOutputLintsName = "function_search_path_mutable" - InstanceAlertFiring V1ProjectAdvisorsResponseOutputLintsName = "instance_alert_firing" - InstanceDbDown V1ProjectAdvisorsResponseOutputLintsName = "instance_db_down" - InstanceTelemetryLost V1ProjectAdvisorsResponseOutputLintsName = "instance_telemetry_lost" - LeakedServiceKey V1ProjectAdvisorsResponseOutputLintsName = "leaked_service_key" - LogAuthErrorRateHigh V1ProjectAdvisorsResponseOutputLintsName = "log_auth_error_rate_high" - LogConnectionsNotEnabled V1ProjectAdvisorsResponseOutputLintsName = "log_connections_not_enabled" - LogDataApiErrorRateHigh V1ProjectAdvisorsResponseOutputLintsName = "log_data_api_error_rate_high" - LogEdgeFunctionErrorRateHigh V1ProjectAdvisorsResponseOutputLintsName = "log_edge_function_error_rate_high" - LogStorageErrorRateHigh V1ProjectAdvisorsResponseOutputLintsName = "log_storage_error_rate_high" - MaterializedViewInApi V1ProjectAdvisorsResponseOutputLintsName = "materialized_view_in_api" - MultiplePermissivePolicies V1ProjectAdvisorsResponseOutputLintsName = "multiple_permissive_policies" - NetworkRestrictionsNotSet V1ProjectAdvisorsResponseOutputLintsName = "network_restrictions_not_set" - NoBackupAdmin V1ProjectAdvisorsResponseOutputLintsName = "no_backup_admin" - NoPrimaryKey V1ProjectAdvisorsResponseOutputLintsName = "no_primary_key" - PasswordRequirementsMinLength V1ProjectAdvisorsResponseOutputLintsName = "password_requirements_min_length" - PitrNotEnabled V1ProjectAdvisorsResponseOutputLintsName = "pitr_not_enabled" - PolicyExistsRlsDisabled V1ProjectAdvisorsResponseOutputLintsName = "policy_exists_rls_disabled" - ProjectNotActive V1ProjectAdvisorsResponseOutputLintsName = "project_not_active" - RlsDisabledInPublic V1ProjectAdvisorsResponseOutputLintsName = "rls_disabled_in_public" - RlsEnabledNoPolicy V1ProjectAdvisorsResponseOutputLintsName = "rls_enabled_no_policy" - RlsReferencesUserMetadata V1ProjectAdvisorsResponseOutputLintsName = "rls_references_user_metadata" - SecurityDefinerView V1ProjectAdvisorsResponseOutputLintsName = "security_definer_view" - SslNotEnforced V1ProjectAdvisorsResponseOutputLintsName = "ssl_not_enforced" - UnindexedForeignKeys V1ProjectAdvisorsResponseOutputLintsName = "unindexed_foreign_keys" - UnsupportedRegTypes V1ProjectAdvisorsResponseOutputLintsName = "unsupported_reg_types" - UnusedIndex V1ProjectAdvisorsResponseOutputLintsName = "unused_index" - VulnerablePostgresVersion V1ProjectAdvisorsResponseOutputLintsName = "vulnerable_postgres_version" -) - -// Valid indicates whether the value is a known member of the V1ProjectAdvisorsResponseOutputLintsName enum. -func (e V1ProjectAdvisorsResponseOutputLintsName) Valid() bool { - switch e { - case AdvisorCheckUnavailable: - return true - case AuthInsufficientMfaOptions: - return true - case AuthLeakedPasswordProtection: - return true - case AuthOtpLongExpiry: - return true - case AuthOtpShortLength: - return true - case AuthPasswordPolicyMissing: - return true - case AuthRlsInitplan: - return true - case AuthUsersExposed: - return true - case DbConnectionFailing: - return true - case DbConnectionLimitReached: - return true - case DbNotReachable: - return true - case DuplicateIndex: - return true - case ExtensionInPublic: - return true - case ForeignTableInApi: - return true - case FunctionSearchPathMutable: - return true - case InstanceAlertFiring: - return true - case InstanceDbDown: - return true - case InstanceTelemetryLost: - return true - case LeakedServiceKey: - return true - case LogAuthErrorRateHigh: - return true - case LogConnectionsNotEnabled: - return true - case LogDataApiErrorRateHigh: - return true - case LogEdgeFunctionErrorRateHigh: - return true - case LogStorageErrorRateHigh: - return true - case MaterializedViewInApi: - return true - case MultiplePermissivePolicies: - return true - case NetworkRestrictionsNotSet: - return true - case NoBackupAdmin: - return true - case NoPrimaryKey: - return true - case PasswordRequirementsMinLength: - return true - case PitrNotEnabled: - return true - case PolicyExistsRlsDisabled: - return true - case ProjectNotActive: - return true - case RlsDisabledInPublic: - return true - case RlsEnabledNoPolicy: - return true - case RlsReferencesUserMetadata: - return true - case SecurityDefinerView: - return true - case SslNotEnforced: - return true - case UnindexedForeignKeys: - return true - case UnsupportedRegTypes: - return true - case UnusedIndex: - return true - case VulnerablePostgresVersion: - return true - default: - return false - } -} - -// Defines values for V1ProjectResponseOutputStatus. -const ( - V1ProjectResponseOutputStatusACTIVEHEALTHY V1ProjectResponseOutputStatus = "ACTIVE_HEALTHY" - V1ProjectResponseOutputStatusACTIVEUNHEALTHY V1ProjectResponseOutputStatus = "ACTIVE_UNHEALTHY" - V1ProjectResponseOutputStatusCOMINGUP V1ProjectResponseOutputStatus = "COMING_UP" - V1ProjectResponseOutputStatusGOINGDOWN V1ProjectResponseOutputStatus = "GOING_DOWN" - V1ProjectResponseOutputStatusINACTIVE V1ProjectResponseOutputStatus = "INACTIVE" - V1ProjectResponseOutputStatusINITFAILED V1ProjectResponseOutputStatus = "INIT_FAILED" - V1ProjectResponseOutputStatusPAUSEFAILED V1ProjectResponseOutputStatus = "PAUSE_FAILED" - V1ProjectResponseOutputStatusPAUSING V1ProjectResponseOutputStatus = "PAUSING" - V1ProjectResponseOutputStatusREMOVED V1ProjectResponseOutputStatus = "REMOVED" - V1ProjectResponseOutputStatusRESIZING V1ProjectResponseOutputStatus = "RESIZING" - V1ProjectResponseOutputStatusRESTARTING V1ProjectResponseOutputStatus = "RESTARTING" - V1ProjectResponseOutputStatusRESTOREFAILED V1ProjectResponseOutputStatus = "RESTORE_FAILED" - V1ProjectResponseOutputStatusRESTORING V1ProjectResponseOutputStatus = "RESTORING" - V1ProjectResponseOutputStatusUNKNOWN V1ProjectResponseOutputStatus = "UNKNOWN" - V1ProjectResponseOutputStatusUPGRADING V1ProjectResponseOutputStatus = "UPGRADING" -) - -// Valid indicates whether the value is a known member of the V1ProjectResponseOutputStatus enum. -func (e V1ProjectResponseOutputStatus) Valid() bool { - switch e { - case V1ProjectResponseOutputStatusACTIVEHEALTHY: - return true - case V1ProjectResponseOutputStatusACTIVEUNHEALTHY: - return true - case V1ProjectResponseOutputStatusCOMINGUP: - return true - case V1ProjectResponseOutputStatusGOINGDOWN: - return true - case V1ProjectResponseOutputStatusINACTIVE: - return true - case V1ProjectResponseOutputStatusINITFAILED: - return true - case V1ProjectResponseOutputStatusPAUSEFAILED: - return true - case V1ProjectResponseOutputStatusPAUSING: - return true - case V1ProjectResponseOutputStatusREMOVED: - return true - case V1ProjectResponseOutputStatusRESIZING: - return true - case V1ProjectResponseOutputStatusRESTARTING: - return true - case V1ProjectResponseOutputStatusRESTOREFAILED: - return true - case V1ProjectResponseOutputStatusRESTORING: - return true - case V1ProjectResponseOutputStatusUNKNOWN: - return true - case V1ProjectResponseOutputStatusUPGRADING: - return true - default: - return false - } -} - -// Defines values for V1ProjectWithDatabaseResponseOutputStatus. -const ( - V1ProjectWithDatabaseResponseOutputStatusACTIVEHEALTHY V1ProjectWithDatabaseResponseOutputStatus = "ACTIVE_HEALTHY" - V1ProjectWithDatabaseResponseOutputStatusACTIVEUNHEALTHY V1ProjectWithDatabaseResponseOutputStatus = "ACTIVE_UNHEALTHY" - V1ProjectWithDatabaseResponseOutputStatusCOMINGUP V1ProjectWithDatabaseResponseOutputStatus = "COMING_UP" - V1ProjectWithDatabaseResponseOutputStatusGOINGDOWN V1ProjectWithDatabaseResponseOutputStatus = "GOING_DOWN" - V1ProjectWithDatabaseResponseOutputStatusINACTIVE V1ProjectWithDatabaseResponseOutputStatus = "INACTIVE" - V1ProjectWithDatabaseResponseOutputStatusINITFAILED V1ProjectWithDatabaseResponseOutputStatus = "INIT_FAILED" - V1ProjectWithDatabaseResponseOutputStatusPAUSEFAILED V1ProjectWithDatabaseResponseOutputStatus = "PAUSE_FAILED" - V1ProjectWithDatabaseResponseOutputStatusPAUSING V1ProjectWithDatabaseResponseOutputStatus = "PAUSING" - V1ProjectWithDatabaseResponseOutputStatusREMOVED V1ProjectWithDatabaseResponseOutputStatus = "REMOVED" - V1ProjectWithDatabaseResponseOutputStatusRESIZING V1ProjectWithDatabaseResponseOutputStatus = "RESIZING" - V1ProjectWithDatabaseResponseOutputStatusRESTARTING V1ProjectWithDatabaseResponseOutputStatus = "RESTARTING" - V1ProjectWithDatabaseResponseOutputStatusRESTOREFAILED V1ProjectWithDatabaseResponseOutputStatus = "RESTORE_FAILED" - V1ProjectWithDatabaseResponseOutputStatusRESTORING V1ProjectWithDatabaseResponseOutputStatus = "RESTORING" - V1ProjectWithDatabaseResponseOutputStatusUNKNOWN V1ProjectWithDatabaseResponseOutputStatus = "UNKNOWN" - V1ProjectWithDatabaseResponseOutputStatusUPGRADING V1ProjectWithDatabaseResponseOutputStatus = "UPGRADING" -) - -// Valid indicates whether the value is a known member of the V1ProjectWithDatabaseResponseOutputStatus enum. -func (e V1ProjectWithDatabaseResponseOutputStatus) Valid() bool { - switch e { - case V1ProjectWithDatabaseResponseOutputStatusACTIVEHEALTHY: - return true - case V1ProjectWithDatabaseResponseOutputStatusACTIVEUNHEALTHY: - return true - case V1ProjectWithDatabaseResponseOutputStatusCOMINGUP: - return true - case V1ProjectWithDatabaseResponseOutputStatusGOINGDOWN: - return true - case V1ProjectWithDatabaseResponseOutputStatusINACTIVE: - return true - case V1ProjectWithDatabaseResponseOutputStatusINITFAILED: - return true - case V1ProjectWithDatabaseResponseOutputStatusPAUSEFAILED: - return true - case V1ProjectWithDatabaseResponseOutputStatusPAUSING: - return true - case V1ProjectWithDatabaseResponseOutputStatusREMOVED: - return true - case V1ProjectWithDatabaseResponseOutputStatusRESIZING: - return true - case V1ProjectWithDatabaseResponseOutputStatusRESTARTING: - return true - case V1ProjectWithDatabaseResponseOutputStatusRESTOREFAILED: - return true - case V1ProjectWithDatabaseResponseOutputStatusRESTORING: - return true - case V1ProjectWithDatabaseResponseOutputStatusUNKNOWN: - return true - case V1ProjectWithDatabaseResponseOutputStatusUPGRADING: - return true - default: - return false - } -} - -// Defines values for V1RestorePointResponseStatus. -const ( - V1RestorePointResponseStatusAVAILABLE V1RestorePointResponseStatus = "AVAILABLE" - V1RestorePointResponseStatusFAILED V1RestorePointResponseStatus = "FAILED" - V1RestorePointResponseStatusPENDING V1RestorePointResponseStatus = "PENDING" - V1RestorePointResponseStatusREMOVED V1RestorePointResponseStatus = "REMOVED" -) - -// Valid indicates whether the value is a known member of the V1RestorePointResponseStatus enum. -func (e V1RestorePointResponseStatus) Valid() bool { - switch e { - case V1RestorePointResponseStatusAVAILABLE: - return true - case V1RestorePointResponseStatusFAILED: - return true - case V1RestorePointResponseStatusPENDING: - return true - case V1RestorePointResponseStatusREMOVED: - return true - default: - return false - } -} - -// Defines values for V1ServiceHealthResponseOutputInfo0Name. -const ( - GoTrue V1ServiceHealthResponseOutputInfo0Name = "GoTrue" -) - -// Valid indicates whether the value is a known member of the V1ServiceHealthResponseOutputInfo0Name enum. -func (e V1ServiceHealthResponseOutputInfo0Name) Valid() bool { - switch e { - case GoTrue: - return true - default: - return false - } -} - -// Defines values for V1ServiceHealthResponseOutputName. -const ( - V1ServiceHealthResponseOutputNameAuth V1ServiceHealthResponseOutputName = "auth" - V1ServiceHealthResponseOutputNameDb V1ServiceHealthResponseOutputName = "db" - V1ServiceHealthResponseOutputNameDbPostgresUser V1ServiceHealthResponseOutputName = "db_postgres_user" - V1ServiceHealthResponseOutputNamePgBouncer V1ServiceHealthResponseOutputName = "pg_bouncer" - V1ServiceHealthResponseOutputNamePooler V1ServiceHealthResponseOutputName = "pooler" - V1ServiceHealthResponseOutputNameRealtime V1ServiceHealthResponseOutputName = "realtime" - V1ServiceHealthResponseOutputNameRest V1ServiceHealthResponseOutputName = "rest" - V1ServiceHealthResponseOutputNameStorage V1ServiceHealthResponseOutputName = "storage" -) - -// Valid indicates whether the value is a known member of the V1ServiceHealthResponseOutputName enum. -func (e V1ServiceHealthResponseOutputName) Valid() bool { - switch e { - case V1ServiceHealthResponseOutputNameAuth: - return true - case V1ServiceHealthResponseOutputNameDb: - return true - case V1ServiceHealthResponseOutputNameDbPostgresUser: - return true - case V1ServiceHealthResponseOutputNamePgBouncer: - return true - case V1ServiceHealthResponseOutputNamePooler: - return true - case V1ServiceHealthResponseOutputNameRealtime: - return true - case V1ServiceHealthResponseOutputNameRest: - return true - case V1ServiceHealthResponseOutputNameStorage: - return true - default: - return false - } -} - -// Defines values for V1ServiceHealthResponseOutputStatus. -const ( - ACTIVEHEALTHY V1ServiceHealthResponseOutputStatus = "ACTIVE_HEALTHY" - COMINGUP V1ServiceHealthResponseOutputStatus = "COMING_UP" - UNHEALTHY V1ServiceHealthResponseOutputStatus = "UNHEALTHY" -) - -// Valid indicates whether the value is a known member of the V1ServiceHealthResponseOutputStatus enum. -func (e V1ServiceHealthResponseOutputStatus) Valid() bool { - switch e { - case ACTIVEHEALTHY: - return true - case COMINGUP: - return true - case UNHEALTHY: - return true - default: - return false - } -} - -// Defines values for VanitySubdomainConfigResponseOutputStatus. -const ( - Active VanitySubdomainConfigResponseOutputStatus = "active" - CustomDomainUsed VanitySubdomainConfigResponseOutputStatus = "custom-domain-used" - NotUsed VanitySubdomainConfigResponseOutputStatus = "not-used" -) - -// Valid indicates whether the value is a known member of the VanitySubdomainConfigResponseOutputStatus enum. -func (e VanitySubdomainConfigResponseOutputStatus) Valid() bool { - switch e { - case Active: - return true - case CustomDomainUsed: - return true - case NotUsed: - return true - default: - return false - } -} - -// Defines values for V1AuthorizeUserParamsResponseType. -const ( - V1AuthorizeUserParamsResponseTypeCode V1AuthorizeUserParamsResponseType = "code" - V1AuthorizeUserParamsResponseTypeIdTokenToken V1AuthorizeUserParamsResponseType = "id_token token" - V1AuthorizeUserParamsResponseTypeToken V1AuthorizeUserParamsResponseType = "token" -) - -// Valid indicates whether the value is a known member of the V1AuthorizeUserParamsResponseType enum. -func (e V1AuthorizeUserParamsResponseType) Valid() bool { - switch e { - case V1AuthorizeUserParamsResponseTypeCode: - return true - case V1AuthorizeUserParamsResponseTypeIdTokenToken: - return true - case V1AuthorizeUserParamsResponseTypeToken: - return true - default: - return false - } -} - -// Defines values for V1AuthorizeUserParamsCodeChallengeMethod. -const ( - V1AuthorizeUserParamsCodeChallengeMethodPlain V1AuthorizeUserParamsCodeChallengeMethod = "plain" - V1AuthorizeUserParamsCodeChallengeMethodS256 V1AuthorizeUserParamsCodeChallengeMethod = "S256" - V1AuthorizeUserParamsCodeChallengeMethodSha256 V1AuthorizeUserParamsCodeChallengeMethod = "sha256" -) - -// Valid indicates whether the value is a known member of the V1AuthorizeUserParamsCodeChallengeMethod enum. -func (e V1AuthorizeUserParamsCodeChallengeMethod) Valid() bool { - switch e { - case V1AuthorizeUserParamsCodeChallengeMethodPlain: - return true - case V1AuthorizeUserParamsCodeChallengeMethodS256: - return true - case V1AuthorizeUserParamsCodeChallengeMethodSha256: - return true - default: - return false - } -} - -// Defines values for V1OauthAuthorizeProjectClaimParamsResponseType. -const ( - V1OauthAuthorizeProjectClaimParamsResponseTypeCode V1OauthAuthorizeProjectClaimParamsResponseType = "code" - V1OauthAuthorizeProjectClaimParamsResponseTypeIdTokenToken V1OauthAuthorizeProjectClaimParamsResponseType = "id_token token" - V1OauthAuthorizeProjectClaimParamsResponseTypeToken V1OauthAuthorizeProjectClaimParamsResponseType = "token" -) - -// Valid indicates whether the value is a known member of the V1OauthAuthorizeProjectClaimParamsResponseType enum. -func (e V1OauthAuthorizeProjectClaimParamsResponseType) Valid() bool { - switch e { - case V1OauthAuthorizeProjectClaimParamsResponseTypeCode: - return true - case V1OauthAuthorizeProjectClaimParamsResponseTypeIdTokenToken: - return true - case V1OauthAuthorizeProjectClaimParamsResponseTypeToken: - return true - default: - return false - } -} - -// Defines values for V1OauthAuthorizeProjectClaimParamsCodeChallengeMethod. -const ( - V1OauthAuthorizeProjectClaimParamsCodeChallengeMethodPlain V1OauthAuthorizeProjectClaimParamsCodeChallengeMethod = "plain" - V1OauthAuthorizeProjectClaimParamsCodeChallengeMethodS256 V1OauthAuthorizeProjectClaimParamsCodeChallengeMethod = "S256" - V1OauthAuthorizeProjectClaimParamsCodeChallengeMethodSha256 V1OauthAuthorizeProjectClaimParamsCodeChallengeMethod = "sha256" -) - -// Valid indicates whether the value is a known member of the V1OauthAuthorizeProjectClaimParamsCodeChallengeMethod enum. -func (e V1OauthAuthorizeProjectClaimParamsCodeChallengeMethod) Valid() bool { - switch e { - case V1OauthAuthorizeProjectClaimParamsCodeChallengeMethodPlain: - return true - case V1OauthAuthorizeProjectClaimParamsCodeChallengeMethodS256: - return true - case V1OauthAuthorizeProjectClaimParamsCodeChallengeMethodSha256: - return true - default: - return false - } -} - -// Defines values for V1GetAllProjectsForOrganizationParamsSort. -const ( - CreatedAsc V1GetAllProjectsForOrganizationParamsSort = "created_asc" - CreatedDesc V1GetAllProjectsForOrganizationParamsSort = "created_desc" - NameAsc V1GetAllProjectsForOrganizationParamsSort = "name_asc" - NameDesc V1GetAllProjectsForOrganizationParamsSort = "name_desc" -) - -// Valid indicates whether the value is a known member of the V1GetAllProjectsForOrganizationParamsSort enum. -func (e V1GetAllProjectsForOrganizationParamsSort) Valid() bool { - switch e { - case CreatedAsc: - return true - case CreatedDesc: - return true - case NameAsc: - return true - case NameDesc: - return true - default: - return false - } -} - -// Defines values for V1GetAvailableRegionsParamsContinent. -const ( - AF V1GetAvailableRegionsParamsContinent = "AF" - AN V1GetAvailableRegionsParamsContinent = "AN" - AS V1GetAvailableRegionsParamsContinent = "AS" - EU V1GetAvailableRegionsParamsContinent = "EU" - NA V1GetAvailableRegionsParamsContinent = "NA" - OC V1GetAvailableRegionsParamsContinent = "OC" - SA V1GetAvailableRegionsParamsContinent = "SA" -) - -// Valid indicates whether the value is a known member of the V1GetAvailableRegionsParamsContinent enum. -func (e V1GetAvailableRegionsParamsContinent) Valid() bool { - switch e { - case AF: - return true - case AN: - return true - case AS: - return true - case EU: - return true - case NA: - return true - case OC: - return true - case SA: - return true - default: - return false - } -} - -// Defines values for V1GetAvailableRegionsParamsDesiredInstanceSize. -const ( - V1GetAvailableRegionsParamsDesiredInstanceSizeLarge V1GetAvailableRegionsParamsDesiredInstanceSize = "large" - V1GetAvailableRegionsParamsDesiredInstanceSizeMedium V1GetAvailableRegionsParamsDesiredInstanceSize = "medium" - V1GetAvailableRegionsParamsDesiredInstanceSizeMicro V1GetAvailableRegionsParamsDesiredInstanceSize = "micro" - V1GetAvailableRegionsParamsDesiredInstanceSizeN12xlarge V1GetAvailableRegionsParamsDesiredInstanceSize = "12xlarge" - V1GetAvailableRegionsParamsDesiredInstanceSizeN16xlarge V1GetAvailableRegionsParamsDesiredInstanceSize = "16xlarge" - V1GetAvailableRegionsParamsDesiredInstanceSizeN24xlarge V1GetAvailableRegionsParamsDesiredInstanceSize = "24xlarge" - V1GetAvailableRegionsParamsDesiredInstanceSizeN24xlargeHighMemory V1GetAvailableRegionsParamsDesiredInstanceSize = "24xlarge_high_memory" - V1GetAvailableRegionsParamsDesiredInstanceSizeN24xlargeOptimizedCpu V1GetAvailableRegionsParamsDesiredInstanceSize = "24xlarge_optimized_cpu" - V1GetAvailableRegionsParamsDesiredInstanceSizeN24xlargeOptimizedMemory V1GetAvailableRegionsParamsDesiredInstanceSize = "24xlarge_optimized_memory" - V1GetAvailableRegionsParamsDesiredInstanceSizeN2xlarge V1GetAvailableRegionsParamsDesiredInstanceSize = "2xlarge" - V1GetAvailableRegionsParamsDesiredInstanceSizeN48xlarge V1GetAvailableRegionsParamsDesiredInstanceSize = "48xlarge" - V1GetAvailableRegionsParamsDesiredInstanceSizeN48xlargeHighMemory V1GetAvailableRegionsParamsDesiredInstanceSize = "48xlarge_high_memory" - V1GetAvailableRegionsParamsDesiredInstanceSizeN48xlargeOptimizedCpu V1GetAvailableRegionsParamsDesiredInstanceSize = "48xlarge_optimized_cpu" - V1GetAvailableRegionsParamsDesiredInstanceSizeN48xlargeOptimizedMemory V1GetAvailableRegionsParamsDesiredInstanceSize = "48xlarge_optimized_memory" - V1GetAvailableRegionsParamsDesiredInstanceSizeN4xlarge V1GetAvailableRegionsParamsDesiredInstanceSize = "4xlarge" - V1GetAvailableRegionsParamsDesiredInstanceSizeN8xlarge V1GetAvailableRegionsParamsDesiredInstanceSize = "8xlarge" - V1GetAvailableRegionsParamsDesiredInstanceSizeNano V1GetAvailableRegionsParamsDesiredInstanceSize = "nano" - V1GetAvailableRegionsParamsDesiredInstanceSizeSmall V1GetAvailableRegionsParamsDesiredInstanceSize = "small" - V1GetAvailableRegionsParamsDesiredInstanceSizeXlarge V1GetAvailableRegionsParamsDesiredInstanceSize = "xlarge" -) - -// Valid indicates whether the value is a known member of the V1GetAvailableRegionsParamsDesiredInstanceSize enum. -func (e V1GetAvailableRegionsParamsDesiredInstanceSize) Valid() bool { - switch e { - case V1GetAvailableRegionsParamsDesiredInstanceSizeLarge: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeMedium: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeMicro: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeN12xlarge: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeN16xlarge: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeN24xlarge: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeN24xlargeHighMemory: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeN24xlargeOptimizedCpu: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeN24xlargeOptimizedMemory: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeN2xlarge: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeN48xlarge: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeN48xlargeHighMemory: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeN48xlargeOptimizedCpu: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeN48xlargeOptimizedMemory: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeN4xlarge: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeN8xlarge: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeNano: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeSmall: - return true - case V1GetAvailableRegionsParamsDesiredInstanceSizeXlarge: - return true - default: - return false - } -} - -// Defines values for V1GetSecurityAdvisorsParamsLintType. -const ( - Sql V1GetSecurityAdvisorsParamsLintType = "sql" -) - -// Valid indicates whether the value is a known member of the V1GetSecurityAdvisorsParamsLintType enum. -func (e V1GetSecurityAdvisorsParamsLintType) Valid() bool { - switch e { - case Sql: - return true - default: - return false - } -} - -// Defines values for V1GetProjectFunctionCombinedStatsParamsInterval. -const ( - V1GetProjectFunctionCombinedStatsParamsIntervalN15min V1GetProjectFunctionCombinedStatsParamsInterval = "15min" - V1GetProjectFunctionCombinedStatsParamsIntervalN1day V1GetProjectFunctionCombinedStatsParamsInterval = "1day" - V1GetProjectFunctionCombinedStatsParamsIntervalN1hr V1GetProjectFunctionCombinedStatsParamsInterval = "1hr" - V1GetProjectFunctionCombinedStatsParamsIntervalN3hr V1GetProjectFunctionCombinedStatsParamsInterval = "3hr" -) - -// Valid indicates whether the value is a known member of the V1GetProjectFunctionCombinedStatsParamsInterval enum. -func (e V1GetProjectFunctionCombinedStatsParamsInterval) Valid() bool { - switch e { - case V1GetProjectFunctionCombinedStatsParamsIntervalN15min: - return true - case V1GetProjectFunctionCombinedStatsParamsIntervalN1day: - return true - case V1GetProjectFunctionCombinedStatsParamsIntervalN1hr: - return true - case V1GetProjectFunctionCombinedStatsParamsIntervalN3hr: - return true - default: - return false - } -} - -// Defines values for V1GetProjectUsageApiCountParamsInterval. -const ( - V1GetProjectUsageApiCountParamsIntervalN15min V1GetProjectUsageApiCountParamsInterval = "15min" - V1GetProjectUsageApiCountParamsIntervalN1day V1GetProjectUsageApiCountParamsInterval = "1day" - V1GetProjectUsageApiCountParamsIntervalN1hr V1GetProjectUsageApiCountParamsInterval = "1hr" - V1GetProjectUsageApiCountParamsIntervalN30min V1GetProjectUsageApiCountParamsInterval = "30min" - V1GetProjectUsageApiCountParamsIntervalN3day V1GetProjectUsageApiCountParamsInterval = "3day" - V1GetProjectUsageApiCountParamsIntervalN3hr V1GetProjectUsageApiCountParamsInterval = "3hr" - V1GetProjectUsageApiCountParamsIntervalN7day V1GetProjectUsageApiCountParamsInterval = "7day" -) - -// Valid indicates whether the value is a known member of the V1GetProjectUsageApiCountParamsInterval enum. -func (e V1GetProjectUsageApiCountParamsInterval) Valid() bool { - switch e { - case V1GetProjectUsageApiCountParamsIntervalN15min: - return true - case V1GetProjectUsageApiCountParamsIntervalN1day: - return true - case V1GetProjectUsageApiCountParamsIntervalN1hr: - return true - case V1GetProjectUsageApiCountParamsIntervalN30min: - return true - case V1GetProjectUsageApiCountParamsIntervalN3day: - return true - case V1GetProjectUsageApiCountParamsIntervalN3hr: - return true - case V1GetProjectUsageApiCountParamsIntervalN7day: - return true - default: - return false - } -} - -// Defines values for V1RemoveProjectAddonParamsAddonVariant0. -const ( - V1RemoveProjectAddonParamsAddonVariant0Ci12xlarge V1RemoveProjectAddonParamsAddonVariant0 = "ci_12xlarge" - V1RemoveProjectAddonParamsAddonVariant0Ci16xlarge V1RemoveProjectAddonParamsAddonVariant0 = "ci_16xlarge" - V1RemoveProjectAddonParamsAddonVariant0Ci24xlarge V1RemoveProjectAddonParamsAddonVariant0 = "ci_24xlarge" - V1RemoveProjectAddonParamsAddonVariant0Ci24xlargeHighMemory V1RemoveProjectAddonParamsAddonVariant0 = "ci_24xlarge_high_memory" - V1RemoveProjectAddonParamsAddonVariant0Ci24xlargeOptimizedCpu V1RemoveProjectAddonParamsAddonVariant0 = "ci_24xlarge_optimized_cpu" - V1RemoveProjectAddonParamsAddonVariant0Ci24xlargeOptimizedMemory V1RemoveProjectAddonParamsAddonVariant0 = "ci_24xlarge_optimized_memory" - V1RemoveProjectAddonParamsAddonVariant0Ci2xlarge V1RemoveProjectAddonParamsAddonVariant0 = "ci_2xlarge" - V1RemoveProjectAddonParamsAddonVariant0Ci48xlarge V1RemoveProjectAddonParamsAddonVariant0 = "ci_48xlarge" - V1RemoveProjectAddonParamsAddonVariant0Ci48xlargeHighMemory V1RemoveProjectAddonParamsAddonVariant0 = "ci_48xlarge_high_memory" - V1RemoveProjectAddonParamsAddonVariant0Ci48xlargeOptimizedCpu V1RemoveProjectAddonParamsAddonVariant0 = "ci_48xlarge_optimized_cpu" - V1RemoveProjectAddonParamsAddonVariant0Ci48xlargeOptimizedMemory V1RemoveProjectAddonParamsAddonVariant0 = "ci_48xlarge_optimized_memory" - V1RemoveProjectAddonParamsAddonVariant0Ci4xlarge V1RemoveProjectAddonParamsAddonVariant0 = "ci_4xlarge" - V1RemoveProjectAddonParamsAddonVariant0Ci8xlarge V1RemoveProjectAddonParamsAddonVariant0 = "ci_8xlarge" - V1RemoveProjectAddonParamsAddonVariant0CiLarge V1RemoveProjectAddonParamsAddonVariant0 = "ci_large" - V1RemoveProjectAddonParamsAddonVariant0CiMedium V1RemoveProjectAddonParamsAddonVariant0 = "ci_medium" - V1RemoveProjectAddonParamsAddonVariant0CiMicro V1RemoveProjectAddonParamsAddonVariant0 = "ci_micro" - V1RemoveProjectAddonParamsAddonVariant0CiSmall V1RemoveProjectAddonParamsAddonVariant0 = "ci_small" - V1RemoveProjectAddonParamsAddonVariant0CiXlarge V1RemoveProjectAddonParamsAddonVariant0 = "ci_xlarge" -) - -// Valid indicates whether the value is a known member of the V1RemoveProjectAddonParamsAddonVariant0 enum. -func (e V1RemoveProjectAddonParamsAddonVariant0) Valid() bool { - switch e { - case V1RemoveProjectAddonParamsAddonVariant0Ci12xlarge: - return true - case V1RemoveProjectAddonParamsAddonVariant0Ci16xlarge: - return true - case V1RemoveProjectAddonParamsAddonVariant0Ci24xlarge: - return true - case V1RemoveProjectAddonParamsAddonVariant0Ci24xlargeHighMemory: - return true - case V1RemoveProjectAddonParamsAddonVariant0Ci24xlargeOptimizedCpu: - return true - case V1RemoveProjectAddonParamsAddonVariant0Ci24xlargeOptimizedMemory: - return true - case V1RemoveProjectAddonParamsAddonVariant0Ci2xlarge: - return true - case V1RemoveProjectAddonParamsAddonVariant0Ci48xlarge: - return true - case V1RemoveProjectAddonParamsAddonVariant0Ci48xlargeHighMemory: - return true - case V1RemoveProjectAddonParamsAddonVariant0Ci48xlargeOptimizedCpu: - return true - case V1RemoveProjectAddonParamsAddonVariant0Ci48xlargeOptimizedMemory: - return true - case V1RemoveProjectAddonParamsAddonVariant0Ci4xlarge: - return true - case V1RemoveProjectAddonParamsAddonVariant0Ci8xlarge: - return true - case V1RemoveProjectAddonParamsAddonVariant0CiLarge: - return true - case V1RemoveProjectAddonParamsAddonVariant0CiMedium: - return true - case V1RemoveProjectAddonParamsAddonVariant0CiMicro: - return true - case V1RemoveProjectAddonParamsAddonVariant0CiSmall: - return true - case V1RemoveProjectAddonParamsAddonVariant0CiXlarge: - return true - default: - return false - } -} - -// Defines values for V1RemoveProjectAddonParamsAddonVariant1. -const ( - V1RemoveProjectAddonParamsAddonVariant1CdDefault V1RemoveProjectAddonParamsAddonVariant1 = "cd_default" -) - -// Valid indicates whether the value is a known member of the V1RemoveProjectAddonParamsAddonVariant1 enum. -func (e V1RemoveProjectAddonParamsAddonVariant1) Valid() bool { - switch e { - case V1RemoveProjectAddonParamsAddonVariant1CdDefault: - return true - default: - return false - } -} - -// Defines values for V1RemoveProjectAddonParamsAddonVariant2. -const ( - V1RemoveProjectAddonParamsAddonVariant2Pitr14 V1RemoveProjectAddonParamsAddonVariant2 = "pitr_14" - V1RemoveProjectAddonParamsAddonVariant2Pitr28 V1RemoveProjectAddonParamsAddonVariant2 = "pitr_28" - V1RemoveProjectAddonParamsAddonVariant2Pitr7 V1RemoveProjectAddonParamsAddonVariant2 = "pitr_7" -) - -// Valid indicates whether the value is a known member of the V1RemoveProjectAddonParamsAddonVariant2 enum. -func (e V1RemoveProjectAddonParamsAddonVariant2) Valid() bool { - switch e { - case V1RemoveProjectAddonParamsAddonVariant2Pitr14: - return true - case V1RemoveProjectAddonParamsAddonVariant2Pitr28: - return true - case V1RemoveProjectAddonParamsAddonVariant2Pitr7: - return true - default: - return false - } -} - -// Defines values for V1RemoveProjectAddonParamsAddonVariant3. -const ( - V1RemoveProjectAddonParamsAddonVariant3Ipv4Default V1RemoveProjectAddonParamsAddonVariant3 = "ipv4_default" -) - -// Valid indicates whether the value is a known member of the V1RemoveProjectAddonParamsAddonVariant3 enum. -func (e V1RemoveProjectAddonParamsAddonVariant3) Valid() bool { - switch e { - case V1RemoveProjectAddonParamsAddonVariant3Ipv4Default: - return true - default: - return false - } -} - -// Defines values for V1GetServicesHealthParamsServices. -const ( - V1GetServicesHealthParamsServicesAuth V1GetServicesHealthParamsServices = "auth" - V1GetServicesHealthParamsServicesDb V1GetServicesHealthParamsServices = "db" - V1GetServicesHealthParamsServicesDbPostgresUser V1GetServicesHealthParamsServices = "db_postgres_user" - V1GetServicesHealthParamsServicesPgBouncer V1GetServicesHealthParamsServices = "pg_bouncer" - V1GetServicesHealthParamsServicesPooler V1GetServicesHealthParamsServices = "pooler" - V1GetServicesHealthParamsServicesRealtime V1GetServicesHealthParamsServices = "realtime" - V1GetServicesHealthParamsServicesRest V1GetServicesHealthParamsServices = "rest" - V1GetServicesHealthParamsServicesStorage V1GetServicesHealthParamsServices = "storage" -) - -// Valid indicates whether the value is a known member of the V1GetServicesHealthParamsServices enum. -func (e V1GetServicesHealthParamsServices) Valid() bool { - switch e { - case V1GetServicesHealthParamsServicesAuth: - return true - case V1GetServicesHealthParamsServicesDb: - return true - case V1GetServicesHealthParamsServicesDbPostgresUser: - return true - case V1GetServicesHealthParamsServicesPgBouncer: - return true - case V1GetServicesHealthParamsServicesPooler: - return true - case V1GetServicesHealthParamsServicesRealtime: - return true - case V1GetServicesHealthParamsServicesRest: - return true - case V1GetServicesHealthParamsServicesStorage: - return true - default: - return false - } -} - -// Defines values for V1GetJitAccessConfig200JSONResponseBody0State. -const ( - V1GetJitAccessConfig200JSONResponseBody0StateDisabled V1GetJitAccessConfig200JSONResponseBody0State = "disabled" - V1GetJitAccessConfig200JSONResponseBody0StateEnabled V1GetJitAccessConfig200JSONResponseBody0State = "enabled" -) - -// Valid indicates whether the value is a known member of the V1GetJitAccessConfig200JSONResponseBody0State enum. -func (e V1GetJitAccessConfig200JSONResponseBody0State) Valid() bool { - switch e { - case V1GetJitAccessConfig200JSONResponseBody0StateDisabled: - return true - case V1GetJitAccessConfig200JSONResponseBody0StateEnabled: - return true - default: - return false - } -} - -// Defines values for V1GetJitAccessConfig200JSONResponseBody1UnavailableReason. -const ( - V1GetJitAccessConfig200JSONResponseBody1UnavailableReasonPlatformUnsupported V1GetJitAccessConfig200JSONResponseBody1UnavailableReason = "platform_unsupported" - V1GetJitAccessConfig200JSONResponseBody1UnavailableReasonPostgresUpgradeRequired V1GetJitAccessConfig200JSONResponseBody1UnavailableReason = "postgres_upgrade_required" - V1GetJitAccessConfig200JSONResponseBody1UnavailableReasonSslEnforcementRequired V1GetJitAccessConfig200JSONResponseBody1UnavailableReason = "ssl_enforcement_required" - V1GetJitAccessConfig200JSONResponseBody1UnavailableReasonTemporarilyUnavailable V1GetJitAccessConfig200JSONResponseBody1UnavailableReason = "temporarily_unavailable" -) - -// Valid indicates whether the value is a known member of the V1GetJitAccessConfig200JSONResponseBody1UnavailableReason enum. -func (e V1GetJitAccessConfig200JSONResponseBody1UnavailableReason) Valid() bool { - switch e { - case V1GetJitAccessConfig200JSONResponseBody1UnavailableReasonPlatformUnsupported: - return true - case V1GetJitAccessConfig200JSONResponseBody1UnavailableReasonPostgresUpgradeRequired: - return true - case V1GetJitAccessConfig200JSONResponseBody1UnavailableReasonSslEnforcementRequired: - return true - case V1GetJitAccessConfig200JSONResponseBody1UnavailableReasonTemporarilyUnavailable: - return true - default: - return false - } -} - -// Defines values for V1UpdateJitAccessConfig200JSONResponseBody0State. -const ( - Disabled V1UpdateJitAccessConfig200JSONResponseBody0State = "disabled" - Enabled V1UpdateJitAccessConfig200JSONResponseBody0State = "enabled" -) - -// Valid indicates whether the value is a known member of the V1UpdateJitAccessConfig200JSONResponseBody0State enum. -func (e V1UpdateJitAccessConfig200JSONResponseBody0State) Valid() bool { - switch e { - case Disabled: - return true - case Enabled: - return true - default: - return false - } -} - -// Defines values for V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReason. -const ( - V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReasonPlatformUnsupported V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReason = "platform_unsupported" - V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReasonPostgresUpgradeRequired V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReason = "postgres_upgrade_required" - V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReasonSslEnforcementRequired V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReason = "ssl_enforcement_required" - V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReasonTemporarilyUnavailable V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReason = "temporarily_unavailable" -) - -// Valid indicates whether the value is a known member of the V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReason enum. -func (e V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReason) Valid() bool { - switch e { - case V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReasonPlatformUnsupported: - return true - case V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReasonPostgresUpgradeRequired: - return true - case V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReasonSslEnforcementRequired: - return true - case V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReasonTemporarilyUnavailable: - return true - default: - return false - } -} - -// Defines values for V1ListAllSnippetsParamsSortBy. -const ( - InsertedAt V1ListAllSnippetsParamsSortBy = "inserted_at" - Name V1ListAllSnippetsParamsSortBy = "name" -) - -// Valid indicates whether the value is a known member of the V1ListAllSnippetsParamsSortBy enum. -func (e V1ListAllSnippetsParamsSortBy) Valid() bool { - switch e { - case InsertedAt: - return true - case Name: - return true - default: - return false - } -} - -// Defines values for V1ListAllSnippetsParamsSortOrder. -const ( - Asc V1ListAllSnippetsParamsSortOrder = "asc" - Desc V1ListAllSnippetsParamsSortOrder = "desc" -) - -// Valid indicates whether the value is a known member of the V1ListAllSnippetsParamsSortOrder enum. -func (e V1ListAllSnippetsParamsSortOrder) Valid() bool { - switch e { - case Asc: - return true - case Desc: - return true - default: - return false - } -} - -// AcceptInviteExternalUserJitAccessBody defines model for AcceptInviteExternalUserJitAccessBody. -type AcceptInviteExternalUserJitAccessBody struct { - Email openapi_types.Email `json:"email"` - Token string `json:"token"` -} - -// ActionRunResponseOutput defines model for ActionRunResponse_Output. -type ActionRunResponseOutput struct { - BranchId string `json:"branch_id"` - CheckRunId nullable.Nullable[float32] `json:"check_run_id"` - CreatedAt string `json:"created_at"` - GitConfig nullable.Nullable[interface{}] `json:"git_config,omitempty"` - Id string `json:"id"` - RunSteps []struct { - CreatedAt string `json:"created_at"` - Name ActionRunResponseOutputRunStepsName `json:"name"` - Status ActionRunResponseOutputRunStepsStatus `json:"status"` - UpdatedAt string `json:"updated_at"` - } `json:"run_steps"` - UpdatedAt string `json:"updated_at"` - Workdir nullable.Nullable[string] `json:"workdir"` -} - -// ActionRunResponseOutputRunStepsName defines model for ActionRunResponseOutput.RunSteps.Name. -type ActionRunResponseOutputRunStepsName string - -// ActionRunResponseOutputRunStepsStatus defines model for ActionRunResponseOutput.RunSteps.Status. -type ActionRunResponseOutputRunStepsStatus string - -// ActivateVanitySubdomainResponseOutput defines model for ActivateVanitySubdomainResponse_Output. -type ActivateVanitySubdomainResponseOutput struct { - CustomDomain string `json:"custom_domain"` -} - -// AnalyticsResponseOutput defines model for AnalyticsResponse_Output. -type AnalyticsResponseOutput struct { - Error *AnalyticsResponseOutput_Error `json:"error,omitempty"` - Result *[]interface{} `json:"result,omitempty"` -} - -// AnalyticsResponseOutputError0 defines model for . -type AnalyticsResponseOutputError0 = string - -// AnalyticsResponseOutputError1 defines model for . -type AnalyticsResponseOutputError1 struct { - Code float32 `json:"code"` - Errors []struct { - Domain string `json:"domain"` - Location string `json:"location"` - LocationType string `json:"locationType"` - Message string `json:"message"` - Reason string `json:"reason"` - } `json:"errors"` - Message string `json:"message"` - Status string `json:"status"` -} - -// AnalyticsResponseOutput_Error defines model for AnalyticsResponseOutput.Error. -type AnalyticsResponseOutput_Error struct { - union json.RawMessage -} - -// ApiKeyResponseOutput defines model for ApiKeyResponse_Output. -type ApiKeyResponseOutput struct { - ApiKey nullable.Nullable[string] `json:"api_key,omitempty"` - Description nullable.Nullable[string] `json:"description,omitempty"` - Hash nullable.Nullable[string] `json:"hash,omitempty"` - Id nullable.Nullable[string] `json:"id,omitempty"` - InsertedAt nullable.Nullable[time.Time] `json:"inserted_at,omitempty"` - Name string `json:"name"` - Prefix nullable.Nullable[string] `json:"prefix,omitempty"` - SecretJwtTemplate nullable.Nullable[map[string]interface{}] `json:"secret_jwt_template,omitempty"` - Type nullable.Nullable[ApiKeyResponseOutputType] `json:"type,omitempty"` - UpdatedAt nullable.Nullable[time.Time] `json:"updated_at,omitempty"` -} - -// ApiKeyResponseOutputType defines model for ApiKeyResponseOutput.Type. -type ApiKeyResponseOutputType string - -// ApplyProjectAddonBody defines model for ApplyProjectAddonBody. -type ApplyProjectAddonBody struct { - AddonType ApplyProjectAddonBodyAddonType `json:"addon_type"` - AddonVariant ApplyProjectAddonBody_AddonVariant `json:"addon_variant"` -} - -// ApplyProjectAddonBodyAddonType defines model for ApplyProjectAddonBody.AddonType. -type ApplyProjectAddonBodyAddonType string - -// ApplyProjectAddonBodyAddonVariant0 defines model for ApplyProjectAddonBody.AddonVariant.0. -type ApplyProjectAddonBodyAddonVariant0 string - -// ApplyProjectAddonBodyAddonVariant1 defines model for ApplyProjectAddonBody.AddonVariant.1. -type ApplyProjectAddonBodyAddonVariant1 string - -// ApplyProjectAddonBodyAddonVariant2 defines model for ApplyProjectAddonBody.AddonVariant.2. -type ApplyProjectAddonBodyAddonVariant2 string - -// ApplyProjectAddonBodyAddonVariant3 defines model for ApplyProjectAddonBody.AddonVariant.3. -type ApplyProjectAddonBodyAddonVariant3 string - -// ApplyProjectAddonBody_AddonVariant defines model for ApplyProjectAddonBody.AddonVariant. -type ApplyProjectAddonBody_AddonVariant struct { - union json.RawMessage -} - -// AuthConfigResponseOutput defines model for AuthConfigResponse_Output. -type AuthConfigResponseOutput struct { - ApiMaxRequestDuration nullable.Nullable[int] `json:"api_max_request_duration"` - CustomOauthEnabled bool `json:"custom_oauth_enabled"` - CustomOauthMaxProviders int `json:"custom_oauth_max_providers"` - DbMaxPoolSize nullable.Nullable[int] `json:"db_max_pool_size"` - DbMaxPoolSizeUnit nullable.Nullable[AuthConfigResponseOutputDbMaxPoolSizeUnit] `json:"db_max_pool_size_unit"` - DisableSignup nullable.Nullable[bool] `json:"disable_signup"` - ExternalAnonymousUsersEnabled nullable.Nullable[bool] `json:"external_anonymous_users_enabled"` - ExternalAppleAdditionalClientIds nullable.Nullable[string] `json:"external_apple_additional_client_ids"` - ExternalAppleClientId nullable.Nullable[string] `json:"external_apple_client_id"` - ExternalAppleEmailOptional nullable.Nullable[bool] `json:"external_apple_email_optional"` - ExternalAppleEnabled nullable.Nullable[bool] `json:"external_apple_enabled"` - ExternalAppleSecret nullable.Nullable[string] `json:"external_apple_secret"` - ExternalAzureClientId nullable.Nullable[string] `json:"external_azure_client_id"` - ExternalAzureEmailOptional nullable.Nullable[bool] `json:"external_azure_email_optional"` - ExternalAzureEnabled nullable.Nullable[bool] `json:"external_azure_enabled"` - ExternalAzureSecret nullable.Nullable[string] `json:"external_azure_secret"` - ExternalAzureUrl nullable.Nullable[string] `json:"external_azure_url"` - ExternalBitbucketClientId nullable.Nullable[string] `json:"external_bitbucket_client_id"` - ExternalBitbucketEmailOptional nullable.Nullable[bool] `json:"external_bitbucket_email_optional"` - ExternalBitbucketEnabled nullable.Nullable[bool] `json:"external_bitbucket_enabled"` - ExternalBitbucketSecret nullable.Nullable[string] `json:"external_bitbucket_secret"` - ExternalDiscordClientId nullable.Nullable[string] `json:"external_discord_client_id"` - ExternalDiscordEmailOptional nullable.Nullable[bool] `json:"external_discord_email_optional"` - ExternalDiscordEnabled nullable.Nullable[bool] `json:"external_discord_enabled"` - ExternalDiscordSecret nullable.Nullable[string] `json:"external_discord_secret"` - ExternalEmailEnabled nullable.Nullable[bool] `json:"external_email_enabled"` - ExternalFacebookClientId nullable.Nullable[string] `json:"external_facebook_client_id"` - ExternalFacebookEmailOptional nullable.Nullable[bool] `json:"external_facebook_email_optional"` - ExternalFacebookEnabled nullable.Nullable[bool] `json:"external_facebook_enabled"` - ExternalFacebookSecret nullable.Nullable[string] `json:"external_facebook_secret"` - ExternalFigmaClientId nullable.Nullable[string] `json:"external_figma_client_id"` - ExternalFigmaEmailOptional nullable.Nullable[bool] `json:"external_figma_email_optional"` - ExternalFigmaEnabled nullable.Nullable[bool] `json:"external_figma_enabled"` - ExternalFigmaSecret nullable.Nullable[string] `json:"external_figma_secret"` - ExternalGithubClientId nullable.Nullable[string] `json:"external_github_client_id"` - ExternalGithubEmailOptional nullable.Nullable[bool] `json:"external_github_email_optional"` - ExternalGithubEnabled nullable.Nullable[bool] `json:"external_github_enabled"` - ExternalGithubSecret nullable.Nullable[string] `json:"external_github_secret"` - ExternalGitlabClientId nullable.Nullable[string] `json:"external_gitlab_client_id"` - ExternalGitlabEmailOptional nullable.Nullable[bool] `json:"external_gitlab_email_optional"` - ExternalGitlabEnabled nullable.Nullable[bool] `json:"external_gitlab_enabled"` - ExternalGitlabSecret nullable.Nullable[string] `json:"external_gitlab_secret"` - ExternalGitlabUrl nullable.Nullable[string] `json:"external_gitlab_url"` - ExternalGoogleAdditionalClientIds nullable.Nullable[string] `json:"external_google_additional_client_ids"` - ExternalGoogleClientId nullable.Nullable[string] `json:"external_google_client_id"` - ExternalGoogleEmailOptional nullable.Nullable[bool] `json:"external_google_email_optional"` - ExternalGoogleEnabled nullable.Nullable[bool] `json:"external_google_enabled"` - ExternalGoogleSecret nullable.Nullable[string] `json:"external_google_secret"` - ExternalGoogleSkipNonceCheck nullable.Nullable[bool] `json:"external_google_skip_nonce_check"` - ExternalKakaoClientId nullable.Nullable[string] `json:"external_kakao_client_id"` - ExternalKakaoEmailOptional nullable.Nullable[bool] `json:"external_kakao_email_optional"` - ExternalKakaoEnabled nullable.Nullable[bool] `json:"external_kakao_enabled"` - ExternalKakaoSecret nullable.Nullable[string] `json:"external_kakao_secret"` - ExternalKeycloakClientId nullable.Nullable[string] `json:"external_keycloak_client_id"` - ExternalKeycloakEmailOptional nullable.Nullable[bool] `json:"external_keycloak_email_optional"` - ExternalKeycloakEnabled nullable.Nullable[bool] `json:"external_keycloak_enabled"` - ExternalKeycloakSecret nullable.Nullable[string] `json:"external_keycloak_secret"` - ExternalKeycloakUrl nullable.Nullable[string] `json:"external_keycloak_url"` - ExternalLinkedinOidcClientId nullable.Nullable[string] `json:"external_linkedin_oidc_client_id"` - ExternalLinkedinOidcEmailOptional nullable.Nullable[bool] `json:"external_linkedin_oidc_email_optional"` - ExternalLinkedinOidcEnabled nullable.Nullable[bool] `json:"external_linkedin_oidc_enabled"` - ExternalLinkedinOidcSecret nullable.Nullable[string] `json:"external_linkedin_oidc_secret"` - ExternalNotionClientId nullable.Nullable[string] `json:"external_notion_client_id"` - ExternalNotionEmailOptional nullable.Nullable[bool] `json:"external_notion_email_optional"` - ExternalNotionEnabled nullable.Nullable[bool] `json:"external_notion_enabled"` - ExternalNotionSecret nullable.Nullable[string] `json:"external_notion_secret"` - ExternalPhoneEnabled nullable.Nullable[bool] `json:"external_phone_enabled"` - ExternalSlackClientId nullable.Nullable[string] `json:"external_slack_client_id"` - ExternalSlackEmailOptional nullable.Nullable[bool] `json:"external_slack_email_optional"` - ExternalSlackEnabled nullable.Nullable[bool] `json:"external_slack_enabled"` - ExternalSlackOidcClientId nullable.Nullable[string] `json:"external_slack_oidc_client_id"` - ExternalSlackOidcEmailOptional nullable.Nullable[bool] `json:"external_slack_oidc_email_optional"` - ExternalSlackOidcEnabled nullable.Nullable[bool] `json:"external_slack_oidc_enabled"` - ExternalSlackOidcSecret nullable.Nullable[string] `json:"external_slack_oidc_secret"` - ExternalSlackSecret nullable.Nullable[string] `json:"external_slack_secret"` - ExternalSpotifyClientId nullable.Nullable[string] `json:"external_spotify_client_id"` - ExternalSpotifyEmailOptional nullable.Nullable[bool] `json:"external_spotify_email_optional"` - ExternalSpotifyEnabled nullable.Nullable[bool] `json:"external_spotify_enabled"` - ExternalSpotifySecret nullable.Nullable[string] `json:"external_spotify_secret"` - ExternalTwitchClientId nullable.Nullable[string] `json:"external_twitch_client_id"` - ExternalTwitchEmailOptional nullable.Nullable[bool] `json:"external_twitch_email_optional"` - ExternalTwitchEnabled nullable.Nullable[bool] `json:"external_twitch_enabled"` - ExternalTwitchSecret nullable.Nullable[string] `json:"external_twitch_secret"` - ExternalTwitterClientId nullable.Nullable[string] `json:"external_twitter_client_id"` - ExternalTwitterEmailOptional nullable.Nullable[bool] `json:"external_twitter_email_optional"` - ExternalTwitterEnabled nullable.Nullable[bool] `json:"external_twitter_enabled"` - ExternalTwitterSecret nullable.Nullable[string] `json:"external_twitter_secret"` - ExternalWeb3EthereumEnabled nullable.Nullable[bool] `json:"external_web3_ethereum_enabled"` - ExternalWeb3SolanaEnabled nullable.Nullable[bool] `json:"external_web3_solana_enabled"` - ExternalWorkosClientId nullable.Nullable[string] `json:"external_workos_client_id"` - ExternalWorkosEnabled nullable.Nullable[bool] `json:"external_workos_enabled"` - ExternalWorkosSecret nullable.Nullable[string] `json:"external_workos_secret"` - ExternalWorkosUrl nullable.Nullable[string] `json:"external_workos_url"` - ExternalXClientId nullable.Nullable[string] `json:"external_x_client_id"` - ExternalXEmailOptional nullable.Nullable[bool] `json:"external_x_email_optional"` - ExternalXEnabled nullable.Nullable[bool] `json:"external_x_enabled"` - ExternalXSecret nullable.Nullable[string] `json:"external_x_secret"` - ExternalZoomClientId nullable.Nullable[string] `json:"external_zoom_client_id"` - ExternalZoomEmailOptional nullable.Nullable[bool] `json:"external_zoom_email_optional"` - ExternalZoomEnabled nullable.Nullable[bool] `json:"external_zoom_enabled"` - ExternalZoomSecret nullable.Nullable[string] `json:"external_zoom_secret"` - HookAfterUserCreatedEnabled nullable.Nullable[bool] `json:"hook_after_user_created_enabled"` - HookAfterUserCreatedSecrets nullable.Nullable[string] `json:"hook_after_user_created_secrets"` - HookAfterUserCreatedUri nullable.Nullable[string] `json:"hook_after_user_created_uri"` - HookBeforeUserCreatedEnabled nullable.Nullable[bool] `json:"hook_before_user_created_enabled"` - HookBeforeUserCreatedSecrets nullable.Nullable[string] `json:"hook_before_user_created_secrets"` - HookBeforeUserCreatedUri nullable.Nullable[string] `json:"hook_before_user_created_uri"` - HookCustomAccessTokenEnabled nullable.Nullable[bool] `json:"hook_custom_access_token_enabled"` - HookCustomAccessTokenSecrets nullable.Nullable[string] `json:"hook_custom_access_token_secrets"` - HookCustomAccessTokenUri nullable.Nullable[string] `json:"hook_custom_access_token_uri"` - HookMfaVerificationAttemptEnabled nullable.Nullable[bool] `json:"hook_mfa_verification_attempt_enabled"` - HookMfaVerificationAttemptSecrets nullable.Nullable[string] `json:"hook_mfa_verification_attempt_secrets"` - HookMfaVerificationAttemptUri nullable.Nullable[string] `json:"hook_mfa_verification_attempt_uri"` - HookPasswordVerificationAttemptEnabled nullable.Nullable[bool] `json:"hook_password_verification_attempt_enabled"` - HookPasswordVerificationAttemptSecrets nullable.Nullable[string] `json:"hook_password_verification_attempt_secrets"` - HookPasswordVerificationAttemptUri nullable.Nullable[string] `json:"hook_password_verification_attempt_uri"` - HookSendEmailEnabled nullable.Nullable[bool] `json:"hook_send_email_enabled"` - HookSendEmailSecrets nullable.Nullable[string] `json:"hook_send_email_secrets"` - HookSendEmailUri nullable.Nullable[string] `json:"hook_send_email_uri"` - HookSendSmsEnabled nullable.Nullable[bool] `json:"hook_send_sms_enabled"` - HookSendSmsSecrets nullable.Nullable[string] `json:"hook_send_sms_secrets"` - HookSendSmsUri nullable.Nullable[string] `json:"hook_send_sms_uri"` - JwtExp nullable.Nullable[int] `json:"jwt_exp"` - MailerAllowUnverifiedEmailSignIns nullable.Nullable[bool] `json:"mailer_allow_unverified_email_sign_ins"` - MailerAutoconfirm nullable.Nullable[bool] `json:"mailer_autoconfirm"` - MailerNotificationsEmailChangedEnabled nullable.Nullable[bool] `json:"mailer_notifications_email_changed_enabled"` - MailerNotificationsIdentityLinkedEnabled nullable.Nullable[bool] `json:"mailer_notifications_identity_linked_enabled"` - MailerNotificationsIdentityUnlinkedEnabled nullable.Nullable[bool] `json:"mailer_notifications_identity_unlinked_enabled"` - MailerNotificationsMfaFactorEnrolledEnabled nullable.Nullable[bool] `json:"mailer_notifications_mfa_factor_enrolled_enabled"` - MailerNotificationsMfaFactorUnenrolledEnabled nullable.Nullable[bool] `json:"mailer_notifications_mfa_factor_unenrolled_enabled"` - MailerNotificationsPasswordChangedEnabled nullable.Nullable[bool] `json:"mailer_notifications_password_changed_enabled"` - MailerNotificationsPhoneChangedEnabled nullable.Nullable[bool] `json:"mailer_notifications_phone_changed_enabled"` - MailerOtpExp int `json:"mailer_otp_exp"` - MailerOtpLength nullable.Nullable[int] `json:"mailer_otp_length"` - MailerSecureEmailChangeEnabled nullable.Nullable[bool] `json:"mailer_secure_email_change_enabled"` - MailerSubjectsConfirmation nullable.Nullable[string] `json:"mailer_subjects_confirmation"` - MailerSubjectsEmailChange nullable.Nullable[string] `json:"mailer_subjects_email_change"` - MailerSubjectsEmailChangedNotification nullable.Nullable[string] `json:"mailer_subjects_email_changed_notification"` - MailerSubjectsIdentityLinkedNotification nullable.Nullable[string] `json:"mailer_subjects_identity_linked_notification"` - MailerSubjectsIdentityUnlinkedNotification nullable.Nullable[string] `json:"mailer_subjects_identity_unlinked_notification"` - MailerSubjectsInvite nullable.Nullable[string] `json:"mailer_subjects_invite"` - MailerSubjectsMagicLink nullable.Nullable[string] `json:"mailer_subjects_magic_link"` - MailerSubjectsMfaFactorEnrolledNotification nullable.Nullable[string] `json:"mailer_subjects_mfa_factor_enrolled_notification"` - MailerSubjectsMfaFactorUnenrolledNotification nullable.Nullable[string] `json:"mailer_subjects_mfa_factor_unenrolled_notification"` - MailerSubjectsPasswordChangedNotification nullable.Nullable[string] `json:"mailer_subjects_password_changed_notification"` - MailerSubjectsPhoneChangedNotification nullable.Nullable[string] `json:"mailer_subjects_phone_changed_notification"` - MailerSubjectsReauthentication nullable.Nullable[string] `json:"mailer_subjects_reauthentication"` - MailerSubjectsRecovery nullable.Nullable[string] `json:"mailer_subjects_recovery"` - MailerTemplatesConfirmationContent nullable.Nullable[string] `json:"mailer_templates_confirmation_content"` - MailerTemplatesEmailChangeContent nullable.Nullable[string] `json:"mailer_templates_email_change_content"` - MailerTemplatesEmailChangedNotificationContent nullable.Nullable[string] `json:"mailer_templates_email_changed_notification_content"` - MailerTemplatesIdentityLinkedNotificationContent nullable.Nullable[string] `json:"mailer_templates_identity_linked_notification_content"` - MailerTemplatesIdentityUnlinkedNotificationContent nullable.Nullable[string] `json:"mailer_templates_identity_unlinked_notification_content"` - MailerTemplatesInviteContent nullable.Nullable[string] `json:"mailer_templates_invite_content"` - MailerTemplatesMagicLinkContent nullable.Nullable[string] `json:"mailer_templates_magic_link_content"` - MailerTemplatesMfaFactorEnrolledNotificationContent nullable.Nullable[string] `json:"mailer_templates_mfa_factor_enrolled_notification_content"` - MailerTemplatesMfaFactorUnenrolledNotificationContent nullable.Nullable[string] `json:"mailer_templates_mfa_factor_unenrolled_notification_content"` - MailerTemplatesPasswordChangedNotificationContent nullable.Nullable[string] `json:"mailer_templates_password_changed_notification_content"` - MailerTemplatesPhoneChangedNotificationContent nullable.Nullable[string] `json:"mailer_templates_phone_changed_notification_content"` - MailerTemplatesReauthenticationContent nullable.Nullable[string] `json:"mailer_templates_reauthentication_content"` - MailerTemplatesRecoveryContent nullable.Nullable[string] `json:"mailer_templates_recovery_content"` - MfaMaxEnrolledFactors nullable.Nullable[int] `json:"mfa_max_enrolled_factors"` - MfaPhoneEnrollEnabled nullable.Nullable[bool] `json:"mfa_phone_enroll_enabled"` - MfaPhoneMaxFrequency nullable.Nullable[int] `json:"mfa_phone_max_frequency"` - MfaPhoneOtpLength int `json:"mfa_phone_otp_length"` - MfaPhoneTemplate nullable.Nullable[string] `json:"mfa_phone_template"` - MfaPhoneVerifyEnabled nullable.Nullable[bool] `json:"mfa_phone_verify_enabled"` - MfaTotpEnrollEnabled nullable.Nullable[bool] `json:"mfa_totp_enroll_enabled"` - MfaTotpVerifyEnabled nullable.Nullable[bool] `json:"mfa_totp_verify_enabled"` - MfaWebAuthnEnrollEnabled nullable.Nullable[bool] `json:"mfa_web_authn_enroll_enabled"` - MfaWebAuthnVerifyEnabled nullable.Nullable[bool] `json:"mfa_web_authn_verify_enabled"` - NimbusOauthClientId nullable.Nullable[string] `json:"nimbus_oauth_client_id"` - NimbusOauthClientSecret nullable.Nullable[string] `json:"nimbus_oauth_client_secret"` - NimbusOauthEmailOptional nullable.Nullable[bool] `json:"nimbus_oauth_email_optional"` - OauthServerAllowDynamicRegistration bool `json:"oauth_server_allow_dynamic_registration"` - OauthServerAuthorizationPath nullable.Nullable[string] `json:"oauth_server_authorization_path"` - OauthServerEnabled bool `json:"oauth_server_enabled"` - PasskeyEnabled bool `json:"passkey_enabled"` - PasswordHibpEnabled nullable.Nullable[bool] `json:"password_hibp_enabled"` - PasswordMinLength nullable.Nullable[int] `json:"password_min_length"` - PasswordRequiredCharacters nullable.Nullable[AuthConfigResponseOutputPasswordRequiredCharacters] `json:"password_required_characters"` - RateLimitAnonymousUsers nullable.Nullable[int] `json:"rate_limit_anonymous_users"` - RateLimitEmailSent nullable.Nullable[int] `json:"rate_limit_email_sent"` - RateLimitOtp nullable.Nullable[int] `json:"rate_limit_otp"` - RateLimitSmsSent nullable.Nullable[int] `json:"rate_limit_sms_sent"` - RateLimitTokenRefresh nullable.Nullable[int] `json:"rate_limit_token_refresh"` - RateLimitVerify nullable.Nullable[int] `json:"rate_limit_verify"` - RateLimitWeb3 nullable.Nullable[int] `json:"rate_limit_web3"` - RefreshTokenRotationEnabled nullable.Nullable[bool] `json:"refresh_token_rotation_enabled"` - SamlAllowEncryptedAssertions nullable.Nullable[bool] `json:"saml_allow_encrypted_assertions"` - SamlEnabled nullable.Nullable[bool] `json:"saml_enabled"` - SamlExternalUrl nullable.Nullable[string] `json:"saml_external_url"` - SecurityCaptchaEnabled nullable.Nullable[bool] `json:"security_captcha_enabled"` - SecurityCaptchaProvider nullable.Nullable[AuthConfigResponseOutputSecurityCaptchaProvider] `json:"security_captcha_provider"` - SecurityCaptchaSecret nullable.Nullable[string] `json:"security_captcha_secret"` - SecurityManualLinkingEnabled nullable.Nullable[bool] `json:"security_manual_linking_enabled"` - SecurityRefreshTokenReuseInterval nullable.Nullable[int] `json:"security_refresh_token_reuse_interval"` - SecuritySbForwardedForEnabled nullable.Nullable[bool] `json:"security_sb_forwarded_for_enabled"` - SecurityUpdatePasswordRequireCurrentPassword nullable.Nullable[bool] `json:"security_update_password_require_current_password"` - SecurityUpdatePasswordRequireReauthentication nullable.Nullable[bool] `json:"security_update_password_require_reauthentication"` - SessionsInactivityTimeout nullable.Nullable[float32] `json:"sessions_inactivity_timeout"` - SessionsSinglePerUser nullable.Nullable[bool] `json:"sessions_single_per_user"` - SessionsTags nullable.Nullable[string] `json:"sessions_tags"` - SessionsTimebox nullable.Nullable[float32] `json:"sessions_timebox"` - SiteUrl nullable.Nullable[string] `json:"site_url"` - SmsAutoconfirm nullable.Nullable[bool] `json:"sms_autoconfirm"` - SmsMaxFrequency nullable.Nullable[int] `json:"sms_max_frequency"` - SmsMessagebirdAccessKey nullable.Nullable[string] `json:"sms_messagebird_access_key"` - SmsMessagebirdOriginator nullable.Nullable[string] `json:"sms_messagebird_originator"` - SmsOtpExp nullable.Nullable[int] `json:"sms_otp_exp"` - SmsOtpLength int `json:"sms_otp_length"` - SmsProvider nullable.Nullable[AuthConfigResponseOutputSmsProvider] `json:"sms_provider"` - SmsTemplate nullable.Nullable[string] `json:"sms_template"` - SmsTestOtp nullable.Nullable[string] `json:"sms_test_otp"` - SmsTestOtpValidUntil nullable.Nullable[time.Time] `json:"sms_test_otp_valid_until"` - SmsTextlocalApiKey nullable.Nullable[string] `json:"sms_textlocal_api_key"` - SmsTextlocalSender nullable.Nullable[string] `json:"sms_textlocal_sender"` - SmsTwilioAccountSid nullable.Nullable[string] `json:"sms_twilio_account_sid"` - SmsTwilioAuthToken nullable.Nullable[string] `json:"sms_twilio_auth_token"` - SmsTwilioContentSid nullable.Nullable[string] `json:"sms_twilio_content_sid"` - SmsTwilioMessageServiceSid nullable.Nullable[string] `json:"sms_twilio_message_service_sid"` - SmsTwilioVerifyAccountSid nullable.Nullable[string] `json:"sms_twilio_verify_account_sid"` - SmsTwilioVerifyAuthToken nullable.Nullable[string] `json:"sms_twilio_verify_auth_token"` - SmsTwilioVerifyMessageServiceSid nullable.Nullable[string] `json:"sms_twilio_verify_message_service_sid"` - SmsVonageApiKey nullable.Nullable[string] `json:"sms_vonage_api_key"` - SmsVonageApiSecret nullable.Nullable[string] `json:"sms_vonage_api_secret"` - SmsVonageFrom nullable.Nullable[string] `json:"sms_vonage_from"` - SmtpAdminEmail nullable.Nullable[openapi_types.Email] `json:"smtp_admin_email"` - SmtpHost nullable.Nullable[string] `json:"smtp_host"` - SmtpMaxFrequency nullable.Nullable[int] `json:"smtp_max_frequency"` - SmtpPass nullable.Nullable[string] `json:"smtp_pass"` - SmtpPort nullable.Nullable[string] `json:"smtp_port"` - SmtpSenderName nullable.Nullable[string] `json:"smtp_sender_name"` - SmtpUser nullable.Nullable[string] `json:"smtp_user"` - UriAllowList nullable.Nullable[string] `json:"uri_allow_list"` - WebauthnRpDisplayName nullable.Nullable[string] `json:"webauthn_rp_display_name"` - WebauthnRpId nullable.Nullable[string] `json:"webauthn_rp_id"` - WebauthnRpOrigins nullable.Nullable[string] `json:"webauthn_rp_origins"` -} - -// AuthConfigResponseOutputDbMaxPoolSizeUnit defines model for AuthConfigResponseOutput.DbMaxPoolSizeUnit. -type AuthConfigResponseOutputDbMaxPoolSizeUnit string - -// AuthConfigResponseOutputPasswordRequiredCharacters defines model for AuthConfigResponseOutput.PasswordRequiredCharacters. -type AuthConfigResponseOutputPasswordRequiredCharacters string - -// AuthConfigResponseOutputSecurityCaptchaProvider defines model for AuthConfigResponseOutput.SecurityCaptchaProvider. -type AuthConfigResponseOutputSecurityCaptchaProvider string - -// AuthConfigResponseOutputSmsProvider defines model for AuthConfigResponseOutput.SmsProvider. -type AuthConfigResponseOutputSmsProvider string - -// AuthorizeJitAccessBody defines model for AuthorizeJitAccessBody. -type AuthorizeJitAccessBody struct { - Rhost AuthorizeJitAccessBody_Rhost `json:"rhost"` - Role string `json:"role"` -} - -// AuthorizeJitAccessBodyRhost0 defines model for . -type AuthorizeJitAccessBodyRhost0 = string - -// AuthorizeJitAccessBodyRhost1 defines model for . -type AuthorizeJitAccessBodyRhost1 = string - -// AuthorizeJitAccessBody_Rhost defines model for AuthorizeJitAccessBody.Rhost. -type AuthorizeJitAccessBody_Rhost struct { - union json.RawMessage -} - -// BranchActionBody defines model for BranchActionBody. -type BranchActionBody struct { - MigrationVersion *string `json:"migration_version,omitempty"` -} - -// BranchDeleteResponseOutput defines model for BranchDeleteResponse_Output. -type BranchDeleteResponseOutput struct { - Message BranchDeleteResponseOutputMessage `json:"message"` -} - -// BranchDeleteResponseOutputMessage defines model for BranchDeleteResponseOutput.Message. -type BranchDeleteResponseOutputMessage string - -// BranchDetailResponseOutput defines model for BranchDetailResponse_Output. -type BranchDetailResponseOutput struct { - DbHost string `json:"db_host"` - DbPass *string `json:"db_pass,omitempty"` - DbPort int `json:"db_port"` - DbUser *string `json:"db_user,omitempty"` - JwtSecret *string `json:"jwt_secret,omitempty"` - PostgresEngine string `json:"postgres_engine"` - PostgresVersion string `json:"postgres_version"` - Ref string `json:"ref"` - ReleaseChannel string `json:"release_channel"` - Status BranchDetailResponseOutputStatus `json:"status"` -} - -// BranchDetailResponseOutputStatus defines model for BranchDetailResponseOutput.Status. -type BranchDetailResponseOutputStatus string - -// BranchResponseOutput defines model for BranchResponse_Output. -type BranchResponseOutput struct { - CreatedAt time.Time `json:"created_at"` - DeletionScheduledAt *time.Time `json:"deletion_scheduled_at,omitempty"` - GitBranch *string `json:"git_branch,omitempty"` - Id openapi_types.UUID `json:"id"` - IsDefault bool `json:"is_default"` - - // LatestCheckRunId This field is deprecated and will not be populated. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - LatestCheckRunId *float32 `json:"latest_check_run_id,omitempty"` - Name string `json:"name"` - NotifyUrl *string `json:"notify_url,omitempty"` - ParentProjectRef string `json:"parent_project_ref"` - Persistent bool `json:"persistent"` - PrNumber *int32 `json:"pr_number,omitempty"` - PreviewProjectStatus *BranchResponseOutputPreviewProjectStatus `json:"preview_project_status,omitempty"` - ProjectRef string `json:"project_ref"` - ReviewRequestedAt *time.Time `json:"review_requested_at,omitempty"` - - // Status This field is deprecated. List action runs to get branch status instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - Status BranchResponseOutputStatus `json:"status"` - UpdatedAt time.Time `json:"updated_at"` - WithData bool `json:"with_data"` -} - -// BranchResponseOutputPreviewProjectStatus defines model for BranchResponseOutput.PreviewProjectStatus. -type BranchResponseOutputPreviewProjectStatus string - -// BranchResponseOutputStatus This field is deprecated. List action runs to get branch status instead. -type BranchResponseOutputStatus string - -// BranchRestoreResponseOutput defines model for BranchRestoreResponse_Output. -type BranchRestoreResponseOutput struct { - Message BranchRestoreResponseOutputMessage `json:"message"` -} - -// BranchRestoreResponseOutputMessage defines model for BranchRestoreResponseOutput.Message. -type BranchRestoreResponseOutputMessage string - -// BranchUpdateResponseOutput defines model for BranchUpdateResponse_Output. -type BranchUpdateResponseOutput struct { - Message BranchUpdateResponseOutputMessage `json:"message"` - WorkflowRunId string `json:"workflow_run_id"` -} - -// BranchUpdateResponseOutputMessage defines model for BranchUpdateResponseOutput.Message. -type BranchUpdateResponseOutputMessage string - -// BulkUpdateFunctionBody defines model for BulkUpdateFunctionBody. -type BulkUpdateFunctionBody = []struct { - CreatedAt *int64 `json:"created_at,omitempty"` - EntrypointPath *string `json:"entrypoint_path,omitempty"` - EzbrSha256 *string `json:"ezbr_sha256,omitempty"` - Id string `json:"id"` - ImportMap *bool `json:"import_map,omitempty"` - ImportMapPath *string `json:"import_map_path,omitempty"` - Name string `json:"name"` - Slug string `json:"slug"` - Status BulkUpdateFunctionBodyStatus `json:"status"` - VerifyJwt *bool `json:"verify_jwt,omitempty"` - Version int `json:"version"` -} - -// BulkUpdateFunctionBodyStatus defines model for BulkUpdateFunctionBody.Status. -type BulkUpdateFunctionBodyStatus string - -// BulkUpdateFunctionResponseOutput defines model for BulkUpdateFunctionResponse_Output. -type BulkUpdateFunctionResponseOutput struct { - Functions []struct { - CreatedAt int64 `json:"created_at"` - EntrypointPath *string `json:"entrypoint_path,omitempty"` - EzbrSha256 *string `json:"ezbr_sha256,omitempty"` - Id string `json:"id"` - ImportMap *bool `json:"import_map,omitempty"` - ImportMapPath *string `json:"import_map_path,omitempty"` - Name string `json:"name"` - Slug string `json:"slug"` - Status BulkUpdateFunctionResponseOutputFunctionsStatus `json:"status"` - UpdatedAt int64 `json:"updated_at"` - VerifyJwt *bool `json:"verify_jwt,omitempty"` - Version int `json:"version"` - } `json:"functions"` -} - -// BulkUpdateFunctionResponseOutputFunctionsStatus defines model for BulkUpdateFunctionResponseOutput.Functions.Status. -type BulkUpdateFunctionResponseOutputFunctionsStatus string - -// CreateApiKeyBody defines model for CreateApiKeyBody. -type CreateApiKeyBody struct { - Description nullable.Nullable[string] `json:"description,omitempty"` - Name string `json:"name"` - SecretJwtTemplate nullable.Nullable[map[string]interface{}] `json:"secret_jwt_template,omitempty"` - Type CreateApiKeyBodyType `json:"type"` -} - -// CreateApiKeyBodyType defines model for CreateApiKeyBody.Type. -type CreateApiKeyBodyType string - -// CreateBranchBody defines model for CreateBranchBody. -type CreateBranchBody struct { - BranchName string `json:"branch_name"` - DesiredInstanceSize *CreateBranchBodyDesiredInstanceSize `json:"desired_instance_size,omitempty"` - GitBranch *string `json:"git_branch,omitempty"` - IsDefault *bool `json:"is_default,omitempty"` - - // NotifyUrl HTTP endpoint to receive branch status updates. - NotifyUrl *string `json:"notify_url,omitempty"` - Persistent *bool `json:"persistent,omitempty"` - - // PostgresEngine Postgres engine version. If not provided, the latest version will be used. - PostgresEngine *CreateBranchBodyPostgresEngine `json:"postgres_engine,omitempty"` - Region *string `json:"region,omitempty"` - - // ReleaseChannel Release channel. If not provided, GA will be used. - ReleaseChannel *CreateBranchBodyReleaseChannel `json:"release_channel,omitempty"` - Secrets *map[string]string `json:"secrets,omitempty"` - WithData *bool `json:"with_data,omitempty"` -} - -// CreateBranchBodyDesiredInstanceSize defines model for CreateBranchBody.DesiredInstanceSize. -type CreateBranchBodyDesiredInstanceSize string - -// CreateBranchBodyPostgresEngine Postgres engine version. If not provided, the latest version will be used. -type CreateBranchBodyPostgresEngine string - -// CreateBranchBodyReleaseChannel Release channel. If not provided, GA will be used. -type CreateBranchBodyReleaseChannel string - -// CreateOrganizationV1 defines model for CreateOrganizationV1. -type CreateOrganizationV1 struct { - Name string `json:"name"` -} - -// CreateProjectClaimTokenResponseOutput defines model for CreateProjectClaimTokenResponse_Output. -type CreateProjectClaimTokenResponseOutput struct { - CreatedAt string `json:"created_at"` - CreatedBy openapi_types.UUID `json:"created_by"` - ExpiresAt string `json:"expires_at"` - Token string `json:"token"` - TokenAlias string `json:"token_alias"` -} - -// CreateProviderBody defines model for CreateProviderBody. -type CreateProviderBody struct { - AttributeMapping *struct { - Keys map[string]struct { - Array *bool `json:"array,omitempty"` - Default interface{} `json:"default,omitempty"` - Name *string `json:"name,omitempty"` - Names *[]string `json:"names,omitempty"` - } `json:"keys"` - } `json:"attribute_mapping,omitempty"` - Domains *[]string `json:"domains,omitempty"` - MetadataUrl *string `json:"metadata_url,omitempty"` - MetadataXml *string `json:"metadata_xml,omitempty"` - NameIdFormat *CreateProviderBodyNameIdFormat `json:"name_id_format,omitempty"` - - // Type What type of provider will be created - Type CreateProviderBodyType `json:"type"` -} - -// CreateProviderBodyNameIdFormat defines model for CreateProviderBody.NameIdFormat. -type CreateProviderBodyNameIdFormat string - -// CreateProviderBodyType What type of provider will be created -type CreateProviderBodyType string - -// CreateProviderResponseOutput defines model for CreateProviderResponse_Output. -type CreateProviderResponseOutput struct { - CreatedAt *string `json:"created_at,omitempty"` - Domains *[]struct { - CreatedAt *string `json:"created_at,omitempty"` - Domain *string `json:"domain,omitempty"` - UpdatedAt *string `json:"updated_at,omitempty"` - } `json:"domains,omitempty"` - Id string `json:"id"` - Saml *struct { - AttributeMapping *struct { - Keys map[string]struct { - Array *bool `json:"array,omitempty"` - Default interface{} `json:"default,omitempty"` - Name *string `json:"name,omitempty"` - Names *[]string `json:"names,omitempty"` - } `json:"keys"` - } `json:"attribute_mapping,omitempty"` - EntityId string `json:"entity_id"` - MetadataUrl *string `json:"metadata_url,omitempty"` - MetadataXml *string `json:"metadata_xml,omitempty"` - NameIdFormat *string `json:"name_id_format,omitempty"` - } `json:"saml,omitempty"` - UpdatedAt *string `json:"updated_at,omitempty"` -} - -// CreateRoleBody defines model for CreateRoleBody. -type CreateRoleBody struct { - ReadOnly bool `json:"read_only"` -} - -// CreateRoleResponseOutput defines model for CreateRoleResponse_Output. -type CreateRoleResponseOutput struct { - Password string `json:"password"` - Role string `json:"role"` - TtlSeconds int64 `json:"ttl_seconds"` -} - -// CreateSecretBody defines model for CreateSecretBody. -type CreateSecretBody = []struct { - // Name Secret name must not start with the SUPABASE_ prefix. - Name string `json:"name"` - Value string `json:"value"` -} - -// CreateSigningKeyBody defines model for CreateSigningKeyBody. -type CreateSigningKeyBody struct { - Algorithm CreateSigningKeyBodyAlgorithm `json:"algorithm"` - PrivateJwk *CreateSigningKeyBody_PrivateJwk `json:"private_jwk,omitempty"` - Status *CreateSigningKeyBodyStatus `json:"status,omitempty"` -} - -// CreateSigningKeyBodyAlgorithm defines model for CreateSigningKeyBody.Algorithm. -type CreateSigningKeyBodyAlgorithm string - -// CreateSigningKeyBodyPrivateJwk0 defines model for . -type CreateSigningKeyBodyPrivateJwk0 struct { - Alg *CreateSigningKeyBodyPrivateJwk0Alg `json:"alg,omitempty"` - D string `json:"d"` - Dp string `json:"dp"` - Dq string `json:"dq"` - E CreateSigningKeyBodyPrivateJwk0E `json:"e"` - Ext *CreateSigningKeyBodyPrivateJwk0Ext `json:"ext,omitempty"` - KeyOps *[]CreateSigningKeyBodyPrivateJwk0KeyOps `json:"key_ops,omitempty"` - Kid *openapi_types.UUID `json:"kid,omitempty"` - Kty CreateSigningKeyBodyPrivateJwk0Kty `json:"kty"` - N string `json:"n"` - P string `json:"p"` - Q string `json:"q"` - Qi string `json:"qi"` - Use *CreateSigningKeyBodyPrivateJwk0Use `json:"use,omitempty"` -} - -// CreateSigningKeyBodyPrivateJwk0Alg defines model for CreateSigningKeyBody.PrivateJwk.0.Alg. -type CreateSigningKeyBodyPrivateJwk0Alg string - -// CreateSigningKeyBodyPrivateJwk0E defines model for CreateSigningKeyBody.PrivateJwk.0.E. -type CreateSigningKeyBodyPrivateJwk0E string - -// CreateSigningKeyBodyPrivateJwk0Ext defines model for CreateSigningKeyBody.PrivateJwk.0.Ext. -type CreateSigningKeyBodyPrivateJwk0Ext bool - -// CreateSigningKeyBodyPrivateJwk0KeyOps defines model for CreateSigningKeyBody.PrivateJwk.0.KeyOps. -type CreateSigningKeyBodyPrivateJwk0KeyOps string - -// CreateSigningKeyBodyPrivateJwk0Kty defines model for CreateSigningKeyBody.PrivateJwk.0.Kty. -type CreateSigningKeyBodyPrivateJwk0Kty string - -// CreateSigningKeyBodyPrivateJwk0Use defines model for CreateSigningKeyBody.PrivateJwk.0.Use. -type CreateSigningKeyBodyPrivateJwk0Use string - -// CreateSigningKeyBodyPrivateJwk1 defines model for . -type CreateSigningKeyBodyPrivateJwk1 struct { - Alg *CreateSigningKeyBodyPrivateJwk1Alg `json:"alg,omitempty"` - Crv CreateSigningKeyBodyPrivateJwk1Crv `json:"crv"` - D string `json:"d"` - Ext *CreateSigningKeyBodyPrivateJwk1Ext `json:"ext,omitempty"` - KeyOps *[]CreateSigningKeyBodyPrivateJwk1KeyOps `json:"key_ops,omitempty"` - Kid *openapi_types.UUID `json:"kid,omitempty"` - Kty CreateSigningKeyBodyPrivateJwk1Kty `json:"kty"` - Use *CreateSigningKeyBodyPrivateJwk1Use `json:"use,omitempty"` - X string `json:"x"` - Y string `json:"y"` -} - -// CreateSigningKeyBodyPrivateJwk1Alg defines model for CreateSigningKeyBody.PrivateJwk.1.Alg. -type CreateSigningKeyBodyPrivateJwk1Alg string - -// CreateSigningKeyBodyPrivateJwk1Crv defines model for CreateSigningKeyBody.PrivateJwk.1.Crv. -type CreateSigningKeyBodyPrivateJwk1Crv string - -// CreateSigningKeyBodyPrivateJwk1Ext defines model for CreateSigningKeyBody.PrivateJwk.1.Ext. -type CreateSigningKeyBodyPrivateJwk1Ext bool - -// CreateSigningKeyBodyPrivateJwk1KeyOps defines model for CreateSigningKeyBody.PrivateJwk.1.KeyOps. -type CreateSigningKeyBodyPrivateJwk1KeyOps string - -// CreateSigningKeyBodyPrivateJwk1Kty defines model for CreateSigningKeyBody.PrivateJwk.1.Kty. -type CreateSigningKeyBodyPrivateJwk1Kty string - -// CreateSigningKeyBodyPrivateJwk1Use defines model for CreateSigningKeyBody.PrivateJwk.1.Use. -type CreateSigningKeyBodyPrivateJwk1Use string - -// CreateSigningKeyBodyPrivateJwk2 defines model for . -type CreateSigningKeyBodyPrivateJwk2 struct { - Alg *CreateSigningKeyBodyPrivateJwk2Alg `json:"alg,omitempty"` - Crv CreateSigningKeyBodyPrivateJwk2Crv `json:"crv"` - D string `json:"d"` - Ext *CreateSigningKeyBodyPrivateJwk2Ext `json:"ext,omitempty"` - KeyOps *[]CreateSigningKeyBodyPrivateJwk2KeyOps `json:"key_ops,omitempty"` - Kid *openapi_types.UUID `json:"kid,omitempty"` - Kty CreateSigningKeyBodyPrivateJwk2Kty `json:"kty"` - Use *CreateSigningKeyBodyPrivateJwk2Use `json:"use,omitempty"` - X string `json:"x"` -} - -// CreateSigningKeyBodyPrivateJwk2Alg defines model for CreateSigningKeyBody.PrivateJwk.2.Alg. -type CreateSigningKeyBodyPrivateJwk2Alg string - -// CreateSigningKeyBodyPrivateJwk2Crv defines model for CreateSigningKeyBody.PrivateJwk.2.Crv. -type CreateSigningKeyBodyPrivateJwk2Crv string - -// CreateSigningKeyBodyPrivateJwk2Ext defines model for CreateSigningKeyBody.PrivateJwk.2.Ext. -type CreateSigningKeyBodyPrivateJwk2Ext bool - -// CreateSigningKeyBodyPrivateJwk2KeyOps defines model for CreateSigningKeyBody.PrivateJwk.2.KeyOps. -type CreateSigningKeyBodyPrivateJwk2KeyOps string - -// CreateSigningKeyBodyPrivateJwk2Kty defines model for CreateSigningKeyBody.PrivateJwk.2.Kty. -type CreateSigningKeyBodyPrivateJwk2Kty string - -// CreateSigningKeyBodyPrivateJwk2Use defines model for CreateSigningKeyBody.PrivateJwk.2.Use. -type CreateSigningKeyBodyPrivateJwk2Use string - -// CreateSigningKeyBodyPrivateJwk3 defines model for . -type CreateSigningKeyBodyPrivateJwk3 struct { - Alg *CreateSigningKeyBodyPrivateJwk3Alg `json:"alg,omitempty"` - Ext *CreateSigningKeyBodyPrivateJwk3Ext `json:"ext,omitempty"` - K string `json:"k"` - KeyOps *[]CreateSigningKeyBodyPrivateJwk3KeyOps `json:"key_ops,omitempty"` - Kid *openapi_types.UUID `json:"kid,omitempty"` - Kty CreateSigningKeyBodyPrivateJwk3Kty `json:"kty"` - Use *CreateSigningKeyBodyPrivateJwk3Use `json:"use,omitempty"` -} - -// CreateSigningKeyBodyPrivateJwk3Alg defines model for CreateSigningKeyBody.PrivateJwk.3.Alg. -type CreateSigningKeyBodyPrivateJwk3Alg string - -// CreateSigningKeyBodyPrivateJwk3Ext defines model for CreateSigningKeyBody.PrivateJwk.3.Ext. -type CreateSigningKeyBodyPrivateJwk3Ext bool - -// CreateSigningKeyBodyPrivateJwk3KeyOps defines model for CreateSigningKeyBody.PrivateJwk.3.KeyOps. -type CreateSigningKeyBodyPrivateJwk3KeyOps string - -// CreateSigningKeyBodyPrivateJwk3Kty defines model for CreateSigningKeyBody.PrivateJwk.3.Kty. -type CreateSigningKeyBodyPrivateJwk3Kty string - -// CreateSigningKeyBodyPrivateJwk3Use defines model for CreateSigningKeyBody.PrivateJwk.3.Use. -type CreateSigningKeyBodyPrivateJwk3Use string - -// CreateSigningKeyBody_PrivateJwk defines model for CreateSigningKeyBody.PrivateJwk. -type CreateSigningKeyBody_PrivateJwk struct { - union json.RawMessage -} - -// CreateSigningKeyBodyStatus defines model for CreateSigningKeyBody.Status. -type CreateSigningKeyBodyStatus string - -// CreateThirdPartyAuthBody defines model for CreateThirdPartyAuthBody. -type CreateThirdPartyAuthBody struct { - CustomJwks interface{} `json:"custom_jwks,omitempty"` - JwksUrl *string `json:"jwks_url,omitempty"` - OidcIssuerUrl *string `json:"oidc_issuer_url,omitempty"` -} - -// DatabaseUpgradeStatusResponseOutput defines model for DatabaseUpgradeStatusResponse_Output. -type DatabaseUpgradeStatusResponseOutput struct { - DatabaseUpgradeStatus nullable.Nullable[struct { - Error *DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError `json:"error,omitempty"` - InitiatedAt string `json:"initiated_at"` - LatestStatusAt string `json:"latest_status_at"` - Progress *DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress `json:"progress,omitempty"` - Status float32 `json:"status"` - TargetVersion string `json:"target_version"` - }] `json:"databaseUpgradeStatus"` -} - -// DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError defines model for DatabaseUpgradeStatusResponseOutput.DatabaseUpgradeStatus.Error. -type DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusError string - -// DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress defines model for DatabaseUpgradeStatusResponseOutput.DatabaseUpgradeStatus.Progress. -type DatabaseUpgradeStatusResponseOutputDatabaseUpgradeStatusProgress string - -// DeleteProviderResponseOutput defines model for DeleteProviderResponse_Output. -type DeleteProviderResponseOutput struct { - CreatedAt *string `json:"created_at,omitempty"` - Domains *[]struct { - CreatedAt *string `json:"created_at,omitempty"` - Domain *string `json:"domain,omitempty"` - UpdatedAt *string `json:"updated_at,omitempty"` - } `json:"domains,omitempty"` - Id string `json:"id"` - Saml *struct { - AttributeMapping *struct { - Keys map[string]struct { - Array *bool `json:"array,omitempty"` - Default interface{} `json:"default,omitempty"` - Name *string `json:"name,omitempty"` - Names *[]string `json:"names,omitempty"` - } `json:"keys"` - } `json:"attribute_mapping,omitempty"` - EntityId string `json:"entity_id"` - MetadataUrl *string `json:"metadata_url,omitempty"` - MetadataXml *string `json:"metadata_xml,omitempty"` - NameIdFormat *string `json:"name_id_format,omitempty"` - } `json:"saml,omitempty"` - UpdatedAt *string `json:"updated_at,omitempty"` -} - -// DeleteRolesResponseOutput defines model for DeleteRolesResponse_Output. -type DeleteRolesResponseOutput struct { - Message DeleteRolesResponseOutputMessage `json:"message"` -} - -// DeleteRolesResponseOutputMessage defines model for DeleteRolesResponseOutput.Message. -type DeleteRolesResponseOutputMessage string - -// DeleteSecretsBody defines model for DeleteSecretsBody. -type DeleteSecretsBody = []string - -// DeployFunctionResponseOutput defines model for DeployFunctionResponse_Output. -type DeployFunctionResponseOutput struct { - CreatedAt *int64 `json:"created_at,omitempty"` - EntrypointPath *string `json:"entrypoint_path,omitempty"` - EzbrSha256 *string `json:"ezbr_sha256,omitempty"` - Id string `json:"id"` - ImportMap *bool `json:"import_map,omitempty"` - ImportMapPath *string `json:"import_map_path,omitempty"` - Name string `json:"name"` - Slug string `json:"slug"` - Status DeployFunctionResponseOutputStatus `json:"status"` - UpdatedAt *int64 `json:"updated_at,omitempty"` - VerifyJwt *bool `json:"verify_jwt,omitempty"` - Version int `json:"version"` -} - -// DeployFunctionResponseOutputStatus defines model for DeployFunctionResponseOutput.Status. -type DeployFunctionResponseOutputStatus string - -// DiskAutoscaleConfigOutput defines model for DiskAutoscaleConfig_Output. -type DiskAutoscaleConfigOutput struct { - // GrowthPercent Growth percentage for disk autoscaling - GrowthPercent nullable.Nullable[int] `json:"growth_percent"` - - // MaxSizeGb Maximum limit the disk size will grow to in GB - MaxSizeGb nullable.Nullable[int] `json:"max_size_gb"` - - // MinIncrementGb Minimum increment size for disk autoscaling in GB - MinIncrementGb nullable.Nullable[int] `json:"min_increment_gb"` -} - -// DiskRequestBody defines model for DiskRequestBody. -type DiskRequestBody struct { - Attributes DiskRequestBody_Attributes `json:"attributes"` -} - -// DiskRequestBodyAttributes0 defines model for . -type DiskRequestBodyAttributes0 struct { - Iops int `json:"iops"` - SizeGb int `json:"size_gb"` - ThroughputMibps *int `json:"throughput_mibps,omitempty"` - Type DiskRequestBodyAttributes0Type `json:"type"` -} - -// DiskRequestBodyAttributes0Type defines model for DiskRequestBody.Attributes.0.Type. -type DiskRequestBodyAttributes0Type string - -// DiskRequestBodyAttributes1 defines model for . -type DiskRequestBodyAttributes1 struct { - Iops int `json:"iops"` - SizeGb int `json:"size_gb"` - Type DiskRequestBodyAttributes1Type `json:"type"` -} - -// DiskRequestBodyAttributes1Type defines model for DiskRequestBody.Attributes.1.Type. -type DiskRequestBodyAttributes1Type string - -// DiskRequestBody_Attributes defines model for DiskRequestBody.Attributes. -type DiskRequestBody_Attributes struct { - union json.RawMessage -} - -// DiskResponseOutput defines model for DiskResponse_Output. -type DiskResponseOutput struct { - Attributes DiskResponseOutput_Attributes `json:"attributes"` - LastModifiedAt *string `json:"last_modified_at,omitempty"` -} - -// DiskResponseOutputAttributes0 defines model for . -type DiskResponseOutputAttributes0 struct { - Iops int `json:"iops"` - SizeGb int `json:"size_gb"` - ThroughputMibps *int `json:"throughput_mibps,omitempty"` - Type DiskResponseOutputAttributes0Type `json:"type"` -} - -// DiskResponseOutputAttributes0Type defines model for DiskResponseOutput.Attributes.0.Type. -type DiskResponseOutputAttributes0Type string - -// DiskResponseOutputAttributes1 defines model for . -type DiskResponseOutputAttributes1 struct { - Iops int `json:"iops"` - SizeGb int `json:"size_gb"` - Type DiskResponseOutputAttributes1Type `json:"type"` -} - -// DiskResponseOutputAttributes1Type defines model for DiskResponseOutput.Attributes.1.Type. -type DiskResponseOutputAttributes1Type string - -// DiskResponseOutput_Attributes defines model for DiskResponseOutput.Attributes. -type DiskResponseOutput_Attributes struct { - union json.RawMessage -} - -// DiskUtilMetricsResponseOutput defines model for DiskUtilMetricsResponse_Output. -type DiskUtilMetricsResponseOutput struct { - Metrics struct { - FsAvailBytes float32 `json:"fs_avail_bytes"` - FsSizeBytes float32 `json:"fs_size_bytes"` - FsUsedBytes float32 `json:"fs_used_bytes"` - } `json:"metrics"` - Timestamp string `json:"timestamp"` -} - -// FunctionDeployBody defines model for FunctionDeployBody. -type FunctionDeployBody struct { - File []openapi_types.File `json:"file"` - Metadata struct { - EntrypointPath string `json:"entrypoint_path"` - ImportMapPath *string `json:"import_map_path,omitempty"` - Name *string `json:"name,omitempty"` - StaticPatterns *[]string `json:"static_patterns,omitempty"` - VerifyJwt *bool `json:"verify_jwt,omitempty"` - } `json:"metadata"` -} - -// FunctionResponseOutput defines model for FunctionResponse_Output. -type FunctionResponseOutput struct { - CreatedAt int64 `json:"created_at"` - EntrypointPath *string `json:"entrypoint_path,omitempty"` - EzbrSha256 *string `json:"ezbr_sha256,omitempty"` - Id string `json:"id"` - ImportMap *bool `json:"import_map,omitempty"` - ImportMapPath *string `json:"import_map_path,omitempty"` - Name string `json:"name"` - Slug string `json:"slug"` - Status FunctionResponseOutputStatus `json:"status"` - UpdatedAt int64 `json:"updated_at"` - VerifyJwt *bool `json:"verify_jwt,omitempty"` - Version int `json:"version"` -} - -// FunctionResponseOutputStatus defines model for FunctionResponseOutput.Status. -type FunctionResponseOutputStatus string - -// FunctionSlugResponseOutput defines model for FunctionSlugResponse_Output. -type FunctionSlugResponseOutput struct { - CreatedAt int64 `json:"created_at"` - EntrypointPath *string `json:"entrypoint_path,omitempty"` - EzbrSha256 *string `json:"ezbr_sha256,omitempty"` - Id string `json:"id"` - ImportMap *bool `json:"import_map,omitempty"` - ImportMapPath *string `json:"import_map_path,omitempty"` - Name string `json:"name"` - Slug string `json:"slug"` - Status FunctionSlugResponseOutputStatus `json:"status"` - UpdatedAt int64 `json:"updated_at"` - VerifyJwt *bool `json:"verify_jwt,omitempty"` - Version int `json:"version"` -} - -// FunctionSlugResponseOutputStatus defines model for FunctionSlugResponseOutput.Status. -type FunctionSlugResponseOutputStatus string - -// GetProjectAvailableRestoreVersionsResponseOutput defines model for GetProjectAvailableRestoreVersionsResponse_Output. -type GetProjectAvailableRestoreVersionsResponseOutput struct { - AvailableVersions []struct { - PostgresEngine GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngine `json:"postgres_engine"` - ReleaseChannel GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannel `json:"release_channel"` - Version string `json:"version"` - } `json:"available_versions"` -} - -// GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngine defines model for GetProjectAvailableRestoreVersionsResponseOutput.AvailableVersions.PostgresEngine. -type GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsPostgresEngine string - -// GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannel defines model for GetProjectAvailableRestoreVersionsResponseOutput.AvailableVersions.ReleaseChannel. -type GetProjectAvailableRestoreVersionsResponseOutputAvailableVersionsReleaseChannel string - -// GetProjectDbMetadataResponseOutput defines model for GetProjectDbMetadataResponse_Output. -type GetProjectDbMetadataResponseOutput struct { - Databases []struct { - Name string `json:"name"` - Schemas []struct { - Name string `json:"name"` - } `json:"schemas"` - } `json:"databases"` -} - -// GetProviderResponseOutput defines model for GetProviderResponse_Output. -type GetProviderResponseOutput struct { - CreatedAt *string `json:"created_at,omitempty"` - Domains *[]struct { - CreatedAt *string `json:"created_at,omitempty"` - Domain *string `json:"domain,omitempty"` - UpdatedAt *string `json:"updated_at,omitempty"` - } `json:"domains,omitempty"` - Id string `json:"id"` - Saml *struct { - AttributeMapping *struct { - Keys map[string]struct { - Array *bool `json:"array,omitempty"` - Default interface{} `json:"default,omitempty"` - Name *string `json:"name,omitempty"` - Names *[]string `json:"names,omitempty"` - } `json:"keys"` - } `json:"attribute_mapping,omitempty"` - EntityId string `json:"entity_id"` - MetadataUrl *string `json:"metadata_url,omitempty"` - MetadataXml *string `json:"metadata_xml,omitempty"` - NameIdFormat *string `json:"name_id_format,omitempty"` - } `json:"saml,omitempty"` - UpdatedAt *string `json:"updated_at,omitempty"` -} - -// InviteExternalUserJitAccessBody defines model for InviteExternalUserJitAccessBody. -type InviteExternalUserJitAccessBody struct { - Email openapi_types.Email `json:"email"` - Roles []struct { - AllowedNetworks *struct { - AllowedCidrs *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs,omitempty"` - AllowedCidrsV6 *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs_v6,omitempty"` - } `json:"allowed_networks,omitempty"` - BranchesOnly *bool `json:"branches_only,omitempty"` - ExpiresAt *float32 `json:"expires_at,omitempty"` - Role string `json:"role"` - } `json:"roles"` -} - -// InviteExternalUserJitResponseOutput defines model for InviteExternalUserJitResponse_Output. -type InviteExternalUserJitResponseOutput struct { - Email openapi_types.Email `json:"email"` - InviteId openapi_types.UUID `json:"invite_id"` - UserRoles []struct { - AllowedNetworks *struct { - AllowedCidrs *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs,omitempty"` - AllowedCidrsV6 *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs_v6,omitempty"` - } `json:"allowed_networks,omitempty"` - BranchesOnly *bool `json:"branches_only,omitempty"` - ExpiresAt *float32 `json:"expires_at,omitempty"` - Role string `json:"role"` - } `json:"user_roles"` -} - -// JitAccessRequestRequest defines model for JitAccessRequestRequest. -type JitAccessRequestRequest struct { - State JitAccessRequestRequestState `json:"state"` -} - -// JitAccessRequestRequestState defines model for JitAccessRequestRequest.State. -type JitAccessRequestRequestState string - -// JitAccessResponseOutput defines model for JitAccessResponse_Output. -type JitAccessResponseOutput struct { - UserId *openapi_types.UUID `json:"user_id,omitempty"` - UserRoles []struct { - AllowedNetworks *struct { - AllowedCidrs *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs,omitempty"` - AllowedCidrsV6 *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs_v6,omitempty"` - } `json:"allowed_networks,omitempty"` - BranchesOnly *bool `json:"branches_only,omitempty"` - ExpiresAt *float32 `json:"expires_at,omitempty"` - Role string `json:"role"` - } `json:"user_roles"` -} - -// JitAuthorizeAccessResponseOutput defines model for JitAuthorizeAccessResponse_Output. -type JitAuthorizeAccessResponseOutput struct { - Act *string `json:"act,omitempty"` - UserId *openapi_types.UUID `json:"user_id,omitempty"` - UserRole struct { - AllowedNetworks *struct { - AllowedCidrs *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs,omitempty"` - AllowedCidrsV6 *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs_v6,omitempty"` - } `json:"allowed_networks,omitempty"` - BranchesOnly *bool `json:"branches_only,omitempty"` - ExpiresAt *float32 `json:"expires_at,omitempty"` - Role string `json:"role"` - } `json:"user_role"` -} - -// JitListAccessResponseOutput defines model for JitListAccessResponse_Output. -type JitListAccessResponseOutput struct { - Items []JitListAccessResponseOutput_Items_Item `json:"items"` -} - -// JitListAccessResponseOutputItems0 defines model for . -type JitListAccessResponseOutputItems0 struct { - ExpiresAt nullable.Nullable[string] `json:"expires_at"` - InviteId nullable.Nullable[openapi_types.UUID] `json:"invite_id"` - PrimaryEmail nullable.Nullable[string] `json:"primary_email"` - UserId openapi_types.UUID `json:"user_id"` - UserRoles []struct { - AllowedNetworks *struct { - AllowedCidrs *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs,omitempty"` - AllowedCidrsV6 *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs_v6,omitempty"` - } `json:"allowed_networks,omitempty"` - BranchesOnly *bool `json:"branches_only,omitempty"` - ExpiresAt *float32 `json:"expires_at,omitempty"` - Role string `json:"role"` - } `json:"user_roles"` -} - -// JitListAccessResponseOutputItems1 defines model for . -type JitListAccessResponseOutputItems1 struct { - ExpiresAt string `json:"expires_at"` - InviteId openapi_types.UUID `json:"invite_id"` - PrimaryEmail string `json:"primary_email"` - UserId nullable.Nullable[openapi_types.UUID] `json:"user_id"` - UserRoles []struct { - AllowedNetworks *struct { - AllowedCidrs *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs,omitempty"` - AllowedCidrsV6 *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs_v6,omitempty"` - } `json:"allowed_networks,omitempty"` - BranchesOnly *bool `json:"branches_only,omitempty"` - ExpiresAt *float32 `json:"expires_at,omitempty"` - Role string `json:"role"` - } `json:"user_roles"` -} - -// JitListAccessResponseOutput_Items_Item defines model for JitListAccessResponse_Output.items.Item. -type JitListAccessResponseOutput_Items_Item struct { - union json.RawMessage -} - -// JsonValueOutput Any JSON-serializable value -type JsonValueOutput struct { - union json.RawMessage -} - -// JsonValueOutput0 defines model for . -type JsonValueOutput0 struct { - union json.RawMessage -} - -// JsonValueOutput00 defines model for . -type JsonValueOutput00 = string - -// JsonValueOutput01 defines model for . -type JsonValueOutput01 = float32 - -// JsonValueOutput02 defines model for . -type JsonValueOutput02 = bool - -// JsonValueOutput1 defines model for . -type JsonValueOutput1 = []JsonValueOutput - -// JsonValueOutput2 defines model for . -type JsonValueOutput2 map[string]JsonValueOutput - -// LegacyApiKeysResponseOutput defines model for LegacyApiKeysResponse_Output. -type LegacyApiKeysResponseOutput struct { - Enabled bool `json:"enabled"` -} - -// ListActionRunResponseOutput defines model for ListActionRunResponse_Output. -type ListActionRunResponseOutput = []struct { - BranchId string `json:"branch_id"` - CheckRunId nullable.Nullable[float32] `json:"check_run_id"` - CreatedAt string `json:"created_at"` - GitConfig nullable.Nullable[interface{}] `json:"git_config,omitempty"` - Id string `json:"id"` - RunSteps []struct { - CreatedAt string `json:"created_at"` - Name ListActionRunResponseOutputRunStepsName `json:"name"` - Status ListActionRunResponseOutputRunStepsStatus `json:"status"` - UpdatedAt string `json:"updated_at"` - } `json:"run_steps"` - UpdatedAt string `json:"updated_at"` - Workdir nullable.Nullable[string] `json:"workdir"` -} - -// ListActionRunResponseOutputRunStepsName defines model for ListActionRunResponseOutput.RunSteps.Name. -type ListActionRunResponseOutputRunStepsName string - -// ListActionRunResponseOutputRunStepsStatus defines model for ListActionRunResponseOutput.RunSteps.Status. -type ListActionRunResponseOutputRunStepsStatus string - -// ListProjectAddonsResponseOutput defines model for ListProjectAddonsResponse_Output. -type ListProjectAddonsResponseOutput struct { - AvailableAddons []struct { - Name string `json:"name"` - Type ListProjectAddonsResponseOutputAvailableAddonsType `json:"type"` - Variants []struct { - Id ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id `json:"id"` - - // Meta Any JSON-serializable value - Meta *JsonValueOutput `json:"meta,omitempty"` - Name string `json:"name"` - Price struct { - Amount float32 `json:"amount"` - Description string `json:"description"` - Interval ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceInterval `json:"interval"` - Type ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceType `json:"type"` - } `json:"price"` - } `json:"variants"` - } `json:"available_addons"` - SelectedAddons []struct { - Type ListProjectAddonsResponseOutputSelectedAddonsType `json:"type"` - Variant struct { - Id ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id `json:"id"` - - // Meta Any JSON-serializable value - Meta *JsonValueOutput `json:"meta,omitempty"` - Name string `json:"name"` - Price struct { - Amount float32 `json:"amount"` - Description string `json:"description"` - Interval ListProjectAddonsResponseOutputSelectedAddonsVariantPriceInterval `json:"interval"` - Type ListProjectAddonsResponseOutputSelectedAddonsVariantPriceType `json:"type"` - } `json:"price"` - } `json:"variant"` - } `json:"selected_addons"` -} - -// ListProjectAddonsResponseOutputAvailableAddonsType defines model for ListProjectAddonsResponseOutput.AvailableAddons.Type. -type ListProjectAddonsResponseOutputAvailableAddonsType string - -// ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 defines model for ListProjectAddonsResponseOutput.AvailableAddons.Variants.Id.0. -type ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 string - -// ListProjectAddonsResponseOutputAvailableAddonsVariantsId1 defines model for ListProjectAddonsResponseOutput.AvailableAddons.Variants.Id.1. -type ListProjectAddonsResponseOutputAvailableAddonsVariantsId1 string - -// ListProjectAddonsResponseOutputAvailableAddonsVariantsId2 defines model for ListProjectAddonsResponseOutput.AvailableAddons.Variants.Id.2. -type ListProjectAddonsResponseOutputAvailableAddonsVariantsId2 string - -// ListProjectAddonsResponseOutputAvailableAddonsVariantsId3 defines model for ListProjectAddonsResponseOutput.AvailableAddons.Variants.Id.3. -type ListProjectAddonsResponseOutputAvailableAddonsVariantsId3 string - -// ListProjectAddonsResponseOutputAvailableAddonsVariantsId4 defines model for ListProjectAddonsResponseOutput.AvailableAddons.Variants.Id.4. -type ListProjectAddonsResponseOutputAvailableAddonsVariantsId4 string - -// ListProjectAddonsResponseOutputAvailableAddonsVariantsId5 defines model for ListProjectAddonsResponseOutput.AvailableAddons.Variants.Id.5. -type ListProjectAddonsResponseOutputAvailableAddonsVariantsId5 string - -// ListProjectAddonsResponseOutputAvailableAddonsVariantsId6 defines model for ListProjectAddonsResponseOutput.AvailableAddons.Variants.Id.6. -type ListProjectAddonsResponseOutputAvailableAddonsVariantsId6 string - -// ListProjectAddonsResponseOutputAvailableAddonsVariantsId7 defines model for ListProjectAddonsResponseOutput.AvailableAddons.Variants.Id.7. -type ListProjectAddonsResponseOutputAvailableAddonsVariantsId7 string - -// ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id defines model for ListProjectAddonsResponseOutput.AvailableAddons.Variants.Id. -type ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id struct { - union json.RawMessage -} - -// ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceInterval defines model for ListProjectAddonsResponseOutput.AvailableAddons.Variants.Price.Interval. -type ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceInterval string - -// ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceType defines model for ListProjectAddonsResponseOutput.AvailableAddons.Variants.Price.Type. -type ListProjectAddonsResponseOutputAvailableAddonsVariantsPriceType string - -// ListProjectAddonsResponseOutputSelectedAddonsType defines model for ListProjectAddonsResponseOutput.SelectedAddons.Type. -type ListProjectAddonsResponseOutputSelectedAddonsType string - -// ListProjectAddonsResponseOutputSelectedAddonsVariantId0 defines model for ListProjectAddonsResponseOutput.SelectedAddons.Variant.Id.0. -type ListProjectAddonsResponseOutputSelectedAddonsVariantId0 string - -// ListProjectAddonsResponseOutputSelectedAddonsVariantId1 defines model for ListProjectAddonsResponseOutput.SelectedAddons.Variant.Id.1. -type ListProjectAddonsResponseOutputSelectedAddonsVariantId1 string - -// ListProjectAddonsResponseOutputSelectedAddonsVariantId2 defines model for ListProjectAddonsResponseOutput.SelectedAddons.Variant.Id.2. -type ListProjectAddonsResponseOutputSelectedAddonsVariantId2 string - -// ListProjectAddonsResponseOutputSelectedAddonsVariantId3 defines model for ListProjectAddonsResponseOutput.SelectedAddons.Variant.Id.3. -type ListProjectAddonsResponseOutputSelectedAddonsVariantId3 string - -// ListProjectAddonsResponseOutputSelectedAddonsVariantId4 defines model for ListProjectAddonsResponseOutput.SelectedAddons.Variant.Id.4. -type ListProjectAddonsResponseOutputSelectedAddonsVariantId4 string - -// ListProjectAddonsResponseOutputSelectedAddonsVariantId5 defines model for ListProjectAddonsResponseOutput.SelectedAddons.Variant.Id.5. -type ListProjectAddonsResponseOutputSelectedAddonsVariantId5 string - -// ListProjectAddonsResponseOutputSelectedAddonsVariantId6 defines model for ListProjectAddonsResponseOutput.SelectedAddons.Variant.Id.6. -type ListProjectAddonsResponseOutputSelectedAddonsVariantId6 string - -// ListProjectAddonsResponseOutputSelectedAddonsVariantId7 defines model for ListProjectAddonsResponseOutput.SelectedAddons.Variant.Id.7. -type ListProjectAddonsResponseOutputSelectedAddonsVariantId7 string - -// ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id defines model for ListProjectAddonsResponseOutput.SelectedAddons.Variant.Id. -type ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id struct { - union json.RawMessage -} - -// ListProjectAddonsResponseOutputSelectedAddonsVariantPriceInterval defines model for ListProjectAddonsResponseOutput.SelectedAddons.Variant.Price.Interval. -type ListProjectAddonsResponseOutputSelectedAddonsVariantPriceInterval string - -// ListProjectAddonsResponseOutputSelectedAddonsVariantPriceType defines model for ListProjectAddonsResponseOutput.SelectedAddons.Variant.Price.Type. -type ListProjectAddonsResponseOutputSelectedAddonsVariantPriceType string - -// ListProvidersResponseOutput defines model for ListProvidersResponse_Output. -type ListProvidersResponseOutput struct { - Items []struct { - CreatedAt *string `json:"created_at,omitempty"` - Domains *[]struct { - CreatedAt *string `json:"created_at,omitempty"` - Domain *string `json:"domain,omitempty"` - UpdatedAt *string `json:"updated_at,omitempty"` - } `json:"domains,omitempty"` - Id string `json:"id"` - Saml *struct { - AttributeMapping *struct { - Keys map[string]struct { - Array *bool `json:"array,omitempty"` - Default interface{} `json:"default,omitempty"` - Name *string `json:"name,omitempty"` - Names *[]string `json:"names,omitempty"` - } `json:"keys"` - } `json:"attribute_mapping,omitempty"` - EntityId string `json:"entity_id"` - MetadataUrl *string `json:"metadata_url,omitempty"` - MetadataXml *string `json:"metadata_xml,omitempty"` - NameIdFormat *string `json:"name_id_format,omitempty"` - } `json:"saml,omitempty"` - UpdatedAt *string `json:"updated_at,omitempty"` - } `json:"items"` -} - -// NetworkBanResponseEnrichedOutput defines model for NetworkBanResponseEnriched_Output. -type NetworkBanResponseEnrichedOutput struct { - BannedIpv4Addresses []struct { - BannedAddress string `json:"banned_address"` - Identifier string `json:"identifier"` - Type string `json:"type"` - } `json:"banned_ipv4_addresses"` -} - -// NetworkBanResponseOutput defines model for NetworkBanResponse_Output. -type NetworkBanResponseOutput struct { - BannedIpv4Addresses []string `json:"banned_ipv4_addresses"` -} - -// NetworkRestrictionsPatchRequest defines model for NetworkRestrictionsPatchRequest. -type NetworkRestrictionsPatchRequest struct { - Add *struct { - DbAllowedCidrs *[]string `json:"dbAllowedCidrs,omitempty"` - DbAllowedCidrsV6 *[]string `json:"dbAllowedCidrsV6,omitempty"` - } `json:"add,omitempty"` - Remove *struct { - DbAllowedCidrs *[]string `json:"dbAllowedCidrs,omitempty"` - DbAllowedCidrsV6 *[]string `json:"dbAllowedCidrsV6,omitempty"` - } `json:"remove,omitempty"` -} - -// NetworkRestrictionsRequest defines model for NetworkRestrictionsRequest. -type NetworkRestrictionsRequest struct { - DbAllowedCidrs *[]string `json:"dbAllowedCidrs,omitempty"` - DbAllowedCidrsV6 *[]string `json:"dbAllowedCidrsV6,omitempty"` -} - -// NetworkRestrictionsResponseOutput defines model for NetworkRestrictionsResponse_Output. -type NetworkRestrictionsResponseOutput struct { - AppliedAt *time.Time `json:"applied_at,omitempty"` - - // Config At any given point in time, this is the config that the user has requested be applied to their project. The `status` field indicates if it has been applied to the project, or is pending. When an updated config is received, the applied config is moved to `old_config`. - Config struct { - DbAllowedCidrs *[]string `json:"dbAllowedCidrs,omitempty"` - DbAllowedCidrsV6 *[]string `json:"dbAllowedCidrsV6,omitempty"` - } `json:"config"` - Entitlement NetworkRestrictionsResponseOutputEntitlement `json:"entitlement"` - - // OldConfig Populated when a new config has been received, but not registered as successfully applied to a project. - OldConfig *struct { - DbAllowedCidrs *[]string `json:"dbAllowedCidrs,omitempty"` - DbAllowedCidrsV6 *[]string `json:"dbAllowedCidrsV6,omitempty"` - } `json:"old_config,omitempty"` - Status NetworkRestrictionsResponseOutputStatus `json:"status"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` -} - -// NetworkRestrictionsResponseOutputEntitlement defines model for NetworkRestrictionsResponseOutput.Entitlement. -type NetworkRestrictionsResponseOutputEntitlement string - -// NetworkRestrictionsResponseOutputStatus defines model for NetworkRestrictionsResponseOutput.Status. -type NetworkRestrictionsResponseOutputStatus string - -// NetworkRestrictionsV2ResponseOutput defines model for NetworkRestrictionsV2Response_Output. -type NetworkRestrictionsV2ResponseOutput struct { - AppliedAt *time.Time `json:"applied_at,omitempty"` - - // Config At any given point in time, this is the config that the user has requested be applied to their project. The `status` field indicates if it has been applied to the project, or is pending. When an updated config is received, the applied config is moved to `old_config`. - Config struct { - DbAllowedCidrs *[]struct { - Address string `json:"address"` - Type NetworkRestrictionsV2ResponseOutputConfigDbAllowedCidrsType `json:"type"` - } `json:"dbAllowedCidrs,omitempty"` - } `json:"config"` - Entitlement NetworkRestrictionsV2ResponseOutputEntitlement `json:"entitlement"` - - // OldConfig Populated when a new config has been received, but not registered as successfully applied to a project. - OldConfig *struct { - DbAllowedCidrs *[]struct { - Address string `json:"address"` - Type NetworkRestrictionsV2ResponseOutputOldConfigDbAllowedCidrsType `json:"type"` - } `json:"dbAllowedCidrs,omitempty"` - } `json:"old_config,omitempty"` - Status NetworkRestrictionsV2ResponseOutputStatus `json:"status"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` -} - -// NetworkRestrictionsV2ResponseOutputConfigDbAllowedCidrsType defines model for NetworkRestrictionsV2ResponseOutput.Config.DbAllowedCidrs.Type. -type NetworkRestrictionsV2ResponseOutputConfigDbAllowedCidrsType string - -// NetworkRestrictionsV2ResponseOutputEntitlement defines model for NetworkRestrictionsV2ResponseOutput.Entitlement. -type NetworkRestrictionsV2ResponseOutputEntitlement string - -// NetworkRestrictionsV2ResponseOutputOldConfigDbAllowedCidrsType defines model for NetworkRestrictionsV2ResponseOutput.OldConfig.DbAllowedCidrs.Type. -type NetworkRestrictionsV2ResponseOutputOldConfigDbAllowedCidrsType string - -// NetworkRestrictionsV2ResponseOutputStatus defines model for NetworkRestrictionsV2ResponseOutput.Status. -type NetworkRestrictionsV2ResponseOutputStatus string - -// OAuthRevokeTokenBody defines model for OAuthRevokeTokenBody. -type OAuthRevokeTokenBody struct { - ClientId openapi_types.UUID `json:"client_id"` - ClientSecret string `json:"client_secret"` - RefreshToken string `json:"refresh_token"` -} - -// OAuthTokenBody defines model for OAuthTokenBody. -type OAuthTokenBody struct { - // Assertion IDJAG assertion JWT for grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer. Beta - available on Team and Enterprise plans only. - Assertion *string `json:"assertion,omitempty"` - ClientId *openapi_types.UUID `json:"client_id,omitempty"` - ClientSecret *string `json:"client_secret,omitempty"` - Code *string `json:"code,omitempty"` - CodeVerifier *string `json:"code_verifier,omitempty"` - GrantType *OAuthTokenBodyGrantType `json:"grant_type,omitempty"` - RedirectUri *string `json:"redirect_uri,omitempty"` - RefreshToken *string `json:"refresh_token,omitempty"` - - // Resource Resource indicator for MCP (Model Context Protocol) clients - Resource *string `json:"resource,omitempty"` - Scope *string `json:"scope,omitempty"` -} - -// OAuthTokenBodyGrantType defines model for OAuthTokenBody.GrantType. -type OAuthTokenBodyGrantType string - -// OAuthTokenResponseOutput defines model for OAuthTokenResponse_Output. -type OAuthTokenResponseOutput struct { - AccessToken string `json:"access_token"` - ExpiresIn int `json:"expires_in"` - - // RefreshToken The `urn:ietf:params:oauth:grant-type:jwt-bearer` grant type issues access tokens only, no refresh token is returned and the token cannot be revoked via `/v1/oauth/revoke`. - RefreshToken *string `json:"refresh_token,omitempty"` - TokenType OAuthTokenResponseOutputTokenType `json:"token_type"` -} - -// OAuthTokenResponseOutputTokenType defines model for OAuthTokenResponseOutput.TokenType. -type OAuthTokenResponseOutputTokenType string - -// OrganizationProjectClaimResponseOutput defines model for OrganizationProjectClaimResponse_Output. -type OrganizationProjectClaimResponseOutput struct { - CreatedAt string `json:"created_at"` - CreatedBy openapi_types.UUID `json:"created_by"` - ExpiresAt string `json:"expires_at"` - Preview struct { - Errors []struct { - Key string `json:"key"` - Message string `json:"message"` - } `json:"errors"` - Info []struct { - Key string `json:"key"` - Message string `json:"message"` - } `json:"info"` - MembersExceedingFreeProjectLimit []struct { - Limit float32 `json:"limit"` - Name string `json:"name"` - } `json:"members_exceeding_free_project_limit"` - SourceSubscriptionPlan OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlan `json:"source_subscription_plan"` - TargetSubscriptionPlan nullable.Nullable[OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlan] `json:"target_subscription_plan"` - Valid bool `json:"valid"` - Warnings []struct { - Key string `json:"key"` - Message string `json:"message"` - } `json:"warnings"` - } `json:"preview"` - Project struct { - Name string `json:"name"` - Ref string `json:"ref"` - } `json:"project"` -} - -// OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlan defines model for OrganizationProjectClaimResponseOutput.Preview.SourceSubscriptionPlan. -type OrganizationProjectClaimResponseOutputPreviewSourceSubscriptionPlan string - -// OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlan defines model for OrganizationProjectClaimResponseOutput.Preview.TargetSubscriptionPlan. -type OrganizationProjectClaimResponseOutputPreviewTargetSubscriptionPlan string - -// OrganizationProjectsResponseOutput defines model for OrganizationProjectsResponse_Output. -type OrganizationProjectsResponseOutput struct { - Pagination struct { - // Count Total number of projects. Use this to calculate the total number of pages. - Count float32 `json:"count"` - - // Limit Maximum number of projects per page - Limit float32 `json:"limit"` - - // Offset Number of projects skipped in this response - Offset float32 `json:"offset"` - } `json:"pagination"` - Projects []struct { - CloudProvider string `json:"cloud_provider"` - Databases []struct { - CloudProvider string `json:"cloud_provider"` - DiskLastModifiedAt *string `json:"disk_last_modified_at,omitempty"` - DiskThroughputMbps *float32 `json:"disk_throughput_mbps,omitempty"` - DiskType *OrganizationProjectsResponseOutputProjectsDatabasesDiskType `json:"disk_type,omitempty"` - DiskVolumeSizeGb *float32 `json:"disk_volume_size_gb,omitempty"` - Identifier string `json:"identifier"` - InfraComputeSize *OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize `json:"infra_compute_size,omitempty"` - Region string `json:"region"` - Status OrganizationProjectsResponseOutputProjectsDatabasesStatus `json:"status"` - Type OrganizationProjectsResponseOutputProjectsDatabasesType `json:"type"` - } `json:"databases"` - InsertedAt string `json:"inserted_at"` - IsBranch bool `json:"is_branch"` - Name string `json:"name"` - Ref string `json:"ref"` - Region string `json:"region"` - Status OrganizationProjectsResponseOutputProjectsStatus `json:"status"` - } `json:"projects"` -} - -// OrganizationProjectsResponseOutputProjectsDatabasesDiskType defines model for OrganizationProjectsResponseOutput.Projects.Databases.DiskType. -type OrganizationProjectsResponseOutputProjectsDatabasesDiskType string - -// OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize defines model for OrganizationProjectsResponseOutput.Projects.Databases.InfraComputeSize. -type OrganizationProjectsResponseOutputProjectsDatabasesInfraComputeSize string - -// OrganizationProjectsResponseOutputProjectsDatabasesStatus defines model for OrganizationProjectsResponseOutput.Projects.Databases.Status. -type OrganizationProjectsResponseOutputProjectsDatabasesStatus string - -// OrganizationProjectsResponseOutputProjectsDatabasesType defines model for OrganizationProjectsResponseOutput.Projects.Databases.Type. -type OrganizationProjectsResponseOutputProjectsDatabasesType string - -// OrganizationProjectsResponseOutputProjectsStatus defines model for OrganizationProjectsResponseOutput.Projects.Status. -type OrganizationProjectsResponseOutputProjectsStatus string - -// OrganizationResponseV1Output defines model for OrganizationResponseV1_Output. -type OrganizationResponseV1Output struct { - // Id Deprecated: Use `slug` instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - Id string `json:"id"` - Name string `json:"name"` - - // Slug Organization slug - Slug string `json:"slug"` -} - -// PgsodiumConfigResponseOutput defines model for PgsodiumConfigResponse_Output. -type PgsodiumConfigResponseOutput struct { - // RootKey The pgsodium root key: 32 bytes, hex-encoded (64 characters). - RootKey string `json:"root_key"` -} - -// PlanGateErrorBody defines model for PlanGateErrorBody. -type PlanGateErrorBody struct { - // Error Present on entitlement denials. Other errors with this status code (validation, billing state) carry only message. - Error *struct { - // Code Machine-readable marker for plan-gated denials - Code PlanGateErrorBodyErrorCode `json:"code"` - - // Feature Entitlement feature key that failed the check - Feature string `json:"feature"` - - // UpgradeUrl Billing page URL for the organization, present when the org is resolvable - UpgradeUrl *string `json:"upgrade_url,omitempty"` - } `json:"error,omitempty"` - - // Message Human-readable explanation of the plan gate - Message string `json:"message"` -} - -// PlanGateErrorBodyErrorCode Machine-readable marker for plan-gated denials -type PlanGateErrorBodyErrorCode string - -// PostgresConfigResponseOutput defines model for PostgresConfigResponse_Output. -type PostgresConfigResponseOutput struct { - AutovacuumMaxWorkers *int `json:"autovacuum_max_workers,omitempty"` - AutovacuumWorkMem *string `json:"autovacuum_work_mem,omitempty"` - - // CheckpointTimeout Default unit: s - CheckpointTimeout *string `json:"checkpoint_timeout,omitempty"` - CronLogStatement *bool `json:"cron.log_statement,omitempty"` - EffectiveCacheSize *string `json:"effective_cache_size,omitempty"` - HotStandbyFeedback *bool `json:"hot_standby_feedback,omitempty"` - - // LogAutovacuumMinDuration Default unit: ms - LogAutovacuumMinDuration *string `json:"log_autovacuum_min_duration,omitempty"` - LogCheckpoints *bool `json:"log_checkpoints,omitempty"` - LogConnections *bool `json:"log_connections,omitempty"` - LogDisconnections *bool `json:"log_disconnections,omitempty"` - LogDuration *bool `json:"log_duration,omitempty"` - LogLockWaits *bool `json:"log_lock_waits,omitempty"` - LogRecoveryConflictWaits *bool `json:"log_recovery_conflict_waits,omitempty"` - LogReplicationCommands *bool `json:"log_replication_commands,omitempty"` - - // LogStartupProgressInterval Default unit: ms - LogStartupProgressInterval *string `json:"log_startup_progress_interval,omitempty"` - LogTempFiles *string `json:"log_temp_files,omitempty"` - LogicalDecodingWorkMem *string `json:"logical_decoding_work_mem,omitempty"` - MaintenanceWorkMem *string `json:"maintenance_work_mem,omitempty"` - MaxConnections *int `json:"max_connections,omitempty"` - MaxLocksPerTransaction *int `json:"max_locks_per_transaction,omitempty"` - MaxLogicalReplicationWorkers *int `json:"max_logical_replication_workers,omitempty"` - MaxParallelMaintenanceWorkers *int `json:"max_parallel_maintenance_workers,omitempty"` - MaxParallelWorkers *int `json:"max_parallel_workers,omitempty"` - MaxParallelWorkersPerGather *int `json:"max_parallel_workers_per_gather,omitempty"` - MaxReplicationSlots *int `json:"max_replication_slots,omitempty"` - MaxSlotWalKeepSize *string `json:"max_slot_wal_keep_size,omitempty"` - MaxStandbyArchiveDelay *string `json:"max_standby_archive_delay,omitempty"` - MaxStandbyStreamingDelay *string `json:"max_standby_streaming_delay,omitempty"` - MaxSyncWorkersPerSubscription *int `json:"max_sync_workers_per_subscription,omitempty"` - MaxWalSenders *int `json:"max_wal_senders,omitempty"` - MaxWalSize *string `json:"max_wal_size,omitempty"` - MaxWorkerProcesses *int `json:"max_worker_processes,omitempty"` - PgNetBatchSize *int `json:"pg_net.batch_size,omitempty"` - PgStatStatementsMax *int `json:"pg_stat_statements.max,omitempty"` - SessionReplicationRole *PostgresConfigResponseOutputSessionReplicationRole `json:"session_replication_role,omitempty"` - SharedBuffers *string `json:"shared_buffers,omitempty"` - - // StatementTimeout Default unit: ms - StatementTimeout *string `json:"statement_timeout,omitempty"` - TrackActivityQuerySize *string `json:"track_activity_query_size,omitempty"` - TrackCommitTimestamp *bool `json:"track_commit_timestamp,omitempty"` - WalKeepSize *string `json:"wal_keep_size,omitempty"` - - // WalSenderTimeout Default unit: ms - WalSenderTimeout *string `json:"wal_sender_timeout,omitempty"` - WorkMem *string `json:"work_mem,omitempty"` -} - -// PostgresConfigResponseOutputSessionReplicationRole defines model for PostgresConfigResponseOutput.SessionReplicationRole. -type PostgresConfigResponseOutputSessionReplicationRole string - -// PostgrestConfigWithJWTSecretResponseOutput defines model for PostgrestConfigWithJWTSecretResponse_Output. -type PostgrestConfigWithJWTSecretResponseOutput struct { - DbExtraSearchPath string `json:"db_extra_search_path"` - - // DbPool If `null`, the value is automatically configured based on compute size. - DbPool nullable.Nullable[int] `json:"db_pool"` - - // DbPoolAcquisitionTimeout If `null`, the value is automatically configured to 10. - DbPoolAcquisitionTimeout nullable.Nullable[int] `json:"db_pool_acquisition_timeout"` - DbSchema string `json:"db_schema"` - JwtSecret *string `json:"jwt_secret,omitempty"` - MaxRows int `json:"max_rows"` -} - -// ProjectClaimTokenResponseOutput defines model for ProjectClaimTokenResponse_Output. -type ProjectClaimTokenResponseOutput struct { - CreatedAt string `json:"created_at"` - CreatedBy openapi_types.UUID `json:"created_by"` - ExpiresAt string `json:"expires_at"` - TokenAlias string `json:"token_alias"` -} - -// ProjectUpgradeEligibilityResponseOutput defines model for ProjectUpgradeEligibilityResponse_Output. -type ProjectUpgradeEligibilityResponseOutput struct { - CurrentAppVersion string `json:"current_app_version"` - CurrentAppVersionReleaseChannel ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannel `json:"current_app_version_release_channel"` - DurationEstimateHours float32 `json:"duration_estimate_hours"` - Eligible bool `json:"eligible"` - LatestAppVersion string `json:"latest_app_version"` - LegacyAuthCustomRoles []string `json:"legacy_auth_custom_roles"` - - // ObjectsToBeDropped Use validation_errors instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - ObjectsToBeDropped []string `json:"objects_to_be_dropped"` - TargetUpgradeVersions []struct { - AppVersion string `json:"app_version"` - PostgresVersion ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsPostgresVersion `json:"postgres_version"` - ReleaseChannel ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannel `json:"release_channel"` - } `json:"target_upgrade_versions"` - - // UnsupportedExtensions Use validation_errors instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - UnsupportedExtensions []string `json:"unsupported_extensions"` - - // UserDefinedObjectsInInternalSchemas Use validation_errors instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - UserDefinedObjectsInInternalSchemas []string `json:"user_defined_objects_in_internal_schemas"` - ValidationErrors []ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item `json:"validation_errors"` - Warnings []ProjectUpgradeEligibilityResponseOutput_Warnings_Item `json:"warnings"` -} - -// ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannel defines model for ProjectUpgradeEligibilityResponseOutput.CurrentAppVersionReleaseChannel. -type ProjectUpgradeEligibilityResponseOutputCurrentAppVersionReleaseChannel string - -// ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsPostgresVersion defines model for ProjectUpgradeEligibilityResponseOutput.TargetUpgradeVersions.PostgresVersion. -type ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsPostgresVersion string - -// ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannel defines model for ProjectUpgradeEligibilityResponseOutput.TargetUpgradeVersions.ReleaseChannel. -type ProjectUpgradeEligibilityResponseOutputTargetUpgradeVersionsReleaseChannel string - -// ProjectUpgradeEligibilityResponseOutputValidationErrors0 defines model for . -type ProjectUpgradeEligibilityResponseOutputValidationErrors0 struct { - Dependents []string `json:"dependents"` - Type ProjectUpgradeEligibilityResponseOutputValidationErrors0Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputValidationErrors0Type defines model for ProjectUpgradeEligibilityResponseOutput.ValidationErrors.0.Type. -type ProjectUpgradeEligibilityResponseOutputValidationErrors0Type string - -// ProjectUpgradeEligibilityResponseOutputValidationErrors1 defines model for . -type ProjectUpgradeEligibilityResponseOutputValidationErrors1 struct { - IndexName string `json:"index_name"` - SchemaName string `json:"schema_name"` - TableName string `json:"table_name"` - Type ProjectUpgradeEligibilityResponseOutputValidationErrors1Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputValidationErrors1Type defines model for ProjectUpgradeEligibilityResponseOutput.ValidationErrors.1.Type. -type ProjectUpgradeEligibilityResponseOutputValidationErrors1Type string - -// ProjectUpgradeEligibilityResponseOutputValidationErrors2 defines model for . -type ProjectUpgradeEligibilityResponseOutputValidationErrors2 struct { - FunctionName string `json:"function_name"` - LangName string `json:"lang_name"` - SchemaName string `json:"schema_name"` - Type ProjectUpgradeEligibilityResponseOutputValidationErrors2Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputValidationErrors2Type defines model for ProjectUpgradeEligibilityResponseOutput.ValidationErrors.2.Type. -type ProjectUpgradeEligibilityResponseOutputValidationErrors2Type string - -// ProjectUpgradeEligibilityResponseOutputValidationErrors3 defines model for . -type ProjectUpgradeEligibilityResponseOutputValidationErrors3 struct { - ExtensionName string `json:"extension_name"` - Type ProjectUpgradeEligibilityResponseOutputValidationErrors3Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputValidationErrors3Type defines model for ProjectUpgradeEligibilityResponseOutput.ValidationErrors.3.Type. -type ProjectUpgradeEligibilityResponseOutputValidationErrors3Type string - -// ProjectUpgradeEligibilityResponseOutputValidationErrors4 defines model for . -type ProjectUpgradeEligibilityResponseOutputValidationErrors4 struct { - FdwHandlerName string `json:"fdw_handler_name"` - FdwName string `json:"fdw_name"` - Type ProjectUpgradeEligibilityResponseOutputValidationErrors4Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputValidationErrors4Type defines model for ProjectUpgradeEligibilityResponseOutput.ValidationErrors.4.Type. -type ProjectUpgradeEligibilityResponseOutputValidationErrors4Type string - -// ProjectUpgradeEligibilityResponseOutputValidationErrors5 defines model for . -type ProjectUpgradeEligibilityResponseOutputValidationErrors5 struct { - SchemaName string `json:"schema_name"` - SequenceName string `json:"sequence_name"` - TableName string `json:"table_name"` - Type ProjectUpgradeEligibilityResponseOutputValidationErrors5Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputValidationErrors5Type defines model for ProjectUpgradeEligibilityResponseOutput.ValidationErrors.5.Type. -type ProjectUpgradeEligibilityResponseOutputValidationErrors5Type string - -// ProjectUpgradeEligibilityResponseOutputValidationErrors6 defines model for . -type ProjectUpgradeEligibilityResponseOutputValidationErrors6 struct { - ObjName string `json:"obj_name"` - ObjType ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType `json:"obj_type"` - SchemaName string `json:"schema_name"` - Type ProjectUpgradeEligibilityResponseOutputValidationErrors6Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0 defines model for ProjectUpgradeEligibilityResponseOutput.ValidationErrors.6.ObjType.0. -type ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0 string - -// ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1 defines model for ProjectUpgradeEligibilityResponseOutput.ValidationErrors.6.ObjType.1. -type ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1 string - -// ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType defines model for ProjectUpgradeEligibilityResponseOutput.ValidationErrors.6.ObjType. -type ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType struct { - union json.RawMessage -} - -// ProjectUpgradeEligibilityResponseOutputValidationErrors6Type defines model for ProjectUpgradeEligibilityResponseOutput.ValidationErrors.6.Type. -type ProjectUpgradeEligibilityResponseOutputValidationErrors6Type string - -// ProjectUpgradeEligibilityResponseOutputValidationErrors7 defines model for . -type ProjectUpgradeEligibilityResponseOutputValidationErrors7 struct { - SlotName string `json:"slot_name"` - Type ProjectUpgradeEligibilityResponseOutputValidationErrors7Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputValidationErrors7Type defines model for ProjectUpgradeEligibilityResponseOutput.ValidationErrors.7.Type. -type ProjectUpgradeEligibilityResponseOutputValidationErrors7Type string - -// ProjectUpgradeEligibilityResponseOutputValidationErrors8 defines model for . -type ProjectUpgradeEligibilityResponseOutputValidationErrors8 struct { - Type ProjectUpgradeEligibilityResponseOutputValidationErrors8Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputValidationErrors8Type defines model for ProjectUpgradeEligibilityResponseOutput.ValidationErrors.8.Type. -type ProjectUpgradeEligibilityResponseOutputValidationErrors8Type string - -// ProjectUpgradeEligibilityResponseOutputValidationErrors9 defines model for . -type ProjectUpgradeEligibilityResponseOutputValidationErrors9 struct { - Type ProjectUpgradeEligibilityResponseOutputValidationErrors9Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputValidationErrors9Type defines model for ProjectUpgradeEligibilityResponseOutput.ValidationErrors.9.Type. -type ProjectUpgradeEligibilityResponseOutputValidationErrors9Type string - -// ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item defines model for ProjectUpgradeEligibilityResponse_Output.validation_errors.Item. -type ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item struct { - union json.RawMessage -} - -// ProjectUpgradeEligibilityResponseOutputWarnings0 defines model for . -type ProjectUpgradeEligibilityResponseOutputWarnings0 struct { - Type ProjectUpgradeEligibilityResponseOutputWarnings0Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputWarnings0Type defines model for ProjectUpgradeEligibilityResponseOutput.Warnings.0.Type. -type ProjectUpgradeEligibilityResponseOutputWarnings0Type string - -// ProjectUpgradeEligibilityResponseOutputWarnings1 defines model for . -type ProjectUpgradeEligibilityResponseOutputWarnings1 struct { - Type ProjectUpgradeEligibilityResponseOutputWarnings1Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputWarnings1Type defines model for ProjectUpgradeEligibilityResponseOutput.Warnings.1.Type. -type ProjectUpgradeEligibilityResponseOutputWarnings1Type string - -// ProjectUpgradeEligibilityResponseOutputWarnings2 defines model for . -type ProjectUpgradeEligibilityResponseOutputWarnings2 struct { - Type ProjectUpgradeEligibilityResponseOutputWarnings2Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputWarnings2Type defines model for ProjectUpgradeEligibilityResponseOutput.Warnings.2.Type. -type ProjectUpgradeEligibilityResponseOutputWarnings2Type string - -// ProjectUpgradeEligibilityResponseOutputWarnings3 defines model for . -type ProjectUpgradeEligibilityResponseOutputWarnings3 struct { - Type ProjectUpgradeEligibilityResponseOutputWarnings3Type `json:"type"` -} - -// ProjectUpgradeEligibilityResponseOutputWarnings3Type defines model for ProjectUpgradeEligibilityResponseOutput.Warnings.3.Type. -type ProjectUpgradeEligibilityResponseOutputWarnings3Type string - -// ProjectUpgradeEligibilityResponseOutput_Warnings_Item defines model for ProjectUpgradeEligibilityResponse_Output.warnings.Item. -type ProjectUpgradeEligibilityResponseOutput_Warnings_Item struct { - union json.RawMessage -} - -// ProjectUpgradeInitiateResponseOutput defines model for ProjectUpgradeInitiateResponse_Output. -type ProjectUpgradeInitiateResponseOutput struct { - TrackingId string `json:"tracking_id"` -} - -// ReadOnlyStatusResponseOutput defines model for ReadOnlyStatusResponse_Output. -type ReadOnlyStatusResponseOutput struct { - Enabled bool `json:"enabled"` - OverrideActiveUntil string `json:"override_active_until"` - OverrideEnabled bool `json:"override_enabled"` -} - -// RealtimeConfigResponseOutput defines model for RealtimeConfigResponse_Output. -type RealtimeConfigResponseOutput struct { - // AdminSuspendedAt If set, the Realtime service has been suspended by an admin. - AdminSuspendedAt nullable.Nullable[time.Time] `json:"admin_suspended_at"` - - // ConnectionPool Sets connection pool size for Realtime Authorization - ConnectionPool nullable.Nullable[int] `json:"connection_pool"` - - // MaxBytesPerSecond Sets maximum number of bytes per second rate per channel limit - MaxBytesPerSecond nullable.Nullable[int] `json:"max_bytes_per_second"` - - // MaxChannelsPerClient Sets maximum number of channels per client rate limit - MaxChannelsPerClient nullable.Nullable[int] `json:"max_channels_per_client"` - - // MaxConcurrentUsers Sets maximum number of concurrent users rate limit - MaxConcurrentUsers nullable.Nullable[int] `json:"max_concurrent_users"` - - // MaxEventsPerSecond Sets maximum number of events per second rate per channel limit - MaxEventsPerSecond nullable.Nullable[int] `json:"max_events_per_second"` - - // MaxJoinsPerSecond Sets maximum number of joins per second rate limit - MaxJoinsPerSecond nullable.Nullable[int] `json:"max_joins_per_second"` - - // MaxPayloadSizeInKb Sets maximum number of payload size in KB rate limit - MaxPayloadSizeInKb nullable.Nullable[int] `json:"max_payload_size_in_kb"` - - // MaxPresenceEventsPerSecond Sets maximum number of presence events per second rate limit - MaxPresenceEventsPerSecond nullable.Nullable[int] `json:"max_presence_events_per_second"` - - // PostgresChangesPool Sets connection pool size used to create Postgres Changes subscriptions - PostgresChangesPool nullable.Nullable[int] `json:"postgres_changes_pool"` - - // PresenceEnabled Whether to enable presence - PresenceEnabled bool `json:"presence_enabled"` - - // PrivateOnly Whether to only allow private channels - PrivateOnly nullable.Nullable[bool] `json:"private_only"` - - // Suspend Disables the Realtime service for this project when true. Set to false to re-enable it. - Suspend nullable.Nullable[bool] `json:"suspend"` -} - -// RegionsInfoOutput defines model for RegionsInfo_Output. -type RegionsInfoOutput struct { - All struct { - SmartGroup []struct { - Code RegionsInfoOutputAllSmartGroupCode `json:"code"` - Name string `json:"name"` - Type RegionsInfoOutputAllSmartGroupType `json:"type"` - } `json:"smartGroup"` - Specific []struct { - Code RegionsInfoOutputAllSpecificCode `json:"code"` - Name string `json:"name"` - Provider RegionsInfoOutputAllSpecificProvider `json:"provider"` - Status *RegionsInfoOutputAllSpecificStatus `json:"status,omitempty"` - Type RegionsInfoOutputAllSpecificType `json:"type"` - } `json:"specific"` - } `json:"all"` - Recommendations struct { - SmartGroup struct { - Code RegionsInfoOutputRecommendationsSmartGroupCode `json:"code"` - Name string `json:"name"` - Type RegionsInfoOutputRecommendationsSmartGroupType `json:"type"` - } `json:"smartGroup"` - Specific []struct { - Code RegionsInfoOutputRecommendationsSpecificCode `json:"code"` - Name string `json:"name"` - Provider RegionsInfoOutputRecommendationsSpecificProvider `json:"provider"` - Status *RegionsInfoOutputRecommendationsSpecificStatus `json:"status,omitempty"` - Type RegionsInfoOutputRecommendationsSpecificType `json:"type"` - } `json:"specific"` - } `json:"recommendations"` -} - -// RegionsInfoOutputAllSmartGroupCode defines model for RegionsInfoOutput.All.SmartGroup.Code. -type RegionsInfoOutputAllSmartGroupCode string - -// RegionsInfoOutputAllSmartGroupType defines model for RegionsInfoOutput.All.SmartGroup.Type. -type RegionsInfoOutputAllSmartGroupType string - -// RegionsInfoOutputAllSpecificCode defines model for RegionsInfoOutput.All.Specific.Code. -type RegionsInfoOutputAllSpecificCode string - -// RegionsInfoOutputAllSpecificProvider defines model for RegionsInfoOutput.All.Specific.Provider. -type RegionsInfoOutputAllSpecificProvider string - -// RegionsInfoOutputAllSpecificStatus defines model for RegionsInfoOutput.All.Specific.Status. -type RegionsInfoOutputAllSpecificStatus string - -// RegionsInfoOutputAllSpecificType defines model for RegionsInfoOutput.All.Specific.Type. -type RegionsInfoOutputAllSpecificType string - -// RegionsInfoOutputRecommendationsSmartGroupCode defines model for RegionsInfoOutput.Recommendations.SmartGroup.Code. -type RegionsInfoOutputRecommendationsSmartGroupCode string - -// RegionsInfoOutputRecommendationsSmartGroupType defines model for RegionsInfoOutput.Recommendations.SmartGroup.Type. -type RegionsInfoOutputRecommendationsSmartGroupType string - -// RegionsInfoOutputRecommendationsSpecificCode defines model for RegionsInfoOutput.Recommendations.Specific.Code. -type RegionsInfoOutputRecommendationsSpecificCode string - -// RegionsInfoOutputRecommendationsSpecificProvider defines model for RegionsInfoOutput.Recommendations.Specific.Provider. -type RegionsInfoOutputRecommendationsSpecificProvider string - -// RegionsInfoOutputRecommendationsSpecificStatus defines model for RegionsInfoOutput.Recommendations.Specific.Status. -type RegionsInfoOutputRecommendationsSpecificStatus string - -// RegionsInfoOutputRecommendationsSpecificType defines model for RegionsInfoOutput.Recommendations.Specific.Type. -type RegionsInfoOutputRecommendationsSpecificType string - -// RemoveNetworkBanRequest defines model for RemoveNetworkBanRequest. -type RemoveNetworkBanRequest struct { - Identifier *string `json:"identifier,omitempty"` - - // Ipv4Addresses List of IP addresses to unban. - Ipv4Addresses []string `json:"ipv4_addresses"` - - // RequesterIp Include requester's public IP in the list of addresses to unban. - RequesterIp *bool `json:"requester_ip,omitempty"` -} - -// RemoveReadReplicaBody defines model for RemoveReadReplicaBody. -type RemoveReadReplicaBody struct { - DatabaseIdentifier string `json:"database_identifier"` -} - -// SecretResponseOutput defines model for SecretResponse_Output. -type SecretResponseOutput struct { - Name string `json:"name"` - UpdatedAt *string `json:"updated_at,omitempty"` - Value string `json:"value"` -} - -// SetUpReadReplicaBody defines model for SetUpReadReplicaBody. -type SetUpReadReplicaBody struct { - // ReadReplicaRegion Region you want your read replica to reside in - ReadReplicaRegion SetUpReadReplicaBodyReadReplicaRegion `json:"read_replica_region"` -} - -// SetUpReadReplicaBodyReadReplicaRegion Region you want your read replica to reside in -type SetUpReadReplicaBodyReadReplicaRegion string - -// SigningKeyResponseOutput defines model for SigningKeyResponse_Output. -type SigningKeyResponseOutput struct { - Algorithm SigningKeyResponseOutputAlgorithm `json:"algorithm"` - CreatedAt time.Time `json:"created_at"` - Id openapi_types.UUID `json:"id"` - PublicJwk nullable.Nullable[interface{}] `json:"public_jwk"` - Status SigningKeyResponseOutputStatus `json:"status"` - UpdatedAt time.Time `json:"updated_at"` -} - -// SigningKeyResponseOutputAlgorithm defines model for SigningKeyResponseOutput.Algorithm. -type SigningKeyResponseOutputAlgorithm string - -// SigningKeyResponseOutputStatus defines model for SigningKeyResponseOutput.Status. -type SigningKeyResponseOutputStatus string - -// SigningKeysResponseOutput defines model for SigningKeysResponse_Output. -type SigningKeysResponseOutput struct { - Keys []struct { - Algorithm SigningKeysResponseOutputKeysAlgorithm `json:"algorithm"` - CreatedAt time.Time `json:"created_at"` - Id openapi_types.UUID `json:"id"` - PublicJwk nullable.Nullable[interface{}] `json:"public_jwk"` - Status SigningKeysResponseOutputKeysStatus `json:"status"` - UpdatedAt time.Time `json:"updated_at"` - } `json:"keys"` -} - -// SigningKeysResponseOutputKeysAlgorithm defines model for SigningKeysResponseOutput.Keys.Algorithm. -type SigningKeysResponseOutputKeysAlgorithm string - -// SigningKeysResponseOutputKeysStatus defines model for SigningKeysResponseOutput.Keys.Status. -type SigningKeysResponseOutputKeysStatus string - -// SnippetListOutput defines model for SnippetList_Output. -type SnippetListOutput struct { - Cursor *string `json:"cursor,omitempty"` - Data []struct { - Description nullable.Nullable[string] `json:"description"` - Favorite bool `json:"favorite"` - Id string `json:"id"` - InsertedAt string `json:"inserted_at"` - Name string `json:"name"` - Owner struct { - Id float32 `json:"id"` - Username string `json:"username"` - } `json:"owner"` - Project struct { - Id float32 `json:"id"` - Name string `json:"name"` - } `json:"project"` - Type SnippetListOutputDataType `json:"type"` - UpdatedAt string `json:"updated_at"` - UpdatedBy struct { - Id float32 `json:"id"` - Username string `json:"username"` - } `json:"updated_by"` - Visibility SnippetListOutputDataVisibility `json:"visibility"` - } `json:"data"` -} - -// SnippetListOutputDataType defines model for SnippetListOutput.Data.Type. -type SnippetListOutputDataType string - -// SnippetListOutputDataVisibility defines model for SnippetListOutput.Data.Visibility. -type SnippetListOutputDataVisibility string - -// SnippetResponseOutput defines model for SnippetResponse_Output. -type SnippetResponseOutput struct { - Content struct { - // Favorite Deprecated: Rely on root-level favorite property instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - Favorite *bool `json:"favorite,omitempty"` - SchemaVersion *string `json:"schema_version,omitempty"` - Sql string `json:"sql"` - } `json:"content"` - Description nullable.Nullable[string] `json:"description"` - Favorite bool `json:"favorite"` - Id string `json:"id"` - InsertedAt string `json:"inserted_at"` - Name string `json:"name"` - Owner struct { - Id float32 `json:"id"` - Username string `json:"username"` - } `json:"owner"` - Project struct { - Id float32 `json:"id"` - Name string `json:"name"` - } `json:"project"` - Type SnippetResponseOutputType `json:"type"` - UpdatedAt string `json:"updated_at"` - UpdatedBy struct { - Id float32 `json:"id"` - Username string `json:"username"` - } `json:"updated_by"` - Visibility SnippetResponseOutputVisibility `json:"visibility"` -} - -// SnippetResponseOutputType defines model for SnippetResponseOutput.Type. -type SnippetResponseOutputType string - -// SnippetResponseOutputVisibility defines model for SnippetResponseOutput.Visibility. -type SnippetResponseOutputVisibility string - -// SslEnforcementRequest defines model for SslEnforcementRequest. -type SslEnforcementRequest struct { - RequestedConfig struct { - Database bool `json:"database"` - } `json:"requestedConfig"` -} - -// SslEnforcementResponseOutput defines model for SslEnforcementResponse_Output. -type SslEnforcementResponseOutput struct { - AppliedSuccessfully bool `json:"appliedSuccessfully"` - CurrentConfig struct { - Database bool `json:"database"` - } `json:"currentConfig"` -} - -// StorageConfigResponseOutput defines model for StorageConfigResponse_Output. -type StorageConfigResponseOutput struct { - Capabilities struct { - IcebergCatalog bool `json:"iceberg_catalog"` - ListV2 bool `json:"list_v2"` - ObjectVersioning bool `json:"object_versioning"` - } `json:"capabilities"` - External struct { - UpstreamTarget StorageConfigResponseOutputExternalUpstreamTarget `json:"upstreamTarget"` - } `json:"external"` - Features struct { - IcebergCatalog struct { - Enabled bool `json:"enabled"` - MaxCatalogs int `json:"maxCatalogs"` - MaxNamespaces int `json:"maxNamespaces"` - MaxTables int `json:"maxTables"` - } `json:"icebergCatalog"` - ImageTransformation struct { - Enabled bool `json:"enabled"` - } `json:"imageTransformation"` - PurgeCache struct { - Enabled bool `json:"enabled"` - } `json:"purgeCache"` - S3Protocol struct { - Enabled bool `json:"enabled"` - } `json:"s3Protocol"` - VectorBuckets struct { - Enabled bool `json:"enabled"` - MaxBuckets int `json:"maxBuckets"` - MaxIndexes int `json:"maxIndexes"` - } `json:"vectorBuckets"` - } `json:"features"` - FileSizeLimit int64 `json:"fileSizeLimit"` - MigrationVersion nullable.Nullable[string] `json:"migrationVersion"` -} - -// StorageConfigResponseOutputExternalUpstreamTarget defines model for StorageConfigResponseOutput.External.UpstreamTarget. -type StorageConfigResponseOutputExternalUpstreamTarget string - -// StreamableFile defines model for StreamableFile. -type StreamableFile = map[string]interface{} - -// SubdomainAvailabilityResponseOutput defines model for SubdomainAvailabilityResponse_Output. -type SubdomainAvailabilityResponseOutput struct { - Available bool `json:"available"` -} - -// SupavisorConfigResponseOutput defines model for SupavisorConfigResponse_Output. -type SupavisorConfigResponseOutput struct { - ConnectionString string `json:"connection_string"` - DatabaseType SupavisorConfigResponseOutputDatabaseType `json:"database_type"` - DbHost string `json:"db_host"` - DbName string `json:"db_name"` - DbPort int `json:"db_port"` - DbUser string `json:"db_user"` - DefaultPoolSize nullable.Nullable[int] `json:"default_pool_size"` - Identifier string `json:"identifier"` - IsUsingScramAuth bool `json:"is_using_scram_auth"` - MaxClientConn nullable.Nullable[int] `json:"max_client_conn"` - PoolMode SupavisorConfigResponseOutputPoolMode `json:"pool_mode"` -} - -// SupavisorConfigResponseOutputDatabaseType defines model for SupavisorConfigResponseOutput.DatabaseType. -type SupavisorConfigResponseOutputDatabaseType string - -// SupavisorConfigResponseOutputPoolMode defines model for SupavisorConfigResponseOutput.PoolMode. -type SupavisorConfigResponseOutputPoolMode string - -// ThirdPartyAuthOutput defines model for ThirdPartyAuth_Output. -type ThirdPartyAuthOutput struct { - CustomJwks nullable.Nullable[interface{}] `json:"custom_jwks,omitempty"` - Id openapi_types.UUID `json:"id"` - InsertedAt string `json:"inserted_at"` - JwksUrl nullable.Nullable[string] `json:"jwks_url,omitempty"` - OidcIssuerUrl nullable.Nullable[string] `json:"oidc_issuer_url,omitempty"` - ResolvedAt nullable.Nullable[string] `json:"resolved_at,omitempty"` - ResolvedJwks nullable.Nullable[interface{}] `json:"resolved_jwks,omitempty"` - Type string `json:"type"` - UpdatedAt string `json:"updated_at"` -} - -// TypescriptResponseOutput defines model for TypescriptResponse_Output. -type TypescriptResponseOutput struct { - Types string `json:"types"` -} - -// UpdateApiKeyBody defines model for UpdateApiKeyBody. -type UpdateApiKeyBody struct { - Description nullable.Nullable[string] `json:"description,omitempty"` - Name *string `json:"name,omitempty"` - SecretJwtTemplate nullable.Nullable[map[string]interface{}] `json:"secret_jwt_template,omitempty"` -} - -// UpdateAuthConfigBody defines model for UpdateAuthConfigBody. -type UpdateAuthConfigBody struct { - ApiMaxRequestDuration nullable.Nullable[int] `json:"api_max_request_duration,omitempty"` - CustomOauthEnabled *bool `json:"custom_oauth_enabled,omitempty"` - DbMaxPoolSize nullable.Nullable[int] `json:"db_max_pool_size,omitempty"` - DbMaxPoolSizeUnit nullable.Nullable[UpdateAuthConfigBodyDbMaxPoolSizeUnit] `json:"db_max_pool_size_unit,omitempty"` - DisableSignup nullable.Nullable[bool] `json:"disable_signup,omitempty"` - ExternalAnonymousUsersEnabled nullable.Nullable[bool] `json:"external_anonymous_users_enabled,omitempty"` - ExternalAppleAdditionalClientIds nullable.Nullable[string] `json:"external_apple_additional_client_ids,omitempty"` - ExternalAppleClientId nullable.Nullable[string] `json:"external_apple_client_id,omitempty"` - ExternalAppleEmailOptional nullable.Nullable[bool] `json:"external_apple_email_optional,omitempty"` - ExternalAppleEnabled nullable.Nullable[bool] `json:"external_apple_enabled,omitempty"` - ExternalAppleSecret nullable.Nullable[string] `json:"external_apple_secret,omitempty"` - ExternalAzureClientId nullable.Nullable[string] `json:"external_azure_client_id,omitempty"` - ExternalAzureEmailOptional nullable.Nullable[bool] `json:"external_azure_email_optional,omitempty"` - ExternalAzureEnabled nullable.Nullable[bool] `json:"external_azure_enabled,omitempty"` - ExternalAzureSecret nullable.Nullable[string] `json:"external_azure_secret,omitempty"` - ExternalAzureUrl nullable.Nullable[string] `json:"external_azure_url,omitempty"` - ExternalBitbucketClientId nullable.Nullable[string] `json:"external_bitbucket_client_id,omitempty"` - ExternalBitbucketEmailOptional nullable.Nullable[bool] `json:"external_bitbucket_email_optional,omitempty"` - ExternalBitbucketEnabled nullable.Nullable[bool] `json:"external_bitbucket_enabled,omitempty"` - ExternalBitbucketSecret nullable.Nullable[string] `json:"external_bitbucket_secret,omitempty"` - ExternalDiscordClientId nullable.Nullable[string] `json:"external_discord_client_id,omitempty"` - ExternalDiscordEmailOptional nullable.Nullable[bool] `json:"external_discord_email_optional,omitempty"` - ExternalDiscordEnabled nullable.Nullable[bool] `json:"external_discord_enabled,omitempty"` - ExternalDiscordSecret nullable.Nullable[string] `json:"external_discord_secret,omitempty"` - ExternalEmailEnabled nullable.Nullable[bool] `json:"external_email_enabled,omitempty"` - ExternalFacebookClientId nullable.Nullable[string] `json:"external_facebook_client_id,omitempty"` - ExternalFacebookEmailOptional nullable.Nullable[bool] `json:"external_facebook_email_optional,omitempty"` - ExternalFacebookEnabled nullable.Nullable[bool] `json:"external_facebook_enabled,omitempty"` - ExternalFacebookSecret nullable.Nullable[string] `json:"external_facebook_secret,omitempty"` - ExternalFigmaClientId nullable.Nullable[string] `json:"external_figma_client_id,omitempty"` - ExternalFigmaEmailOptional nullable.Nullable[bool] `json:"external_figma_email_optional,omitempty"` - ExternalFigmaEnabled nullable.Nullable[bool] `json:"external_figma_enabled,omitempty"` - ExternalFigmaSecret nullable.Nullable[string] `json:"external_figma_secret,omitempty"` - ExternalGithubClientId nullable.Nullable[string] `json:"external_github_client_id,omitempty"` - ExternalGithubEmailOptional nullable.Nullable[bool] `json:"external_github_email_optional,omitempty"` - ExternalGithubEnabled nullable.Nullable[bool] `json:"external_github_enabled,omitempty"` - ExternalGithubSecret nullable.Nullable[string] `json:"external_github_secret,omitempty"` - ExternalGitlabClientId nullable.Nullable[string] `json:"external_gitlab_client_id,omitempty"` - ExternalGitlabEmailOptional nullable.Nullable[bool] `json:"external_gitlab_email_optional,omitempty"` - ExternalGitlabEnabled nullable.Nullable[bool] `json:"external_gitlab_enabled,omitempty"` - ExternalGitlabSecret nullable.Nullable[string] `json:"external_gitlab_secret,omitempty"` - ExternalGitlabUrl nullable.Nullable[string] `json:"external_gitlab_url,omitempty"` - ExternalGoogleAdditionalClientIds nullable.Nullable[string] `json:"external_google_additional_client_ids,omitempty"` - ExternalGoogleClientId nullable.Nullable[string] `json:"external_google_client_id,omitempty"` - ExternalGoogleEmailOptional nullable.Nullable[bool] `json:"external_google_email_optional,omitempty"` - ExternalGoogleEnabled nullable.Nullable[bool] `json:"external_google_enabled,omitempty"` - ExternalGoogleSecret nullable.Nullable[string] `json:"external_google_secret,omitempty"` - ExternalGoogleSkipNonceCheck nullable.Nullable[bool] `json:"external_google_skip_nonce_check,omitempty"` - ExternalKakaoClientId nullable.Nullable[string] `json:"external_kakao_client_id,omitempty"` - ExternalKakaoEmailOptional nullable.Nullable[bool] `json:"external_kakao_email_optional,omitempty"` - ExternalKakaoEnabled nullable.Nullable[bool] `json:"external_kakao_enabled,omitempty"` - ExternalKakaoSecret nullable.Nullable[string] `json:"external_kakao_secret,omitempty"` - ExternalKeycloakClientId nullable.Nullable[string] `json:"external_keycloak_client_id,omitempty"` - ExternalKeycloakEmailOptional nullable.Nullable[bool] `json:"external_keycloak_email_optional,omitempty"` - ExternalKeycloakEnabled nullable.Nullable[bool] `json:"external_keycloak_enabled,omitempty"` - ExternalKeycloakSecret nullable.Nullable[string] `json:"external_keycloak_secret,omitempty"` - ExternalKeycloakUrl nullable.Nullable[string] `json:"external_keycloak_url,omitempty"` - ExternalLinkedinOidcClientId nullable.Nullable[string] `json:"external_linkedin_oidc_client_id,omitempty"` - ExternalLinkedinOidcEmailOptional nullable.Nullable[bool] `json:"external_linkedin_oidc_email_optional,omitempty"` - ExternalLinkedinOidcEnabled nullable.Nullable[bool] `json:"external_linkedin_oidc_enabled,omitempty"` - ExternalLinkedinOidcSecret nullable.Nullable[string] `json:"external_linkedin_oidc_secret,omitempty"` - ExternalNotionClientId nullable.Nullable[string] `json:"external_notion_client_id,omitempty"` - ExternalNotionEmailOptional nullable.Nullable[bool] `json:"external_notion_email_optional,omitempty"` - ExternalNotionEnabled nullable.Nullable[bool] `json:"external_notion_enabled,omitempty"` - ExternalNotionSecret nullable.Nullable[string] `json:"external_notion_secret,omitempty"` - ExternalPhoneEnabled nullable.Nullable[bool] `json:"external_phone_enabled,omitempty"` - ExternalSlackClientId nullable.Nullable[string] `json:"external_slack_client_id,omitempty"` - ExternalSlackEmailOptional nullable.Nullable[bool] `json:"external_slack_email_optional,omitempty"` - ExternalSlackEnabled nullable.Nullable[bool] `json:"external_slack_enabled,omitempty"` - ExternalSlackOidcClientId nullable.Nullable[string] `json:"external_slack_oidc_client_id,omitempty"` - ExternalSlackOidcEmailOptional nullable.Nullable[bool] `json:"external_slack_oidc_email_optional,omitempty"` - ExternalSlackOidcEnabled nullable.Nullable[bool] `json:"external_slack_oidc_enabled,omitempty"` - ExternalSlackOidcSecret nullable.Nullable[string] `json:"external_slack_oidc_secret,omitempty"` - ExternalSlackSecret nullable.Nullable[string] `json:"external_slack_secret,omitempty"` - ExternalSpotifyClientId nullable.Nullable[string] `json:"external_spotify_client_id,omitempty"` - ExternalSpotifyEmailOptional nullable.Nullable[bool] `json:"external_spotify_email_optional,omitempty"` - ExternalSpotifyEnabled nullable.Nullable[bool] `json:"external_spotify_enabled,omitempty"` - ExternalSpotifySecret nullable.Nullable[string] `json:"external_spotify_secret,omitempty"` - ExternalTwitchClientId nullable.Nullable[string] `json:"external_twitch_client_id,omitempty"` - ExternalTwitchEmailOptional nullable.Nullable[bool] `json:"external_twitch_email_optional,omitempty"` - ExternalTwitchEnabled nullable.Nullable[bool] `json:"external_twitch_enabled,omitempty"` - ExternalTwitchSecret nullable.Nullable[string] `json:"external_twitch_secret,omitempty"` - ExternalTwitterClientId nullable.Nullable[string] `json:"external_twitter_client_id,omitempty"` - ExternalTwitterEmailOptional nullable.Nullable[bool] `json:"external_twitter_email_optional,omitempty"` - ExternalTwitterEnabled nullable.Nullable[bool] `json:"external_twitter_enabled,omitempty"` - ExternalTwitterSecret nullable.Nullable[string] `json:"external_twitter_secret,omitempty"` - ExternalWeb3EthereumEnabled nullable.Nullable[bool] `json:"external_web3_ethereum_enabled,omitempty"` - ExternalWeb3SolanaEnabled nullable.Nullable[bool] `json:"external_web3_solana_enabled,omitempty"` - ExternalWorkosClientId nullable.Nullable[string] `json:"external_workos_client_id,omitempty"` - ExternalWorkosEnabled nullable.Nullable[bool] `json:"external_workos_enabled,omitempty"` - ExternalWorkosSecret nullable.Nullable[string] `json:"external_workos_secret,omitempty"` - ExternalWorkosUrl nullable.Nullable[string] `json:"external_workos_url,omitempty"` - ExternalXClientId nullable.Nullable[string] `json:"external_x_client_id,omitempty"` - ExternalXEmailOptional nullable.Nullable[bool] `json:"external_x_email_optional,omitempty"` - ExternalXEnabled nullable.Nullable[bool] `json:"external_x_enabled,omitempty"` - ExternalXSecret nullable.Nullable[string] `json:"external_x_secret,omitempty"` - ExternalZoomClientId nullable.Nullable[string] `json:"external_zoom_client_id,omitempty"` - ExternalZoomEmailOptional nullable.Nullable[bool] `json:"external_zoom_email_optional,omitempty"` - ExternalZoomEnabled nullable.Nullable[bool] `json:"external_zoom_enabled,omitempty"` - ExternalZoomSecret nullable.Nullable[string] `json:"external_zoom_secret,omitempty"` - HookAfterUserCreatedEnabled nullable.Nullable[bool] `json:"hook_after_user_created_enabled,omitempty"` - HookAfterUserCreatedSecrets nullable.Nullable[string] `json:"hook_after_user_created_secrets,omitempty"` - HookAfterUserCreatedUri nullable.Nullable[string] `json:"hook_after_user_created_uri,omitempty"` - HookBeforeUserCreatedEnabled nullable.Nullable[bool] `json:"hook_before_user_created_enabled,omitempty"` - HookBeforeUserCreatedSecrets nullable.Nullable[string] `json:"hook_before_user_created_secrets,omitempty"` - HookBeforeUserCreatedUri nullable.Nullable[string] `json:"hook_before_user_created_uri,omitempty"` - HookCustomAccessTokenEnabled nullable.Nullable[bool] `json:"hook_custom_access_token_enabled,omitempty"` - HookCustomAccessTokenSecrets nullable.Nullable[string] `json:"hook_custom_access_token_secrets,omitempty"` - HookCustomAccessTokenUri nullable.Nullable[string] `json:"hook_custom_access_token_uri,omitempty"` - HookMfaVerificationAttemptEnabled nullable.Nullable[bool] `json:"hook_mfa_verification_attempt_enabled,omitempty"` - HookMfaVerificationAttemptSecrets nullable.Nullable[string] `json:"hook_mfa_verification_attempt_secrets,omitempty"` - HookMfaVerificationAttemptUri nullable.Nullable[string] `json:"hook_mfa_verification_attempt_uri,omitempty"` - HookPasswordVerificationAttemptEnabled nullable.Nullable[bool] `json:"hook_password_verification_attempt_enabled,omitempty"` - HookPasswordVerificationAttemptSecrets nullable.Nullable[string] `json:"hook_password_verification_attempt_secrets,omitempty"` - HookPasswordVerificationAttemptUri nullable.Nullable[string] `json:"hook_password_verification_attempt_uri,omitempty"` - HookSendEmailEnabled nullable.Nullable[bool] `json:"hook_send_email_enabled,omitempty"` - HookSendEmailSecrets nullable.Nullable[string] `json:"hook_send_email_secrets,omitempty"` - HookSendEmailUri nullable.Nullable[string] `json:"hook_send_email_uri,omitempty"` - HookSendSmsEnabled nullable.Nullable[bool] `json:"hook_send_sms_enabled,omitempty"` - HookSendSmsSecrets nullable.Nullable[string] `json:"hook_send_sms_secrets,omitempty"` - HookSendSmsUri nullable.Nullable[string] `json:"hook_send_sms_uri,omitempty"` - JwtExp nullable.Nullable[int] `json:"jwt_exp,omitempty"` - MailerAllowUnverifiedEmailSignIns nullable.Nullable[bool] `json:"mailer_allow_unverified_email_sign_ins,omitempty"` - MailerAutoconfirm nullable.Nullable[bool] `json:"mailer_autoconfirm,omitempty"` - MailerNotificationsEmailChangedEnabled nullable.Nullable[bool] `json:"mailer_notifications_email_changed_enabled,omitempty"` - MailerNotificationsIdentityLinkedEnabled nullable.Nullable[bool] `json:"mailer_notifications_identity_linked_enabled,omitempty"` - MailerNotificationsIdentityUnlinkedEnabled nullable.Nullable[bool] `json:"mailer_notifications_identity_unlinked_enabled,omitempty"` - MailerNotificationsMfaFactorEnrolledEnabled nullable.Nullable[bool] `json:"mailer_notifications_mfa_factor_enrolled_enabled,omitempty"` - MailerNotificationsMfaFactorUnenrolledEnabled nullable.Nullable[bool] `json:"mailer_notifications_mfa_factor_unenrolled_enabled,omitempty"` - MailerNotificationsPasswordChangedEnabled nullable.Nullable[bool] `json:"mailer_notifications_password_changed_enabled,omitempty"` - MailerNotificationsPhoneChangedEnabled nullable.Nullable[bool] `json:"mailer_notifications_phone_changed_enabled,omitempty"` - MailerOtpExp *int `json:"mailer_otp_exp,omitempty"` - MailerOtpLength nullable.Nullable[int] `json:"mailer_otp_length,omitempty"` - MailerSecureEmailChangeEnabled nullable.Nullable[bool] `json:"mailer_secure_email_change_enabled,omitempty"` - MailerSubjectsConfirmation nullable.Nullable[string] `json:"mailer_subjects_confirmation,omitempty"` - MailerSubjectsEmailChange nullable.Nullable[string] `json:"mailer_subjects_email_change,omitempty"` - MailerSubjectsEmailChangedNotification nullable.Nullable[string] `json:"mailer_subjects_email_changed_notification,omitempty"` - MailerSubjectsIdentityLinkedNotification nullable.Nullable[string] `json:"mailer_subjects_identity_linked_notification,omitempty"` - MailerSubjectsIdentityUnlinkedNotification nullable.Nullable[string] `json:"mailer_subjects_identity_unlinked_notification,omitempty"` - MailerSubjectsInvite nullable.Nullable[string] `json:"mailer_subjects_invite,omitempty"` - MailerSubjectsMagicLink nullable.Nullable[string] `json:"mailer_subjects_magic_link,omitempty"` - MailerSubjectsMfaFactorEnrolledNotification nullable.Nullable[string] `json:"mailer_subjects_mfa_factor_enrolled_notification,omitempty"` - MailerSubjectsMfaFactorUnenrolledNotification nullable.Nullable[string] `json:"mailer_subjects_mfa_factor_unenrolled_notification,omitempty"` - MailerSubjectsPasswordChangedNotification nullable.Nullable[string] `json:"mailer_subjects_password_changed_notification,omitempty"` - MailerSubjectsPhoneChangedNotification nullable.Nullable[string] `json:"mailer_subjects_phone_changed_notification,omitempty"` - MailerSubjectsReauthentication nullable.Nullable[string] `json:"mailer_subjects_reauthentication,omitempty"` - MailerSubjectsRecovery nullable.Nullable[string] `json:"mailer_subjects_recovery,omitempty"` - MailerTemplatesConfirmationContent nullable.Nullable[string] `json:"mailer_templates_confirmation_content,omitempty"` - MailerTemplatesEmailChangeContent nullable.Nullable[string] `json:"mailer_templates_email_change_content,omitempty"` - MailerTemplatesEmailChangedNotificationContent nullable.Nullable[string] `json:"mailer_templates_email_changed_notification_content,omitempty"` - MailerTemplatesIdentityLinkedNotificationContent nullable.Nullable[string] `json:"mailer_templates_identity_linked_notification_content,omitempty"` - MailerTemplatesIdentityUnlinkedNotificationContent nullable.Nullable[string] `json:"mailer_templates_identity_unlinked_notification_content,omitempty"` - MailerTemplatesInviteContent nullable.Nullable[string] `json:"mailer_templates_invite_content,omitempty"` - MailerTemplatesMagicLinkContent nullable.Nullable[string] `json:"mailer_templates_magic_link_content,omitempty"` - MailerTemplatesMfaFactorEnrolledNotificationContent nullable.Nullable[string] `json:"mailer_templates_mfa_factor_enrolled_notification_content,omitempty"` - MailerTemplatesMfaFactorUnenrolledNotificationContent nullable.Nullable[string] `json:"mailer_templates_mfa_factor_unenrolled_notification_content,omitempty"` - MailerTemplatesPasswordChangedNotificationContent nullable.Nullable[string] `json:"mailer_templates_password_changed_notification_content,omitempty"` - MailerTemplatesPhoneChangedNotificationContent nullable.Nullable[string] `json:"mailer_templates_phone_changed_notification_content,omitempty"` - MailerTemplatesReauthenticationContent nullable.Nullable[string] `json:"mailer_templates_reauthentication_content,omitempty"` - MailerTemplatesRecoveryContent nullable.Nullable[string] `json:"mailer_templates_recovery_content,omitempty"` - MfaMaxEnrolledFactors nullable.Nullable[int] `json:"mfa_max_enrolled_factors,omitempty"` - MfaPhoneEnrollEnabled nullable.Nullable[bool] `json:"mfa_phone_enroll_enabled,omitempty"` - MfaPhoneMaxFrequency nullable.Nullable[int] `json:"mfa_phone_max_frequency,omitempty"` - MfaPhoneOtpLength nullable.Nullable[int] `json:"mfa_phone_otp_length,omitempty"` - MfaPhoneTemplate nullable.Nullable[string] `json:"mfa_phone_template,omitempty"` - MfaPhoneVerifyEnabled nullable.Nullable[bool] `json:"mfa_phone_verify_enabled,omitempty"` - MfaTotpEnrollEnabled nullable.Nullable[bool] `json:"mfa_totp_enroll_enabled,omitempty"` - MfaTotpVerifyEnabled nullable.Nullable[bool] `json:"mfa_totp_verify_enabled,omitempty"` - MfaWebAuthnEnrollEnabled nullable.Nullable[bool] `json:"mfa_web_authn_enroll_enabled,omitempty"` - MfaWebAuthnVerifyEnabled nullable.Nullable[bool] `json:"mfa_web_authn_verify_enabled,omitempty"` - NimbusOauthClientId nullable.Nullable[string] `json:"nimbus_oauth_client_id,omitempty"` - NimbusOauthClientSecret nullable.Nullable[string] `json:"nimbus_oauth_client_secret,omitempty"` - OauthServerAllowDynamicRegistration nullable.Nullable[bool] `json:"oauth_server_allow_dynamic_registration,omitempty"` - OauthServerAuthorizationPath nullable.Nullable[string] `json:"oauth_server_authorization_path,omitempty"` - OauthServerEnabled nullable.Nullable[bool] `json:"oauth_server_enabled,omitempty"` - PasskeyEnabled *bool `json:"passkey_enabled,omitempty"` - PasswordHibpEnabled nullable.Nullable[bool] `json:"password_hibp_enabled,omitempty"` - PasswordMinLength nullable.Nullable[int] `json:"password_min_length,omitempty"` - PasswordRequiredCharacters nullable.Nullable[UpdateAuthConfigBodyPasswordRequiredCharacters] `json:"password_required_characters,omitempty"` - RateLimitAnonymousUsers nullable.Nullable[int] `json:"rate_limit_anonymous_users,omitempty"` - RateLimitEmailSent nullable.Nullable[int] `json:"rate_limit_email_sent,omitempty"` - RateLimitOtp nullable.Nullable[int] `json:"rate_limit_otp,omitempty"` - RateLimitSmsSent nullable.Nullable[int] `json:"rate_limit_sms_sent,omitempty"` - RateLimitTokenRefresh nullable.Nullable[int] `json:"rate_limit_token_refresh,omitempty"` - RateLimitVerify nullable.Nullable[int] `json:"rate_limit_verify,omitempty"` - RateLimitWeb3 nullable.Nullable[int] `json:"rate_limit_web3,omitempty"` - RefreshTokenRotationEnabled nullable.Nullable[bool] `json:"refresh_token_rotation_enabled,omitempty"` - SamlEnabled nullable.Nullable[bool] `json:"saml_enabled,omitempty"` - SamlExternalUrl nullable.Nullable[string] `json:"saml_external_url,omitempty"` - SecurityCaptchaEnabled nullable.Nullable[bool] `json:"security_captcha_enabled,omitempty"` - SecurityCaptchaProvider nullable.Nullable[UpdateAuthConfigBodySecurityCaptchaProvider] `json:"security_captcha_provider,omitempty"` - SecurityCaptchaSecret nullable.Nullable[string] `json:"security_captcha_secret,omitempty"` - SecurityManualLinkingEnabled nullable.Nullable[bool] `json:"security_manual_linking_enabled,omitempty"` - - // SecurityRefreshTokenReuseInterval Refresh token reuse interval in seconds. Maximum 300 seconds (5 minutes). - SecurityRefreshTokenReuseInterval nullable.Nullable[int] `json:"security_refresh_token_reuse_interval,omitempty"` - SecuritySbForwardedForEnabled nullable.Nullable[bool] `json:"security_sb_forwarded_for_enabled,omitempty"` - - // SecurityUpdatePasswordRequireCurrentPassword Require the user's current password when updating their password. - SecurityUpdatePasswordRequireCurrentPassword nullable.Nullable[bool] `json:"security_update_password_require_current_password,omitempty"` - SecurityUpdatePasswordRequireReauthentication nullable.Nullable[bool] `json:"security_update_password_require_reauthentication,omitempty"` - - // SessionsInactivityTimeout Session inactivity timeout in hours. Maximum 8760 hours (1 year). - SessionsInactivityTimeout nullable.Nullable[float32] `json:"sessions_inactivity_timeout,omitempty"` - SessionsSinglePerUser nullable.Nullable[bool] `json:"sessions_single_per_user,omitempty"` - SessionsTags nullable.Nullable[string] `json:"sessions_tags,omitempty"` - - // SessionsTimebox Session timebox in hours. Maximum 8760 hours (1 year). - SessionsTimebox nullable.Nullable[float32] `json:"sessions_timebox,omitempty"` - SiteUrl nullable.Nullable[string] `json:"site_url,omitempty"` - SmsAutoconfirm nullable.Nullable[bool] `json:"sms_autoconfirm,omitempty"` - SmsMaxFrequency nullable.Nullable[int] `json:"sms_max_frequency,omitempty"` - SmsMessagebirdAccessKey nullable.Nullable[string] `json:"sms_messagebird_access_key,omitempty"` - SmsMessagebirdOriginator nullable.Nullable[string] `json:"sms_messagebird_originator,omitempty"` - SmsOtpExp nullable.Nullable[int] `json:"sms_otp_exp,omitempty"` - SmsOtpLength *int `json:"sms_otp_length,omitempty"` - SmsProvider nullable.Nullable[UpdateAuthConfigBodySmsProvider] `json:"sms_provider,omitempty"` - SmsTemplate nullable.Nullable[string] `json:"sms_template,omitempty"` - SmsTestOtp nullable.Nullable[string] `json:"sms_test_otp,omitempty"` - SmsTestOtpValidUntil nullable.Nullable[time.Time] `json:"sms_test_otp_valid_until,omitempty"` - SmsTextlocalApiKey nullable.Nullable[string] `json:"sms_textlocal_api_key,omitempty"` - SmsTextlocalSender nullable.Nullable[string] `json:"sms_textlocal_sender,omitempty"` - SmsTwilioAccountSid nullable.Nullable[string] `json:"sms_twilio_account_sid,omitempty"` - SmsTwilioAuthToken nullable.Nullable[string] `json:"sms_twilio_auth_token,omitempty"` - SmsTwilioContentSid nullable.Nullable[string] `json:"sms_twilio_content_sid,omitempty"` - SmsTwilioMessageServiceSid nullable.Nullable[string] `json:"sms_twilio_message_service_sid,omitempty"` - SmsTwilioVerifyAccountSid nullable.Nullable[string] `json:"sms_twilio_verify_account_sid,omitempty"` - SmsTwilioVerifyAuthToken nullable.Nullable[string] `json:"sms_twilio_verify_auth_token,omitempty"` - SmsTwilioVerifyMessageServiceSid nullable.Nullable[string] `json:"sms_twilio_verify_message_service_sid,omitempty"` - SmsVonageApiKey nullable.Nullable[string] `json:"sms_vonage_api_key,omitempty"` - SmsVonageApiSecret nullable.Nullable[string] `json:"sms_vonage_api_secret,omitempty"` - SmsVonageFrom nullable.Nullable[string] `json:"sms_vonage_from,omitempty"` - SmtpAdminEmail nullable.Nullable[openapi_types.Email] `json:"smtp_admin_email,omitempty"` - SmtpHost nullable.Nullable[string] `json:"smtp_host,omitempty"` - SmtpMaxFrequency nullable.Nullable[int] `json:"smtp_max_frequency,omitempty"` - SmtpPass nullable.Nullable[string] `json:"smtp_pass,omitempty"` - SmtpPort nullable.Nullable[string] `json:"smtp_port,omitempty"` - SmtpSenderName nullable.Nullable[string] `json:"smtp_sender_name,omitempty"` - SmtpUser nullable.Nullable[string] `json:"smtp_user,omitempty"` - UriAllowList nullable.Nullable[string] `json:"uri_allow_list,omitempty"` - WebauthnRpDisplayName nullable.Nullable[string] `json:"webauthn_rp_display_name,omitempty"` - WebauthnRpId nullable.Nullable[string] `json:"webauthn_rp_id,omitempty"` - WebauthnRpOrigins nullable.Nullable[string] `json:"webauthn_rp_origins,omitempty"` -} - -// UpdateAuthConfigBodyDbMaxPoolSizeUnit defines model for UpdateAuthConfigBody.DbMaxPoolSizeUnit. -type UpdateAuthConfigBodyDbMaxPoolSizeUnit string - -// UpdateAuthConfigBodyPasswordRequiredCharacters defines model for UpdateAuthConfigBody.PasswordRequiredCharacters. -type UpdateAuthConfigBodyPasswordRequiredCharacters string - -// UpdateAuthConfigBodySecurityCaptchaProvider defines model for UpdateAuthConfigBody.SecurityCaptchaProvider. -type UpdateAuthConfigBodySecurityCaptchaProvider string - -// UpdateAuthConfigBodySmsProvider defines model for UpdateAuthConfigBody.SmsProvider. -type UpdateAuthConfigBodySmsProvider string - -// UpdateBranchBody defines model for UpdateBranchBody. -type UpdateBranchBody struct { - BranchName *string `json:"branch_name,omitempty"` - GitBranch *string `json:"git_branch,omitempty"` - - // NotifyUrl HTTP endpoint to receive branch status updates. - NotifyUrl *string `json:"notify_url,omitempty"` - Persistent *bool `json:"persistent,omitempty"` - RequestReview *bool `json:"request_review,omitempty"` - - // ResetOnPush This field is deprecated and will be ignored. Use v1-reset-a-branch endpoint directly instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - ResetOnPush *bool `json:"reset_on_push,omitempty"` - Status *UpdateBranchBodyStatus `json:"status,omitempty"` -} - -// UpdateBranchBodyStatus defines model for UpdateBranchBody.Status. -type UpdateBranchBodyStatus string - -// UpdateCustomHostnameBody defines model for UpdateCustomHostnameBody. -type UpdateCustomHostnameBody struct { - CustomHostname string `json:"custom_hostname"` -} - -// UpdateCustomHostnameResponseOutput defines model for UpdateCustomHostnameResponse_Output. -type UpdateCustomHostnameResponseOutput struct { - CustomHostname *string `json:"custom_hostname,omitempty"` - Data struct { - Errors []JsonValueOutput `json:"errors"` - Messages []JsonValueOutput `json:"messages"` - Result struct { - CustomOriginServer *string `json:"custom_origin_server,omitempty"` - Hostname string `json:"hostname"` - Id string `json:"id"` - OwnershipVerification *struct { - Name *string `json:"name,omitempty"` - Type *string `json:"type,omitempty"` - Value *string `json:"value,omitempty"` - } `json:"ownership_verification,omitempty"` - Ssl *struct { - Status *string `json:"status,omitempty"` - ValidationErrors *[]struct { - Message string `json:"message"` - } `json:"validation_errors,omitempty"` - ValidationRecords *[]struct { - TxtName *string `json:"txt_name,omitempty"` - TxtValue *string `json:"txt_value,omitempty"` - } `json:"validation_records,omitempty"` - } `json:"ssl,omitempty"` - Status *string `json:"status,omitempty"` - VerificationErrors *[]string `json:"verification_errors,omitempty"` - } `json:"result"` - Success bool `json:"success"` - } `json:"data"` - Status UpdateCustomHostnameResponseOutputStatus `json:"status"` -} - -// UpdateCustomHostnameResponseOutputStatus defines model for UpdateCustomHostnameResponseOutput.Status. -type UpdateCustomHostnameResponseOutputStatus string - -// UpdateJitAccessBody defines model for UpdateJitAccessBody. -type UpdateJitAccessBody struct { - Roles []struct { - AllowedNetworks *struct { - AllowedCidrs *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs,omitempty"` - AllowedCidrsV6 *[]struct { - Cidr string `json:"cidr"` - } `json:"allowed_cidrs_v6,omitempty"` - } `json:"allowed_networks,omitempty"` - BranchesOnly *bool `json:"branches_only,omitempty"` - ExpiresAt *float32 `json:"expires_at,omitempty"` - Role string `json:"role"` - } `json:"roles"` - UserId openapi_types.UUID `json:"user_id"` -} - -// UpdatePgsodiumConfigBody defines model for UpdatePgsodiumConfigBody. -type UpdatePgsodiumConfigBody struct { - // RootKey The pgsodium root key: 32 bytes, hex-encoded (64 characters). - RootKey string `json:"root_key"` -} - -// UpdatePostgresConfigBody defines model for UpdatePostgresConfigBody. -type UpdatePostgresConfigBody struct { - AutovacuumMaxWorkers *int `json:"autovacuum_max_workers,omitempty"` - AutovacuumWorkMem *string `json:"autovacuum_work_mem,omitempty"` - - // CheckpointTimeout Default unit: s - CheckpointTimeout *string `json:"checkpoint_timeout,omitempty"` - CronLogStatement *bool `json:"cron.log_statement,omitempty"` - EffectiveCacheSize *string `json:"effective_cache_size,omitempty"` - HotStandbyFeedback *bool `json:"hot_standby_feedback,omitempty"` - - // LogAutovacuumMinDuration Default unit: ms - LogAutovacuumMinDuration *string `json:"log_autovacuum_min_duration,omitempty"` - LogCheckpoints *bool `json:"log_checkpoints,omitempty"` - LogConnections *bool `json:"log_connections,omitempty"` - LogDisconnections *bool `json:"log_disconnections,omitempty"` - LogDuration *bool `json:"log_duration,omitempty"` - LogLockWaits *bool `json:"log_lock_waits,omitempty"` - LogRecoveryConflictWaits *bool `json:"log_recovery_conflict_waits,omitempty"` - LogReplicationCommands *bool `json:"log_replication_commands,omitempty"` - - // LogStartupProgressInterval Default unit: ms - LogStartupProgressInterval *string `json:"log_startup_progress_interval,omitempty"` - LogTempFiles *string `json:"log_temp_files,omitempty"` - LogicalDecodingWorkMem *string `json:"logical_decoding_work_mem,omitempty"` - MaintenanceWorkMem *string `json:"maintenance_work_mem,omitempty"` - MaxConnections *int `json:"max_connections,omitempty"` - MaxLocksPerTransaction *int `json:"max_locks_per_transaction,omitempty"` - MaxLogicalReplicationWorkers *int `json:"max_logical_replication_workers,omitempty"` - MaxParallelMaintenanceWorkers *int `json:"max_parallel_maintenance_workers,omitempty"` - MaxParallelWorkers *int `json:"max_parallel_workers,omitempty"` - MaxParallelWorkersPerGather *int `json:"max_parallel_workers_per_gather,omitempty"` - MaxReplicationSlots *int `json:"max_replication_slots,omitempty"` - MaxSlotWalKeepSize *string `json:"max_slot_wal_keep_size,omitempty"` - MaxStandbyArchiveDelay *string `json:"max_standby_archive_delay,omitempty"` - MaxStandbyStreamingDelay *string `json:"max_standby_streaming_delay,omitempty"` - MaxSyncWorkersPerSubscription *int `json:"max_sync_workers_per_subscription,omitempty"` - MaxWalSenders *int `json:"max_wal_senders,omitempty"` - MaxWalSize *string `json:"max_wal_size,omitempty"` - MaxWorkerProcesses *int `json:"max_worker_processes,omitempty"` - PgNetBatchSize *int `json:"pg_net.batch_size,omitempty"` - PgStatStatementsMax *int `json:"pg_stat_statements.max,omitempty"` - RestartDatabase *bool `json:"restart_database,omitempty"` - SessionReplicationRole *UpdatePostgresConfigBodySessionReplicationRole `json:"session_replication_role,omitempty"` - SharedBuffers *string `json:"shared_buffers,omitempty"` - - // StatementTimeout Default unit: ms - StatementTimeout *string `json:"statement_timeout,omitempty"` - TrackActivityQuerySize *string `json:"track_activity_query_size,omitempty"` - TrackCommitTimestamp *bool `json:"track_commit_timestamp,omitempty"` - WalKeepSize *string `json:"wal_keep_size,omitempty"` - - // WalSenderTimeout Default unit: ms - WalSenderTimeout *string `json:"wal_sender_timeout,omitempty"` - WorkMem *string `json:"work_mem,omitempty"` -} - -// UpdatePostgresConfigBodySessionReplicationRole defines model for UpdatePostgresConfigBody.SessionReplicationRole. -type UpdatePostgresConfigBodySessionReplicationRole string - -// UpdateProviderBody defines model for UpdateProviderBody. -type UpdateProviderBody struct { - AttributeMapping *struct { - Keys map[string]struct { - Array *bool `json:"array,omitempty"` - Default interface{} `json:"default,omitempty"` - Name *string `json:"name,omitempty"` - Names *[]string `json:"names,omitempty"` - } `json:"keys"` - } `json:"attribute_mapping,omitempty"` - Domains *[]string `json:"domains,omitempty"` - MetadataUrl *string `json:"metadata_url,omitempty"` - MetadataXml *string `json:"metadata_xml,omitempty"` - NameIdFormat *UpdateProviderBodyNameIdFormat `json:"name_id_format,omitempty"` -} - -// UpdateProviderBodyNameIdFormat defines model for UpdateProviderBody.NameIdFormat. -type UpdateProviderBodyNameIdFormat string - -// UpdateProviderResponseOutput defines model for UpdateProviderResponse_Output. -type UpdateProviderResponseOutput struct { - CreatedAt *string `json:"created_at,omitempty"` - Domains *[]struct { - CreatedAt *string `json:"created_at,omitempty"` - Domain *string `json:"domain,omitempty"` - UpdatedAt *string `json:"updated_at,omitempty"` - } `json:"domains,omitempty"` - Id string `json:"id"` - Saml *struct { - AttributeMapping *struct { - Keys map[string]struct { - Array *bool `json:"array,omitempty"` - Default interface{} `json:"default,omitempty"` - Name *string `json:"name,omitempty"` - Names *[]string `json:"names,omitempty"` - } `json:"keys"` - } `json:"attribute_mapping,omitempty"` - EntityId string `json:"entity_id"` - MetadataUrl *string `json:"metadata_url,omitempty"` - MetadataXml *string `json:"metadata_xml,omitempty"` - NameIdFormat *string `json:"name_id_format,omitempty"` - } `json:"saml,omitempty"` - UpdatedAt *string `json:"updated_at,omitempty"` -} - -// UpdateRealtimeConfigBody defines model for UpdateRealtimeConfigBody. -type UpdateRealtimeConfigBody struct { - // ConnectionPool Sets connection pool size for Realtime Authorization - ConnectionPool *int `json:"connection_pool,omitempty"` - - // MaxBytesPerSecond Sets maximum number of bytes per second rate per channel limit - MaxBytesPerSecond *int `json:"max_bytes_per_second,omitempty"` - - // MaxChannelsPerClient Sets maximum number of channels per client rate limit - MaxChannelsPerClient *int `json:"max_channels_per_client,omitempty"` - - // MaxConcurrentUsers Sets maximum number of concurrent users rate limit - MaxConcurrentUsers *int `json:"max_concurrent_users,omitempty"` - - // MaxEventsPerSecond Sets maximum number of events per second rate per channel limit - MaxEventsPerSecond *int `json:"max_events_per_second,omitempty"` - - // MaxJoinsPerSecond Sets maximum number of joins per second rate limit - MaxJoinsPerSecond *int `json:"max_joins_per_second,omitempty"` - - // MaxPayloadSizeInKb Sets maximum number of payload size in KB rate limit - MaxPayloadSizeInKb *int `json:"max_payload_size_in_kb,omitempty"` - - // MaxPresenceEventsPerSecond Sets maximum number of presence events per second rate limit - MaxPresenceEventsPerSecond *int `json:"max_presence_events_per_second,omitempty"` - - // PostgresChangesPool Sets connection pool size used to create Postgres Changes subscriptions - PostgresChangesPool *int `json:"postgres_changes_pool,omitempty"` - - // PresenceEnabled Whether to enable presence - PresenceEnabled *bool `json:"presence_enabled,omitempty"` - - // PrivateOnly Whether to only allow private channels - PrivateOnly *bool `json:"private_only,omitempty"` - - // Suspend Disables the Realtime service for this project when true. Set to false to re-enable it. - Suspend *bool `json:"suspend,omitempty"` -} - -// UpdateRunStatusBody defines model for UpdateRunStatusBody. -type UpdateRunStatusBody struct { - Clone *UpdateRunStatusBodyClone `json:"clone,omitempty"` - Configure *UpdateRunStatusBodyConfigure `json:"configure,omitempty"` - Deploy *UpdateRunStatusBodyDeploy `json:"deploy,omitempty"` - Health *UpdateRunStatusBodyHealth `json:"health,omitempty"` - Migrate *UpdateRunStatusBodyMigrate `json:"migrate,omitempty"` - Pull *UpdateRunStatusBodyPull `json:"pull,omitempty"` - Seed *UpdateRunStatusBodySeed `json:"seed,omitempty"` -} - -// UpdateRunStatusBodyClone defines model for UpdateRunStatusBody.Clone. -type UpdateRunStatusBodyClone string - -// UpdateRunStatusBodyConfigure defines model for UpdateRunStatusBody.Configure. -type UpdateRunStatusBodyConfigure string - -// UpdateRunStatusBodyDeploy defines model for UpdateRunStatusBody.Deploy. -type UpdateRunStatusBodyDeploy string - -// UpdateRunStatusBodyHealth defines model for UpdateRunStatusBody.Health. -type UpdateRunStatusBodyHealth string - -// UpdateRunStatusBodyMigrate defines model for UpdateRunStatusBody.Migrate. -type UpdateRunStatusBodyMigrate string - -// UpdateRunStatusBodyPull defines model for UpdateRunStatusBody.Pull. -type UpdateRunStatusBodyPull string - -// UpdateRunStatusBodySeed defines model for UpdateRunStatusBody.Seed. -type UpdateRunStatusBodySeed string - -// UpdateRunStatusResponseOutput defines model for UpdateRunStatusResponse_Output. -type UpdateRunStatusResponseOutput struct { - Message UpdateRunStatusResponseOutputMessage `json:"message"` -} - -// UpdateRunStatusResponseOutputMessage defines model for UpdateRunStatusResponseOutput.Message. -type UpdateRunStatusResponseOutputMessage string - -// UpdateSigningKeyBody defines model for UpdateSigningKeyBody. -type UpdateSigningKeyBody struct { - Status UpdateSigningKeyBodyStatus `json:"status"` -} - -// UpdateSigningKeyBodyStatus defines model for UpdateSigningKeyBody.Status. -type UpdateSigningKeyBodyStatus string - -// UpdateStorageConfigBody defines model for UpdateStorageConfigBody. -type UpdateStorageConfigBody struct { - External *struct { - UpstreamTarget UpdateStorageConfigBodyExternalUpstreamTarget `json:"upstreamTarget"` - } `json:"external,omitempty"` - Features *struct { - IcebergCatalog *struct { - Enabled bool `json:"enabled"` - MaxCatalogs int `json:"maxCatalogs"` - MaxNamespaces int `json:"maxNamespaces"` - MaxTables int `json:"maxTables"` - } `json:"icebergCatalog,omitempty"` - ImageTransformation *struct { - Enabled bool `json:"enabled"` - } `json:"imageTransformation,omitempty"` - PurgeCache *struct { - Enabled bool `json:"enabled"` - } `json:"purgeCache,omitempty"` - S3Protocol *struct { - Enabled bool `json:"enabled"` - } `json:"s3Protocol,omitempty"` - VectorBuckets *struct { - Enabled bool `json:"enabled"` - MaxBuckets int `json:"maxBuckets"` - MaxIndexes int `json:"maxIndexes"` - } `json:"vectorBuckets,omitempty"` - } `json:"features,omitempty"` - FileSizeLimit *int64 `json:"fileSizeLimit,omitempty"` -} - -// UpdateStorageConfigBodyExternalUpstreamTarget defines model for UpdateStorageConfigBody.External.UpstreamTarget. -type UpdateStorageConfigBodyExternalUpstreamTarget string - -// UpdateSupavisorConfigBody defines model for UpdateSupavisorConfigBody. -type UpdateSupavisorConfigBody struct { - DefaultPoolSize nullable.Nullable[int] `json:"default_pool_size,omitempty"` - - // PoolMode Dedicated pooler mode for the project - PoolMode *UpdateSupavisorConfigBodyPoolMode `json:"pool_mode,omitempty"` -} - -// UpdateSupavisorConfigBodyPoolMode Dedicated pooler mode for the project -type UpdateSupavisorConfigBodyPoolMode string - -// UpdateSupavisorConfigResponseOutput defines model for UpdateSupavisorConfigResponse_Output. -type UpdateSupavisorConfigResponseOutput struct { - DefaultPoolSize nullable.Nullable[int] `json:"default_pool_size"` - PoolMode string `json:"pool_mode"` -} - -// UpgradeDatabaseBody defines model for UpgradeDatabaseBody. -type UpgradeDatabaseBody struct { - ReleaseChannel *UpgradeDatabaseBodyReleaseChannel `json:"release_channel,omitempty"` - TargetVersion string `json:"target_version"` -} - -// UpgradeDatabaseBodyReleaseChannel defines model for UpgradeDatabaseBody.ReleaseChannel. -type UpgradeDatabaseBodyReleaseChannel string - -// V1BackupScheduleResponseOutput defines model for V1BackupScheduleResponse_Output. -type V1BackupScheduleResponseOutput struct { - // ScheduleFor Time of day to schedule daily backups, in UTC. Format: HH:MM:SS. - ScheduleFor string `json:"schedule_for"` - - // UpdatedAt Timestamp of when the backup schedule was last updated. - UpdatedAt time.Time `json:"updated_at"` -} - -// V1BackupsResponseOutput defines model for V1BackupsResponse_Output. -type V1BackupsResponseOutput struct { - Backups []struct { - Id int `json:"id"` - InsertedAt string `json:"inserted_at"` - IsPhysicalBackup bool `json:"is_physical_backup"` - Status V1BackupsResponseOutputBackupsStatus `json:"status"` - } `json:"backups"` - PhysicalBackupData struct { - EarliestPhysicalBackupDateUnix *int `json:"earliest_physical_backup_date_unix,omitempty"` - LatestPhysicalBackupDateUnix *int `json:"latest_physical_backup_date_unix,omitempty"` - } `json:"physical_backup_data"` - PitrEnabled bool `json:"pitr_enabled"` - Region string `json:"region"` - WalgEnabled bool `json:"walg_enabled"` -} - -// V1BackupsResponseOutputBackupsStatus defines model for V1BackupsResponseOutput.Backups.Status. -type V1BackupsResponseOutputBackupsStatus string - -// V1CreateFunctionBody defines model for V1CreateFunctionBody. -type V1CreateFunctionBody struct { - Body string `json:"body"` - Name string `json:"name"` - Slug string `json:"slug"` - VerifyJwt *bool `json:"verify_jwt,omitempty"` -} - -// V1CreateMigrationBody defines model for V1CreateMigrationBody. -type V1CreateMigrationBody struct { - Name *string `json:"name,omitempty"` - Query string `json:"query"` - Rollback *string `json:"rollback,omitempty"` -} - -// V1CreateProjectBody defines model for V1CreateProjectBody. -type V1CreateProjectBody struct { - // DbPass Database password - DbPass string `json:"db_pass"` - - // DesiredInstanceSize Desired instance size. Omit this field to always default to the smallest possible size. - DesiredInstanceSize *V1CreateProjectBodyDesiredInstanceSize `json:"desired_instance_size,omitempty"` - - // HighAvailability [Experimental] Whether to enable high availability for the project. - HighAvailability *bool `json:"high_availability,omitempty"` - - // KpsEnabled This field is deprecated and is ignored in this request - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - KpsEnabled *bool `json:"kps_enabled,omitempty"` - - // Name Name of your project - Name string `json:"name"` - - // OrganizationId Deprecated: Use `organization_slug` instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - OrganizationId *string `json:"organization_id,omitempty"` - - // OrganizationSlug Organization slug - OrganizationSlug string `json:"organization_slug"` - - // Plan Subscription Plan is now set on organization level and is ignored in this request - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - Plan *V1CreateProjectBodyPlan `json:"plan,omitempty"` - - // Region Region you want your server to reside in. Use region_selection instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - Region *V1CreateProjectBodyRegion `json:"region,omitempty"` - - // RegionSelection Region selection. Only one of region or region_selection can be specified. - RegionSelection *V1CreateProjectBody_RegionSelection `json:"region_selection,omitempty"` - - // TemplateUrl Template URL used to create the project from the CLI. - TemplateUrl *string `json:"template_url,omitempty"` -} - -// V1CreateProjectBodyDesiredInstanceSize Desired instance size. Omit this field to always default to the smallest possible size. -type V1CreateProjectBodyDesiredInstanceSize string - -// V1CreateProjectBodyPlan Subscription Plan is now set on organization level and is ignored in this request -type V1CreateProjectBodyPlan string - -// V1CreateProjectBodyRegion Region you want your server to reside in. Use region_selection instead. -type V1CreateProjectBodyRegion string - -// V1CreateProjectBodyRegionSelection0 defines model for . -type V1CreateProjectBodyRegionSelection0 struct { - // Code Specific region code. The codes supported are not a stable API, and should be retrieved from the /available-regions endpoint. - Code V1CreateProjectBodyRegionSelection0Code `json:"code"` - Type V1CreateProjectBodyRegionSelection0Type `json:"type"` -} - -// V1CreateProjectBodyRegionSelection0Code Specific region code. The codes supported are not a stable API, and should be retrieved from the /available-regions endpoint. -type V1CreateProjectBodyRegionSelection0Code string - -// V1CreateProjectBodyRegionSelection0Type defines model for V1CreateProjectBody.RegionSelection.0.Type. -type V1CreateProjectBodyRegionSelection0Type string - -// V1CreateProjectBodyRegionSelection1 defines model for . -type V1CreateProjectBodyRegionSelection1 struct { - // Code The Smart Region Group's code. The codes supported are not a stable API, and should be retrieved from the /available-regions endpoint. - Code V1CreateProjectBodyRegionSelection1Code `json:"code"` - Type V1CreateProjectBodyRegionSelection1Type `json:"type"` -} - -// V1CreateProjectBodyRegionSelection1Code The Smart Region Group's code. The codes supported are not a stable API, and should be retrieved from the /available-regions endpoint. -type V1CreateProjectBodyRegionSelection1Code string - -// V1CreateProjectBodyRegionSelection1Type defines model for V1CreateProjectBody.RegionSelection.1.Type. -type V1CreateProjectBodyRegionSelection1Type string - -// V1CreateProjectBody_RegionSelection Region selection. Only one of region or region_selection can be specified. -type V1CreateProjectBody_RegionSelection struct { - union json.RawMessage -} - -// V1GetMigrationResponseOutput defines model for V1GetMigrationResponse_Output. -type V1GetMigrationResponseOutput struct { - CreatedBy *string `json:"created_by,omitempty"` - IdempotencyKey *string `json:"idempotency_key,omitempty"` - Name *string `json:"name,omitempty"` - Rollback *[]string `json:"rollback,omitempty"` - Statements *[]string `json:"statements,omitempty"` - Version string `json:"version"` -} - -// V1GetUsageApiCountResponseOutput defines model for V1GetUsageApiCountResponse_Output. -type V1GetUsageApiCountResponseOutput struct { - Error *V1GetUsageApiCountResponseOutput_Error `json:"error,omitempty"` - Result *[]struct { - Timestamp time.Time `json:"timestamp"` - TotalAuthRequests float32 `json:"total_auth_requests"` - TotalRealtimeRequests float32 `json:"total_realtime_requests"` - TotalRestRequests float32 `json:"total_rest_requests"` - TotalStorageRequests float32 `json:"total_storage_requests"` - } `json:"result,omitempty"` -} - -// V1GetUsageApiCountResponseOutputError0 defines model for . -type V1GetUsageApiCountResponseOutputError0 = string - -// V1GetUsageApiCountResponseOutputError1 defines model for . -type V1GetUsageApiCountResponseOutputError1 struct { - Code float32 `json:"code"` - Errors []struct { - Domain string `json:"domain"` - Location string `json:"location"` - LocationType string `json:"locationType"` - Message string `json:"message"` - Reason string `json:"reason"` - } `json:"errors"` - Message string `json:"message"` - Status string `json:"status"` -} - -// V1GetUsageApiCountResponseOutput_Error defines model for V1GetUsageApiCountResponseOutput.Error. -type V1GetUsageApiCountResponseOutput_Error struct { - union json.RawMessage -} - -// V1GetUsageApiRequestsCountResponseOutput defines model for V1GetUsageApiRequestsCountResponse_Output. -type V1GetUsageApiRequestsCountResponseOutput struct { - Error *V1GetUsageApiRequestsCountResponseOutput_Error `json:"error,omitempty"` - Result *[]struct { - Count float32 `json:"count"` - } `json:"result,omitempty"` -} - -// V1GetUsageApiRequestsCountResponseOutputError0 defines model for . -type V1GetUsageApiRequestsCountResponseOutputError0 = string - -// V1GetUsageApiRequestsCountResponseOutputError1 defines model for . -type V1GetUsageApiRequestsCountResponseOutputError1 struct { - Code float32 `json:"code"` - Errors []struct { - Domain string `json:"domain"` - Location string `json:"location"` - LocationType string `json:"locationType"` - Message string `json:"message"` - Reason string `json:"reason"` - } `json:"errors"` - Message string `json:"message"` - Status string `json:"status"` -} - -// V1GetUsageApiRequestsCountResponseOutput_Error defines model for V1GetUsageApiRequestsCountResponseOutput.Error. -type V1GetUsageApiRequestsCountResponseOutput_Error struct { - union json.RawMessage -} - -// V1ListEntitlementsResponseOutput defines model for V1ListEntitlementsResponse_Output. -type V1ListEntitlementsResponseOutput struct { - Entitlements []struct { - Config V1ListEntitlementsResponseOutput_Entitlements_Config `json:"config"` - Feature struct { - Key V1ListEntitlementsResponseOutputEntitlementsFeatureKey `json:"key"` - Type V1ListEntitlementsResponseOutputEntitlementsFeatureType `json:"type"` - } `json:"feature"` - HasAccess bool `json:"hasAccess"` - Type V1ListEntitlementsResponseOutputEntitlementsType `json:"type"` - } `json:"entitlements"` -} - -// V1ListEntitlementsResponseOutputEntitlementsConfig0 defines model for . -type V1ListEntitlementsResponseOutputEntitlementsConfig0 struct { - Enabled bool `json:"enabled"` -} - -// V1ListEntitlementsResponseOutputEntitlementsConfig1 defines model for . -type V1ListEntitlementsResponseOutputEntitlementsConfig1 struct { - Enabled bool `json:"enabled"` - Unit string `json:"unit"` - Unlimited bool `json:"unlimited"` - Value float32 `json:"value"` -} - -// V1ListEntitlementsResponseOutputEntitlementsConfig2 defines model for . -type V1ListEntitlementsResponseOutputEntitlementsConfig2 struct { - Enabled bool `json:"enabled"` - Set []string `json:"set"` -} - -// V1ListEntitlementsResponseOutput_Entitlements_Config defines model for V1ListEntitlementsResponseOutput.Entitlements.Config. -type V1ListEntitlementsResponseOutput_Entitlements_Config struct { - union json.RawMessage -} - -// V1ListEntitlementsResponseOutputEntitlementsFeatureKey defines model for V1ListEntitlementsResponseOutput.Entitlements.Feature.Key. -type V1ListEntitlementsResponseOutputEntitlementsFeatureKey string - -// V1ListEntitlementsResponseOutputEntitlementsFeatureType defines model for V1ListEntitlementsResponseOutput.Entitlements.Feature.Type. -type V1ListEntitlementsResponseOutputEntitlementsFeatureType string - -// V1ListEntitlementsResponseOutputEntitlementsType defines model for V1ListEntitlementsResponseOutput.Entitlements.Type. -type V1ListEntitlementsResponseOutputEntitlementsType string - -// V1ListMigrationsResponseOutput defines model for V1ListMigrationsResponse_Output. -type V1ListMigrationsResponseOutput = []struct { - Name *string `json:"name,omitempty"` - Version string `json:"version"` -} - -// V1OrganizationMemberResponseOutput defines model for V1OrganizationMemberResponse_Output. -type V1OrganizationMemberResponseOutput struct { - AvatarUrl nullable.Nullable[string] `json:"avatar_url"` - Email *string `json:"email,omitempty"` - MfaEnabled bool `json:"mfa_enabled"` - RoleName *string `json:"role_name,omitempty"` - UserId string `json:"user_id"` - UserName string `json:"user_name"` -} - -// V1OrganizationSlugResponseOutput defines model for V1OrganizationSlugResponse_Output. -type V1OrganizationSlugResponseOutput struct { - AllowedReleaseChannels []V1OrganizationSlugResponseOutputAllowedReleaseChannels `json:"allowed_release_channels"` - Id string `json:"id"` - Name string `json:"name"` - OptInTags []interface{} `json:"opt_in_tags"` - Plan *V1OrganizationSlugResponseOutputPlan `json:"plan,omitempty"` -} - -// V1OrganizationSlugResponseOutputAllowedReleaseChannels defines model for V1OrganizationSlugResponseOutput.AllowedReleaseChannels. -type V1OrganizationSlugResponseOutputAllowedReleaseChannels string - -// V1OrganizationSlugResponseOutputPlan defines model for V1OrganizationSlugResponseOutput.Plan. -type V1OrganizationSlugResponseOutputPlan string - -// V1PatchMigrationBody defines model for V1PatchMigrationBody. -type V1PatchMigrationBody struct { - Name *string `json:"name,omitempty"` - Rollback *string `json:"rollback,omitempty"` -} - -// V1PgbouncerConfigResponseOutput defines model for V1PgbouncerConfigResponse_Output. -type V1PgbouncerConfigResponseOutput struct { - ConnectionString *string `json:"connection_string,omitempty"` - DefaultPoolSize *int `json:"default_pool_size,omitempty"` - IgnoreStartupParameters *string `json:"ignore_startup_parameters,omitempty"` - MaxClientConn *int `json:"max_client_conn,omitempty"` - PoolMode *V1PgbouncerConfigResponseOutputPoolMode `json:"pool_mode,omitempty"` - QueryWaitTimeout *int `json:"query_wait_timeout,omitempty"` - ReservePoolSize *int `json:"reserve_pool_size,omitempty"` - ServerIdleTimeout *int `json:"server_idle_timeout,omitempty"` - ServerLifetime *int `json:"server_lifetime,omitempty"` -} - -// V1PgbouncerConfigResponseOutputPoolMode defines model for V1PgbouncerConfigResponseOutput.PoolMode. -type V1PgbouncerConfigResponseOutputPoolMode string - -// V1PostgrestConfigResponseOutput defines model for V1PostgrestConfigResponse_Output. -type V1PostgrestConfigResponseOutput struct { - DbExtraSearchPath string `json:"db_extra_search_path"` - - // DbPool If `null`, the value is automatically configured based on compute size. - DbPool nullable.Nullable[int] `json:"db_pool"` - - // DbPoolAcquisitionTimeout If `null`, the value is automatically configured to 10. - DbPoolAcquisitionTimeout nullable.Nullable[int] `json:"db_pool_acquisition_timeout"` - DbSchema string `json:"db_schema"` - MaxRows int `json:"max_rows"` -} - -// V1ProfileResponseOutput defines model for V1ProfileResponse_Output. -type V1ProfileResponseOutput struct { - GotrueId string `json:"gotrue_id"` - PrimaryEmail string `json:"primary_email"` - Username string `json:"username"` -} - -// V1ProjectAdvisorsResponseOutput defines model for V1ProjectAdvisorsResponse_Output. -type V1ProjectAdvisorsResponseOutput struct { - Lints []struct { - CacheKey string `json:"cache_key"` - Categories []V1ProjectAdvisorsResponseOutputLintsCategories `json:"categories"` - Description string `json:"description"` - Detail string `json:"detail"` - Facing V1ProjectAdvisorsResponseOutputLintsFacing `json:"facing"` - Level V1ProjectAdvisorsResponseOutputLintsLevel `json:"level"` - Metadata *struct { - Entity *string `json:"entity,omitempty"` - FkeyColumns *[]float32 `json:"fkey_columns,omitempty"` - FkeyName *string `json:"fkey_name,omitempty"` - Name *string `json:"name,omitempty"` - Schema *string `json:"schema,omitempty"` - Type *V1ProjectAdvisorsResponseOutputLintsMetadataType `json:"type,omitempty"` - } `json:"metadata,omitempty"` - Name V1ProjectAdvisorsResponseOutputLintsName `json:"name"` - ObservedAt *time.Time `json:"observed_at,omitempty"` - Remediation string `json:"remediation"` - Title string `json:"title"` - } `json:"lints"` -} - -// V1ProjectAdvisorsResponseOutputLintsCategories defines model for V1ProjectAdvisorsResponseOutput.Lints.Categories. -type V1ProjectAdvisorsResponseOutputLintsCategories string - -// V1ProjectAdvisorsResponseOutputLintsFacing defines model for V1ProjectAdvisorsResponseOutput.Lints.Facing. -type V1ProjectAdvisorsResponseOutputLintsFacing string - -// V1ProjectAdvisorsResponseOutputLintsLevel defines model for V1ProjectAdvisorsResponseOutput.Lints.Level. -type V1ProjectAdvisorsResponseOutputLintsLevel string - -// V1ProjectAdvisorsResponseOutputLintsMetadataType defines model for V1ProjectAdvisorsResponseOutput.Lints.Metadata.Type. -type V1ProjectAdvisorsResponseOutputLintsMetadataType string - -// V1ProjectAdvisorsResponseOutputLintsName defines model for V1ProjectAdvisorsResponseOutput.Lints.Name. -type V1ProjectAdvisorsResponseOutputLintsName string - -// V1ProjectRefResponseOutput defines model for V1ProjectRefResponse_Output. -type V1ProjectRefResponseOutput struct { - Id int `json:"id"` - Name string `json:"name"` - Ref string `json:"ref"` -} - -// V1ProjectResponseOutput defines model for V1ProjectResponse_Output. -type V1ProjectResponseOutput struct { - // CreatedAt Creation timestamp - CreatedAt string `json:"created_at"` - - // Id Deprecated: Use `ref` instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - Id string `json:"id"` - - // Name Name of your project - Name string `json:"name"` - - // OrganizationId Deprecated: Use `organization_slug` instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - OrganizationId string `json:"organization_id"` - - // OrganizationSlug Organization slug - OrganizationSlug string `json:"organization_slug"` - - // Ref Project ref - Ref string `json:"ref"` - - // Region Region of your project - Region string `json:"region"` - Status V1ProjectResponseOutputStatus `json:"status"` -} - -// V1ProjectResponseOutputStatus defines model for V1ProjectResponseOutput.Status. -type V1ProjectResponseOutputStatus string - -// V1ProjectWithDatabaseResponseOutput defines model for V1ProjectWithDatabaseResponse_Output. -type V1ProjectWithDatabaseResponseOutput struct { - // CreatedAt Creation timestamp - CreatedAt string `json:"created_at"` - Database struct { - // Host Database host - Host string `json:"host"` - - // PostgresEngine Database engine - PostgresEngine string `json:"postgres_engine"` - - // ReleaseChannel Release channel - ReleaseChannel string `json:"release_channel"` - - // Version Database version - Version string `json:"version"` - } `json:"database"` - - // Id Deprecated: Use `ref` instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - Id string `json:"id"` - - // Name Name of your project - Name string `json:"name"` - - // OrganizationId Deprecated: Use `organization_slug` instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - OrganizationId string `json:"organization_id"` - - // OrganizationSlug Organization slug - OrganizationSlug string `json:"organization_slug"` - - // Ref Project ref - Ref string `json:"ref"` - - // Region Region of your project - Region string `json:"region"` - Status V1ProjectWithDatabaseResponseOutputStatus `json:"status"` -} - -// V1ProjectWithDatabaseResponseOutputStatus defines model for V1ProjectWithDatabaseResponseOutput.Status. -type V1ProjectWithDatabaseResponseOutputStatus string - -// V1ReadOnlyQueryBody defines model for V1ReadOnlyQueryBody. -type V1ReadOnlyQueryBody struct { - Parameters *[]interface{} `json:"parameters,omitempty"` - Query string `json:"query"` -} - -// V1RestoreBackupBody defines model for V1RestoreBackupBody. -type V1RestoreBackupBody struct { - Id int `json:"id"` -} - -// V1RestorePitrBody defines model for V1RestorePitrBody. -type V1RestorePitrBody struct { - RecoveryTimeTargetUnix int64 `json:"recovery_time_target_unix"` -} - -// V1RestorePointPostBody defines model for V1RestorePointPostBody. -type V1RestorePointPostBody struct { - Name string `json:"name"` -} - -// V1RestorePointResponse defines model for V1RestorePointResponse. -type V1RestorePointResponse struct { - CompletedOn nullable.Nullable[time.Time] `json:"completed_on"` - Name string `json:"name"` - Status V1RestorePointResponseStatus `json:"status"` -} - -// V1RestorePointResponseStatus defines model for V1RestorePointResponse.Status. -type V1RestorePointResponseStatus string - -// V1RunQueryBody defines model for V1RunQueryBody. -type V1RunQueryBody struct { - Parameters *[]interface{} `json:"parameters,omitempty"` - Query string `json:"query"` - ReadOnly *bool `json:"read_only,omitempty"` -} - -// V1ServiceHealthResponseOutput defines model for V1ServiceHealthResponse_Output. -type V1ServiceHealthResponseOutput struct { - Error *string `json:"error,omitempty"` - - // Healthy Deprecated. Use `status` instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - Healthy bool `json:"healthy"` - Info *V1ServiceHealthResponseOutput_Info `json:"info,omitempty"` - Name V1ServiceHealthResponseOutputName `json:"name"` - Status V1ServiceHealthResponseOutputStatus `json:"status"` -} - -// V1ServiceHealthResponseOutputInfo0 defines model for . -type V1ServiceHealthResponseOutputInfo0 struct { - Description string `json:"description"` - Name V1ServiceHealthResponseOutputInfo0Name `json:"name"` - Version string `json:"version"` -} - -// V1ServiceHealthResponseOutputInfo0Name defines model for V1ServiceHealthResponseOutput.Info.0.Name. -type V1ServiceHealthResponseOutputInfo0Name string - -// V1ServiceHealthResponseOutputInfo1 defines model for . -type V1ServiceHealthResponseOutputInfo1 struct { - ConnectedCluster int `json:"connected_cluster"` - DbConnected bool `json:"db_connected"` - - // Healthy Deprecated. Use `status` instead. - // Deprecated: this property has been marked as deprecated upstream, but no `x-deprecated-reason` was set - Healthy bool `json:"healthy"` - ReplicationConnected bool `json:"replication_connected"` -} - -// V1ServiceHealthResponseOutputInfo2 defines model for . -type V1ServiceHealthResponseOutputInfo2 struct { - DbSchema string `json:"db_schema"` -} - -// V1ServiceHealthResponseOutput_Info defines model for V1ServiceHealthResponseOutput.Info. -type V1ServiceHealthResponseOutput_Info struct { - union json.RawMessage -} - -// V1ServiceHealthResponseOutputName defines model for V1ServiceHealthResponseOutput.Name. -type V1ServiceHealthResponseOutputName string - -// V1ServiceHealthResponseOutputStatus defines model for V1ServiceHealthResponseOutput.Status. -type V1ServiceHealthResponseOutputStatus string - -// V1StorageBucketResponseOutput defines model for V1StorageBucketResponse_Output. -type V1StorageBucketResponseOutput struct { - CreatedAt string `json:"created_at"` - Id string `json:"id"` - Name string `json:"name"` - Owner string `json:"owner"` - Public bool `json:"public"` - UpdatedAt string `json:"updated_at"` -} - -// V1UndoBody defines model for V1UndoBody. -type V1UndoBody struct { - Name string `json:"name"` -} - -// V1UpdateBackupScheduleBody defines model for V1UpdateBackupScheduleBody. -type V1UpdateBackupScheduleBody struct { - // ScheduleFor Time of day to schedule daily backups, in UTC. Format: HH:MM:SS. - ScheduleFor string `json:"schedule_for"` -} - -// V1UpdateFunctionBody defines model for V1UpdateFunctionBody. -type V1UpdateFunctionBody struct { - Body *string `json:"body,omitempty"` - Name *string `json:"name,omitempty"` - VerifyJwt *bool `json:"verify_jwt,omitempty"` -} - -// V1UpdatePasswordBody defines model for V1UpdatePasswordBody. -type V1UpdatePasswordBody struct { - Password string `json:"password"` -} - -// V1UpdatePasswordResponseOutput defines model for V1UpdatePasswordResponse_Output. -type V1UpdatePasswordResponseOutput struct { - Message string `json:"message"` -} - -// V1UpdatePostgrestConfigBody defines model for V1UpdatePostgrestConfigBody. -type V1UpdatePostgrestConfigBody struct { - DbExtraSearchPath *string `json:"db_extra_search_path,omitempty"` - DbPool *int `json:"db_pool,omitempty"` - DbPoolAcquisitionTimeout *int `json:"db_pool_acquisition_timeout,omitempty"` - DbSchema *string `json:"db_schema,omitempty"` - MaxRows *int `json:"max_rows,omitempty"` -} - -// V1UpdateProjectBody defines model for V1UpdateProjectBody. -type V1UpdateProjectBody struct { - Name string `json:"name"` -} - -// V1UpsertMigrationBody defines model for V1UpsertMigrationBody. -type V1UpsertMigrationBody struct { - Name *string `json:"name,omitempty"` - Query string `json:"query"` - Rollback *string `json:"rollback,omitempty"` -} - -// VanitySubdomainBody defines model for VanitySubdomainBody. -type VanitySubdomainBody struct { - VanitySubdomain string `json:"vanity_subdomain"` -} - -// VanitySubdomainConfigResponseOutput defines model for VanitySubdomainConfigResponse_Output. -type VanitySubdomainConfigResponseOutput struct { - CustomDomain *string `json:"custom_domain,omitempty"` - Status VanitySubdomainConfigResponseOutputStatus `json:"status"` -} - -// VanitySubdomainConfigResponseOutputStatus defines model for VanitySubdomainConfigResponseOutput.Status. -type VanitySubdomainConfigResponseOutputStatus string - -// bearerContextKey is the context key for bearer security scheme -type bearerContextKey string - -// oauth2ContextKey is the context key for oauth2 security scheme -type oauth2ContextKey string - -// V1DeleteABranchParams defines parameters for V1DeleteABranch. -type V1DeleteABranchParams struct { - // Force If set to false, schedule deletion with 1-hour grace period (only when soft deletion is enabled). - Force *string `form:"force,omitempty" json:"force,omitempty"` -} - -// V1DiffABranchParams defines parameters for V1DiffABranch. -type V1DiffABranchParams struct { - IncludedSchemas *string `form:"included_schemas,omitempty" json:"included_schemas,omitempty"` - - // Pgdelta Use pg-delta instead of Migra for diffing when true. - // Boolean string. - // - // Truthy values: `true`, `1`, `yes`, `on`, `y`, `enabled` - // - // Falsy values: `false`, `0`, `no`, `off`, `n`, `disabled` - Pgdelta *string `form:"pgdelta,omitempty" json:"pgdelta,omitempty"` -} - -// V1AuthorizeUserParams defines parameters for V1AuthorizeUser. -type V1AuthorizeUserParams struct { - ClientId openapi_types.UUID `form:"client_id" json:"client_id"` - ResponseType V1AuthorizeUserParamsResponseType `form:"response_type" json:"response_type"` - RedirectUri string `form:"redirect_uri" json:"redirect_uri"` - Scope *string `form:"scope,omitempty" json:"scope,omitempty"` - State *string `form:"state,omitempty" json:"state,omitempty"` - ResponseMode *string `form:"response_mode,omitempty" json:"response_mode,omitempty"` - CodeChallenge *string `form:"code_challenge,omitempty" json:"code_challenge,omitempty"` - CodeChallengeMethod *V1AuthorizeUserParamsCodeChallengeMethod `form:"code_challenge_method,omitempty" json:"code_challenge_method,omitempty"` - - // OrganizationSlug Organization slug - OrganizationSlug *string `form:"organization_slug,omitempty" json:"organization_slug,omitempty"` - TargetFlow *string `form:"target_flow,omitempty" json:"target_flow,omitempty"` - - // Resource Resource indicator for MCP (Model Context Protocol) clients - Resource *string `form:"resource,omitempty" json:"resource,omitempty"` -} - -// V1AuthorizeUserParamsResponseType defines parameters for V1AuthorizeUser. -type V1AuthorizeUserParamsResponseType string - -// V1AuthorizeUserParamsCodeChallengeMethod defines parameters for V1AuthorizeUser. -type V1AuthorizeUserParamsCodeChallengeMethod string - -// V1OauthAuthorizeProjectClaimParams defines parameters for V1OauthAuthorizeProjectClaim. -type V1OauthAuthorizeProjectClaimParams struct { - // ProjectRef Project ref - ProjectRef string `form:"project_ref" json:"project_ref"` - ClientId openapi_types.UUID `form:"client_id" json:"client_id"` - ResponseType V1OauthAuthorizeProjectClaimParamsResponseType `form:"response_type" json:"response_type"` - RedirectUri string `form:"redirect_uri" json:"redirect_uri"` - State *string `form:"state,omitempty" json:"state,omitempty"` - ResponseMode *string `form:"response_mode,omitempty" json:"response_mode,omitempty"` - CodeChallenge *string `form:"code_challenge,omitempty" json:"code_challenge,omitempty"` - CodeChallengeMethod *V1OauthAuthorizeProjectClaimParamsCodeChallengeMethod `form:"code_challenge_method,omitempty" json:"code_challenge_method,omitempty"` -} - -// V1OauthAuthorizeProjectClaimParamsResponseType defines parameters for V1OauthAuthorizeProjectClaim. -type V1OauthAuthorizeProjectClaimParamsResponseType string - -// V1OauthAuthorizeProjectClaimParamsCodeChallengeMethod defines parameters for V1OauthAuthorizeProjectClaim. -type V1OauthAuthorizeProjectClaimParamsCodeChallengeMethod string - -// V1GetAllProjectsForOrganizationParams defines parameters for V1GetAllProjectsForOrganization. -type V1GetAllProjectsForOrganizationParams struct { - // Offset Number of projects to skip - Offset *int `form:"offset,omitempty" json:"offset,omitempty"` - - // Limit Number of projects to return per page - Limit *int `form:"limit,omitempty" json:"limit,omitempty"` - - // Search Search projects by name - Search *string `form:"search,omitempty" json:"search,omitempty"` - - // Sort Sort order for projects - Sort *V1GetAllProjectsForOrganizationParamsSort `form:"sort,omitempty" json:"sort,omitempty"` - - // Statuses A comma-separated list of project statuses to filter by. - // - // The following values are supported: `ACTIVE_HEALTHY`, `INACTIVE`. - Statuses *string `form:"statuses,omitempty" json:"statuses,omitempty"` -} - -// V1GetAllProjectsForOrganizationParamsSort defines parameters for V1GetAllProjectsForOrganization. -type V1GetAllProjectsForOrganizationParamsSort string - -// V1GetAvailableRegionsParams defines parameters for V1GetAvailableRegions. -type V1GetAvailableRegionsParams struct { - // OrganizationSlug Slug of your organization - OrganizationSlug string `form:"organization_slug" json:"organization_slug"` - - // Continent Continent code to determine regional recommendations: NA (North America), SA (South America), EU (Europe), AF (Africa), AS (Asia), OC (Oceania), AN (Antarctica) - Continent *V1GetAvailableRegionsParamsContinent `form:"continent,omitempty" json:"continent,omitempty"` - - // DesiredInstanceSize Desired instance size. Omit this field to always default to the smallest possible size. - DesiredInstanceSize *V1GetAvailableRegionsParamsDesiredInstanceSize `form:"desired_instance_size,omitempty" json:"desired_instance_size,omitempty"` -} - -// V1GetAvailableRegionsParamsContinent defines parameters for V1GetAvailableRegions. -type V1GetAvailableRegionsParamsContinent string - -// V1GetAvailableRegionsParamsDesiredInstanceSize defines parameters for V1GetAvailableRegions. -type V1GetAvailableRegionsParamsDesiredInstanceSize string - -// V1ListActionRunsParams defines parameters for V1ListActionRuns. -type V1ListActionRunsParams struct { - Offset *float32 `form:"offset,omitempty" json:"offset,omitempty"` - Limit *float32 `form:"limit,omitempty" json:"limit,omitempty"` -} - -// V1GetSecurityAdvisorsParams defines parameters for V1GetSecurityAdvisors. -type V1GetSecurityAdvisorsParams struct { - LintType *V1GetSecurityAdvisorsParamsLintType `form:"lint_type,omitempty" json:"lint_type,omitempty"` -} - -// V1GetSecurityAdvisorsParamsLintType defines parameters for V1GetSecurityAdvisors. -type V1GetSecurityAdvisorsParamsLintType string - -// V1GetProjectFunctionCombinedStatsParams defines parameters for V1GetProjectFunctionCombinedStats. -type V1GetProjectFunctionCombinedStatsParams struct { - Interval V1GetProjectFunctionCombinedStatsParamsInterval `form:"interval" json:"interval"` - FunctionId string `form:"function_id" json:"function_id"` -} - -// V1GetProjectFunctionCombinedStatsParamsInterval defines parameters for V1GetProjectFunctionCombinedStats. -type V1GetProjectFunctionCombinedStatsParamsInterval string - -// V1GetProjectLogsParams defines parameters for V1GetProjectLogs. -type V1GetProjectLogsParams struct { - // Sql Custom SQL query to execute on the logs. See [querying logs](https://supabase.com/docs/guides/monitoring-and-debugging/logs#querying-with-the-logs-explorer) for more details. - Sql *string `form:"sql,omitempty" json:"sql,omitempty"` - IsoTimestampStart *time.Time `form:"iso_timestamp_start,omitempty" json:"iso_timestamp_start,omitempty"` - IsoTimestampEnd *time.Time `form:"iso_timestamp_end,omitempty" json:"iso_timestamp_end,omitempty"` -} - -// V1GetProjectUsageApiCountParams defines parameters for V1GetProjectUsageApiCount. -type V1GetProjectUsageApiCountParams struct { - Interval *V1GetProjectUsageApiCountParamsInterval `form:"interval,omitempty" json:"interval,omitempty"` -} - -// V1GetProjectUsageApiCountParamsInterval defines parameters for V1GetProjectUsageApiCount. -type V1GetProjectUsageApiCountParamsInterval string - -// V1GetProjectApiKeysParams defines parameters for V1GetProjectApiKeys. -type V1GetProjectApiKeysParams struct { - // Reveal Boolean string. - // - // Truthy values: `true`, `1`, `yes`, `on`, `y`, `enabled` - // - // Falsy values: `false`, `0`, `no`, `off`, `n`, `disabled` - Reveal *string `form:"reveal,omitempty" json:"reveal,omitempty"` -} - -// V1CreateProjectApiKeyParams defines parameters for V1CreateProjectApiKey. -type V1CreateProjectApiKeyParams struct { - // Reveal Boolean string. - // - // Truthy values: `true`, `1`, `yes`, `on`, `y`, `enabled` - // - // Falsy values: `false`, `0`, `no`, `off`, `n`, `disabled` - Reveal *string `form:"reveal,omitempty" json:"reveal,omitempty"` -} - -// V1UpdateProjectLegacyApiKeysParams defines parameters for V1UpdateProjectLegacyApiKeys. -type V1UpdateProjectLegacyApiKeysParams struct { - // Enabled Boolean string. - // - // Truthy values: `true`, `1`, `yes`, `on`, `y`, `enabled` - // - // Falsy values: `false`, `0`, `no`, `off`, `n`, `disabled` - Enabled string `form:"enabled" json:"enabled"` -} - -// V1DeleteProjectApiKeyParams defines parameters for V1DeleteProjectApiKey. -type V1DeleteProjectApiKeyParams struct { - // Reveal Boolean string, true or false - Reveal *string `form:"reveal,omitempty" json:"reveal,omitempty"` - - // WasCompromised Boolean string, true or false - WasCompromised *string `form:"was_compromised,omitempty" json:"was_compromised,omitempty"` - Reason *string `form:"reason,omitempty" json:"reason,omitempty"` -} - -// V1GetProjectApiKeyParams defines parameters for V1GetProjectApiKey. -type V1GetProjectApiKeyParams struct { - // Reveal Boolean string. - // - // Truthy values: `true`, `1`, `yes`, `on`, `y`, `enabled` - // - // Falsy values: `false`, `0`, `no`, `off`, `n`, `disabled` - Reveal *string `form:"reveal,omitempty" json:"reveal,omitempty"` -} - -// V1UpdateProjectApiKeyParams defines parameters for V1UpdateProjectApiKey. -type V1UpdateProjectApiKeyParams struct { - // Reveal Boolean string. - // - // Truthy values: `true`, `1`, `yes`, `on`, `y`, `enabled` - // - // Falsy values: `false`, `0`, `no`, `off`, `n`, `disabled` - Reveal *string `form:"reveal,omitempty" json:"reveal,omitempty"` -} - -// V1RemoveProjectAddonParamsAddonVariant0 defines parameters for V1RemoveProjectAddon. -type V1RemoveProjectAddonParamsAddonVariant0 string - -// V1RemoveProjectAddonParamsAddonVariant1 defines parameters for V1RemoveProjectAddon. -type V1RemoveProjectAddonParamsAddonVariant1 string - -// V1RemoveProjectAddonParamsAddonVariant2 defines parameters for V1RemoveProjectAddon. -type V1RemoveProjectAddonParamsAddonVariant2 string - -// V1RemoveProjectAddonParamsAddonVariant3 defines parameters for V1RemoveProjectAddon. -type V1RemoveProjectAddonParamsAddonVariant3 string - -// V1DeleteHostnameConfigParams defines parameters for V1DeleteHostnameConfig. -type V1DeleteHostnameConfigParams struct { - // RemoveAddon If true, also removes the custom domain add-on from the project subscription. - RemoveAddon *string `form:"remove_addon,omitempty" json:"remove_addon,omitempty"` -} - -// V1GetRestorePointParams defines parameters for V1GetRestorePoint. -type V1GetRestorePointParams struct { - Name *string `form:"name,omitempty" json:"name,omitempty"` -} - -// V1RollbackMigrationsParams defines parameters for V1RollbackMigrations. -type V1RollbackMigrationsParams struct { - // Gte Rollback migrations greater or equal to this version - Gte string `form:"gte" json:"gte"` -} - -// V1ApplyAMigrationParams defines parameters for V1ApplyAMigration. -type V1ApplyAMigrationParams struct { - // IdempotencyKey A unique key to ensure the same migration is tracked only once. - IdempotencyKey *string `json:"Idempotency-Key,omitempty"` -} - -// V1UpsertAMigrationParams defines parameters for V1UpsertAMigration. -type V1UpsertAMigrationParams struct { - // IdempotencyKey A unique key to ensure the same migration is tracked only once. - IdempotencyKey *string `json:"Idempotency-Key,omitempty"` -} - -// V1GetDatabaseOpenapiParams defines parameters for V1GetDatabaseOpenapi. -type V1GetDatabaseOpenapiParams struct { - // Schema The database schema to generate the OpenAPI spec for - Schema *string `form:"schema,omitempty" json:"schema,omitempty"` -} - -// V1CreateAFunctionParams defines parameters for V1CreateAFunction. -type V1CreateAFunctionParams struct { - Slug *string `form:"slug,omitempty" json:"slug,omitempty"` - Name *string `form:"name,omitempty" json:"name,omitempty"` - VerifyJwt *string `form:"verify_jwt,omitempty" json:"verify_jwt,omitempty"` - ImportMap *string `form:"import_map,omitempty" json:"import_map,omitempty"` - EntrypointPath *string `form:"entrypoint_path,omitempty" json:"entrypoint_path,omitempty"` - ImportMapPath *string `form:"import_map_path,omitempty" json:"import_map_path,omitempty"` - EzbrSha256 *string `form:"ezbr_sha256,omitempty" json:"ezbr_sha256,omitempty"` -} - -// V1DeployAFunctionParams defines parameters for V1DeployAFunction. -type V1DeployAFunctionParams struct { - Slug *string `form:"slug,omitempty" json:"slug,omitempty"` - BundleOnly *string `form:"bundleOnly,omitempty" json:"bundleOnly,omitempty"` -} - -// V1UpdateAFunctionParams defines parameters for V1UpdateAFunction. -type V1UpdateAFunctionParams struct { - Slug *string `form:"slug,omitempty" json:"slug,omitempty"` - Name *string `form:"name,omitempty" json:"name,omitempty"` - VerifyJwt *string `form:"verify_jwt,omitempty" json:"verify_jwt,omitempty"` - ImportMap *string `form:"import_map,omitempty" json:"import_map,omitempty"` - EntrypointPath *string `form:"entrypoint_path,omitempty" json:"entrypoint_path,omitempty"` - ImportMapPath *string `form:"import_map_path,omitempty" json:"import_map_path,omitempty"` - EzbrSha256 *string `form:"ezbr_sha256,omitempty" json:"ezbr_sha256,omitempty"` -} - -// V1GetServicesHealthParams defines parameters for V1GetServicesHealth. -type V1GetServicesHealthParams struct { - // Services Comma-separated list of enums or array of enums. - Services []V1GetServicesHealthParamsServices `form:"services" json:"services"` - TimeoutMs *int `form:"timeout_ms,omitempty" json:"timeout_ms,omitempty"` -} - -// V1GetServicesHealthParamsServices defines parameters for V1GetServicesHealth. -type V1GetServicesHealthParamsServices string - -// V1GetJitAccessConfig200JSONResponseBody0 defines parameters for V1GetJitAccessConfig. -type V1GetJitAccessConfig200JSONResponseBody0 struct { - AppliedSuccessfully *bool `json:"appliedSuccessfully,omitempty"` - State V1GetJitAccessConfig200JSONResponseBody0State `json:"state"` -} - -// V1GetJitAccessConfig200JSONResponseBody0State defines parameters for V1GetJitAccessConfig. -type V1GetJitAccessConfig200JSONResponseBody0State string - -// V1GetJitAccessConfig200JSONResponseBody1 defines parameters for V1GetJitAccessConfig. -type V1GetJitAccessConfig200JSONResponseBody1 struct { - State string `json:"state"` - UnavailableReason V1GetJitAccessConfig200JSONResponseBody1UnavailableReason `json:"unavailableReason"` -} - -// V1GetJitAccessConfig200JSONResponseBody1UnavailableReason defines parameters for V1GetJitAccessConfig. -type V1GetJitAccessConfig200JSONResponseBody1UnavailableReason string - -// V1GetJitAccessConfig200JSONResponseBody defines parameters for V1GetJitAccessConfig. -type V1GetJitAccessConfig200JSONResponseBody struct { - union json.RawMessage -} - -// V1UpdateJitAccessConfig200JSONResponseBody0 defines parameters for V1UpdateJitAccessConfig. -type V1UpdateJitAccessConfig200JSONResponseBody0 struct { - AppliedSuccessfully *bool `json:"appliedSuccessfully,omitempty"` - State V1UpdateJitAccessConfig200JSONResponseBody0State `json:"state"` -} - -// V1UpdateJitAccessConfig200JSONResponseBody0State defines parameters for V1UpdateJitAccessConfig. -type V1UpdateJitAccessConfig200JSONResponseBody0State string - -// V1UpdateJitAccessConfig200JSONResponseBody1 defines parameters for V1UpdateJitAccessConfig. -type V1UpdateJitAccessConfig200JSONResponseBody1 struct { - State string `json:"state"` - UnavailableReason V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReason `json:"unavailableReason"` -} - -// V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReason defines parameters for V1UpdateJitAccessConfig. -type V1UpdateJitAccessConfig200JSONResponseBody1UnavailableReason string - -// V1UpdateJitAccessConfig200JSONResponseBody defines parameters for V1UpdateJitAccessConfig. -type V1UpdateJitAccessConfig200JSONResponseBody struct { - union json.RawMessage -} - -// V1GenerateTypescriptTypesParams defines parameters for V1GenerateTypescriptTypes. -type V1GenerateTypescriptTypesParams struct { - IncludedSchemas *string `form:"included_schemas,omitempty" json:"included_schemas,omitempty"` -} - -// V1GetPostgresUpgradeStatusParams defines parameters for V1GetPostgresUpgradeStatus. -type V1GetPostgresUpgradeStatusParams struct { - TrackingId *string `form:"tracking_id,omitempty" json:"tracking_id,omitempty"` -} - -// V1ListAllSnippetsParams defines parameters for V1ListAllSnippets. -type V1ListAllSnippetsParams struct { - // ProjectRef Project ref - ProjectRef *string `form:"project_ref,omitempty" json:"project_ref,omitempty"` - Cursor *string `form:"cursor,omitempty" json:"cursor,omitempty"` - Limit *string `form:"limit,omitempty" json:"limit,omitempty"` - SortBy *V1ListAllSnippetsParamsSortBy `form:"sort_by,omitempty" json:"sort_by,omitempty"` - SortOrder *V1ListAllSnippetsParamsSortOrder `form:"sort_order,omitempty" json:"sort_order,omitempty"` -} - -// V1ListAllSnippetsParamsSortBy defines parameters for V1ListAllSnippets. -type V1ListAllSnippetsParamsSortBy string - -// V1ListAllSnippetsParamsSortOrder defines parameters for V1ListAllSnippets. -type V1ListAllSnippetsParamsSortOrder string - -// V1UpdateABranchConfigJSONRequestBody defines body for V1UpdateABranchConfig for application/json ContentType. -type V1UpdateABranchConfigJSONRequestBody = UpdateBranchBody - -// V1MergeABranchJSONRequestBody defines body for V1MergeABranch for application/json ContentType. -type V1MergeABranchJSONRequestBody = BranchActionBody - -// V1PushABranchJSONRequestBody defines body for V1PushABranch for application/json ContentType. -type V1PushABranchJSONRequestBody = BranchActionBody - -// V1ResetABranchJSONRequestBody defines body for V1ResetABranch for application/json ContentType. -type V1ResetABranchJSONRequestBody = BranchActionBody - -// V1RevokeTokenJSONRequestBody defines body for V1RevokeToken for application/json ContentType. -type V1RevokeTokenJSONRequestBody = OAuthRevokeTokenBody - -// V1ExchangeOauthTokenFormdataRequestBody defines body for V1ExchangeOauthToken for application/x-www-form-urlencoded ContentType. -type V1ExchangeOauthTokenFormdataRequestBody = OAuthTokenBody - -// V1CreateAnOrganizationJSONRequestBody defines body for V1CreateAnOrganization for application/json ContentType. -type V1CreateAnOrganizationJSONRequestBody = CreateOrganizationV1 - -// V1CreateAProjectJSONRequestBody defines body for V1CreateAProject for application/json ContentType. -type V1CreateAProjectJSONRequestBody = V1CreateProjectBody - -// V1UpdateAProjectJSONRequestBody defines body for V1UpdateAProject for application/json ContentType. -type V1UpdateAProjectJSONRequestBody = V1UpdateProjectBody - -// V1UpdateActionRunStatusJSONRequestBody defines body for V1UpdateActionRunStatus for application/json ContentType. -type V1UpdateActionRunStatusJSONRequestBody = UpdateRunStatusBody - -// V1CreateProjectApiKeyJSONRequestBody defines body for V1CreateProjectApiKey for application/json ContentType. -type V1CreateProjectApiKeyJSONRequestBody = CreateApiKeyBody - -// V1UpdateProjectApiKeyJSONRequestBody defines body for V1UpdateProjectApiKey for application/json ContentType. -type V1UpdateProjectApiKeyJSONRequestBody = UpdateApiKeyBody - -// V1ApplyProjectAddonJSONRequestBody defines body for V1ApplyProjectAddon for application/json ContentType. -type V1ApplyProjectAddonJSONRequestBody = ApplyProjectAddonBody - -// V1CreateABranchJSONRequestBody defines body for V1CreateABranch for application/json ContentType. -type V1CreateABranchJSONRequestBody = CreateBranchBody - -// V1CreateLoginRoleJSONRequestBody defines body for V1CreateLoginRole for application/json ContentType. -type V1CreateLoginRoleJSONRequestBody = CreateRoleBody - -// V1UpdateAuthServiceConfigJSONRequestBody defines body for V1UpdateAuthServiceConfig for application/json ContentType. -type V1UpdateAuthServiceConfigJSONRequestBody = UpdateAuthConfigBody - -// V1CreateProjectSigningKeyJSONRequestBody defines body for V1CreateProjectSigningKey for application/json ContentType. -type V1CreateProjectSigningKeyJSONRequestBody = CreateSigningKeyBody - -// V1UpdateProjectSigningKeyJSONRequestBody defines body for V1UpdateProjectSigningKey for application/json ContentType. -type V1UpdateProjectSigningKeyJSONRequestBody = UpdateSigningKeyBody - -// V1CreateASsoProviderJSONRequestBody defines body for V1CreateASsoProvider for application/json ContentType. -type V1CreateASsoProviderJSONRequestBody = CreateProviderBody - -// V1UpdateASsoProviderJSONRequestBody defines body for V1UpdateASsoProvider for application/json ContentType. -type V1UpdateASsoProviderJSONRequestBody = UpdateProviderBody - -// V1CreateProjectTpaIntegrationJSONRequestBody defines body for V1CreateProjectTpaIntegration for application/json ContentType. -type V1CreateProjectTpaIntegrationJSONRequestBody = CreateThirdPartyAuthBody - -// V1UpdatePoolerConfigJSONRequestBody defines body for V1UpdatePoolerConfig for application/json ContentType. -type V1UpdatePoolerConfigJSONRequestBody = UpdateSupavisorConfigBody - -// V1UpdatePostgresConfigJSONRequestBody defines body for V1UpdatePostgresConfig for application/json ContentType. -type V1UpdatePostgresConfigJSONRequestBody = UpdatePostgresConfigBody - -// V1ModifyDatabaseDiskJSONRequestBody defines body for V1ModifyDatabaseDisk for application/json ContentType. -type V1ModifyDatabaseDiskJSONRequestBody = DiskRequestBody - -// V1UpdateRealtimeConfigJSONRequestBody defines body for V1UpdateRealtimeConfig for application/json ContentType. -type V1UpdateRealtimeConfigJSONRequestBody = UpdateRealtimeConfigBody - -// V1UpdateStorageConfigJSONRequestBody defines body for V1UpdateStorageConfig for application/json ContentType. -type V1UpdateStorageConfigJSONRequestBody = UpdateStorageConfigBody - -// V1UpdateHostnameConfigJSONRequestBody defines body for V1UpdateHostnameConfig for application/json ContentType. -type V1UpdateHostnameConfigJSONRequestBody = UpdateCustomHostnameBody - -// V1RestorePhysicalBackupJSONRequestBody defines body for V1RestorePhysicalBackup for application/json ContentType. -type V1RestorePhysicalBackupJSONRequestBody = V1RestoreBackupBody - -// V1RestorePitrBackupJSONRequestBody defines body for V1RestorePitrBackup for application/json ContentType. -type V1RestorePitrBackupJSONRequestBody = V1RestorePitrBody - -// V1CreateRestorePointJSONRequestBody defines body for V1CreateRestorePoint for application/json ContentType. -type V1CreateRestorePointJSONRequestBody = V1RestorePointPostBody - -// V1UpdateBackupScheduleJSONRequestBody defines body for V1UpdateBackupSchedule for application/json ContentType. -type V1UpdateBackupScheduleJSONRequestBody = V1UpdateBackupScheduleBody - -// V1UndoJSONRequestBody defines body for V1Undo for application/json ContentType. -type V1UndoJSONRequestBody = V1UndoBody - -// V1AuthorizeJitAccessJSONRequestBody defines body for V1AuthorizeJitAccess for application/json ContentType. -type V1AuthorizeJitAccessJSONRequestBody = AuthorizeJitAccessBody - -// V1UpdateJitAccessJSONRequestBody defines body for V1UpdateJitAccess for application/json ContentType. -type V1UpdateJitAccessJSONRequestBody = UpdateJitAccessBody - -// V1InviteExternalJitAccessJSONRequestBody defines body for V1InviteExternalJitAccess for application/json ContentType. -type V1InviteExternalJitAccessJSONRequestBody = InviteExternalUserJitAccessBody - -// V1AcceptInviteExternalJitAccessJSONRequestBody defines body for V1AcceptInviteExternalJitAccess for application/json ContentType. -type V1AcceptInviteExternalJitAccessJSONRequestBody = AcceptInviteExternalUserJitAccessBody - -// V1ApplyAMigrationJSONRequestBody defines body for V1ApplyAMigration for application/json ContentType. -type V1ApplyAMigrationJSONRequestBody = V1CreateMigrationBody - -// V1UpsertAMigrationJSONRequestBody defines body for V1UpsertAMigration for application/json ContentType. -type V1UpsertAMigrationJSONRequestBody = V1UpsertMigrationBody - -// V1PatchAMigrationJSONRequestBody defines body for V1PatchAMigration for application/json ContentType. -type V1PatchAMigrationJSONRequestBody = V1PatchMigrationBody - -// V1UpdateDatabasePasswordJSONRequestBody defines body for V1UpdateDatabasePassword for application/json ContentType. -type V1UpdateDatabasePasswordJSONRequestBody = V1UpdatePasswordBody - -// V1RunAQueryJSONRequestBody defines body for V1RunAQuery for application/json ContentType. -type V1RunAQueryJSONRequestBody = V1RunQueryBody - -// V1ReadOnlyQueryJSONRequestBody defines body for V1ReadOnlyQuery for application/json ContentType. -type V1ReadOnlyQueryJSONRequestBody = V1ReadOnlyQueryBody - -// V1CreateAFunctionJSONRequestBody defines body for V1CreateAFunction for application/json ContentType. -type V1CreateAFunctionJSONRequestBody = V1CreateFunctionBody - -// V1BulkUpdateFunctionsJSONRequestBody defines body for V1BulkUpdateFunctions for application/json ContentType. -type V1BulkUpdateFunctionsJSONRequestBody = BulkUpdateFunctionBody - -// V1DeployAFunctionMultipartRequestBody defines body for V1DeployAFunction for multipart/form-data ContentType. -type V1DeployAFunctionMultipartRequestBody = FunctionDeployBody - -// V1UpdateAFunctionJSONRequestBody defines body for V1UpdateAFunction for application/json ContentType. -type V1UpdateAFunctionJSONRequestBody = V1UpdateFunctionBody - -// V1UpdateJitAccessConfigJSONRequestBody defines body for V1UpdateJitAccessConfig for application/json ContentType. -type V1UpdateJitAccessConfigJSONRequestBody = JitAccessRequestRequest - -// V1DeleteNetworkBansJSONRequestBody defines body for V1DeleteNetworkBans for application/json ContentType. -type V1DeleteNetworkBansJSONRequestBody = RemoveNetworkBanRequest - -// V1PatchNetworkRestrictionsJSONRequestBody defines body for V1PatchNetworkRestrictions for application/json ContentType. -type V1PatchNetworkRestrictionsJSONRequestBody = NetworkRestrictionsPatchRequest - -// V1UpdateNetworkRestrictionsJSONRequestBody defines body for V1UpdateNetworkRestrictions for application/json ContentType. -type V1UpdateNetworkRestrictionsJSONRequestBody = NetworkRestrictionsRequest - -// V1UpdatePgsodiumConfigJSONRequestBody defines body for V1UpdatePgsodiumConfig for application/json ContentType. -type V1UpdatePgsodiumConfigJSONRequestBody = UpdatePgsodiumConfigBody - -// V1UpdatePostgrestServiceConfigJSONRequestBody defines body for V1UpdatePostgrestServiceConfig for application/json ContentType. -type V1UpdatePostgrestServiceConfigJSONRequestBody = V1UpdatePostgrestConfigBody - -// V1RemoveAReadReplicaJSONRequestBody defines body for V1RemoveAReadReplica for application/json ContentType. -type V1RemoveAReadReplicaJSONRequestBody = RemoveReadReplicaBody - -// V1SetupAReadReplicaJSONRequestBody defines body for V1SetupAReadReplica for application/json ContentType. -type V1SetupAReadReplicaJSONRequestBody = SetUpReadReplicaBody - -// V1BulkDeleteSecretsJSONRequestBody defines body for V1BulkDeleteSecrets for application/json ContentType. -type V1BulkDeleteSecretsJSONRequestBody = DeleteSecretsBody - -// V1BulkCreateSecretsJSONRequestBody defines body for V1BulkCreateSecrets for application/json ContentType. -type V1BulkCreateSecretsJSONRequestBody = CreateSecretBody - -// V1UpdateSslEnforcementConfigJSONRequestBody defines body for V1UpdateSslEnforcementConfig for application/json ContentType. -type V1UpdateSslEnforcementConfigJSONRequestBody = SslEnforcementRequest - -// V1UpgradePostgresVersionJSONRequestBody defines body for V1UpgradePostgresVersion for application/json ContentType. -type V1UpgradePostgresVersionJSONRequestBody = UpgradeDatabaseBody - -// V1ActivateVanitySubdomainConfigJSONRequestBody defines body for V1ActivateVanitySubdomainConfig for application/json ContentType. -type V1ActivateVanitySubdomainConfigJSONRequestBody = VanitySubdomainBody - -// V1CheckVanitySubdomainAvailabilityJSONRequestBody defines body for V1CheckVanitySubdomainAvailability for application/json ContentType. -type V1CheckVanitySubdomainAvailabilityJSONRequestBody = VanitySubdomainBody - -// AsAnalyticsResponseOutputError0 returns the union data inside the AnalyticsResponseOutput_Error as a AnalyticsResponseOutputError0 -func (t AnalyticsResponseOutput_Error) AsAnalyticsResponseOutputError0() (AnalyticsResponseOutputError0, error) { - var body AnalyticsResponseOutputError0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromAnalyticsResponseOutputError0 overwrites any union data inside the AnalyticsResponseOutput_Error as the provided AnalyticsResponseOutputError0 -func (t *AnalyticsResponseOutput_Error) FromAnalyticsResponseOutputError0(v AnalyticsResponseOutputError0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeAnalyticsResponseOutputError0 performs a merge with any union data inside the AnalyticsResponseOutput_Error, using the provided AnalyticsResponseOutputError0 -func (t *AnalyticsResponseOutput_Error) MergeAnalyticsResponseOutputError0(v AnalyticsResponseOutputError0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsAnalyticsResponseOutputError1 returns the union data inside the AnalyticsResponseOutput_Error as a AnalyticsResponseOutputError1 -func (t AnalyticsResponseOutput_Error) AsAnalyticsResponseOutputError1() (AnalyticsResponseOutputError1, error) { - var body AnalyticsResponseOutputError1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromAnalyticsResponseOutputError1 overwrites any union data inside the AnalyticsResponseOutput_Error as the provided AnalyticsResponseOutputError1 -func (t *AnalyticsResponseOutput_Error) FromAnalyticsResponseOutputError1(v AnalyticsResponseOutputError1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeAnalyticsResponseOutputError1 performs a merge with any union data inside the AnalyticsResponseOutput_Error, using the provided AnalyticsResponseOutputError1 -func (t *AnalyticsResponseOutput_Error) MergeAnalyticsResponseOutputError1(v AnalyticsResponseOutputError1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t AnalyticsResponseOutput_Error) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *AnalyticsResponseOutput_Error) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsApplyProjectAddonBodyAddonVariant0 returns the union data inside the ApplyProjectAddonBody_AddonVariant as a ApplyProjectAddonBodyAddonVariant0 -func (t ApplyProjectAddonBody_AddonVariant) AsApplyProjectAddonBodyAddonVariant0() (ApplyProjectAddonBodyAddonVariant0, error) { - var body ApplyProjectAddonBodyAddonVariant0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromApplyProjectAddonBodyAddonVariant0 overwrites any union data inside the ApplyProjectAddonBody_AddonVariant as the provided ApplyProjectAddonBodyAddonVariant0 -func (t *ApplyProjectAddonBody_AddonVariant) FromApplyProjectAddonBodyAddonVariant0(v ApplyProjectAddonBodyAddonVariant0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeApplyProjectAddonBodyAddonVariant0 performs a merge with any union data inside the ApplyProjectAddonBody_AddonVariant, using the provided ApplyProjectAddonBodyAddonVariant0 -func (t *ApplyProjectAddonBody_AddonVariant) MergeApplyProjectAddonBodyAddonVariant0(v ApplyProjectAddonBodyAddonVariant0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsApplyProjectAddonBodyAddonVariant1 returns the union data inside the ApplyProjectAddonBody_AddonVariant as a ApplyProjectAddonBodyAddonVariant1 -func (t ApplyProjectAddonBody_AddonVariant) AsApplyProjectAddonBodyAddonVariant1() (ApplyProjectAddonBodyAddonVariant1, error) { - var body ApplyProjectAddonBodyAddonVariant1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromApplyProjectAddonBodyAddonVariant1 overwrites any union data inside the ApplyProjectAddonBody_AddonVariant as the provided ApplyProjectAddonBodyAddonVariant1 -func (t *ApplyProjectAddonBody_AddonVariant) FromApplyProjectAddonBodyAddonVariant1(v ApplyProjectAddonBodyAddonVariant1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeApplyProjectAddonBodyAddonVariant1 performs a merge with any union data inside the ApplyProjectAddonBody_AddonVariant, using the provided ApplyProjectAddonBodyAddonVariant1 -func (t *ApplyProjectAddonBody_AddonVariant) MergeApplyProjectAddonBodyAddonVariant1(v ApplyProjectAddonBodyAddonVariant1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsApplyProjectAddonBodyAddonVariant2 returns the union data inside the ApplyProjectAddonBody_AddonVariant as a ApplyProjectAddonBodyAddonVariant2 -func (t ApplyProjectAddonBody_AddonVariant) AsApplyProjectAddonBodyAddonVariant2() (ApplyProjectAddonBodyAddonVariant2, error) { - var body ApplyProjectAddonBodyAddonVariant2 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromApplyProjectAddonBodyAddonVariant2 overwrites any union data inside the ApplyProjectAddonBody_AddonVariant as the provided ApplyProjectAddonBodyAddonVariant2 -func (t *ApplyProjectAddonBody_AddonVariant) FromApplyProjectAddonBodyAddonVariant2(v ApplyProjectAddonBodyAddonVariant2) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeApplyProjectAddonBodyAddonVariant2 performs a merge with any union data inside the ApplyProjectAddonBody_AddonVariant, using the provided ApplyProjectAddonBodyAddonVariant2 -func (t *ApplyProjectAddonBody_AddonVariant) MergeApplyProjectAddonBodyAddonVariant2(v ApplyProjectAddonBodyAddonVariant2) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsApplyProjectAddonBodyAddonVariant3 returns the union data inside the ApplyProjectAddonBody_AddonVariant as a ApplyProjectAddonBodyAddonVariant3 -func (t ApplyProjectAddonBody_AddonVariant) AsApplyProjectAddonBodyAddonVariant3() (ApplyProjectAddonBodyAddonVariant3, error) { - var body ApplyProjectAddonBodyAddonVariant3 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromApplyProjectAddonBodyAddonVariant3 overwrites any union data inside the ApplyProjectAddonBody_AddonVariant as the provided ApplyProjectAddonBodyAddonVariant3 -func (t *ApplyProjectAddonBody_AddonVariant) FromApplyProjectAddonBodyAddonVariant3(v ApplyProjectAddonBodyAddonVariant3) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeApplyProjectAddonBodyAddonVariant3 performs a merge with any union data inside the ApplyProjectAddonBody_AddonVariant, using the provided ApplyProjectAddonBodyAddonVariant3 -func (t *ApplyProjectAddonBody_AddonVariant) MergeApplyProjectAddonBodyAddonVariant3(v ApplyProjectAddonBodyAddonVariant3) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t ApplyProjectAddonBody_AddonVariant) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *ApplyProjectAddonBody_AddonVariant) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsAuthorizeJitAccessBodyRhost0 returns the union data inside the AuthorizeJitAccessBody_Rhost as a AuthorizeJitAccessBodyRhost0 -func (t AuthorizeJitAccessBody_Rhost) AsAuthorizeJitAccessBodyRhost0() (AuthorizeJitAccessBodyRhost0, error) { - var body AuthorizeJitAccessBodyRhost0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromAuthorizeJitAccessBodyRhost0 overwrites any union data inside the AuthorizeJitAccessBody_Rhost as the provided AuthorizeJitAccessBodyRhost0 -func (t *AuthorizeJitAccessBody_Rhost) FromAuthorizeJitAccessBodyRhost0(v AuthorizeJitAccessBodyRhost0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeAuthorizeJitAccessBodyRhost0 performs a merge with any union data inside the AuthorizeJitAccessBody_Rhost, using the provided AuthorizeJitAccessBodyRhost0 -func (t *AuthorizeJitAccessBody_Rhost) MergeAuthorizeJitAccessBodyRhost0(v AuthorizeJitAccessBodyRhost0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsAuthorizeJitAccessBodyRhost1 returns the union data inside the AuthorizeJitAccessBody_Rhost as a AuthorizeJitAccessBodyRhost1 -func (t AuthorizeJitAccessBody_Rhost) AsAuthorizeJitAccessBodyRhost1() (AuthorizeJitAccessBodyRhost1, error) { - var body AuthorizeJitAccessBodyRhost1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromAuthorizeJitAccessBodyRhost1 overwrites any union data inside the AuthorizeJitAccessBody_Rhost as the provided AuthorizeJitAccessBodyRhost1 -func (t *AuthorizeJitAccessBody_Rhost) FromAuthorizeJitAccessBodyRhost1(v AuthorizeJitAccessBodyRhost1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeAuthorizeJitAccessBodyRhost1 performs a merge with any union data inside the AuthorizeJitAccessBody_Rhost, using the provided AuthorizeJitAccessBodyRhost1 -func (t *AuthorizeJitAccessBody_Rhost) MergeAuthorizeJitAccessBodyRhost1(v AuthorizeJitAccessBodyRhost1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t AuthorizeJitAccessBody_Rhost) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *AuthorizeJitAccessBody_Rhost) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsCreateSigningKeyBodyPrivateJwk0 returns the union data inside the CreateSigningKeyBody_PrivateJwk as a CreateSigningKeyBodyPrivateJwk0 -func (t CreateSigningKeyBody_PrivateJwk) AsCreateSigningKeyBodyPrivateJwk0() (CreateSigningKeyBodyPrivateJwk0, error) { - var body CreateSigningKeyBodyPrivateJwk0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromCreateSigningKeyBodyPrivateJwk0 overwrites any union data inside the CreateSigningKeyBody_PrivateJwk as the provided CreateSigningKeyBodyPrivateJwk0 -func (t *CreateSigningKeyBody_PrivateJwk) FromCreateSigningKeyBodyPrivateJwk0(v CreateSigningKeyBodyPrivateJwk0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeCreateSigningKeyBodyPrivateJwk0 performs a merge with any union data inside the CreateSigningKeyBody_PrivateJwk, using the provided CreateSigningKeyBodyPrivateJwk0 -func (t *CreateSigningKeyBody_PrivateJwk) MergeCreateSigningKeyBodyPrivateJwk0(v CreateSigningKeyBodyPrivateJwk0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsCreateSigningKeyBodyPrivateJwk1 returns the union data inside the CreateSigningKeyBody_PrivateJwk as a CreateSigningKeyBodyPrivateJwk1 -func (t CreateSigningKeyBody_PrivateJwk) AsCreateSigningKeyBodyPrivateJwk1() (CreateSigningKeyBodyPrivateJwk1, error) { - var body CreateSigningKeyBodyPrivateJwk1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromCreateSigningKeyBodyPrivateJwk1 overwrites any union data inside the CreateSigningKeyBody_PrivateJwk as the provided CreateSigningKeyBodyPrivateJwk1 -func (t *CreateSigningKeyBody_PrivateJwk) FromCreateSigningKeyBodyPrivateJwk1(v CreateSigningKeyBodyPrivateJwk1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeCreateSigningKeyBodyPrivateJwk1 performs a merge with any union data inside the CreateSigningKeyBody_PrivateJwk, using the provided CreateSigningKeyBodyPrivateJwk1 -func (t *CreateSigningKeyBody_PrivateJwk) MergeCreateSigningKeyBodyPrivateJwk1(v CreateSigningKeyBodyPrivateJwk1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsCreateSigningKeyBodyPrivateJwk2 returns the union data inside the CreateSigningKeyBody_PrivateJwk as a CreateSigningKeyBodyPrivateJwk2 -func (t CreateSigningKeyBody_PrivateJwk) AsCreateSigningKeyBodyPrivateJwk2() (CreateSigningKeyBodyPrivateJwk2, error) { - var body CreateSigningKeyBodyPrivateJwk2 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromCreateSigningKeyBodyPrivateJwk2 overwrites any union data inside the CreateSigningKeyBody_PrivateJwk as the provided CreateSigningKeyBodyPrivateJwk2 -func (t *CreateSigningKeyBody_PrivateJwk) FromCreateSigningKeyBodyPrivateJwk2(v CreateSigningKeyBodyPrivateJwk2) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeCreateSigningKeyBodyPrivateJwk2 performs a merge with any union data inside the CreateSigningKeyBody_PrivateJwk, using the provided CreateSigningKeyBodyPrivateJwk2 -func (t *CreateSigningKeyBody_PrivateJwk) MergeCreateSigningKeyBodyPrivateJwk2(v CreateSigningKeyBodyPrivateJwk2) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsCreateSigningKeyBodyPrivateJwk3 returns the union data inside the CreateSigningKeyBody_PrivateJwk as a CreateSigningKeyBodyPrivateJwk3 -func (t CreateSigningKeyBody_PrivateJwk) AsCreateSigningKeyBodyPrivateJwk3() (CreateSigningKeyBodyPrivateJwk3, error) { - var body CreateSigningKeyBodyPrivateJwk3 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromCreateSigningKeyBodyPrivateJwk3 overwrites any union data inside the CreateSigningKeyBody_PrivateJwk as the provided CreateSigningKeyBodyPrivateJwk3 -func (t *CreateSigningKeyBody_PrivateJwk) FromCreateSigningKeyBodyPrivateJwk3(v CreateSigningKeyBodyPrivateJwk3) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeCreateSigningKeyBodyPrivateJwk3 performs a merge with any union data inside the CreateSigningKeyBody_PrivateJwk, using the provided CreateSigningKeyBodyPrivateJwk3 -func (t *CreateSigningKeyBody_PrivateJwk) MergeCreateSigningKeyBodyPrivateJwk3(v CreateSigningKeyBodyPrivateJwk3) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t CreateSigningKeyBody_PrivateJwk) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *CreateSigningKeyBody_PrivateJwk) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsDiskRequestBodyAttributes0 returns the union data inside the DiskRequestBody_Attributes as a DiskRequestBodyAttributes0 -func (t DiskRequestBody_Attributes) AsDiskRequestBodyAttributes0() (DiskRequestBodyAttributes0, error) { - var body DiskRequestBodyAttributes0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromDiskRequestBodyAttributes0 overwrites any union data inside the DiskRequestBody_Attributes as the provided DiskRequestBodyAttributes0 -func (t *DiskRequestBody_Attributes) FromDiskRequestBodyAttributes0(v DiskRequestBodyAttributes0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeDiskRequestBodyAttributes0 performs a merge with any union data inside the DiskRequestBody_Attributes, using the provided DiskRequestBodyAttributes0 -func (t *DiskRequestBody_Attributes) MergeDiskRequestBodyAttributes0(v DiskRequestBodyAttributes0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsDiskRequestBodyAttributes1 returns the union data inside the DiskRequestBody_Attributes as a DiskRequestBodyAttributes1 -func (t DiskRequestBody_Attributes) AsDiskRequestBodyAttributes1() (DiskRequestBodyAttributes1, error) { - var body DiskRequestBodyAttributes1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromDiskRequestBodyAttributes1 overwrites any union data inside the DiskRequestBody_Attributes as the provided DiskRequestBodyAttributes1 -func (t *DiskRequestBody_Attributes) FromDiskRequestBodyAttributes1(v DiskRequestBodyAttributes1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeDiskRequestBodyAttributes1 performs a merge with any union data inside the DiskRequestBody_Attributes, using the provided DiskRequestBodyAttributes1 -func (t *DiskRequestBody_Attributes) MergeDiskRequestBodyAttributes1(v DiskRequestBodyAttributes1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t DiskRequestBody_Attributes) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *DiskRequestBody_Attributes) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsDiskResponseOutputAttributes0 returns the union data inside the DiskResponseOutput_Attributes as a DiskResponseOutputAttributes0 -func (t DiskResponseOutput_Attributes) AsDiskResponseOutputAttributes0() (DiskResponseOutputAttributes0, error) { - var body DiskResponseOutputAttributes0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromDiskResponseOutputAttributes0 overwrites any union data inside the DiskResponseOutput_Attributes as the provided DiskResponseOutputAttributes0 -func (t *DiskResponseOutput_Attributes) FromDiskResponseOutputAttributes0(v DiskResponseOutputAttributes0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeDiskResponseOutputAttributes0 performs a merge with any union data inside the DiskResponseOutput_Attributes, using the provided DiskResponseOutputAttributes0 -func (t *DiskResponseOutput_Attributes) MergeDiskResponseOutputAttributes0(v DiskResponseOutputAttributes0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsDiskResponseOutputAttributes1 returns the union data inside the DiskResponseOutput_Attributes as a DiskResponseOutputAttributes1 -func (t DiskResponseOutput_Attributes) AsDiskResponseOutputAttributes1() (DiskResponseOutputAttributes1, error) { - var body DiskResponseOutputAttributes1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromDiskResponseOutputAttributes1 overwrites any union data inside the DiskResponseOutput_Attributes as the provided DiskResponseOutputAttributes1 -func (t *DiskResponseOutput_Attributes) FromDiskResponseOutputAttributes1(v DiskResponseOutputAttributes1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeDiskResponseOutputAttributes1 performs a merge with any union data inside the DiskResponseOutput_Attributes, using the provided DiskResponseOutputAttributes1 -func (t *DiskResponseOutput_Attributes) MergeDiskResponseOutputAttributes1(v DiskResponseOutputAttributes1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t DiskResponseOutput_Attributes) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *DiskResponseOutput_Attributes) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsJitListAccessResponseOutputItems0 returns the union data inside the JitListAccessResponseOutput_Items_Item as a JitListAccessResponseOutputItems0 -func (t JitListAccessResponseOutput_Items_Item) AsJitListAccessResponseOutputItems0() (JitListAccessResponseOutputItems0, error) { - var body JitListAccessResponseOutputItems0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromJitListAccessResponseOutputItems0 overwrites any union data inside the JitListAccessResponseOutput_Items_Item as the provided JitListAccessResponseOutputItems0 -func (t *JitListAccessResponseOutput_Items_Item) FromJitListAccessResponseOutputItems0(v JitListAccessResponseOutputItems0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeJitListAccessResponseOutputItems0 performs a merge with any union data inside the JitListAccessResponseOutput_Items_Item, using the provided JitListAccessResponseOutputItems0 -func (t *JitListAccessResponseOutput_Items_Item) MergeJitListAccessResponseOutputItems0(v JitListAccessResponseOutputItems0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsJitListAccessResponseOutputItems1 returns the union data inside the JitListAccessResponseOutput_Items_Item as a JitListAccessResponseOutputItems1 -func (t JitListAccessResponseOutput_Items_Item) AsJitListAccessResponseOutputItems1() (JitListAccessResponseOutputItems1, error) { - var body JitListAccessResponseOutputItems1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromJitListAccessResponseOutputItems1 overwrites any union data inside the JitListAccessResponseOutput_Items_Item as the provided JitListAccessResponseOutputItems1 -func (t *JitListAccessResponseOutput_Items_Item) FromJitListAccessResponseOutputItems1(v JitListAccessResponseOutputItems1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeJitListAccessResponseOutputItems1 performs a merge with any union data inside the JitListAccessResponseOutput_Items_Item, using the provided JitListAccessResponseOutputItems1 -func (t *JitListAccessResponseOutput_Items_Item) MergeJitListAccessResponseOutputItems1(v JitListAccessResponseOutputItems1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t JitListAccessResponseOutput_Items_Item) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *JitListAccessResponseOutput_Items_Item) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsJsonValueOutput0 returns the union data inside the JsonValueOutput as a JsonValueOutput0 -func (t JsonValueOutput) AsJsonValueOutput0() (JsonValueOutput0, error) { - var body JsonValueOutput0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromJsonValueOutput0 overwrites any union data inside the JsonValueOutput as the provided JsonValueOutput0 -func (t *JsonValueOutput) FromJsonValueOutput0(v JsonValueOutput0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeJsonValueOutput0 performs a merge with any union data inside the JsonValueOutput, using the provided JsonValueOutput0 -func (t *JsonValueOutput) MergeJsonValueOutput0(v JsonValueOutput0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsJsonValueOutput1 returns the union data inside the JsonValueOutput as a JsonValueOutput1 -func (t JsonValueOutput) AsJsonValueOutput1() (JsonValueOutput1, error) { - var body JsonValueOutput1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromJsonValueOutput1 overwrites any union data inside the JsonValueOutput as the provided JsonValueOutput1 -func (t *JsonValueOutput) FromJsonValueOutput1(v JsonValueOutput1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeJsonValueOutput1 performs a merge with any union data inside the JsonValueOutput, using the provided JsonValueOutput1 -func (t *JsonValueOutput) MergeJsonValueOutput1(v JsonValueOutput1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsJsonValueOutput2 returns the union data inside the JsonValueOutput as a JsonValueOutput2 -func (t JsonValueOutput) AsJsonValueOutput2() (JsonValueOutput2, error) { - var body JsonValueOutput2 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromJsonValueOutput2 overwrites any union data inside the JsonValueOutput as the provided JsonValueOutput2 -func (t *JsonValueOutput) FromJsonValueOutput2(v JsonValueOutput2) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeJsonValueOutput2 performs a merge with any union data inside the JsonValueOutput, using the provided JsonValueOutput2 -func (t *JsonValueOutput) MergeJsonValueOutput2(v JsonValueOutput2) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t JsonValueOutput) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *JsonValueOutput) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsJsonValueOutput00 returns the union data inside the JsonValueOutput0 as a JsonValueOutput00 -func (t JsonValueOutput0) AsJsonValueOutput00() (JsonValueOutput00, error) { - var body JsonValueOutput00 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromJsonValueOutput00 overwrites any union data inside the JsonValueOutput0 as the provided JsonValueOutput00 -func (t *JsonValueOutput0) FromJsonValueOutput00(v JsonValueOutput00) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeJsonValueOutput00 performs a merge with any union data inside the JsonValueOutput0, using the provided JsonValueOutput00 -func (t *JsonValueOutput0) MergeJsonValueOutput00(v JsonValueOutput00) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsJsonValueOutput01 returns the union data inside the JsonValueOutput0 as a JsonValueOutput01 -func (t JsonValueOutput0) AsJsonValueOutput01() (JsonValueOutput01, error) { - var body JsonValueOutput01 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromJsonValueOutput01 overwrites any union data inside the JsonValueOutput0 as the provided JsonValueOutput01 -func (t *JsonValueOutput0) FromJsonValueOutput01(v JsonValueOutput01) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeJsonValueOutput01 performs a merge with any union data inside the JsonValueOutput0, using the provided JsonValueOutput01 -func (t *JsonValueOutput0) MergeJsonValueOutput01(v JsonValueOutput01) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsJsonValueOutput02 returns the union data inside the JsonValueOutput0 as a JsonValueOutput02 -func (t JsonValueOutput0) AsJsonValueOutput02() (JsonValueOutput02, error) { - var body JsonValueOutput02 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromJsonValueOutput02 overwrites any union data inside the JsonValueOutput0 as the provided JsonValueOutput02 -func (t *JsonValueOutput0) FromJsonValueOutput02(v JsonValueOutput02) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeJsonValueOutput02 performs a merge with any union data inside the JsonValueOutput0, using the provided JsonValueOutput02 -func (t *JsonValueOutput0) MergeJsonValueOutput02(v JsonValueOutput02) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t JsonValueOutput0) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *JsonValueOutput0) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsListProjectAddonsResponseOutputAvailableAddonsVariantsId0 returns the union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as a ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 -func (t ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) AsListProjectAddonsResponseOutputAvailableAddonsVariantsId0() (ListProjectAddonsResponseOutputAvailableAddonsVariantsId0, error) { - var body ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputAvailableAddonsVariantsId0 overwrites any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) FromListProjectAddonsResponseOutputAvailableAddonsVariantsId0(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId0 performs a merge with any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id, using the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId0 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId0(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputAvailableAddonsVariantsId1 returns the union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as a ListProjectAddonsResponseOutputAvailableAddonsVariantsId1 -func (t ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) AsListProjectAddonsResponseOutputAvailableAddonsVariantsId1() (ListProjectAddonsResponseOutputAvailableAddonsVariantsId1, error) { - var body ListProjectAddonsResponseOutputAvailableAddonsVariantsId1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputAvailableAddonsVariantsId1 overwrites any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId1 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) FromListProjectAddonsResponseOutputAvailableAddonsVariantsId1(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId1 performs a merge with any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id, using the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId1 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId1(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputAvailableAddonsVariantsId2 returns the union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as a ListProjectAddonsResponseOutputAvailableAddonsVariantsId2 -func (t ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) AsListProjectAddonsResponseOutputAvailableAddonsVariantsId2() (ListProjectAddonsResponseOutputAvailableAddonsVariantsId2, error) { - var body ListProjectAddonsResponseOutputAvailableAddonsVariantsId2 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputAvailableAddonsVariantsId2 overwrites any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId2 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) FromListProjectAddonsResponseOutputAvailableAddonsVariantsId2(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId2) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId2 performs a merge with any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id, using the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId2 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId2(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId2) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputAvailableAddonsVariantsId3 returns the union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as a ListProjectAddonsResponseOutputAvailableAddonsVariantsId3 -func (t ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) AsListProjectAddonsResponseOutputAvailableAddonsVariantsId3() (ListProjectAddonsResponseOutputAvailableAddonsVariantsId3, error) { - var body ListProjectAddonsResponseOutputAvailableAddonsVariantsId3 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputAvailableAddonsVariantsId3 overwrites any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId3 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) FromListProjectAddonsResponseOutputAvailableAddonsVariantsId3(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId3) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId3 performs a merge with any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id, using the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId3 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId3(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId3) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputAvailableAddonsVariantsId4 returns the union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as a ListProjectAddonsResponseOutputAvailableAddonsVariantsId4 -func (t ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) AsListProjectAddonsResponseOutputAvailableAddonsVariantsId4() (ListProjectAddonsResponseOutputAvailableAddonsVariantsId4, error) { - var body ListProjectAddonsResponseOutputAvailableAddonsVariantsId4 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputAvailableAddonsVariantsId4 overwrites any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId4 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) FromListProjectAddonsResponseOutputAvailableAddonsVariantsId4(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId4) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId4 performs a merge with any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id, using the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId4 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId4(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId4) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputAvailableAddonsVariantsId5 returns the union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as a ListProjectAddonsResponseOutputAvailableAddonsVariantsId5 -func (t ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) AsListProjectAddonsResponseOutputAvailableAddonsVariantsId5() (ListProjectAddonsResponseOutputAvailableAddonsVariantsId5, error) { - var body ListProjectAddonsResponseOutputAvailableAddonsVariantsId5 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputAvailableAddonsVariantsId5 overwrites any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId5 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) FromListProjectAddonsResponseOutputAvailableAddonsVariantsId5(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId5) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId5 performs a merge with any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id, using the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId5 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId5(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId5) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputAvailableAddonsVariantsId6 returns the union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as a ListProjectAddonsResponseOutputAvailableAddonsVariantsId6 -func (t ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) AsListProjectAddonsResponseOutputAvailableAddonsVariantsId6() (ListProjectAddonsResponseOutputAvailableAddonsVariantsId6, error) { - var body ListProjectAddonsResponseOutputAvailableAddonsVariantsId6 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputAvailableAddonsVariantsId6 overwrites any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId6 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) FromListProjectAddonsResponseOutputAvailableAddonsVariantsId6(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId6) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId6 performs a merge with any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id, using the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId6 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId6(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId6) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputAvailableAddonsVariantsId7 returns the union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as a ListProjectAddonsResponseOutputAvailableAddonsVariantsId7 -func (t ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) AsListProjectAddonsResponseOutputAvailableAddonsVariantsId7() (ListProjectAddonsResponseOutputAvailableAddonsVariantsId7, error) { - var body ListProjectAddonsResponseOutputAvailableAddonsVariantsId7 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputAvailableAddonsVariantsId7 overwrites any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id as the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId7 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) FromListProjectAddonsResponseOutputAvailableAddonsVariantsId7(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId7) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId7 performs a merge with any union data inside the ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id, using the provided ListProjectAddonsResponseOutputAvailableAddonsVariantsId7 -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) MergeListProjectAddonsResponseOutputAvailableAddonsVariantsId7(v ListProjectAddonsResponseOutputAvailableAddonsVariantsId7) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *ListProjectAddonsResponseOutput_AvailableAddons_Variants_Id) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsListProjectAddonsResponseOutputSelectedAddonsVariantId0 returns the union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as a ListProjectAddonsResponseOutputSelectedAddonsVariantId0 -func (t ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) AsListProjectAddonsResponseOutputSelectedAddonsVariantId0() (ListProjectAddonsResponseOutputSelectedAddonsVariantId0, error) { - var body ListProjectAddonsResponseOutputSelectedAddonsVariantId0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputSelectedAddonsVariantId0 overwrites any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId0 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) FromListProjectAddonsResponseOutputSelectedAddonsVariantId0(v ListProjectAddonsResponseOutputSelectedAddonsVariantId0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputSelectedAddonsVariantId0 performs a merge with any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id, using the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId0 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) MergeListProjectAddonsResponseOutputSelectedAddonsVariantId0(v ListProjectAddonsResponseOutputSelectedAddonsVariantId0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputSelectedAddonsVariantId1 returns the union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as a ListProjectAddonsResponseOutputSelectedAddonsVariantId1 -func (t ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) AsListProjectAddonsResponseOutputSelectedAddonsVariantId1() (ListProjectAddonsResponseOutputSelectedAddonsVariantId1, error) { - var body ListProjectAddonsResponseOutputSelectedAddonsVariantId1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputSelectedAddonsVariantId1 overwrites any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId1 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) FromListProjectAddonsResponseOutputSelectedAddonsVariantId1(v ListProjectAddonsResponseOutputSelectedAddonsVariantId1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputSelectedAddonsVariantId1 performs a merge with any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id, using the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId1 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) MergeListProjectAddonsResponseOutputSelectedAddonsVariantId1(v ListProjectAddonsResponseOutputSelectedAddonsVariantId1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputSelectedAddonsVariantId2 returns the union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as a ListProjectAddonsResponseOutputSelectedAddonsVariantId2 -func (t ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) AsListProjectAddonsResponseOutputSelectedAddonsVariantId2() (ListProjectAddonsResponseOutputSelectedAddonsVariantId2, error) { - var body ListProjectAddonsResponseOutputSelectedAddonsVariantId2 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputSelectedAddonsVariantId2 overwrites any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId2 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) FromListProjectAddonsResponseOutputSelectedAddonsVariantId2(v ListProjectAddonsResponseOutputSelectedAddonsVariantId2) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputSelectedAddonsVariantId2 performs a merge with any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id, using the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId2 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) MergeListProjectAddonsResponseOutputSelectedAddonsVariantId2(v ListProjectAddonsResponseOutputSelectedAddonsVariantId2) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputSelectedAddonsVariantId3 returns the union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as a ListProjectAddonsResponseOutputSelectedAddonsVariantId3 -func (t ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) AsListProjectAddonsResponseOutputSelectedAddonsVariantId3() (ListProjectAddonsResponseOutputSelectedAddonsVariantId3, error) { - var body ListProjectAddonsResponseOutputSelectedAddonsVariantId3 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputSelectedAddonsVariantId3 overwrites any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId3 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) FromListProjectAddonsResponseOutputSelectedAddonsVariantId3(v ListProjectAddonsResponseOutputSelectedAddonsVariantId3) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputSelectedAddonsVariantId3 performs a merge with any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id, using the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId3 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) MergeListProjectAddonsResponseOutputSelectedAddonsVariantId3(v ListProjectAddonsResponseOutputSelectedAddonsVariantId3) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputSelectedAddonsVariantId4 returns the union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as a ListProjectAddonsResponseOutputSelectedAddonsVariantId4 -func (t ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) AsListProjectAddonsResponseOutputSelectedAddonsVariantId4() (ListProjectAddonsResponseOutputSelectedAddonsVariantId4, error) { - var body ListProjectAddonsResponseOutputSelectedAddonsVariantId4 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputSelectedAddonsVariantId4 overwrites any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId4 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) FromListProjectAddonsResponseOutputSelectedAddonsVariantId4(v ListProjectAddonsResponseOutputSelectedAddonsVariantId4) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputSelectedAddonsVariantId4 performs a merge with any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id, using the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId4 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) MergeListProjectAddonsResponseOutputSelectedAddonsVariantId4(v ListProjectAddonsResponseOutputSelectedAddonsVariantId4) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputSelectedAddonsVariantId5 returns the union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as a ListProjectAddonsResponseOutputSelectedAddonsVariantId5 -func (t ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) AsListProjectAddonsResponseOutputSelectedAddonsVariantId5() (ListProjectAddonsResponseOutputSelectedAddonsVariantId5, error) { - var body ListProjectAddonsResponseOutputSelectedAddonsVariantId5 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputSelectedAddonsVariantId5 overwrites any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId5 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) FromListProjectAddonsResponseOutputSelectedAddonsVariantId5(v ListProjectAddonsResponseOutputSelectedAddonsVariantId5) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputSelectedAddonsVariantId5 performs a merge with any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id, using the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId5 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) MergeListProjectAddonsResponseOutputSelectedAddonsVariantId5(v ListProjectAddonsResponseOutputSelectedAddonsVariantId5) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputSelectedAddonsVariantId6 returns the union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as a ListProjectAddonsResponseOutputSelectedAddonsVariantId6 -func (t ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) AsListProjectAddonsResponseOutputSelectedAddonsVariantId6() (ListProjectAddonsResponseOutputSelectedAddonsVariantId6, error) { - var body ListProjectAddonsResponseOutputSelectedAddonsVariantId6 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputSelectedAddonsVariantId6 overwrites any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId6 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) FromListProjectAddonsResponseOutputSelectedAddonsVariantId6(v ListProjectAddonsResponseOutputSelectedAddonsVariantId6) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputSelectedAddonsVariantId6 performs a merge with any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id, using the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId6 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) MergeListProjectAddonsResponseOutputSelectedAddonsVariantId6(v ListProjectAddonsResponseOutputSelectedAddonsVariantId6) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsListProjectAddonsResponseOutputSelectedAddonsVariantId7 returns the union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as a ListProjectAddonsResponseOutputSelectedAddonsVariantId7 -func (t ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) AsListProjectAddonsResponseOutputSelectedAddonsVariantId7() (ListProjectAddonsResponseOutputSelectedAddonsVariantId7, error) { - var body ListProjectAddonsResponseOutputSelectedAddonsVariantId7 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromListProjectAddonsResponseOutputSelectedAddonsVariantId7 overwrites any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id as the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId7 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) FromListProjectAddonsResponseOutputSelectedAddonsVariantId7(v ListProjectAddonsResponseOutputSelectedAddonsVariantId7) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeListProjectAddonsResponseOutputSelectedAddonsVariantId7 performs a merge with any union data inside the ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id, using the provided ListProjectAddonsResponseOutputSelectedAddonsVariantId7 -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) MergeListProjectAddonsResponseOutputSelectedAddonsVariantId7(v ListProjectAddonsResponseOutputSelectedAddonsVariantId7) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *ListProjectAddonsResponseOutput_SelectedAddons_Variant_Id) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType as a ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0 -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType) AsProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0() (ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0, error) { - var body ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType as the provided ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType) FromProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0(v ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType, using the provided ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType) MergeProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0(v ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType as a ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1 -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType) AsProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1() (ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1, error) { - var body ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType as the provided ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType) FromProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1(v ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType, using the provided ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType) MergeProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1(v ProjectUpgradeEligibilityResponseOutputValidationErrors6ObjType1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_6_ObjType) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsProjectUpgradeEligibilityResponseOutputValidationErrors0 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as a ProjectUpgradeEligibilityResponseOutputValidationErrors0 -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) AsProjectUpgradeEligibilityResponseOutputValidationErrors0() (ProjectUpgradeEligibilityResponseOutputValidationErrors0, error) { - var body ProjectUpgradeEligibilityResponseOutputValidationErrors0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputValidationErrors0 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as the provided ProjectUpgradeEligibilityResponseOutputValidationErrors0 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) FromProjectUpgradeEligibilityResponseOutputValidationErrors0(v ProjectUpgradeEligibilityResponseOutputValidationErrors0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputValidationErrors0 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item, using the provided ProjectUpgradeEligibilityResponseOutputValidationErrors0 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) MergeProjectUpgradeEligibilityResponseOutputValidationErrors0(v ProjectUpgradeEligibilityResponseOutputValidationErrors0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsProjectUpgradeEligibilityResponseOutputValidationErrors1 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as a ProjectUpgradeEligibilityResponseOutputValidationErrors1 -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) AsProjectUpgradeEligibilityResponseOutputValidationErrors1() (ProjectUpgradeEligibilityResponseOutputValidationErrors1, error) { - var body ProjectUpgradeEligibilityResponseOutputValidationErrors1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputValidationErrors1 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as the provided ProjectUpgradeEligibilityResponseOutputValidationErrors1 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) FromProjectUpgradeEligibilityResponseOutputValidationErrors1(v ProjectUpgradeEligibilityResponseOutputValidationErrors1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputValidationErrors1 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item, using the provided ProjectUpgradeEligibilityResponseOutputValidationErrors1 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) MergeProjectUpgradeEligibilityResponseOutputValidationErrors1(v ProjectUpgradeEligibilityResponseOutputValidationErrors1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsProjectUpgradeEligibilityResponseOutputValidationErrors2 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as a ProjectUpgradeEligibilityResponseOutputValidationErrors2 -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) AsProjectUpgradeEligibilityResponseOutputValidationErrors2() (ProjectUpgradeEligibilityResponseOutputValidationErrors2, error) { - var body ProjectUpgradeEligibilityResponseOutputValidationErrors2 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputValidationErrors2 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as the provided ProjectUpgradeEligibilityResponseOutputValidationErrors2 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) FromProjectUpgradeEligibilityResponseOutputValidationErrors2(v ProjectUpgradeEligibilityResponseOutputValidationErrors2) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputValidationErrors2 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item, using the provided ProjectUpgradeEligibilityResponseOutputValidationErrors2 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) MergeProjectUpgradeEligibilityResponseOutputValidationErrors2(v ProjectUpgradeEligibilityResponseOutputValidationErrors2) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsProjectUpgradeEligibilityResponseOutputValidationErrors3 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as a ProjectUpgradeEligibilityResponseOutputValidationErrors3 -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) AsProjectUpgradeEligibilityResponseOutputValidationErrors3() (ProjectUpgradeEligibilityResponseOutputValidationErrors3, error) { - var body ProjectUpgradeEligibilityResponseOutputValidationErrors3 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputValidationErrors3 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as the provided ProjectUpgradeEligibilityResponseOutputValidationErrors3 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) FromProjectUpgradeEligibilityResponseOutputValidationErrors3(v ProjectUpgradeEligibilityResponseOutputValidationErrors3) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputValidationErrors3 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item, using the provided ProjectUpgradeEligibilityResponseOutputValidationErrors3 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) MergeProjectUpgradeEligibilityResponseOutputValidationErrors3(v ProjectUpgradeEligibilityResponseOutputValidationErrors3) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsProjectUpgradeEligibilityResponseOutputValidationErrors4 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as a ProjectUpgradeEligibilityResponseOutputValidationErrors4 -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) AsProjectUpgradeEligibilityResponseOutputValidationErrors4() (ProjectUpgradeEligibilityResponseOutputValidationErrors4, error) { - var body ProjectUpgradeEligibilityResponseOutputValidationErrors4 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputValidationErrors4 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as the provided ProjectUpgradeEligibilityResponseOutputValidationErrors4 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) FromProjectUpgradeEligibilityResponseOutputValidationErrors4(v ProjectUpgradeEligibilityResponseOutputValidationErrors4) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputValidationErrors4 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item, using the provided ProjectUpgradeEligibilityResponseOutputValidationErrors4 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) MergeProjectUpgradeEligibilityResponseOutputValidationErrors4(v ProjectUpgradeEligibilityResponseOutputValidationErrors4) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsProjectUpgradeEligibilityResponseOutputValidationErrors5 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as a ProjectUpgradeEligibilityResponseOutputValidationErrors5 -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) AsProjectUpgradeEligibilityResponseOutputValidationErrors5() (ProjectUpgradeEligibilityResponseOutputValidationErrors5, error) { - var body ProjectUpgradeEligibilityResponseOutputValidationErrors5 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputValidationErrors5 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as the provided ProjectUpgradeEligibilityResponseOutputValidationErrors5 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) FromProjectUpgradeEligibilityResponseOutputValidationErrors5(v ProjectUpgradeEligibilityResponseOutputValidationErrors5) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputValidationErrors5 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item, using the provided ProjectUpgradeEligibilityResponseOutputValidationErrors5 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) MergeProjectUpgradeEligibilityResponseOutputValidationErrors5(v ProjectUpgradeEligibilityResponseOutputValidationErrors5) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsProjectUpgradeEligibilityResponseOutputValidationErrors6 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as a ProjectUpgradeEligibilityResponseOutputValidationErrors6 -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) AsProjectUpgradeEligibilityResponseOutputValidationErrors6() (ProjectUpgradeEligibilityResponseOutputValidationErrors6, error) { - var body ProjectUpgradeEligibilityResponseOutputValidationErrors6 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputValidationErrors6 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as the provided ProjectUpgradeEligibilityResponseOutputValidationErrors6 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) FromProjectUpgradeEligibilityResponseOutputValidationErrors6(v ProjectUpgradeEligibilityResponseOutputValidationErrors6) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputValidationErrors6 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item, using the provided ProjectUpgradeEligibilityResponseOutputValidationErrors6 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) MergeProjectUpgradeEligibilityResponseOutputValidationErrors6(v ProjectUpgradeEligibilityResponseOutputValidationErrors6) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsProjectUpgradeEligibilityResponseOutputValidationErrors7 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as a ProjectUpgradeEligibilityResponseOutputValidationErrors7 -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) AsProjectUpgradeEligibilityResponseOutputValidationErrors7() (ProjectUpgradeEligibilityResponseOutputValidationErrors7, error) { - var body ProjectUpgradeEligibilityResponseOutputValidationErrors7 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputValidationErrors7 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as the provided ProjectUpgradeEligibilityResponseOutputValidationErrors7 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) FromProjectUpgradeEligibilityResponseOutputValidationErrors7(v ProjectUpgradeEligibilityResponseOutputValidationErrors7) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputValidationErrors7 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item, using the provided ProjectUpgradeEligibilityResponseOutputValidationErrors7 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) MergeProjectUpgradeEligibilityResponseOutputValidationErrors7(v ProjectUpgradeEligibilityResponseOutputValidationErrors7) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsProjectUpgradeEligibilityResponseOutputValidationErrors8 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as a ProjectUpgradeEligibilityResponseOutputValidationErrors8 -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) AsProjectUpgradeEligibilityResponseOutputValidationErrors8() (ProjectUpgradeEligibilityResponseOutputValidationErrors8, error) { - var body ProjectUpgradeEligibilityResponseOutputValidationErrors8 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputValidationErrors8 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as the provided ProjectUpgradeEligibilityResponseOutputValidationErrors8 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) FromProjectUpgradeEligibilityResponseOutputValidationErrors8(v ProjectUpgradeEligibilityResponseOutputValidationErrors8) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputValidationErrors8 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item, using the provided ProjectUpgradeEligibilityResponseOutputValidationErrors8 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) MergeProjectUpgradeEligibilityResponseOutputValidationErrors8(v ProjectUpgradeEligibilityResponseOutputValidationErrors8) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsProjectUpgradeEligibilityResponseOutputValidationErrors9 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as a ProjectUpgradeEligibilityResponseOutputValidationErrors9 -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) AsProjectUpgradeEligibilityResponseOutputValidationErrors9() (ProjectUpgradeEligibilityResponseOutputValidationErrors9, error) { - var body ProjectUpgradeEligibilityResponseOutputValidationErrors9 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputValidationErrors9 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item as the provided ProjectUpgradeEligibilityResponseOutputValidationErrors9 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) FromProjectUpgradeEligibilityResponseOutputValidationErrors9(v ProjectUpgradeEligibilityResponseOutputValidationErrors9) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputValidationErrors9 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item, using the provided ProjectUpgradeEligibilityResponseOutputValidationErrors9 -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) MergeProjectUpgradeEligibilityResponseOutputValidationErrors9(v ProjectUpgradeEligibilityResponseOutputValidationErrors9) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *ProjectUpgradeEligibilityResponseOutput_ValidationErrors_Item) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsProjectUpgradeEligibilityResponseOutputWarnings0 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_Warnings_Item as a ProjectUpgradeEligibilityResponseOutputWarnings0 -func (t ProjectUpgradeEligibilityResponseOutput_Warnings_Item) AsProjectUpgradeEligibilityResponseOutputWarnings0() (ProjectUpgradeEligibilityResponseOutputWarnings0, error) { - var body ProjectUpgradeEligibilityResponseOutputWarnings0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputWarnings0 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_Warnings_Item as the provided ProjectUpgradeEligibilityResponseOutputWarnings0 -func (t *ProjectUpgradeEligibilityResponseOutput_Warnings_Item) FromProjectUpgradeEligibilityResponseOutputWarnings0(v ProjectUpgradeEligibilityResponseOutputWarnings0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputWarnings0 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_Warnings_Item, using the provided ProjectUpgradeEligibilityResponseOutputWarnings0 -func (t *ProjectUpgradeEligibilityResponseOutput_Warnings_Item) MergeProjectUpgradeEligibilityResponseOutputWarnings0(v ProjectUpgradeEligibilityResponseOutputWarnings0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsProjectUpgradeEligibilityResponseOutputWarnings1 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_Warnings_Item as a ProjectUpgradeEligibilityResponseOutputWarnings1 -func (t ProjectUpgradeEligibilityResponseOutput_Warnings_Item) AsProjectUpgradeEligibilityResponseOutputWarnings1() (ProjectUpgradeEligibilityResponseOutputWarnings1, error) { - var body ProjectUpgradeEligibilityResponseOutputWarnings1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputWarnings1 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_Warnings_Item as the provided ProjectUpgradeEligibilityResponseOutputWarnings1 -func (t *ProjectUpgradeEligibilityResponseOutput_Warnings_Item) FromProjectUpgradeEligibilityResponseOutputWarnings1(v ProjectUpgradeEligibilityResponseOutputWarnings1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputWarnings1 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_Warnings_Item, using the provided ProjectUpgradeEligibilityResponseOutputWarnings1 -func (t *ProjectUpgradeEligibilityResponseOutput_Warnings_Item) MergeProjectUpgradeEligibilityResponseOutputWarnings1(v ProjectUpgradeEligibilityResponseOutputWarnings1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsProjectUpgradeEligibilityResponseOutputWarnings2 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_Warnings_Item as a ProjectUpgradeEligibilityResponseOutputWarnings2 -func (t ProjectUpgradeEligibilityResponseOutput_Warnings_Item) AsProjectUpgradeEligibilityResponseOutputWarnings2() (ProjectUpgradeEligibilityResponseOutputWarnings2, error) { - var body ProjectUpgradeEligibilityResponseOutputWarnings2 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputWarnings2 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_Warnings_Item as the provided ProjectUpgradeEligibilityResponseOutputWarnings2 -func (t *ProjectUpgradeEligibilityResponseOutput_Warnings_Item) FromProjectUpgradeEligibilityResponseOutputWarnings2(v ProjectUpgradeEligibilityResponseOutputWarnings2) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputWarnings2 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_Warnings_Item, using the provided ProjectUpgradeEligibilityResponseOutputWarnings2 -func (t *ProjectUpgradeEligibilityResponseOutput_Warnings_Item) MergeProjectUpgradeEligibilityResponseOutputWarnings2(v ProjectUpgradeEligibilityResponseOutputWarnings2) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsProjectUpgradeEligibilityResponseOutputWarnings3 returns the union data inside the ProjectUpgradeEligibilityResponseOutput_Warnings_Item as a ProjectUpgradeEligibilityResponseOutputWarnings3 -func (t ProjectUpgradeEligibilityResponseOutput_Warnings_Item) AsProjectUpgradeEligibilityResponseOutputWarnings3() (ProjectUpgradeEligibilityResponseOutputWarnings3, error) { - var body ProjectUpgradeEligibilityResponseOutputWarnings3 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromProjectUpgradeEligibilityResponseOutputWarnings3 overwrites any union data inside the ProjectUpgradeEligibilityResponseOutput_Warnings_Item as the provided ProjectUpgradeEligibilityResponseOutputWarnings3 -func (t *ProjectUpgradeEligibilityResponseOutput_Warnings_Item) FromProjectUpgradeEligibilityResponseOutputWarnings3(v ProjectUpgradeEligibilityResponseOutputWarnings3) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeProjectUpgradeEligibilityResponseOutputWarnings3 performs a merge with any union data inside the ProjectUpgradeEligibilityResponseOutput_Warnings_Item, using the provided ProjectUpgradeEligibilityResponseOutputWarnings3 -func (t *ProjectUpgradeEligibilityResponseOutput_Warnings_Item) MergeProjectUpgradeEligibilityResponseOutputWarnings3(v ProjectUpgradeEligibilityResponseOutputWarnings3) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t ProjectUpgradeEligibilityResponseOutput_Warnings_Item) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *ProjectUpgradeEligibilityResponseOutput_Warnings_Item) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsV1CreateProjectBodyRegionSelection0 returns the union data inside the V1CreateProjectBody_RegionSelection as a V1CreateProjectBodyRegionSelection0 -func (t V1CreateProjectBody_RegionSelection) AsV1CreateProjectBodyRegionSelection0() (V1CreateProjectBodyRegionSelection0, error) { - var body V1CreateProjectBodyRegionSelection0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1CreateProjectBodyRegionSelection0 overwrites any union data inside the V1CreateProjectBody_RegionSelection as the provided V1CreateProjectBodyRegionSelection0 -func (t *V1CreateProjectBody_RegionSelection) FromV1CreateProjectBodyRegionSelection0(v V1CreateProjectBodyRegionSelection0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1CreateProjectBodyRegionSelection0 performs a merge with any union data inside the V1CreateProjectBody_RegionSelection, using the provided V1CreateProjectBodyRegionSelection0 -func (t *V1CreateProjectBody_RegionSelection) MergeV1CreateProjectBodyRegionSelection0(v V1CreateProjectBodyRegionSelection0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsV1CreateProjectBodyRegionSelection1 returns the union data inside the V1CreateProjectBody_RegionSelection as a V1CreateProjectBodyRegionSelection1 -func (t V1CreateProjectBody_RegionSelection) AsV1CreateProjectBodyRegionSelection1() (V1CreateProjectBodyRegionSelection1, error) { - var body V1CreateProjectBodyRegionSelection1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1CreateProjectBodyRegionSelection1 overwrites any union data inside the V1CreateProjectBody_RegionSelection as the provided V1CreateProjectBodyRegionSelection1 -func (t *V1CreateProjectBody_RegionSelection) FromV1CreateProjectBodyRegionSelection1(v V1CreateProjectBodyRegionSelection1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1CreateProjectBodyRegionSelection1 performs a merge with any union data inside the V1CreateProjectBody_RegionSelection, using the provided V1CreateProjectBodyRegionSelection1 -func (t *V1CreateProjectBody_RegionSelection) MergeV1CreateProjectBodyRegionSelection1(v V1CreateProjectBodyRegionSelection1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t V1CreateProjectBody_RegionSelection) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *V1CreateProjectBody_RegionSelection) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsV1GetUsageApiCountResponseOutputError0 returns the union data inside the V1GetUsageApiCountResponseOutput_Error as a V1GetUsageApiCountResponseOutputError0 -func (t V1GetUsageApiCountResponseOutput_Error) AsV1GetUsageApiCountResponseOutputError0() (V1GetUsageApiCountResponseOutputError0, error) { - var body V1GetUsageApiCountResponseOutputError0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1GetUsageApiCountResponseOutputError0 overwrites any union data inside the V1GetUsageApiCountResponseOutput_Error as the provided V1GetUsageApiCountResponseOutputError0 -func (t *V1GetUsageApiCountResponseOutput_Error) FromV1GetUsageApiCountResponseOutputError0(v V1GetUsageApiCountResponseOutputError0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1GetUsageApiCountResponseOutputError0 performs a merge with any union data inside the V1GetUsageApiCountResponseOutput_Error, using the provided V1GetUsageApiCountResponseOutputError0 -func (t *V1GetUsageApiCountResponseOutput_Error) MergeV1GetUsageApiCountResponseOutputError0(v V1GetUsageApiCountResponseOutputError0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsV1GetUsageApiCountResponseOutputError1 returns the union data inside the V1GetUsageApiCountResponseOutput_Error as a V1GetUsageApiCountResponseOutputError1 -func (t V1GetUsageApiCountResponseOutput_Error) AsV1GetUsageApiCountResponseOutputError1() (V1GetUsageApiCountResponseOutputError1, error) { - var body V1GetUsageApiCountResponseOutputError1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1GetUsageApiCountResponseOutputError1 overwrites any union data inside the V1GetUsageApiCountResponseOutput_Error as the provided V1GetUsageApiCountResponseOutputError1 -func (t *V1GetUsageApiCountResponseOutput_Error) FromV1GetUsageApiCountResponseOutputError1(v V1GetUsageApiCountResponseOutputError1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1GetUsageApiCountResponseOutputError1 performs a merge with any union data inside the V1GetUsageApiCountResponseOutput_Error, using the provided V1GetUsageApiCountResponseOutputError1 -func (t *V1GetUsageApiCountResponseOutput_Error) MergeV1GetUsageApiCountResponseOutputError1(v V1GetUsageApiCountResponseOutputError1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t V1GetUsageApiCountResponseOutput_Error) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *V1GetUsageApiCountResponseOutput_Error) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsV1GetUsageApiRequestsCountResponseOutputError0 returns the union data inside the V1GetUsageApiRequestsCountResponseOutput_Error as a V1GetUsageApiRequestsCountResponseOutputError0 -func (t V1GetUsageApiRequestsCountResponseOutput_Error) AsV1GetUsageApiRequestsCountResponseOutputError0() (V1GetUsageApiRequestsCountResponseOutputError0, error) { - var body V1GetUsageApiRequestsCountResponseOutputError0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1GetUsageApiRequestsCountResponseOutputError0 overwrites any union data inside the V1GetUsageApiRequestsCountResponseOutput_Error as the provided V1GetUsageApiRequestsCountResponseOutputError0 -func (t *V1GetUsageApiRequestsCountResponseOutput_Error) FromV1GetUsageApiRequestsCountResponseOutputError0(v V1GetUsageApiRequestsCountResponseOutputError0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1GetUsageApiRequestsCountResponseOutputError0 performs a merge with any union data inside the V1GetUsageApiRequestsCountResponseOutput_Error, using the provided V1GetUsageApiRequestsCountResponseOutputError0 -func (t *V1GetUsageApiRequestsCountResponseOutput_Error) MergeV1GetUsageApiRequestsCountResponseOutputError0(v V1GetUsageApiRequestsCountResponseOutputError0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsV1GetUsageApiRequestsCountResponseOutputError1 returns the union data inside the V1GetUsageApiRequestsCountResponseOutput_Error as a V1GetUsageApiRequestsCountResponseOutputError1 -func (t V1GetUsageApiRequestsCountResponseOutput_Error) AsV1GetUsageApiRequestsCountResponseOutputError1() (V1GetUsageApiRequestsCountResponseOutputError1, error) { - var body V1GetUsageApiRequestsCountResponseOutputError1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1GetUsageApiRequestsCountResponseOutputError1 overwrites any union data inside the V1GetUsageApiRequestsCountResponseOutput_Error as the provided V1GetUsageApiRequestsCountResponseOutputError1 -func (t *V1GetUsageApiRequestsCountResponseOutput_Error) FromV1GetUsageApiRequestsCountResponseOutputError1(v V1GetUsageApiRequestsCountResponseOutputError1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1GetUsageApiRequestsCountResponseOutputError1 performs a merge with any union data inside the V1GetUsageApiRequestsCountResponseOutput_Error, using the provided V1GetUsageApiRequestsCountResponseOutputError1 -func (t *V1GetUsageApiRequestsCountResponseOutput_Error) MergeV1GetUsageApiRequestsCountResponseOutputError1(v V1GetUsageApiRequestsCountResponseOutputError1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t V1GetUsageApiRequestsCountResponseOutput_Error) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *V1GetUsageApiRequestsCountResponseOutput_Error) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsV1ListEntitlementsResponseOutputEntitlementsConfig0 returns the union data inside the V1ListEntitlementsResponseOutput_Entitlements_Config as a V1ListEntitlementsResponseOutputEntitlementsConfig0 -func (t V1ListEntitlementsResponseOutput_Entitlements_Config) AsV1ListEntitlementsResponseOutputEntitlementsConfig0() (V1ListEntitlementsResponseOutputEntitlementsConfig0, error) { - var body V1ListEntitlementsResponseOutputEntitlementsConfig0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1ListEntitlementsResponseOutputEntitlementsConfig0 overwrites any union data inside the V1ListEntitlementsResponseOutput_Entitlements_Config as the provided V1ListEntitlementsResponseOutputEntitlementsConfig0 -func (t *V1ListEntitlementsResponseOutput_Entitlements_Config) FromV1ListEntitlementsResponseOutputEntitlementsConfig0(v V1ListEntitlementsResponseOutputEntitlementsConfig0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1ListEntitlementsResponseOutputEntitlementsConfig0 performs a merge with any union data inside the V1ListEntitlementsResponseOutput_Entitlements_Config, using the provided V1ListEntitlementsResponseOutputEntitlementsConfig0 -func (t *V1ListEntitlementsResponseOutput_Entitlements_Config) MergeV1ListEntitlementsResponseOutputEntitlementsConfig0(v V1ListEntitlementsResponseOutputEntitlementsConfig0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsV1ListEntitlementsResponseOutputEntitlementsConfig1 returns the union data inside the V1ListEntitlementsResponseOutput_Entitlements_Config as a V1ListEntitlementsResponseOutputEntitlementsConfig1 -func (t V1ListEntitlementsResponseOutput_Entitlements_Config) AsV1ListEntitlementsResponseOutputEntitlementsConfig1() (V1ListEntitlementsResponseOutputEntitlementsConfig1, error) { - var body V1ListEntitlementsResponseOutputEntitlementsConfig1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1ListEntitlementsResponseOutputEntitlementsConfig1 overwrites any union data inside the V1ListEntitlementsResponseOutput_Entitlements_Config as the provided V1ListEntitlementsResponseOutputEntitlementsConfig1 -func (t *V1ListEntitlementsResponseOutput_Entitlements_Config) FromV1ListEntitlementsResponseOutputEntitlementsConfig1(v V1ListEntitlementsResponseOutputEntitlementsConfig1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1ListEntitlementsResponseOutputEntitlementsConfig1 performs a merge with any union data inside the V1ListEntitlementsResponseOutput_Entitlements_Config, using the provided V1ListEntitlementsResponseOutputEntitlementsConfig1 -func (t *V1ListEntitlementsResponseOutput_Entitlements_Config) MergeV1ListEntitlementsResponseOutputEntitlementsConfig1(v V1ListEntitlementsResponseOutputEntitlementsConfig1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsV1ListEntitlementsResponseOutputEntitlementsConfig2 returns the union data inside the V1ListEntitlementsResponseOutput_Entitlements_Config as a V1ListEntitlementsResponseOutputEntitlementsConfig2 -func (t V1ListEntitlementsResponseOutput_Entitlements_Config) AsV1ListEntitlementsResponseOutputEntitlementsConfig2() (V1ListEntitlementsResponseOutputEntitlementsConfig2, error) { - var body V1ListEntitlementsResponseOutputEntitlementsConfig2 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1ListEntitlementsResponseOutputEntitlementsConfig2 overwrites any union data inside the V1ListEntitlementsResponseOutput_Entitlements_Config as the provided V1ListEntitlementsResponseOutputEntitlementsConfig2 -func (t *V1ListEntitlementsResponseOutput_Entitlements_Config) FromV1ListEntitlementsResponseOutputEntitlementsConfig2(v V1ListEntitlementsResponseOutputEntitlementsConfig2) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1ListEntitlementsResponseOutputEntitlementsConfig2 performs a merge with any union data inside the V1ListEntitlementsResponseOutput_Entitlements_Config, using the provided V1ListEntitlementsResponseOutputEntitlementsConfig2 -func (t *V1ListEntitlementsResponseOutput_Entitlements_Config) MergeV1ListEntitlementsResponseOutputEntitlementsConfig2(v V1ListEntitlementsResponseOutputEntitlementsConfig2) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t V1ListEntitlementsResponseOutput_Entitlements_Config) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *V1ListEntitlementsResponseOutput_Entitlements_Config) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsV1ServiceHealthResponseOutputInfo0 returns the union data inside the V1ServiceHealthResponseOutput_Info as a V1ServiceHealthResponseOutputInfo0 -func (t V1ServiceHealthResponseOutput_Info) AsV1ServiceHealthResponseOutputInfo0() (V1ServiceHealthResponseOutputInfo0, error) { - var body V1ServiceHealthResponseOutputInfo0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1ServiceHealthResponseOutputInfo0 overwrites any union data inside the V1ServiceHealthResponseOutput_Info as the provided V1ServiceHealthResponseOutputInfo0 -func (t *V1ServiceHealthResponseOutput_Info) FromV1ServiceHealthResponseOutputInfo0(v V1ServiceHealthResponseOutputInfo0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1ServiceHealthResponseOutputInfo0 performs a merge with any union data inside the V1ServiceHealthResponseOutput_Info, using the provided V1ServiceHealthResponseOutputInfo0 -func (t *V1ServiceHealthResponseOutput_Info) MergeV1ServiceHealthResponseOutputInfo0(v V1ServiceHealthResponseOutputInfo0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsV1ServiceHealthResponseOutputInfo1 returns the union data inside the V1ServiceHealthResponseOutput_Info as a V1ServiceHealthResponseOutputInfo1 -func (t V1ServiceHealthResponseOutput_Info) AsV1ServiceHealthResponseOutputInfo1() (V1ServiceHealthResponseOutputInfo1, error) { - var body V1ServiceHealthResponseOutputInfo1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1ServiceHealthResponseOutputInfo1 overwrites any union data inside the V1ServiceHealthResponseOutput_Info as the provided V1ServiceHealthResponseOutputInfo1 -func (t *V1ServiceHealthResponseOutput_Info) FromV1ServiceHealthResponseOutputInfo1(v V1ServiceHealthResponseOutputInfo1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1ServiceHealthResponseOutputInfo1 performs a merge with any union data inside the V1ServiceHealthResponseOutput_Info, using the provided V1ServiceHealthResponseOutputInfo1 -func (t *V1ServiceHealthResponseOutput_Info) MergeV1ServiceHealthResponseOutputInfo1(v V1ServiceHealthResponseOutputInfo1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsV1ServiceHealthResponseOutputInfo2 returns the union data inside the V1ServiceHealthResponseOutput_Info as a V1ServiceHealthResponseOutputInfo2 -func (t V1ServiceHealthResponseOutput_Info) AsV1ServiceHealthResponseOutputInfo2() (V1ServiceHealthResponseOutputInfo2, error) { - var body V1ServiceHealthResponseOutputInfo2 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1ServiceHealthResponseOutputInfo2 overwrites any union data inside the V1ServiceHealthResponseOutput_Info as the provided V1ServiceHealthResponseOutputInfo2 -func (t *V1ServiceHealthResponseOutput_Info) FromV1ServiceHealthResponseOutputInfo2(v V1ServiceHealthResponseOutputInfo2) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1ServiceHealthResponseOutputInfo2 performs a merge with any union data inside the V1ServiceHealthResponseOutput_Info, using the provided V1ServiceHealthResponseOutputInfo2 -func (t *V1ServiceHealthResponseOutput_Info) MergeV1ServiceHealthResponseOutputInfo2(v V1ServiceHealthResponseOutputInfo2) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t V1ServiceHealthResponseOutput_Info) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *V1ServiceHealthResponseOutput_Info) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsV1GetJitAccessConfig200JSONResponseBody0 returns the union data inside the V1GetJitAccessConfig200JSONResponseBody as a V1GetJitAccessConfig200JSONResponseBody0 -func (t V1GetJitAccessConfig200JSONResponseBody) AsV1GetJitAccessConfig200JSONResponseBody0() (V1GetJitAccessConfig200JSONResponseBody0, error) { - var body V1GetJitAccessConfig200JSONResponseBody0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1GetJitAccessConfig200JSONResponseBody0 overwrites any union data inside the V1GetJitAccessConfig200JSONResponseBody as the provided V1GetJitAccessConfig200JSONResponseBody0 -func (t *V1GetJitAccessConfig200JSONResponseBody) FromV1GetJitAccessConfig200JSONResponseBody0(v V1GetJitAccessConfig200JSONResponseBody0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1GetJitAccessConfig200JSONResponseBody0 performs a merge with any union data inside the V1GetJitAccessConfig200JSONResponseBody, using the provided V1GetJitAccessConfig200JSONResponseBody0 -func (t *V1GetJitAccessConfig200JSONResponseBody) MergeV1GetJitAccessConfig200JSONResponseBody0(v V1GetJitAccessConfig200JSONResponseBody0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsV1GetJitAccessConfig200JSONResponseBody1 returns the union data inside the V1GetJitAccessConfig200JSONResponseBody as a V1GetJitAccessConfig200JSONResponseBody1 -func (t V1GetJitAccessConfig200JSONResponseBody) AsV1GetJitAccessConfig200JSONResponseBody1() (V1GetJitAccessConfig200JSONResponseBody1, error) { - var body V1GetJitAccessConfig200JSONResponseBody1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1GetJitAccessConfig200JSONResponseBody1 overwrites any union data inside the V1GetJitAccessConfig200JSONResponseBody as the provided V1GetJitAccessConfig200JSONResponseBody1 -func (t *V1GetJitAccessConfig200JSONResponseBody) FromV1GetJitAccessConfig200JSONResponseBody1(v V1GetJitAccessConfig200JSONResponseBody1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1GetJitAccessConfig200JSONResponseBody1 performs a merge with any union data inside the V1GetJitAccessConfig200JSONResponseBody, using the provided V1GetJitAccessConfig200JSONResponseBody1 -func (t *V1GetJitAccessConfig200JSONResponseBody) MergeV1GetJitAccessConfig200JSONResponseBody1(v V1GetJitAccessConfig200JSONResponseBody1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t V1GetJitAccessConfig200JSONResponseBody) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *V1GetJitAccessConfig200JSONResponseBody) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} - -// AsV1UpdateJitAccessConfig200JSONResponseBody0 returns the union data inside the V1UpdateJitAccessConfig200JSONResponseBody as a V1UpdateJitAccessConfig200JSONResponseBody0 -func (t V1UpdateJitAccessConfig200JSONResponseBody) AsV1UpdateJitAccessConfig200JSONResponseBody0() (V1UpdateJitAccessConfig200JSONResponseBody0, error) { - var body V1UpdateJitAccessConfig200JSONResponseBody0 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1UpdateJitAccessConfig200JSONResponseBody0 overwrites any union data inside the V1UpdateJitAccessConfig200JSONResponseBody as the provided V1UpdateJitAccessConfig200JSONResponseBody0 -func (t *V1UpdateJitAccessConfig200JSONResponseBody) FromV1UpdateJitAccessConfig200JSONResponseBody0(v V1UpdateJitAccessConfig200JSONResponseBody0) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1UpdateJitAccessConfig200JSONResponseBody0 performs a merge with any union data inside the V1UpdateJitAccessConfig200JSONResponseBody, using the provided V1UpdateJitAccessConfig200JSONResponseBody0 -func (t *V1UpdateJitAccessConfig200JSONResponseBody) MergeV1UpdateJitAccessConfig200JSONResponseBody0(v V1UpdateJitAccessConfig200JSONResponseBody0) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -// AsV1UpdateJitAccessConfig200JSONResponseBody1 returns the union data inside the V1UpdateJitAccessConfig200JSONResponseBody as a V1UpdateJitAccessConfig200JSONResponseBody1 -func (t V1UpdateJitAccessConfig200JSONResponseBody) AsV1UpdateJitAccessConfig200JSONResponseBody1() (V1UpdateJitAccessConfig200JSONResponseBody1, error) { - var body V1UpdateJitAccessConfig200JSONResponseBody1 - err := json.Unmarshal(t.union, &body) - return body, err -} - -// FromV1UpdateJitAccessConfig200JSONResponseBody1 overwrites any union data inside the V1UpdateJitAccessConfig200JSONResponseBody as the provided V1UpdateJitAccessConfig200JSONResponseBody1 -func (t *V1UpdateJitAccessConfig200JSONResponseBody) FromV1UpdateJitAccessConfig200JSONResponseBody1(v V1UpdateJitAccessConfig200JSONResponseBody1) error { - b, err := json.Marshal(v) - t.union = b - return err -} - -// MergeV1UpdateJitAccessConfig200JSONResponseBody1 performs a merge with any union data inside the V1UpdateJitAccessConfig200JSONResponseBody, using the provided V1UpdateJitAccessConfig200JSONResponseBody1 -func (t *V1UpdateJitAccessConfig200JSONResponseBody) MergeV1UpdateJitAccessConfig200JSONResponseBody1(v V1UpdateJitAccessConfig200JSONResponseBody1) error { - b, err := json.Marshal(v) - if err != nil { - return err - } - - merged, err := runtime.JSONMerge(t.union, b) - t.union = merged - return err -} - -func (t V1UpdateJitAccessConfig200JSONResponseBody) MarshalJSON() ([]byte, error) { - b, err := t.union.MarshalJSON() - return b, err -} - -func (t *V1UpdateJitAccessConfig200JSONResponseBody) UnmarshalJSON(b []byte) error { - err := t.union.UnmarshalJSON(b) - return err -} diff --git a/apps/cli-go/pkg/cast/cast.go b/apps/cli-go/pkg/cast/cast.go deleted file mode 100644 index c4038e1815..0000000000 --- a/apps/cli-go/pkg/cast/cast.go +++ /dev/null @@ -1,62 +0,0 @@ -package cast - -import ( - "math" - "strconv" -) - -// UintToInt converts a uint to an int, handling potential overflow -func UintToInt(value uint) int { - if value <= math.MaxInt { - return int(value) - } - return math.MaxInt -} - -// UIntToUInt16 converts a uint to an uint16, handling potential overflow -func UIntToUInt16(value uint) uint16 { - if value <= math.MaxUint16 { - return uint16(value) - } - return math.MaxUint16 -} - -// IntToUint converts an int to a uint, handling negative values -func IntToUint(value int) uint { - if value < 0 { - return 0 - } - return uint(value) -} - -func UintToIntPtr(value *uint) *int { - if value == nil { - return nil - } - return Ptr(UintToInt(*value)) -} - -func IntToUintPtr(value *int) *uint { - if value == nil { - return nil - } - return Ptr(IntToUint(*value)) -} - -func BoolToStringPtr(value *bool) *string { - if value == nil { - return nil - } - return Ptr(strconv.FormatBool(*value)) -} - -func Ptr[T any](v T) *T { - return &v -} - -func Val[T any](v *T, def T) T { - if v == nil { - return def - } - return *v -} diff --git a/apps/cli-go/pkg/config/api.go b/apps/cli-go/pkg/config/api.go deleted file mode 100644 index 2c97565b7b..0000000000 --- a/apps/cli-go/pkg/config/api.go +++ /dev/null @@ -1,106 +0,0 @@ -package config - -import ( - "strings" - - v1API "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" - "github.com/supabase/cli/pkg/diff" -) - -type ( - api struct { - Enabled bool `toml:"enabled" json:"enabled"` - Schemas []string `toml:"schemas" json:"schemas"` - ExtraSearchPath []string `toml:"extra_search_path" json:"extra_search_path"` - MaxRows uint `toml:"max_rows" json:"max_rows"` - // When unset (default), new tables, views, sequences and functions created in the - // `public` schema by `postgres` are NOT automatically reachable through the Data API - // roles `anon`, `authenticated`, and `service_role`; explicit GRANTs are required, - // matching the new cloud default. Set to true to keep the legacy behaviour of - // auto-exposing new entities (deprecated; the field is removed on 2026-10-30). Set to - // false to pin the new default explicitly. Stored as a pointer so the migration path - // (unset -> default true before May 30 2026, default false after, removed in October) - // can flip the implicit value without losing the explicit user choice. - AutoExposeNewTables *bool `toml:"auto_expose_new_tables,omitempty" json:"auto_expose_new_tables,omitempty"` - // Local only config - Image string `toml:"-" json:"-"` - KongImage string `toml:"-" json:"-"` - Port uint16 `toml:"port" json:"port"` - Tls tlsKong `toml:"tls" json:"tls"` - // TODO: replace [auth|studio].api_url - ExternalUrl string `toml:"external_url" json:"external_url"` - } - - tlsKong struct { - Enabled bool `toml:"enabled" json:"enabled"` - CertPath string `toml:"cert_path" json:"cert_path"` - CertContent []byte `toml:"-" json:"-"` - KeyPath string `toml:"key_path" json:"key_path"` - KeyContent []byte `toml:"-" json:"-"` - } -) - -func (a *api) ToUpdatePostgrestConfigBody() v1API.V1UpdatePostgrestConfigBody { - body := v1API.V1UpdatePostgrestConfigBody{} - - // When the api is disabled, remote side it just set the dbSchema to an empty value - if !a.Enabled { - body.DbSchema = cast.Ptr("") - return body - } - - // Convert Schemas to a comma-separated string - if len(a.Schemas) > 0 { - schemas := strings.Join(a.Schemas, ",") - body.DbSchema = &schemas - } - - // Convert ExtraSearchPath to a comma-separated string - body.DbExtraSearchPath = cast.Ptr(strings.Join(a.ExtraSearchPath, ",")) - - // Convert MaxRows to int pointer - if a.MaxRows > 0 { - body.MaxRows = cast.Ptr(cast.UintToInt(a.MaxRows)) - } - - // Note: DbPool is not present in the Api struct, so it's not set here - return body -} - -func (a *api) FromRemoteApiConfig(remoteConfig v1API.PostgrestConfigWithJWTSecretResponseOutput) { - if a.Enabled = len(remoteConfig.DbSchema) > 0; !a.Enabled { - return - } - - // Update Schemas if present in remoteConfig - a.Schemas = strToArr(remoteConfig.DbSchema) - // TODO: use slices.Map when upgrade go version - for i, schema := range a.Schemas { - a.Schemas[i] = strings.TrimSpace(schema) - } - - // Update ExtraSearchPath if present in remoteConfig - a.ExtraSearchPath = strToArr(remoteConfig.DbExtraSearchPath) - for i, path := range a.ExtraSearchPath { - a.ExtraSearchPath[i] = strings.TrimSpace(path) - } - - // Update MaxRows if present in remoteConfig - a.MaxRows = cast.IntToUint(remoteConfig.MaxRows) -} - -func (a *api) DiffWithRemote(remoteConfig v1API.PostgrestConfigWithJWTSecretResponseOutput) ([]byte, error) { - copy := *a - // Convert the config values into easily comparable remoteConfig values - currentValue, err := ToTomlBytes(copy) - if err != nil { - return nil, err - } - copy.FromRemoteApiConfig(remoteConfig) - remoteCompare, err := ToTomlBytes(copy) - if err != nil { - return nil, err - } - return diff.Diff("remote[api]", remoteCompare, "local[api]", currentValue), nil -} diff --git a/apps/cli-go/pkg/config/api_test.go b/apps/cli-go/pkg/config/api_test.go deleted file mode 100644 index 8ce75c5365..0000000000 --- a/apps/cli-go/pkg/config/api_test.go +++ /dev/null @@ -1,197 +0,0 @@ -package config - -import ( - "bytes" - "io" - "os" - "testing" - fs "testing/fstest" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - v1API "github.com/supabase/cli/pkg/api" -) - -func TestApiToUpdatePostgrestConfigBody(t *testing.T) { - t.Run("converts all fields correctly", func(t *testing.T) { - api := &api{ - Enabled: true, - Schemas: []string{"public", "private"}, - ExtraSearchPath: []string{"extensions", "public"}, - MaxRows: 1000, - } - - body := api.ToUpdatePostgrestConfigBody() - - assert.Equal(t, "public,private", *body.DbSchema) - assert.Equal(t, "extensions,public", *body.DbExtraSearchPath) - assert.Equal(t, 1000, *body.MaxRows) - }) - - t.Run("handles empty fields", func(t *testing.T) { - api := &api{} - - body := api.ToUpdatePostgrestConfigBody() - - // remote api will be false by default, leading to an empty schema on api side - assert.Equal(t, "", *body.DbSchema) - }) -} - -func TestApiDiff(t *testing.T) { - t.Run("detects differences", func(t *testing.T) { - api := &api{ - Enabled: true, - Schemas: []string{"public", "private"}, - ExtraSearchPath: []string{"extensions", "public"}, - MaxRows: 1000, - } - - remoteConfig := v1API.PostgrestConfigWithJWTSecretResponseOutput{ - DbSchema: "public", - DbExtraSearchPath: "public", - MaxRows: 500, - } - - diff, err := api.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - - assertSnapshotEqual(t, diff) - }) - - t.Run("handles no differences", func(t *testing.T) { - api := &api{ - Enabled: true, - Schemas: []string{"public"}, - ExtraSearchPath: []string{"public"}, - MaxRows: 500, - } - - remoteConfig := v1API.PostgrestConfigWithJWTSecretResponseOutput{ - DbSchema: "public", - DbExtraSearchPath: "public", - MaxRows: 500, - } - - diff, err := api.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - - assert.Empty(t, diff) - }) - - t.Run("handles multiple schemas and search paths with spaces", func(t *testing.T) { - api := &api{ - Enabled: true, - Schemas: []string{"public", "private"}, - ExtraSearchPath: []string{"extensions", "public"}, - MaxRows: 500, - } - - remoteConfig := v1API.PostgrestConfigWithJWTSecretResponseOutput{ - DbSchema: "public, private", - DbExtraSearchPath: "extensions, public", - MaxRows: 500, - } - - diff, err := api.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - - assert.Empty(t, diff) - }) - - t.Run("handles api disabled on remote side", func(t *testing.T) { - api := &api{ - Enabled: true, - Schemas: []string{"public", "private"}, - ExtraSearchPath: []string{"extensions", "public"}, - MaxRows: 500, - } - - remoteConfig := v1API.PostgrestConfigWithJWTSecretResponseOutput{ - DbSchema: "", - DbExtraSearchPath: "", - MaxRows: 0, - } - - diff, err := api.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - - assertSnapshotEqual(t, diff) - }) - - t.Run("handles api disabled on local side", func(t *testing.T) { - api := &api{ - Enabled: false, - Schemas: []string{"public"}, - ExtraSearchPath: []string{"public"}, - MaxRows: 500, - } - - remoteConfig := v1API.PostgrestConfigWithJWTSecretResponseOutput{ - DbSchema: "public", - DbExtraSearchPath: "public", - MaxRows: 500, - } - - diff, err := api.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - - assertSnapshotEqual(t, diff) - }) -} - -func TestApiAutoExposeNewTablesDefault(t *testing.T) { - t.Run("is unset on a fresh config so the implicit revoke-by-default behaviour applies", func(t *testing.T) { - cfg := NewConfig() - assert.Nil(t, cfg.Api.AutoExposeNewTables) - }) -} - -func TestApiAutoExposeNewTablesWarning(t *testing.T) { - captureStderr := func(t *testing.T, run func()) string { - t.Helper() - r, w, err := os.Pipe() - require.NoError(t, err) - orig := os.Stderr - os.Stderr = w - defer func() { os.Stderr = orig }() - run() - require.NoError(t, w.Close()) - var out bytes.Buffer - _, err = io.Copy(&out, r) - require.NoError(t, err) - return out.String() - } - - t.Run("warns when auto_expose_new_tables is explicitly true", func(t *testing.T) { - cfg := NewConfig() - fsys := fs.MapFS{"config.toml": &fs.MapFile{Data: []byte("[api]\nauto_expose_new_tables = true\n")}} - stderr := captureStderr(t, func() { - require.NoError(t, cfg.Load("config.toml", fsys)) - }) - require.NotNil(t, cfg.Api.AutoExposeNewTables) - assert.True(t, *cfg.Api.AutoExposeNewTables) - assert.Contains(t, stderr, "api.auto_expose_new_tables is deprecated") - assert.Contains(t, stderr, "2026-10-30") - }) - - t.Run("does not warn when unset", func(t *testing.T) { - cfg := NewConfig() - stderr := captureStderr(t, func() { - require.NoError(t, cfg.Load("", fs.MapFS{})) - }) - assert.Nil(t, cfg.Api.AutoExposeNewTables) - assert.NotContains(t, stderr, "auto_expose_new_tables is deprecated") - }) - - t.Run("does not warn when explicitly false", func(t *testing.T) { - cfg := NewConfig() - fsys := fs.MapFS{"config.toml": &fs.MapFile{Data: []byte("[api]\nauto_expose_new_tables = false\n")}} - stderr := captureStderr(t, func() { - require.NoError(t, cfg.Load("config.toml", fsys)) - }) - require.NotNil(t, cfg.Api.AutoExposeNewTables) - assert.False(t, *cfg.Api.AutoExposeNewTables) - assert.NotContains(t, stderr, "auto_expose_new_tables is deprecated") - }) -} diff --git a/apps/cli-go/pkg/config/apikeys.go b/apps/cli-go/pkg/config/apikeys.go deleted file mode 100644 index cb1b457cab..0000000000 --- a/apps/cli-go/pkg/config/apikeys.go +++ /dev/null @@ -1,211 +0,0 @@ -package config - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rsa" - "encoding/base64" - "math/big" - "time" - - "github.com/go-errors/errors" - "github.com/golang-jwt/jwt/v5" -) - -const ( - defaultJwtSecret = "super-secret-jwt-token-with-at-least-32-characters-long" - defaultJwtExpiry = 1983812996 - defaultPublishableKey = "sb_publishable_ACJWlzQHlZjBrEguHvfOxg_3BJgxAaH" - defaultSecretKey = "sb_secret_N7UND0UgjKTVK-Uodkm0Hg_xSvEMPvz" -) - -type CustomClaims struct { - // Overrides Issuer to maintain json order when marshalling - Issuer string `json:"iss,omitempty"` - Ref string `json:"ref,omitempty"` - Role string `json:"role"` - IsAnon bool `json:"is_anonymous,omitempty"` - jwt.RegisteredClaims -} - -func (c CustomClaims) NewToken() *jwt.Token { - if c.ExpiresAt == nil { - c.ExpiresAt = jwt.NewNumericDate(time.Unix(defaultJwtExpiry, 0)) - } - if len(c.Issuer) == 0 { - c.Issuer = "supabase-demo" - } - return jwt.NewWithClaims(jwt.SigningMethodHS256, c) -} - -// generateAPIKeys generates JWT tokens using the appropriate signing method -func (a *auth) generateAPIKeys() error { - if len(a.JwtSecret.Value) == 0 { - a.JwtSecret.Value = defaultJwtSecret - } else if len(a.JwtSecret.Value) < 16 { - return errors.Errorf("Invalid config for auth.jwt_secret. Must be at least 16 characters") - } - // Generate anon key if not provided - if len(a.AnonKey.Value) == 0 { - signed, err := a.generateJWT("anon") - if err != nil { - return err - } - a.AnonKey.Value = signed - } - // Generate service_role key if not provided - if len(a.ServiceRoleKey.Value) == 0 { - signed, err := a.generateJWT("service_role") - if err != nil { - return err - } - a.ServiceRoleKey.Value = signed - } - // Set hardcoded opaque keys - if len(a.PublishableKey.Value) == 0 { - a.PublishableKey.Value = defaultPublishableKey - } - if len(a.SecretKey.Value) == 0 { - a.SecretKey.Value = defaultSecretKey - } - return nil -} - -func (a auth) generateJWT(role string) (string, error) { - claims := CustomClaims{Issuer: "supabase-demo", Role: role} - if len(a.SigningKeysPath) > 0 && len(a.SigningKeys) > 0 { - claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 365 * 10)) // 10 years - return GenerateAsymmetricJWT(a.SigningKeys[0], claims) - } - // Fallback to generating symmetric keys - signed, err := claims.NewToken().SignedString([]byte(a.JwtSecret.Value)) - if err != nil { - return "", errors.Errorf("failed to generate JWT: %w", err) - } - return signed, nil -} - -// GenerateAsymmetricJWT generates a JWT token signed with the provided JWK private key -func GenerateAsymmetricJWT(jwk JWK, claims jwt.Claims) (string, error) { - privateKey, err := jwkToPrivateKey(jwk) - if err != nil { - return "", errors.Errorf("failed to convert JWK to private key: %w", err) - } - - // Determine signing method based on algorithm - var token *jwt.Token - switch jwk.Algorithm { - case AlgRS256: - token = jwt.NewWithClaims(jwt.SigningMethodRS256, claims) - case AlgES256: - token = jwt.NewWithClaims(jwt.SigningMethodES256, claims) - default: - return "", errors.Errorf("unsupported algorithm: %s", jwk.Algorithm) - } - - if len(jwk.KeyID) > 0 { - token.Header["kid"] = jwk.KeyID - } - - tokenString, err := token.SignedString(privateKey) - if err != nil { - return "", errors.Errorf("failed to sign JWT: %w", err) - } - - return tokenString, nil -} - -// jwkToPrivateKey converts a JWK to a crypto.PrivateKey -func jwkToPrivateKey(jwk JWK) (crypto.PrivateKey, error) { - switch jwk.KeyType { - case "RSA": - return jwkToRSAPrivateKey(jwk) - case "EC": - return jwkToECDSAPrivateKey(jwk) - default: - return nil, errors.Errorf("unsupported key type: %s", jwk.KeyType) - } -} - -// jwkToRSAPrivateKey converts a JWK to an RSA private key -func jwkToRSAPrivateKey(jwk JWK) (*rsa.PrivateKey, error) { - nBytes, err := base64.RawURLEncoding.DecodeString(jwk.Modulus) - if err != nil { - return nil, errors.Errorf("failed to decode modulus: %w", err) - } - n := new(big.Int).SetBytes(nBytes) - - eBytes, err := base64.RawURLEncoding.DecodeString(jwk.Exponent) - if err != nil { - return nil, errors.Errorf("failed to decode exponent: %w", err) - } - e := int(new(big.Int).SetBytes(eBytes).Int64()) - - dBytes, err := base64.RawURLEncoding.DecodeString(jwk.PrivateExponent) - if err != nil { - return nil, errors.Errorf("failed to decode private exponent: %w", err) - } - d := new(big.Int).SetBytes(dBytes) - - pBytes, err := base64.RawURLEncoding.DecodeString(jwk.FirstPrimeFactor) - if err != nil { - return nil, errors.Errorf("failed to decode first prime factor: %w", err) - } - p := new(big.Int).SetBytes(pBytes) - - qBytes, err := base64.RawURLEncoding.DecodeString(jwk.SecondPrimeFactor) - if err != nil { - return nil, errors.Errorf("failed to decode second prime factor: %w", err) - } - q := new(big.Int).SetBytes(qBytes) - - return &rsa.PrivateKey{ - PublicKey: rsa.PublicKey{N: n, E: e}, - D: d, - Primes: []*big.Int{p, q}, - }, nil -} - -// jwkToECDSAPrivateKey converts a JWK to an ECDSA private key -func jwkToECDSAPrivateKey(jwk JWK) (*ecdsa.PrivateKey, error) { - // Only support P-256 curve for ES256 - if jwk.Curve != "P-256" { - return nil, errors.Errorf("unsupported curve: %s", jwk.Curve) - } - - xBytes, err := base64.RawURLEncoding.DecodeString(jwk.X) - if err != nil { - return nil, errors.Errorf("failed to decode x coordinate: %w", err) - } - x := new(big.Int).SetBytes(xBytes) - - yBytes, err := base64.RawURLEncoding.DecodeString(jwk.Y) - if err != nil { - return nil, errors.Errorf("failed to decode y coordinate: %w", err) - } - y := new(big.Int).SetBytes(yBytes) - - dBytes, err := base64.RawURLEncoding.DecodeString(jwk.PrivateExponent) - if err != nil { - return nil, errors.Errorf("failed to decode private key: %w", err) - } - d := new(big.Int).SetBytes(dBytes) - - return &ecdsa.PrivateKey{ - PublicKey: ecdsa.PublicKey{ - Curve: elliptic.P256(), - X: x, - Y: y, - }, - D: d, - }, nil -} - -func NewBigIntFromBase64(n string) (*big.Int, error) { - nBytes, err := base64.RawURLEncoding.DecodeString(n) - if err != nil { - return nil, errors.Errorf("failed to decode base64: %w", err) - } - return new(big.Int).SetBytes(nBytes), nil -} diff --git a/apps/cli-go/pkg/config/auth.go b/apps/cli-go/pkg/config/auth.go deleted file mode 100644 index 75bbf609c3..0000000000 --- a/apps/cli-go/pkg/config/auth.go +++ /dev/null @@ -1,1727 +0,0 @@ -package config - -import ( - "slices" - "strconv" - "strings" - "time" - - "github.com/go-errors/errors" - "github.com/oapi-codegen/nullable" - openapi_types "github.com/oapi-codegen/runtime/types" - v1API "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" - "github.com/supabase/cli/pkg/diff" -) - -type PasswordRequirements string - -const ( - NoRequirements PasswordRequirements = "" - LettersDigits PasswordRequirements = "letters_digits" - LowerUpperLettersDigits PasswordRequirements = "lower_upper_letters_digits" - LowerUpperLettersDigitsSymbols PasswordRequirements = "lower_upper_letters_digits_symbols" -) - -func (r *PasswordRequirements) UnmarshalText(text []byte) error { - allowed := []PasswordRequirements{NoRequirements, LettersDigits, LowerUpperLettersDigits, LowerUpperLettersDigitsSymbols} - if *r = PasswordRequirements(text); !slices.Contains(allowed, *r) { - return errors.Errorf("must be one of %v", allowed) - } - return nil -} - -func (r PasswordRequirements) ToChar() v1API.UpdateAuthConfigBodyPasswordRequiredCharacters { - switch r { - case LettersDigits: - return v1API.UpdateAuthConfigBodyPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 - case LowerUpperLettersDigits: - return v1API.UpdateAuthConfigBodyPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567891 - case LowerUpperLettersDigitsSymbols: - return v1API.UpdateAuthConfigBodyPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567892 - } - return v1API.UpdateAuthConfigBodyPasswordRequiredCharactersEmpty -} - -func NewPasswordRequirement(c v1API.UpdateAuthConfigBodyPasswordRequiredCharacters) PasswordRequirements { - switch c { - case v1API.UpdateAuthConfigBodyPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789: - return LettersDigits - case v1API.UpdateAuthConfigBodyPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567891: - return LowerUpperLettersDigits - case v1API.UpdateAuthConfigBodyPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567892: - return LowerUpperLettersDigitsSymbols - } - return NoRequirements -} - -type CaptchaProvider string - -const ( - HCaptchaProvider CaptchaProvider = "hcaptcha" - TurnstileProvider CaptchaProvider = "turnstile" -) - -func (p *CaptchaProvider) UnmarshalText(text []byte) error { - allowed := []CaptchaProvider{HCaptchaProvider, TurnstileProvider} - if *p = CaptchaProvider(text); !slices.Contains(allowed, *p) { - return errors.Errorf("must be one of %v", allowed) - } - return nil -} - -type Algorithm string - -const ( - AlgRS256 Algorithm = "RS256" - AlgES256 Algorithm = "ES256" -) - -func (p *Algorithm) UnmarshalText(text []byte) error { - allowed := []Algorithm{AlgRS256, AlgES256} - if *p = Algorithm(text); !slices.Contains(allowed, *p) { - return errors.Errorf("must be one of %v", allowed) - } - return nil -} - -type JWK struct { - KeyType string `toml:"kty" json:"kty"` - KeyID string `toml:"kid,omitempty" json:"kid,omitempty"` - Use string `toml:"use,omitempty" json:"use,omitempty"` - KeyOps []string `toml:"key_ops,omitempty" json:"key_ops,omitempty"` - Algorithm Algorithm `toml:"alg,omitempty" json:"alg,omitempty"` - Extractable *bool `toml:"ext,omitempty" json:"ext,omitempty"` - // RSA specific fields - Modulus string `toml:"n,omitempty" json:"n,omitempty"` - Exponent string `toml:"e,omitempty" json:"e,omitempty"` - // RSA private key fields - PrivateExponent string `toml:"d,omitempty" json:"d,omitempty"` - FirstPrimeFactor string `toml:"p,omitempty" json:"p,omitempty"` - SecondPrimeFactor string `toml:"q,omitempty" json:"q,omitempty"` - FirstFactorCRTExponent string `toml:"dp,omitempty" json:"dp,omitempty"` - SecondFactorCRTExponent string `toml:"dq,omitempty" json:"dq,omitempty"` - FirstCRTCoefficient string `toml:"qi,omitempty" json:"qi,omitempty"` - // EC specific fields - Curve string `toml:"crv,omitempty" json:"crv,omitempty"` - X string `toml:"x,omitempty" json:"x,omitempty"` - Y string `toml:"y,omitempty" json:"y,omitempty"` -} - -// ToPublicJWK converts a JWK to a public-only version by removing private key components -func (j JWK) ToPublicJWK() JWK { - publicJWK := JWK{ - KeyType: j.KeyType, - KeyID: j.KeyID, - Use: j.Use, - Algorithm: j.Algorithm, - } - - // Copy the underlying type instead of the pointer - if j.Extractable != nil { - publicJWK.Extractable = cast.Ptr(*j.Extractable) - } - - // Only include key_ops for verification (not signing) for public keys - for _, op := range j.KeyOps { - if op == "verify" { - publicJWK.KeyOps = append(publicJWK.KeyOps, op) - } - } - - switch j.KeyType { - case "RSA": - // Include only public key components for RSA - publicJWK.Modulus = j.Modulus - publicJWK.Exponent = j.Exponent - case "EC": - // Include only public key components for ECDSA - publicJWK.Curve = j.Curve - publicJWK.X = j.X - publicJWK.Y = j.Y - } - - return publicJWK -} - -type ( - auth struct { - Enabled bool `toml:"enabled" json:"enabled"` - Image string `toml:"-" json:"-"` - - SiteUrl string `toml:"site_url" json:"site_url"` - ExternalUrl string `toml:"external_url" json:"external_url"` - AdditionalRedirectUrls []string `toml:"additional_redirect_urls" json:"additional_redirect_urls"` - JwtExpiry uint `toml:"jwt_expiry" json:"jwt_expiry"` - JwtIssuer string `toml:"jwt_issuer" json:"jwt_issuer"` - EnableRefreshTokenRotation bool `toml:"enable_refresh_token_rotation" json:"enable_refresh_token_rotation"` - RefreshTokenReuseInterval uint `toml:"refresh_token_reuse_interval" json:"refresh_token_reuse_interval"` - EnableManualLinking bool `toml:"enable_manual_linking" json:"enable_manual_linking"` - EnableSignup bool `toml:"enable_signup" json:"enable_signup"` - EnableAnonymousSignIns bool `toml:"enable_anonymous_sign_ins" json:"enable_anonymous_sign_ins"` - MinimumPasswordLength uint `toml:"minimum_password_length" json:"minimum_password_length"` - PasswordRequirements PasswordRequirements `toml:"password_requirements" json:"password_requirements"` - SigningKeysPath string `toml:"signing_keys_path" json:"signing_keys_path"` - SigningKeys []JWK `toml:"-" json:"-"` - Passkey *Passkey `toml:"passkey" json:"passkey"` - Webauthn *Webauthn `toml:"webauthn" json:"webauthn"` - - RateLimit rateLimit `toml:"rate_limit" json:"rate_limit"` - Captcha *captcha `toml:"captcha" json:"captcha"` - Hook hook `toml:"hook" json:"hook"` - MFA mfa `toml:"mfa" json:"mfa"` - Sessions sessions `toml:"sessions" json:"sessions"` - Email email `toml:"email" json:"email"` - Sms sms `toml:"sms" json:"sms"` - External external `toml:"external" json:"external"` - Web3 web3 `toml:"web3" json:"web3"` - OAuthServer OAuthServer `toml:"oauth_server" json:"oauth_server"` - - // Custom secrets can be injected from .env file - PublishableKey Secret `toml:"publishable_key" json:"publishable_key"` - SecretKey Secret `toml:"secret_key" json:"secret_key"` - JwtSecret Secret `toml:"jwt_secret" json:"jwt_secret"` - AnonKey Secret `toml:"anon_key" json:"anon_key"` - ServiceRoleKey Secret `toml:"service_role_key" json:"service_role_key"` - - ThirdParty thirdParty `toml:"third_party" json:"third_party"` - } - - external map[string]provider - - thirdParty struct { - Firebase tpaFirebase `toml:"firebase" json:"firebase"` - Auth0 tpaAuth0 `toml:"auth0" json:"auth0"` - Cognito tpaCognito `toml:"aws_cognito" json:"aws_cognito"` - Clerk tpaClerk `toml:"clerk" json:"clerk"` - WorkOs tpaWorkOs `toml:"workos" json:"workos"` - } - - rateLimit struct { - AnonymousUsers uint `toml:"anonymous_users" json:"anonymous_users"` - TokenRefresh uint `toml:"token_refresh" json:"token_refresh"` - SignInSignUps uint `toml:"sign_in_sign_ups" json:"sign_in_sign_ups"` - TokenVerifications uint `toml:"token_verifications" json:"token_verifications"` - EmailSent uint `toml:"email_sent" json:"email_sent"` - SmsSent uint `toml:"sms_sent" json:"sms_sent"` - Web3 uint `toml:"web3" json:"web3"` - } - - tpaFirebase struct { - Enabled bool `toml:"enabled" json:"enabled"` - - ProjectID string `toml:"project_id" json:"project_id"` - } - - tpaAuth0 struct { - Enabled bool `toml:"enabled" json:"enabled"` - - Tenant string `toml:"tenant" json:"tenant"` - TenantRegion string `toml:"tenant_region" json:"tenant_region"` - } - - tpaCognito struct { - Enabled bool `toml:"enabled" json:"enabled"` - - UserPoolID string `toml:"user_pool_id" json:"user_pool_id"` - UserPoolRegion string `toml:"user_pool_region" json:"user_pool_region"` - } - - tpaClerk struct { - Enabled bool `toml:"enabled" json:"enabled"` - - Domain string `toml:"domain" json:"domain"` - } - - tpaWorkOs struct { - Enabled bool `toml:"enabled" json:"enabled"` - - IssuerUrl string `toml:"issuer_url" json:"issuer_url"` - } - - email struct { - EnableSignup bool `toml:"enable_signup" json:"enable_signup"` - DoubleConfirmChanges bool `toml:"double_confirm_changes" json:"double_confirm_changes"` - EnableConfirmations bool `toml:"enable_confirmations" json:"enable_confirmations"` - SecurePasswordChange bool `toml:"secure_password_change" json:"secure_password_change"` - Template map[string]emailTemplate `toml:"template" json:"template"` - Notification map[string]notification `toml:"notification" json:"notification"` - Smtp *smtp `toml:"smtp" json:"smtp"` - MaxFrequency time.Duration `toml:"max_frequency" json:"max_frequency"` - OtpLength uint `toml:"otp_length" json:"otp_length"` - OtpExpiry uint `toml:"otp_expiry" json:"otp_expiry"` - } - - smtp struct { - Enabled bool `toml:"enabled" json:"enabled"` - Host string `toml:"host" json:"host"` - Port uint16 `toml:"port" json:"port"` - User string `toml:"user" json:"user"` - Pass Secret `toml:"pass" json:"pass"` - AdminEmail openapi_types.Email `toml:"admin_email" json:"admin_email"` - SenderName string `toml:"sender_name" json:"sender_name"` - } - - emailTemplate struct { - Subject *string `toml:"subject" json:"subject"` - Content *string `toml:"content" json:"content"` - // Only content path is accepted in config.toml - ContentPath string `toml:"content_path" json:"content_path"` - } - - notification struct { - Enabled bool `toml:"enabled" json:"enabled"` - emailTemplate - } - - sms struct { - EnableSignup bool `toml:"enable_signup" json:"enable_signup"` - EnableConfirmations bool `toml:"enable_confirmations" json:"enable_confirmations"` - Template string `toml:"template" json:"template"` - Twilio twilioConfig `toml:"twilio" json:"twilio"` - TwilioVerify twilioConfig `toml:"twilio_verify" json:"twilio_verify"` - Messagebird messagebirdConfig `toml:"messagebird" json:"messagebird"` - Textlocal textlocalConfig `toml:"textlocal" json:"textlocal"` - Vonage vonageConfig `toml:"vonage" json:"vonage"` - TestOTP map[string]string `toml:"test_otp" json:"test_otp"` - MaxFrequency time.Duration `toml:"max_frequency" json:"max_frequency"` - } - - captcha struct { - Enabled bool `toml:"enabled" json:"enabled"` - Provider CaptchaProvider `toml:"provider" json:"provider"` - Secret Secret `toml:"secret" json:"secret"` - } - - hook struct { - MFAVerificationAttempt *hookConfig `toml:"mfa_verification_attempt" json:"mfa_verification_attempt"` - PasswordVerificationAttempt *hookConfig `toml:"password_verification_attempt" json:"password_verification_attempt"` - CustomAccessToken *hookConfig `toml:"custom_access_token" json:"custom_access_token"` - SendSMS *hookConfig `toml:"send_sms" json:"send_sms"` - SendEmail *hookConfig `toml:"send_email" json:"send_email"` - BeforeUserCreated *hookConfig `toml:"before_user_created" json:"before_user_created"` - } - - factorTypeConfiguration struct { - EnrollEnabled bool `toml:"enroll_enabled" json:"enroll_enabled"` - VerifyEnabled bool `toml:"verify_enabled" json:"verify_enabled"` - } - - phoneFactorTypeConfiguration struct { - factorTypeConfiguration - OtpLength uint `toml:"otp_length" json:"otp_length"` - Template string `toml:"template" json:"template"` - MaxFrequency time.Duration `toml:"max_frequency" json:"max_frequency"` - } - - mfa struct { - TOTP factorTypeConfiguration `toml:"totp" json:"totp"` - Phone phoneFactorTypeConfiguration `toml:"phone" json:"phone"` - WebAuthn factorTypeConfiguration `toml:"web_authn" json:"web_authn"` - MaxEnrolledFactors uint `toml:"max_enrolled_factors" json:"max_enrolled_factors"` - } - - hookConfig struct { - Enabled bool `toml:"enabled" json:"enabled"` - URI string `toml:"uri" json:"uri"` - Secrets Secret `toml:"secrets" json:"secrets"` - } - - sessions struct { - Timebox time.Duration `toml:"timebox" json:"timebox"` - InactivityTimeout time.Duration `toml:"inactivity_timeout" json:"inactivity_timeout"` - } - - twilioConfig struct { - Enabled bool `toml:"enabled" json:"enabled"` - AccountSid string `toml:"account_sid" json:"account_sid"` - MessageServiceSid string `toml:"message_service_sid" json:"message_service_sid"` - AuthToken Secret `toml:"auth_token" json:"auth_token"` - } - - messagebirdConfig struct { - Enabled bool `toml:"enabled" json:"enabled"` - Originator string `toml:"originator" json:"originator"` - AccessKey Secret `toml:"access_key" json:"access_key"` - } - - textlocalConfig struct { - Enabled bool `toml:"enabled" json:"enabled"` - Sender string `toml:"sender" json:"sender"` - ApiKey Secret `toml:"api_key" json:"api_key"` - } - - vonageConfig struct { - Enabled bool `toml:"enabled" json:"enabled"` - From string `toml:"from" json:"from"` - ApiKey string `toml:"api_key" json:"api_key"` - ApiSecret Secret `toml:"api_secret" json:"api_secret"` - } - - provider struct { - Enabled bool `toml:"enabled" json:"enabled"` - ClientId string `toml:"client_id" json:"client_id"` - Secret Secret `toml:"secret" json:"secret"` - Url string `toml:"url" json:"url"` - RedirectUri string `toml:"redirect_uri" json:"redirect_uri"` - SkipNonceCheck bool `toml:"skip_nonce_check" json:"skip_nonce_check"` - EmailOptional bool `toml:"email_optional" json:"email_optional"` - } - - solana struct { - Enabled bool `toml:"enabled" json:"enabled"` - } - - ethereum struct { - Enabled bool `toml:"enabled" json:"enabled"` - } - - web3 struct { - Solana solana `toml:"solana" json:"solana"` - Ethereum ethereum `toml:"ethereum" json:"ethereum"` - } - - Passkey struct { - Enabled bool `toml:"enabled" json:"enabled"` - } - - Webauthn struct { - RpDisplayName string `toml:"rp_display_name" json:"rp_display_name"` - RpId string `toml:"rp_id" json:"rp_id"` - RpOrigins []string `toml:"rp_origins" json:"rp_origins"` - } - - OAuthServer struct { - Enabled bool `toml:"enabled" json:"enabled"` - AllowDynamicRegistration bool `toml:"allow_dynamic_registration" json:"allow_dynamic_registration"` - AuthorizationUrlPath string `toml:"authorization_url_path" json:"authorization_url_path"` - } -) - -func (a auth) GetExternalURL(apiExternalURL string) string { - if len(a.ExternalUrl) > 0 { - return a.ExternalUrl - } - return strings.TrimRight(apiExternalURL, "/") + "/auth/v1" -} - -func (a *auth) ToUpdateAuthConfigBody() v1API.UpdateAuthConfigBody { - body := v1API.UpdateAuthConfigBody{ - SiteUrl: nullable.NewNullableWithValue(a.SiteUrl), - UriAllowList: nullable.NewNullableWithValue(strings.Join(a.AdditionalRedirectUrls, ",")), - JwtExp: nullable.NewNullableWithValue(cast.UintToInt(a.JwtExpiry)), - RefreshTokenRotationEnabled: nullable.NewNullableWithValue(a.EnableRefreshTokenRotation), - SecurityRefreshTokenReuseInterval: nullable.NewNullableWithValue(cast.UintToInt(a.RefreshTokenReuseInterval)), - SecurityManualLinkingEnabled: nullable.NewNullableWithValue(a.EnableManualLinking), - DisableSignup: nullable.NewNullableWithValue(!a.EnableSignup), - ExternalAnonymousUsersEnabled: nullable.NewNullableWithValue(a.EnableAnonymousSignIns), - PasswordMinLength: nullable.NewNullableWithValue(cast.UintToInt(a.MinimumPasswordLength)), - PasswordRequiredCharacters: nullable.NewNullableWithValue(a.PasswordRequirements.ToChar()), - } - // Add rate limit fields - a.RateLimit.toAuthConfigBody(&body) - if s := a.Email.Smtp; s != nil && s.Enabled { - body.RateLimitEmailSent = nullable.NewNullableWithValue(cast.UintToInt(a.RateLimit.EmailSent)) - } - // When local config is not set, we assume platform defaults should not change - if a.Captcha != nil { - a.Captcha.toAuthConfigBody(&body) - } - if a.Passkey != nil { - a.Passkey.toAuthConfigBody(&body) - } - if a.Webauthn != nil { - a.Webauthn.toAuthConfigBody(&body) - } - a.Hook.toAuthConfigBody(&body) - a.MFA.toAuthConfigBody(&body) - a.Sessions.toAuthConfigBody(&body) - a.Email.toAuthConfigBody(&body) - a.Sms.toAuthConfigBody(&body) - a.External.toAuthConfigBody(&body) - a.Web3.toAuthConfigBody(&body) - a.OAuthServer.toAuthConfigBody(&body) - return body -} - -func (a *auth) FromRemoteAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - a.SiteUrl = ValOrDefault(remoteConfig.SiteUrl, "") - a.AdditionalRedirectUrls = strToArr(ValOrDefault(remoteConfig.UriAllowList, "")) - a.JwtExpiry = cast.IntToUint(ValOrDefault(remoteConfig.JwtExp, 0)) - a.EnableRefreshTokenRotation = ValOrDefault(remoteConfig.RefreshTokenRotationEnabled, false) - a.RefreshTokenReuseInterval = cast.IntToUint(ValOrDefault(remoteConfig.SecurityRefreshTokenReuseInterval, 0)) - a.EnableManualLinking = ValOrDefault(remoteConfig.SecurityManualLinkingEnabled, false) - a.EnableSignup = !ValOrDefault(remoteConfig.DisableSignup, false) - a.EnableAnonymousSignIns = ValOrDefault(remoteConfig.ExternalAnonymousUsersEnabled, false) - a.MinimumPasswordLength = cast.IntToUint(ValOrDefault(remoteConfig.PasswordMinLength, 0)) - prc := ValOrDefault(remoteConfig.PasswordRequiredCharacters, "") - a.PasswordRequirements = NewPasswordRequirement(v1API.UpdateAuthConfigBodyPasswordRequiredCharacters(prc)) - a.Passkey.fromAuthConfig(remoteConfig) - a.Webauthn.fromAuthConfig(remoteConfig) - a.RateLimit.fromAuthConfig(remoteConfig) - if s := a.Email.Smtp; s != nil && s.Enabled { - a.RateLimit.EmailSent = cast.IntToUint(ValOrDefault(remoteConfig.RateLimitEmailSent, 0)) - } - a.Captcha.fromAuthConfig(remoteConfig) - a.Hook.fromAuthConfig(remoteConfig) - a.MFA.fromAuthConfig(remoteConfig) - a.Sessions.fromAuthConfig(remoteConfig) - a.Email.fromAuthConfig(remoteConfig) - a.Sms.fromAuthConfig(remoteConfig) - a.External.fromAuthConfig(remoteConfig) - a.Web3.fromAuthConfig(remoteConfig) - a.OAuthServer.fromAuthConfig(remoteConfig) -} - -func (r rateLimit) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - body.RateLimitAnonymousUsers = nullable.NewNullableWithValue(cast.UintToInt(r.AnonymousUsers)) - body.RateLimitTokenRefresh = nullable.NewNullableWithValue(cast.UintToInt(r.TokenRefresh)) - body.RateLimitOtp = nullable.NewNullableWithValue(cast.UintToInt(r.SignInSignUps)) - body.RateLimitVerify = nullable.NewNullableWithValue(cast.UintToInt(r.TokenVerifications)) - // Email rate limit is only updated when SMTP is enabled - body.RateLimitSmsSent = nullable.NewNullableWithValue(cast.UintToInt(r.SmsSent)) - body.RateLimitWeb3 = nullable.NewNullableWithValue((cast.UintToInt(r.Web3))) -} - -func (r *rateLimit) fromAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - r.AnonymousUsers = cast.IntToUint(ValOrDefault(remoteConfig.RateLimitAnonymousUsers, 0)) - r.TokenRefresh = cast.IntToUint(ValOrDefault(remoteConfig.RateLimitTokenRefresh, 0)) - r.SignInSignUps = cast.IntToUint(ValOrDefault(remoteConfig.RateLimitOtp, 0)) - r.TokenVerifications = cast.IntToUint(ValOrDefault(remoteConfig.RateLimitVerify, 0)) - // Email rate limit is only updated when SMTP is enabled - r.SmsSent = cast.IntToUint(ValOrDefault(remoteConfig.RateLimitSmsSent, 0)) - r.Web3 = cast.IntToUint(ValOrDefault(remoteConfig.RateLimitWeb3, 0)) -} - -func (c captcha) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - if body.SecurityCaptchaEnabled = nullable.NewNullableWithValue(c.Enabled); c.Enabled { - body.SecurityCaptchaProvider = nullable.NewNullableWithValue(v1API.UpdateAuthConfigBodySecurityCaptchaProvider(c.Provider)) - if len(c.Secret.SHA256) > 0 { - body.SecurityCaptchaSecret = nullable.NewNullableWithValue(c.Secret.Value) - } - } -} - -func (c *captcha) fromAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - // When local config is not set, we assume platform defaults should not change - if c == nil { - return - } - // Ignore disabled captcha fields to minimise config diff - if c.Enabled { - c.Provider = CaptchaProvider(ValOrDefault(remoteConfig.SecurityCaptchaProvider, "")) - if len(c.Secret.SHA256) > 0 { - c.Secret.SHA256 = ValOrDefault(remoteConfig.SecurityCaptchaSecret, "") - } - } - c.Enabled = ValOrDefault(remoteConfig.SecurityCaptchaEnabled, false) -} - -func (p Passkey) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - body.PasskeyEnabled = cast.Ptr(p.Enabled) -} - -func (p *Passkey) fromAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - // When local config is not set, we assume platform defaults should not change - if p == nil { - return - } - p.Enabled = remoteConfig.PasskeyEnabled -} - -func (w Webauthn) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - body.WebauthnRpDisplayName = nullable.NewNullableWithValue(w.RpDisplayName) - body.WebauthnRpId = nullable.NewNullableWithValue(w.RpId) - body.WebauthnRpOrigins = nullable.NewNullableWithValue(strings.Join(w.RpOrigins, ",")) -} - -func (w *Webauthn) fromAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - // When local config is not set, we assume platform defaults should not change - if w == nil { - return - } - w.RpDisplayName = ValOrDefault(remoteConfig.WebauthnRpDisplayName, "") - w.RpId = ValOrDefault(remoteConfig.WebauthnRpId, "") - w.RpOrigins = strToArr(ValOrDefault(remoteConfig.WebauthnRpOrigins, "")) -} - -func (h hook) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - // When local config is not set, we assume platform defaults should not change - if hook := h.BeforeUserCreated; hook != nil { - if body.HookBeforeUserCreatedEnabled = nullable.NewNullableWithValue(hook.Enabled); hook.Enabled { - body.HookBeforeUserCreatedUri = nullable.NewNullableWithValue(hook.URI) - if len(hook.Secrets.SHA256) > 0 { - body.HookBeforeUserCreatedSecrets = nullable.NewNullableWithValue(hook.Secrets.Value) - } - } - } - if hook := h.CustomAccessToken; hook != nil { - if body.HookCustomAccessTokenEnabled = nullable.NewNullableWithValue(hook.Enabled); hook.Enabled { - body.HookCustomAccessTokenUri = nullable.NewNullableWithValue(hook.URI) - if len(hook.Secrets.SHA256) > 0 { - body.HookCustomAccessTokenSecrets = nullable.NewNullableWithValue(hook.Secrets.Value) - } - } - } - if hook := h.SendEmail; hook != nil { - if body.HookSendEmailEnabled = nullable.NewNullableWithValue(hook.Enabled); hook.Enabled { - body.HookSendEmailUri = nullable.NewNullableWithValue(hook.URI) - if len(hook.Secrets.SHA256) > 0 { - body.HookSendEmailSecrets = nullable.NewNullableWithValue(hook.Secrets.Value) - } - } - } - if hook := h.SendSMS; hook != nil { - if body.HookSendSmsEnabled = nullable.NewNullableWithValue(hook.Enabled); hook.Enabled { - body.HookSendSmsUri = nullable.NewNullableWithValue(hook.URI) - if len(hook.Secrets.SHA256) > 0 { - body.HookSendSmsSecrets = nullable.NewNullableWithValue(hook.Secrets.Value) - } - } - } - // Enterprise and team only features - if hook := h.MFAVerificationAttempt; hook != nil { - if body.HookMfaVerificationAttemptEnabled = nullable.NewNullableWithValue(hook.Enabled); hook.Enabled { - body.HookMfaVerificationAttemptUri = nullable.NewNullableWithValue(hook.URI) - if len(hook.Secrets.SHA256) > 0 { - body.HookMfaVerificationAttemptSecrets = nullable.NewNullableWithValue(hook.Secrets.Value) - } - } - } - if hook := h.PasswordVerificationAttempt; hook != nil { - if body.HookPasswordVerificationAttemptEnabled = nullable.NewNullableWithValue(hook.Enabled); hook.Enabled { - body.HookPasswordVerificationAttemptUri = nullable.NewNullableWithValue(hook.URI) - if len(hook.Secrets.SHA256) > 0 { - body.HookPasswordVerificationAttemptSecrets = nullable.NewNullableWithValue(hook.Secrets.Value) - } - } - } -} -func (h *hook) fromAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - // When local config is not set, we assume platform defaults should not change - if hook := h.BeforeUserCreated; hook != nil { - // Ignore disabled hooks because their envs are not loaded - if hook.Enabled { - hook.URI = ValOrDefault(remoteConfig.HookBeforeUserCreatedUri, "") - if len(hook.Secrets.SHA256) > 0 { - hook.Secrets.SHA256 = ValOrDefault(remoteConfig.HookBeforeUserCreatedSecrets, "") - } - } - hook.Enabled = ValOrDefault(remoteConfig.HookBeforeUserCreatedEnabled, false) - } - if hook := h.CustomAccessToken; hook != nil { - // Ignore disabled hooks because their envs are not loaded - if hook.Enabled { - hook.URI = ValOrDefault(remoteConfig.HookCustomAccessTokenUri, "") - if len(hook.Secrets.SHA256) > 0 { - hook.Secrets.SHA256 = ValOrDefault(remoteConfig.HookCustomAccessTokenSecrets, "") - } - } - hook.Enabled = ValOrDefault(remoteConfig.HookCustomAccessTokenEnabled, false) - } - if hook := h.SendEmail; hook != nil { - if hook.Enabled { - hook.URI = ValOrDefault(remoteConfig.HookSendEmailUri, "") - if len(hook.Secrets.SHA256) > 0 { - hook.Secrets.SHA256 = ValOrDefault(remoteConfig.HookSendEmailSecrets, "") - } - } - hook.Enabled = ValOrDefault(remoteConfig.HookSendEmailEnabled, false) - } - if hook := h.SendSMS; hook != nil { - if hook.Enabled { - hook.URI = ValOrDefault(remoteConfig.HookSendSmsUri, "") - if len(hook.Secrets.SHA256) > 0 { - hook.Secrets.SHA256 = ValOrDefault(remoteConfig.HookSendSmsSecrets, "") - } - } - hook.Enabled = ValOrDefault(remoteConfig.HookSendSmsEnabled, false) - } - // Enterprise and team only features - if hook := h.MFAVerificationAttempt; hook != nil { - if hook.Enabled { - hook.URI = ValOrDefault(remoteConfig.HookMfaVerificationAttemptUri, "") - if len(hook.Secrets.SHA256) > 0 { - hook.Secrets.SHA256 = ValOrDefault(remoteConfig.HookMfaVerificationAttemptSecrets, "") - } - } - hook.Enabled = ValOrDefault(remoteConfig.HookMfaVerificationAttemptEnabled, false) - } - if hook := h.PasswordVerificationAttempt; hook != nil { - if hook.Enabled { - hook.URI = ValOrDefault(remoteConfig.HookPasswordVerificationAttemptUri, "") - if len(hook.Secrets.SHA256) > 0 { - hook.Secrets.SHA256 = ValOrDefault(remoteConfig.HookPasswordVerificationAttemptSecrets, "") - } - } - hook.Enabled = ValOrDefault(remoteConfig.HookPasswordVerificationAttemptEnabled, false) - } -} - -func (m mfa) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - body.MfaMaxEnrolledFactors = nullable.NewNullableWithValue(cast.UintToInt(m.MaxEnrolledFactors)) - body.MfaTotpEnrollEnabled = nullable.NewNullableWithValue(m.TOTP.EnrollEnabled) - body.MfaTotpVerifyEnabled = nullable.NewNullableWithValue(m.TOTP.VerifyEnabled) - body.MfaPhoneEnrollEnabled = nullable.NewNullableWithValue(m.Phone.EnrollEnabled) - body.MfaPhoneVerifyEnabled = nullable.NewNullableWithValue(m.Phone.VerifyEnabled) - body.MfaPhoneOtpLength = nullable.NewNullableWithValue(cast.UintToInt(m.Phone.OtpLength)) - body.MfaPhoneTemplate = nullable.NewNullableWithValue(m.Phone.Template) - body.MfaPhoneMaxFrequency = nullable.NewNullableWithValue(int(m.Phone.MaxFrequency.Seconds())) - body.MfaWebAuthnEnrollEnabled = nullable.NewNullableWithValue(m.WebAuthn.EnrollEnabled) - body.MfaWebAuthnVerifyEnabled = nullable.NewNullableWithValue(m.WebAuthn.VerifyEnabled) -} - -func (m *mfa) fromAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - m.MaxEnrolledFactors = cast.IntToUint(ValOrDefault(remoteConfig.MfaMaxEnrolledFactors, 0)) - m.TOTP.EnrollEnabled = ValOrDefault(remoteConfig.MfaTotpEnrollEnabled, false) - m.TOTP.VerifyEnabled = ValOrDefault(remoteConfig.MfaTotpVerifyEnabled, false) - m.Phone.EnrollEnabled = ValOrDefault(remoteConfig.MfaPhoneEnrollEnabled, false) - m.Phone.VerifyEnabled = ValOrDefault(remoteConfig.MfaPhoneVerifyEnabled, false) - m.Phone.OtpLength = cast.IntToUint(remoteConfig.MfaPhoneOtpLength) - m.Phone.Template = ValOrDefault(remoteConfig.MfaPhoneTemplate, "") - m.Phone.MaxFrequency = time.Duration(ValOrDefault(remoteConfig.MfaPhoneMaxFrequency, 0)) * time.Second - m.WebAuthn.EnrollEnabled = ValOrDefault(remoteConfig.MfaWebAuthnEnrollEnabled, false) - m.WebAuthn.VerifyEnabled = ValOrDefault(remoteConfig.MfaWebAuthnVerifyEnabled, false) -} - -func (s sessions) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - body.SessionsTimebox = nullable.NewNullableWithValue(float32(s.Timebox.Hours())) - body.SessionsInactivityTimeout = nullable.NewNullableWithValue(float32(s.InactivityTimeout.Hours())) -} - -func (s *sessions) fromAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - s.Timebox = time.Duration(ValOrDefault(remoteConfig.SessionsTimebox, 0)) * time.Hour - s.InactivityTimeout = time.Duration(ValOrDefault(remoteConfig.SessionsInactivityTimeout, 0)) * time.Hour -} - -func (e email) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - body.ExternalEmailEnabled = nullable.NewNullableWithValue(e.EnableSignup) - body.MailerSecureEmailChangeEnabled = nullable.NewNullableWithValue(e.DoubleConfirmChanges) - body.MailerAutoconfirm = nullable.NewNullableWithValue(!e.EnableConfirmations) - body.MailerOtpLength = nullable.NewNullableWithValue(cast.UintToInt(e.OtpLength)) - body.MailerOtpExp = cast.UintToIntPtr(&e.OtpExpiry) - body.SecurityUpdatePasswordRequireReauthentication = nullable.NewNullableWithValue(e.SecurePasswordChange) - body.SmtpMaxFrequency = nullable.NewNullableWithValue(int(e.MaxFrequency.Seconds())) - // When local config is not set, we assume platform defaults should not change - if e.Smtp != nil { - e.Smtp.toAuthConfigBody(body) - } - if len(e.Template) > 0 { - var tmpl *emailTemplate - tmpl = cast.Ptr(e.Template["invite"]) - if tmpl.Subject != nil { - body.MailerSubjectsInvite = nullable.NewNullableWithValue(*tmpl.Subject) - } - if tmpl.Content != nil { - body.MailerTemplatesInviteContent = nullable.NewNullableWithValue(*tmpl.Content) - } - tmpl = cast.Ptr(e.Template["confirmation"]) - if tmpl.Subject != nil { - body.MailerSubjectsConfirmation = nullable.NewNullableWithValue(*tmpl.Subject) - } - if tmpl.Content != nil { - body.MailerTemplatesConfirmationContent = nullable.NewNullableWithValue(*tmpl.Content) - } - tmpl = cast.Ptr(e.Template["recovery"]) - if tmpl.Subject != nil { - body.MailerSubjectsRecovery = nullable.NewNullableWithValue(*tmpl.Subject) - } - if tmpl.Content != nil { - body.MailerTemplatesRecoveryContent = nullable.NewNullableWithValue(*tmpl.Content) - } - tmpl = cast.Ptr(e.Template["magic_link"]) - if tmpl.Subject != nil { - body.MailerSubjectsMagicLink = nullable.NewNullableWithValue(*tmpl.Subject) - } - if tmpl.Content != nil { - body.MailerTemplatesMagicLinkContent = nullable.NewNullableWithValue(*tmpl.Content) - } - tmpl = cast.Ptr(e.Template["email_change"]) - if tmpl.Subject != nil { - body.MailerSubjectsEmailChange = nullable.NewNullableWithValue(*tmpl.Subject) - } - if tmpl.Content != nil { - body.MailerTemplatesEmailChangeContent = nullable.NewNullableWithValue(*tmpl.Content) - } - tmpl = cast.Ptr(e.Template["reauthentication"]) - if tmpl.Subject != nil { - body.MailerSubjectsReauthentication = nullable.NewNullableWithValue(*tmpl.Subject) - } - if tmpl.Content != nil { - body.MailerTemplatesReauthenticationContent = nullable.NewNullableWithValue(*tmpl.Content) - } - } - // Notifications - if len(e.Notification) > 0 { - if n, ok := e.Notification["password_changed"]; ok { - body.MailerNotificationsPasswordChangedEnabled = nullable.NewNullableWithValue(n.Enabled) - if n.Subject != nil { - body.MailerSubjectsPasswordChangedNotification = nullable.NewNullableWithValue(*n.Subject) - } - if n.Content != nil { - body.MailerTemplatesPasswordChangedNotificationContent = nullable.NewNullableWithValue(*n.Content) - } - } - if n, ok := e.Notification["email_changed"]; ok { - body.MailerNotificationsEmailChangedEnabled = nullable.NewNullableWithValue(n.Enabled) - if n.Subject != nil { - body.MailerSubjectsEmailChangedNotification = nullable.NewNullableWithValue(*n.Subject) - } - if n.Content != nil { - body.MailerTemplatesEmailChangedNotificationContent = nullable.NewNullableWithValue(*n.Content) - } - } - if n, ok := e.Notification["phone_changed"]; ok { - body.MailerNotificationsPhoneChangedEnabled = nullable.NewNullableWithValue(n.Enabled) - if n.Subject != nil { - body.MailerSubjectsPhoneChangedNotification = nullable.NewNullableWithValue(*n.Subject) - } - if n.Content != nil { - body.MailerTemplatesPhoneChangedNotificationContent = nullable.NewNullableWithValue(*n.Content) - } - } - if n, ok := e.Notification["identity_linked"]; ok { - body.MailerNotificationsIdentityLinkedEnabled = nullable.NewNullableWithValue(n.Enabled) - if n.Subject != nil { - body.MailerSubjectsIdentityLinkedNotification = nullable.NewNullableWithValue(*n.Subject) - } - if n.Content != nil { - body.MailerTemplatesIdentityLinkedNotificationContent = nullable.NewNullableWithValue(*n.Content) - } - } - if n, ok := e.Notification["identity_unlinked"]; ok { - body.MailerNotificationsIdentityUnlinkedEnabled = nullable.NewNullableWithValue(n.Enabled) - if n.Subject != nil { - body.MailerSubjectsIdentityUnlinkedNotification = nullable.NewNullableWithValue(*n.Subject) - } - if n.Content != nil { - body.MailerTemplatesIdentityUnlinkedNotificationContent = nullable.NewNullableWithValue(*n.Content) - } - } - if n, ok := e.Notification["mfa_factor_enrolled"]; ok { - body.MailerNotificationsMfaFactorEnrolledEnabled = nullable.NewNullableWithValue(n.Enabled) - if n.Subject != nil { - body.MailerSubjectsMfaFactorEnrolledNotification = nullable.NewNullableWithValue(*n.Subject) - } - if n.Content != nil { - body.MailerTemplatesMfaFactorEnrolledNotificationContent = nullable.NewNullableWithValue(*n.Content) - } - } - if n, ok := e.Notification["mfa_factor_unenrolled"]; ok { - body.MailerNotificationsMfaFactorUnenrolledEnabled = nullable.NewNullableWithValue(n.Enabled) - if n.Subject != nil { - body.MailerSubjectsMfaFactorUnenrolledNotification = nullable.NewNullableWithValue(*n.Subject) - } - if n.Content != nil { - body.MailerTemplatesMfaFactorUnenrolledNotificationContent = nullable.NewNullableWithValue(*n.Content) - } - } - } -} - -func (e *email) fromAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - e.EnableSignup = ValOrDefault(remoteConfig.ExternalEmailEnabled, false) - e.DoubleConfirmChanges = ValOrDefault(remoteConfig.MailerSecureEmailChangeEnabled, false) - e.EnableConfirmations = !ValOrDefault(remoteConfig.MailerAutoconfirm, false) - e.OtpLength = cast.IntToUint(ValOrDefault(remoteConfig.MailerOtpLength, 0)) - e.OtpExpiry = cast.IntToUint(remoteConfig.MailerOtpExp) - e.SecurePasswordChange = ValOrDefault(remoteConfig.SecurityUpdatePasswordRequireReauthentication, false) - e.MaxFrequency = time.Duration(ValOrDefault(remoteConfig.SmtpMaxFrequency, 0)) * time.Second - e.Smtp.fromAuthConfig(remoteConfig) - if len(e.Template) > 0 { - if t, ok := e.Template["invite"]; ok { - if t.Subject != nil { - if value, err := remoteConfig.MailerSubjectsInvite.Get(); err == nil { - t.Subject = &value - } else { - t.Subject = nil - } - } - if t.Content != nil { - if value, err := remoteConfig.MailerTemplatesInviteContent.Get(); err == nil { - t.Content = &value - } else { - t.Content = nil - } - } - e.Template["invite"] = t - } - if t, ok := e.Template["confirmation"]; ok { - if t.Subject != nil { - if value, err := remoteConfig.MailerSubjectsConfirmation.Get(); err == nil { - t.Subject = &value - } else { - t.Subject = nil - } - } - if t.Content != nil { - if value, err := remoteConfig.MailerTemplatesConfirmationContent.Get(); err == nil { - t.Content = &value - } else { - t.Content = nil - } - } - e.Template["confirmation"] = t - } - if t, ok := e.Template["recovery"]; ok { - if t.Subject != nil { - if value, err := remoteConfig.MailerSubjectsRecovery.Get(); err == nil { - t.Subject = &value - } else { - t.Subject = nil - } - } - if t.Content != nil { - if value, err := remoteConfig.MailerTemplatesRecoveryContent.Get(); err == nil { - t.Content = &value - } else { - t.Content = nil - } - } - e.Template["recovery"] = t - } - if t, ok := e.Template["magic_link"]; ok { - if t.Subject != nil { - if value, err := remoteConfig.MailerSubjectsMagicLink.Get(); err == nil { - t.Subject = &value - } else { - t.Subject = nil - } - } - if t.Content != nil { - if value, err := remoteConfig.MailerTemplatesMagicLinkContent.Get(); err == nil { - t.Content = &value - } else { - t.Content = nil - } - } - e.Template["magic_link"] = t - } - if t, ok := e.Template["email_change"]; ok { - if t.Subject != nil { - if value, err := remoteConfig.MailerSubjectsEmailChange.Get(); err == nil { - t.Subject = &value - } else { - t.Subject = nil - } - } - if t.Content != nil { - if value, err := remoteConfig.MailerTemplatesEmailChangeContent.Get(); err == nil { - t.Content = &value - } else { - t.Content = nil - } - } - e.Template["email_change"] = t - } - if t, ok := e.Template["reauthentication"]; ok { - if t.Subject != nil { - if value, err := remoteConfig.MailerSubjectsReauthentication.Get(); err == nil { - t.Subject = &value - } else { - t.Subject = nil - } - } - if t.Content != nil { - if value, err := remoteConfig.MailerTemplatesReauthenticationContent.Get(); err == nil { - t.Content = &value - } else { - t.Content = nil - } - } - e.Template["reauthentication"] = t - } - } - // Notifications - if len(e.Notification) > 0 { - if n, ok := e.Notification["password_changed"]; ok { - if value, err := remoteConfig.MailerNotificationsPasswordChangedEnabled.Get(); err == nil { - n.Enabled = value - } - if n.Subject != nil { - if value, err := remoteConfig.MailerSubjectsPasswordChangedNotification.Get(); err == nil { - n.Subject = &value - } else { - n.Subject = nil - } - } - if n.Content != nil { - if value, err := remoteConfig.MailerTemplatesPasswordChangedNotificationContent.Get(); err == nil { - n.Content = &value - } else { - n.Content = nil - } - } - e.Notification["password_changed"] = n - } - if n, ok := e.Notification["email_changed"]; ok { - if value, err := remoteConfig.MailerNotificationsEmailChangedEnabled.Get(); err == nil { - n.Enabled = value - } - if n.Subject != nil { - if value, err := remoteConfig.MailerSubjectsEmailChangedNotification.Get(); err == nil { - n.Subject = &value - } else { - n.Subject = nil - } - } - if n.Content != nil { - if value, err := remoteConfig.MailerTemplatesEmailChangedNotificationContent.Get(); err == nil { - n.Content = &value - } else { - n.Content = nil - } - } - e.Notification["email_changed"] = n - } - if n, ok := e.Notification["phone_changed"]; ok { - if value, err := remoteConfig.MailerNotificationsPhoneChangedEnabled.Get(); err == nil { - n.Enabled = value - } - if n.Subject != nil { - if value, err := remoteConfig.MailerSubjectsPhoneChangedNotification.Get(); err == nil { - n.Subject = &value - } else { - n.Subject = nil - } - } - if n.Content != nil { - if value, err := remoteConfig.MailerTemplatesPhoneChangedNotificationContent.Get(); err == nil { - n.Content = &value - } else { - n.Content = nil - } - } - e.Notification["phone_changed"] = n - } - if n, ok := e.Notification["identity_linked"]; ok { - if value, err := remoteConfig.MailerNotificationsIdentityLinkedEnabled.Get(); err == nil { - n.Enabled = value - } - if n.Subject != nil { - if value, err := remoteConfig.MailerSubjectsIdentityLinkedNotification.Get(); err == nil { - n.Subject = &value - } else { - n.Subject = nil - } - } - if n.Content != nil { - if value, err := remoteConfig.MailerTemplatesIdentityLinkedNotificationContent.Get(); err == nil { - n.Content = &value - } else { - n.Content = nil - } - } - e.Notification["identity_linked"] = n - } - if n, ok := e.Notification["identity_unlinked"]; ok { - if value, err := remoteConfig.MailerNotificationsIdentityUnlinkedEnabled.Get(); err == nil { - n.Enabled = value - } - if n.Subject != nil { - if value, err := remoteConfig.MailerSubjectsIdentityUnlinkedNotification.Get(); err == nil { - n.Subject = &value - } else { - n.Subject = nil - } - } - if n.Content != nil { - if value, err := remoteConfig.MailerTemplatesIdentityUnlinkedNotificationContent.Get(); err == nil { - n.Content = &value - } else { - n.Content = nil - } - } - e.Notification["identity_unlinked"] = n - } - if n, ok := e.Notification["mfa_factor_enrolled"]; ok { - if value, err := remoteConfig.MailerNotificationsMfaFactorEnrolledEnabled.Get(); err == nil { - n.Enabled = value - } - if n.Subject != nil { - if value, err := remoteConfig.MailerSubjectsMfaFactorEnrolledNotification.Get(); err == nil { - n.Subject = &value - } else { - n.Subject = nil - } - } - if n.Content != nil { - if value, err := remoteConfig.MailerTemplatesMfaFactorEnrolledNotificationContent.Get(); err == nil { - n.Content = &value - } else { - n.Content = nil - } - } - e.Notification["mfa_factor_enrolled"] = n - } - if n, ok := e.Notification["mfa_factor_unenrolled"]; ok { - if value, err := remoteConfig.MailerNotificationsMfaFactorUnenrolledEnabled.Get(); err == nil { - n.Enabled = value - } - if n.Subject != nil { - if value, err := remoteConfig.MailerSubjectsMfaFactorUnenrolledNotification.Get(); err == nil { - n.Subject = &value - } else { - n.Subject = nil - } - } - if n.Content != nil { - if value, err := remoteConfig.MailerTemplatesMfaFactorUnenrolledNotificationContent.Get(); err == nil { - n.Content = &value - } else { - n.Content = nil - } - } - e.Notification["mfa_factor_unenrolled"] = n - } - } -} - -func (s smtp) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - if !s.Enabled { - // Setting a single empty string disables SMTP - body.SmtpHost = nullable.NewNullableWithValue("") - return - } - body.SmtpHost = nullable.NewNullableWithValue(s.Host) - body.SmtpPort = nullable.NewNullableWithValue(strconv.Itoa(int(s.Port))) - body.SmtpUser = nullable.NewNullableWithValue(s.User) - if len(s.Pass.SHA256) > 0 { - body.SmtpPass = nullable.NewNullableWithValue(s.Pass.Value) - } - body.SmtpAdminEmail = nullable.NewNullableWithValue(s.AdminEmail) - body.SmtpSenderName = nullable.NewNullableWithValue(s.SenderName) -} - -func (s *smtp) fromAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - // When local config is not set, we assume platform defaults should not change - if s == nil { - return - } - if s.Enabled { - s.Host = ValOrDefault(remoteConfig.SmtpHost, "") - s.User = ValOrDefault(remoteConfig.SmtpUser, "") - if len(s.Pass.SHA256) > 0 { - s.Pass.SHA256 = ValOrDefault(remoteConfig.SmtpPass, "") - } - s.AdminEmail = ValOrDefault(remoteConfig.SmtpAdminEmail, openapi_types.Email("")) - s.SenderName = ValOrDefault(remoteConfig.SmtpSenderName, "") - portStr := ValOrDefault(remoteConfig.SmtpPort, "0") - if port, err := strconv.ParseUint(portStr, 10, 16); err == nil { - s.Port = uint16(port) - } - } - // Api resets all values when SMTP is disabled - s.Enabled = remoteConfig.SmtpHost != nil -} - -func (s sms) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - body.ExternalPhoneEnabled = nullable.NewNullableWithValue(s.EnableSignup) - body.SmsMaxFrequency = nullable.NewNullableWithValue(int(s.MaxFrequency.Seconds())) - body.SmsAutoconfirm = nullable.NewNullableWithValue(s.EnableConfirmations) - body.SmsTemplate = nullable.NewNullableWithValue(s.Template) - if otpString := mapToEnv(s.TestOTP); len(otpString) > 0 { - body.SmsTestOtp = nullable.NewNullableWithValue(otpString) - // Set a 10 year validity for test OTP - timestamp := time.Now().UTC().AddDate(10, 0, 0) - body.SmsTestOtpValidUntil = nullable.NewNullableWithValue(timestamp) - } - // Api only overrides configs of enabled providers - switch { - case s.Twilio.Enabled: - body.SmsProvider = nullable.NewNullableWithValue(v1API.Twilio) - if len(s.Twilio.AuthToken.SHA256) > 0 { - body.SmsTwilioAuthToken = nullable.NewNullableWithValue(s.Twilio.AuthToken.Value) - } - body.SmsTwilioAccountSid = nullable.NewNullableWithValue(s.Twilio.AccountSid) - body.SmsTwilioMessageServiceSid = nullable.NewNullableWithValue(s.Twilio.MessageServiceSid) - case s.TwilioVerify.Enabled: - body.SmsProvider = nullable.NewNullableWithValue(v1API.TwilioVerify) - if len(s.TwilioVerify.AuthToken.SHA256) > 0 { - body.SmsTwilioVerifyAuthToken = nullable.NewNullableWithValue(s.TwilioVerify.AuthToken.Value) - } - body.SmsTwilioVerifyAccountSid = nullable.NewNullableWithValue(s.TwilioVerify.AccountSid) - body.SmsTwilioVerifyMessageServiceSid = nullable.NewNullableWithValue(s.TwilioVerify.MessageServiceSid) - case s.Messagebird.Enabled: - body.SmsProvider = nullable.NewNullableWithValue(v1API.Messagebird) - if len(s.Messagebird.AccessKey.SHA256) > 0 { - body.SmsMessagebirdAccessKey = nullable.NewNullableWithValue(s.Messagebird.AccessKey.Value) - } - body.SmsMessagebirdOriginator = nullable.NewNullableWithValue(s.Messagebird.Originator) - case s.Textlocal.Enabled: - body.SmsProvider = nullable.NewNullableWithValue(v1API.Textlocal) - if len(s.Textlocal.ApiKey.SHA256) > 0 { - body.SmsTextlocalApiKey = nullable.NewNullableWithValue(s.Textlocal.ApiKey.Value) - } - body.SmsTextlocalSender = nullable.NewNullableWithValue(s.Textlocal.Sender) - case s.Vonage.Enabled: - body.SmsProvider = nullable.NewNullableWithValue(v1API.Vonage) - if len(s.Vonage.ApiSecret.SHA256) > 0 { - body.SmsVonageApiSecret = nullable.NewNullableWithValue(s.Vonage.ApiSecret.Value) - } - body.SmsVonageApiKey = nullable.NewNullableWithValue(s.Vonage.ApiKey) - body.SmsVonageFrom = nullable.NewNullableWithValue(s.Vonage.From) - } -} - -func (s *sms) fromAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - s.EnableSignup = ValOrDefault(remoteConfig.ExternalPhoneEnabled, false) - s.MaxFrequency = time.Duration(ValOrDefault(remoteConfig.SmsMaxFrequency, 0)) * time.Second - s.EnableConfirmations = ValOrDefault(remoteConfig.SmsAutoconfirm, false) - s.Template = ValOrDefault(remoteConfig.SmsTemplate, "") - s.TestOTP = envToMap(ValOrDefault(remoteConfig.SmsTestOtp, "")) - // We are only interested in the provider that's enabled locally - switch { - case s.Twilio.Enabled: - if len(s.Twilio.AuthToken.SHA256) > 0 { - s.Twilio.AuthToken.SHA256 = ValOrDefault(remoteConfig.SmsTwilioAuthToken, "") - } - s.Twilio.AccountSid = ValOrDefault(remoteConfig.SmsTwilioAccountSid, "") - s.Twilio.MessageServiceSid = ValOrDefault(remoteConfig.SmsTwilioMessageServiceSid, "") - case s.TwilioVerify.Enabled: - if len(s.TwilioVerify.AuthToken.SHA256) > 0 { - s.TwilioVerify.AuthToken.SHA256 = ValOrDefault(remoteConfig.SmsTwilioVerifyAuthToken, "") - } - s.TwilioVerify.AccountSid = ValOrDefault(remoteConfig.SmsTwilioVerifyAccountSid, "") - s.TwilioVerify.MessageServiceSid = ValOrDefault(remoteConfig.SmsTwilioVerifyMessageServiceSid, "") - case s.Messagebird.Enabled: - if len(s.Messagebird.AccessKey.SHA256) > 0 { - s.Messagebird.AccessKey.SHA256 = ValOrDefault(remoteConfig.SmsMessagebirdAccessKey, "") - } - s.Messagebird.Originator = ValOrDefault(remoteConfig.SmsMessagebirdOriginator, "") - case s.Textlocal.Enabled: - if len(s.Textlocal.ApiKey.SHA256) > 0 { - s.Textlocal.ApiKey.SHA256 = ValOrDefault(remoteConfig.SmsTextlocalApiKey, "") - } - s.Textlocal.Sender = ValOrDefault(remoteConfig.SmsTextlocalSender, "") - case s.Vonage.Enabled: - if len(s.Vonage.ApiSecret.SHA256) > 0 { - s.Vonage.ApiSecret.SHA256 = ValOrDefault(remoteConfig.SmsVonageApiSecret, "") - } - s.Vonage.ApiKey = ValOrDefault(remoteConfig.SmsVonageApiKey, "") - s.Vonage.From = ValOrDefault(remoteConfig.SmsVonageFrom, "") - case !s.EnableSignup: - // Nothing to do if both local and remote providers are disabled. - return - } - if provider := ValOrDefault(remoteConfig.SmsProvider, ""); len(provider) > 0 { - s.Twilio.Enabled = provider == "twilio" - s.TwilioVerify.Enabled = provider == "twilio_verify" - s.Messagebird.Enabled = provider == "messagebird" - s.Textlocal.Enabled = provider == "textlocal" - s.Vonage.Enabled = provider == "vonage" - } -} - -func (e external) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - if len(e) == 0 { - return - } - // Ignore configs of disabled providers because their envs are not loaded - if p, ok := e["apple"]; ok { - if body.ExternalAppleEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalAppleClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalAppleSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalAppleEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["azure"]; ok { - if body.ExternalAzureEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalAzureClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalAzureSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalAzureUrl = nullable.NewNullableWithValue(p.Url) - body.ExternalAzureEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["bitbucket"]; ok { - if body.ExternalBitbucketEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalBitbucketClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalBitbucketSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalBitbucketEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["discord"]; ok { - if body.ExternalDiscordEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalDiscordClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalDiscordSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalDiscordEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["facebook"]; ok { - if body.ExternalFacebookEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalFacebookClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalFacebookSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalFacebookEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["figma"]; ok { - if body.ExternalFigmaEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalFigmaClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalFigmaSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalFigmaEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["github"]; ok { - if body.ExternalGithubEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalGithubClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalGithubSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalGithubEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["gitlab"]; ok { - if body.ExternalGitlabEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalGitlabClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalGitlabSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalGitlabUrl = nullable.NewNullableWithValue(p.Url) - body.ExternalGitlabEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["google"]; ok { - if body.ExternalGoogleEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalGoogleClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalGoogleSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalGoogleSkipNonceCheck = nullable.NewNullableWithValue(p.SkipNonceCheck) - body.ExternalGoogleEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["kakao"]; ok { - if body.ExternalKakaoEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalKakaoClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalKakaoSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalKakaoEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["keycloak"]; ok { - if body.ExternalKeycloakEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalKeycloakClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalKeycloakSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalKeycloakUrl = nullable.NewNullableWithValue(p.Url) - body.ExternalKeycloakEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["linkedin_oidc"]; ok { - if body.ExternalLinkedinOidcEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalLinkedinOidcClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalLinkedinOidcSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalLinkedinOidcEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["notion"]; ok { - if body.ExternalNotionEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalNotionClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalNotionSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalNotionEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["slack_oidc"]; ok { - if body.ExternalSlackOidcEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalSlackOidcClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalSlackOidcSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalSlackOidcEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["spotify"]; ok { - if body.ExternalSpotifyEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalSpotifyClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalSpotifySecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalSpotifyEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["twitch"]; ok { - if body.ExternalTwitchEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalTwitchClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalTwitchSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalTwitchEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["twitter"]; ok { - if body.ExternalTwitterEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalTwitterClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalTwitterSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalTwitterEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["x"]; ok { - if body.ExternalXEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalXClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalXSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalXEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } - if p, ok := e["workos"]; ok { - if body.ExternalWorkosEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalWorkosClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalWorkosSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalWorkosUrl = nullable.NewNullableWithValue(p.Url) - } - } - if p, ok := e["zoom"]; ok { - if body.ExternalZoomEnabled = nullable.NewNullableWithValue(p.Enabled); p.Enabled { - body.ExternalZoomClientId = nullable.NewNullableWithValue(p.ClientId) - if len(p.Secret.SHA256) > 0 { - body.ExternalZoomSecret = nullable.NewNullableWithValue(p.Secret.Value) - } - body.ExternalZoomEmailOptional = nullable.NewNullableWithValue(p.EmailOptional) - } - } -} - -func (e external) fromAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - if len(e) == 0 { - return - } - // Ignore configs of disabled providers because their envs are not loaded - if p, ok := e["apple"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalAppleClientId, "") - if ids := ValOrDefault(remoteConfig.ExternalAppleAdditionalClientIds, ""); len(ids) > 0 { - p.ClientId += "," + ids - } - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalAppleSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalAppleEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalAppleEnabled, false) - e["apple"] = p - } - - if p, ok := e["azure"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalAzureClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalAzureSecret, "") - } - p.Url = ValOrDefault(remoteConfig.ExternalAzureUrl, "") - p.EmailOptional = ValOrDefault(remoteConfig.ExternalAzureEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalAzureEnabled, false) - e["azure"] = p - } - - if p, ok := e["bitbucket"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalBitbucketClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalBitbucketSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalBitbucketEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalBitbucketEnabled, false) - e["bitbucket"] = p - } - - if p, ok := e["discord"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalDiscordClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalDiscordSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalDiscordEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalDiscordEnabled, false) - e["discord"] = p - } - - if p, ok := e["facebook"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalFacebookClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalFacebookSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalFacebookEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalFacebookEnabled, false) - e["facebook"] = p - } - - if p, ok := e["figma"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalFigmaClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalFigmaSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalFigmaEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalFigmaEnabled, false) - e["figma"] = p - } - - if p, ok := e["github"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalGithubClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalGithubSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalGithubEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalGithubEnabled, false) - e["github"] = p - } - - if p, ok := e["gitlab"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalGitlabClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalGitlabSecret, "") - } - p.Url = ValOrDefault(remoteConfig.ExternalGitlabUrl, "") - p.EmailOptional = ValOrDefault(remoteConfig.ExternalGitlabEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalGitlabEnabled, false) - e["gitlab"] = p - } - - if p, ok := e["google"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalGoogleClientId, "") - if ids := ValOrDefault(remoteConfig.ExternalGoogleAdditionalClientIds, ""); len(ids) > 0 { - p.ClientId += "," + ids - } - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalGoogleSecret, "") - } - p.SkipNonceCheck = ValOrDefault(remoteConfig.ExternalGoogleSkipNonceCheck, false) - p.EmailOptional = ValOrDefault(remoteConfig.ExternalGoogleEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalGoogleEnabled, false) - e["google"] = p - } - - if p, ok := e["kakao"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalKakaoClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalKakaoSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalKakaoEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalKakaoEnabled, false) - e["kakao"] = p - } - - if p, ok := e["keycloak"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalKeycloakClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalKeycloakSecret, "") - } - p.Url = ValOrDefault(remoteConfig.ExternalKeycloakUrl, "") - p.EmailOptional = ValOrDefault(remoteConfig.ExternalKeycloakEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalKeycloakEnabled, false) - e["keycloak"] = p - } - - if p, ok := e["linkedin_oidc"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalLinkedinOidcClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalLinkedinOidcSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalLinkedinOidcEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalLinkedinOidcEnabled, false) - e["linkedin_oidc"] = p - } - - if p, ok := e["notion"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalNotionClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalNotionSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalNotionEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalNotionEnabled, false) - e["notion"] = p - } - - if p, ok := e["slack_oidc"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalSlackOidcClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalSlackOidcSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalSlackOidcEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalSlackOidcEnabled, false) - e["slack_oidc"] = p - } - - if p, ok := e["spotify"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalSpotifyClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalSpotifySecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalSpotifyEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalSpotifyEnabled, false) - e["spotify"] = p - } - - if p, ok := e["twitch"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalTwitchClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalTwitchSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalTwitchEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalTwitchEnabled, false) - e["twitch"] = p - } - - if p, ok := e["twitter"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalTwitterClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalTwitterSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalTwitterEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalTwitterEnabled, false) - e["twitter"] = p - } - - if p, ok := e["x"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalXClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalXSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalXEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalXEnabled, false) - e["x"] = p - } - - if p, ok := e["workos"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalWorkosClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalWorkosSecret, "") - } - p.Url = ValOrDefault(remoteConfig.ExternalWorkosUrl, "") - } - p.Enabled = ValOrDefault(remoteConfig.ExternalWorkosEnabled, false) - e["workos"] = p - } - - if p, ok := e["zoom"]; ok { - if p.Enabled { - p.ClientId = ValOrDefault(remoteConfig.ExternalZoomClientId, "") - if len(p.Secret.SHA256) > 0 { - p.Secret.SHA256 = ValOrDefault(remoteConfig.ExternalZoomSecret, "") - } - p.EmailOptional = ValOrDefault(remoteConfig.ExternalZoomEmailOptional, false) - } - p.Enabled = ValOrDefault(remoteConfig.ExternalZoomEnabled, false) - e["zoom"] = p - } -} - -func (w web3) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - body.ExternalWeb3SolanaEnabled = nullable.NewNullableWithValue(w.Solana.Enabled) - body.ExternalWeb3EthereumEnabled = nullable.NewNullableWithValue(w.Ethereum.Enabled) -} - -func (w *web3) fromAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - if value, err := remoteConfig.ExternalWeb3SolanaEnabled.Get(); err == nil { - w.Solana.Enabled = value - } - - if value, err := remoteConfig.ExternalWeb3EthereumEnabled.Get(); err == nil { - w.Ethereum.Enabled = value - } -} - -func (o OAuthServer) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - // TODO(cemal) :: implement me - // OAuth server configuration is behind a feature flag in the remote API - // Will be implemented when the feature reaches GA -} - -func (o *OAuthServer) fromAuthConfig(remoteConfig v1API.AuthConfigResponseOutput) { - // TODO(cemal) :: implement me - // OAuth server configuration is behind a feature flag in the remote API - // Will be implemented when the feature reaches GA -} - -func (a *auth) DiffWithRemote(remoteConfig v1API.AuthConfigResponseOutput, filter ...func(string) bool) ([]byte, error) { - copy := a.Clone() - copy.FromRemoteAuthConfig(remoteConfig) - // Confirm cost before enabling addons - for _, keep := range filter { - if a.MFA.Phone.VerifyEnabled && !copy.MFA.Phone.VerifyEnabled { - if !keep(string(v1API.ListProjectAddonsResponseOutputAvailableAddonsTypeAuthMfaPhone)) { - a.MFA.Phone.VerifyEnabled = false - // Enroll cannot be enabled on its own - a.MFA.Phone.EnrollEnabled = false - } - } - if a.MFA.WebAuthn.VerifyEnabled && !copy.MFA.WebAuthn.VerifyEnabled { - if !keep(string(v1API.ListProjectAddonsResponseOutputAvailableAddonsTypeAuthMfaWebAuthn)) { - a.MFA.WebAuthn.VerifyEnabled = false - // Enroll cannot be enabled on its own - a.MFA.WebAuthn.EnrollEnabled = false - } - } - } - // Convert the config values into easily comparable remoteConfig values - currentValue, err := ToTomlBytes(a) - if err != nil { - return nil, err - } - remoteCompare, err := ToTomlBytes(copy) - if err != nil { - return nil, err - } - return diff.Diff("remote[auth]", remoteCompare, "local[auth]", currentValue), nil -} - -func ValOrDefault[T any](v nullable.Nullable[T], def T) T { - if value, err := v.Get(); err == nil { - return value - } - return def -} diff --git a/apps/cli-go/pkg/config/auth_test.go b/apps/cli-go/pkg/config/auth_test.go deleted file mode 100644 index 50772a5571..0000000000 --- a/apps/cli-go/pkg/config/auth_test.go +++ /dev/null @@ -1,1497 +0,0 @@ -package config - -import ( - "os" - "path/filepath" - "testing" - "time" - - "github.com/go-errors/errors" - "github.com/oapi-codegen/nullable" - openapi_types "github.com/oapi-codegen/runtime/types" - "github.com/stretchr/testify/assert" - v1API "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" -) - -func newWithDefaults() auth { - return auth{ - EnableSignup: true, - AdditionalRedirectUrls: []string{}, - Email: email{ - EnableConfirmations: true, - }, - Sms: sms{ - TestOTP: map[string]string{}, - }, - } -} - -func assertSnapshotEqual(t *testing.T, actual []byte) { - snapshot := filepath.Join("testdata", filepath.FromSlash(t.Name())) + ".diff" - expected, err := os.ReadFile(snapshot) - if errors.Is(err, os.ErrNotExist) { - assert.NoError(t, os.MkdirAll(filepath.Dir(snapshot), 0755)) - assert.NoError(t, os.WriteFile(snapshot, actual, 0600)) - } - assert.Equal(t, string(expected), string(actual)) -} - -func TestAuthDiff(t *testing.T) { - t.Run("local and remote enabled", func(t *testing.T) { - c := newWithDefaults() - c.SiteUrl = "http://127.0.0.1:3000" - c.AdditionalRedirectUrls = []string{"https://127.0.0.1:3000"} - c.JwtExpiry = 3600 - c.EnableRefreshTokenRotation = true - c.RefreshTokenReuseInterval = 10 - c.EnableManualLinking = true - c.EnableSignup = true - c.EnableAnonymousSignIns = true - c.MinimumPasswordLength = 6 - c.PasswordRequirements = LettersDigits - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - SiteUrl: nullable.NewNullableWithValue("http://127.0.0.1:3000"), - UriAllowList: nullable.NewNullableWithValue("https://127.0.0.1:3000"), - JwtExp: nullable.NewNullableWithValue(3600), - RefreshTokenRotationEnabled: nullable.NewNullableWithValue(true), - SecurityRefreshTokenReuseInterval: nullable.NewNullableWithValue(10), - SecurityManualLinkingEnabled: nullable.NewNullableWithValue(true), - DisableSignup: nullable.NewNullableWithValue(false), - ExternalAnonymousUsersEnabled: nullable.NewNullableWithValue(true), - PasswordMinLength: nullable.NewNullableWithValue(6), - PasswordRequiredCharacters: nullable.NewNullableWithValue(v1API.AuthConfigResponseOutputPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) - - t.Run("local enabled and disabled", func(t *testing.T) { - c := newWithDefaults() - c.SiteUrl = "http://127.0.0.1:3000" - c.AdditionalRedirectUrls = []string{"https://127.0.0.1:3000"} - c.JwtExpiry = 3600 - c.EnableRefreshTokenRotation = false - c.RefreshTokenReuseInterval = 10 - c.EnableManualLinking = false - c.EnableSignup = false - c.EnableAnonymousSignIns = false - c.MinimumPasswordLength = 6 - c.PasswordRequirements = LowerUpperLettersDigitsSymbols - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - SiteUrl: nullable.NewNullableWithValue(""), - UriAllowList: nullable.NewNullableWithValue("https://127.0.0.1:3000,https://ref.supabase.co"), - JwtExp: nullable.NewNullableWithValue(0), - RefreshTokenRotationEnabled: nullable.NewNullableWithValue(true), - SecurityRefreshTokenReuseInterval: nullable.NewNullableWithValue(0), - SecurityManualLinkingEnabled: nullable.NewNullableWithValue(true), - DisableSignup: nullable.NewNullableWithValue(false), - ExternalAnonymousUsersEnabled: nullable.NewNullableWithValue(true), - PasswordMinLength: nullable.NewNullableWithValue(8), - PasswordRequiredCharacters: nullable.NewNullableWithValue(v1API.AuthConfigResponseOutputPasswordRequiredCharactersAbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789), - }) - // Check error - assert.NoError(t, err) - assertSnapshotEqual(t, diff) - }) - - t.Run("local and remote disabled", func(t *testing.T) { - c := newWithDefaults() - c.EnableSignup = false - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - SiteUrl: nullable.NewNullableWithValue(""), - UriAllowList: nullable.NewNullableWithValue(""), - JwtExp: nullable.NewNullableWithValue(0), - RefreshTokenRotationEnabled: nullable.NewNullableWithValue(false), - SecurityRefreshTokenReuseInterval: nullable.NewNullableWithValue(0), - SecurityManualLinkingEnabled: nullable.NewNullableWithValue(false), - DisableSignup: nullable.NewNullableWithValue(true), - ExternalAnonymousUsersEnabled: nullable.NewNullableWithValue(false), - PasswordMinLength: nullable.NewNullableWithValue(0), - PasswordRequiredCharacters: nullable.NewNullableWithValue(v1API.AuthConfigResponseOutputPasswordRequiredCharactersEmpty), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) -} - -func TestCaptchaDiff(t *testing.T) { - t.Run("local and remote enabled", func(t *testing.T) { - c := newWithDefaults() - c.Captcha = &captcha{ - Enabled: true, - Provider: HCaptchaProvider, - Secret: Secret{ - Value: "test-secret", - SHA256: "ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252", - }, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - SecurityCaptchaEnabled: nullable.NewNullableWithValue(true), - SecurityCaptchaProvider: nullable.NewNullableWithValue(v1API.AuthConfigResponseOutputSecurityCaptchaProviderHcaptcha), - SecurityCaptchaSecret: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) - - t.Run("local disabled remote enabled", func(t *testing.T) { - c := newWithDefaults() - c.Captcha = &captcha{ - Enabled: false, - Provider: TurnstileProvider, - Secret: Secret{ - Value: "test-key", - SHA256: "ed64b7695a606bc6ab4fcb41fe815b5ddf1063ccbc87afe1fa89756635db520e", - }, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - SecurityCaptchaEnabled: nullable.NewNullableWithValue(true), - SecurityCaptchaProvider: nullable.NewNullableWithValue(v1API.AuthConfigResponseOutputSecurityCaptchaProviderHcaptcha), - SecurityCaptchaSecret: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - }) - // Check error - assert.NoError(t, err) - assertSnapshotEqual(t, diff) - }) - - t.Run("local enabled remote disabled", func(t *testing.T) { - c := newWithDefaults() - c.Captcha = &captcha{ - Enabled: true, - Provider: TurnstileProvider, - Secret: Secret{ - Value: "test-key", - SHA256: "ed64b7695a606bc6ab4fcb41fe815b5ddf1063ccbc87afe1fa89756635db520e", - }, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - SecurityCaptchaEnabled: nullable.NewNullableWithValue(false), - SecurityCaptchaProvider: nullable.NewNullableWithValue(v1API.AuthConfigResponseOutputSecurityCaptchaProviderHcaptcha), - SecurityCaptchaSecret: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - }) - // Check error - assert.NoError(t, err) - assertSnapshotEqual(t, diff) - }) - - t.Run("local and remote disabled", func(t *testing.T) { - c := newWithDefaults() - c.Captcha = &captcha{ - Enabled: false, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - SecurityCaptchaEnabled: nullable.NewNullableWithValue(false), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) - - t.Run("ignores undefined config", func(t *testing.T) { - c := newWithDefaults() - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - SecurityCaptchaEnabled: nullable.NewNullableWithValue(true), - SecurityCaptchaProvider: nullable.NewNullableWithValue(v1API.AuthConfigResponseOutputSecurityCaptchaProviderHcaptcha), - SecurityCaptchaSecret: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) -} - -func TestPasskeyConfigMapping(t *testing.T) { - t.Run("serializes passkey config to update body", func(t *testing.T) { - c := newWithDefaults() - c.Passkey = &Passkey{Enabled: true} - c.Webauthn = &Webauthn{ - RpDisplayName: "Supabase CLI", - RpId: "localhost", - RpOrigins: []string{ - "http://127.0.0.1:3000", - "https://localhost:3000", - }, - } - // Run test - body := c.ToUpdateAuthConfigBody() - // Check result - if assert.NotNil(t, body.PasskeyEnabled) { - assert.True(t, *body.PasskeyEnabled) - } - assert.Equal(t, "Supabase CLI", ValOrDefault(body.WebauthnRpDisplayName, "")) - assert.Equal(t, "localhost", ValOrDefault(body.WebauthnRpId, "")) - assert.Equal(t, "http://127.0.0.1:3000,https://localhost:3000", ValOrDefault(body.WebauthnRpOrigins, "")) - }) - - t.Run("does not serialize rp fields when webauthn is undefined", func(t *testing.T) { - c := newWithDefaults() - c.Passkey = &Passkey{Enabled: false} - // Run test - body := c.ToUpdateAuthConfigBody() - // Check result - if assert.NotNil(t, body.PasskeyEnabled) { - assert.False(t, *body.PasskeyEnabled) - } - _, err := body.WebauthnRpDisplayName.Get() - assert.Error(t, err) - _, err = body.WebauthnRpId.Get() - assert.Error(t, err) - _, err = body.WebauthnRpOrigins.Get() - assert.Error(t, err) - }) - - t.Run("serializes webauthn fields independently of passkey", func(t *testing.T) { - c := newWithDefaults() - c.Webauthn = &Webauthn{ - RpDisplayName: "Supabase CLI", - RpId: "localhost", - RpOrigins: []string{"http://127.0.0.1:3000"}, - } - // Run test - body := c.ToUpdateAuthConfigBody() - // Check result - assert.Nil(t, body.PasskeyEnabled) - assert.Equal(t, "Supabase CLI", ValOrDefault(body.WebauthnRpDisplayName, "")) - assert.Equal(t, "localhost", ValOrDefault(body.WebauthnRpId, "")) - assert.Equal(t, "http://127.0.0.1:3000", ValOrDefault(body.WebauthnRpOrigins, "")) - }) - - t.Run("hydrates passkey and webauthn config from remote", func(t *testing.T) { - c := newWithDefaults() - c.Passkey = &Passkey{Enabled: true} - c.Webauthn = &Webauthn{} - // Run test - c.FromRemoteAuthConfig(v1API.AuthConfigResponseOutput{ - PasskeyEnabled: true, - WebauthnRpDisplayName: nullable.NewNullableWithValue("Supabase CLI"), - WebauthnRpId: nullable.NewNullableWithValue("localhost"), - WebauthnRpOrigins: nullable.NewNullableWithValue("http://127.0.0.1:3000,https://localhost:3000"), - }) - // Check result - if assert.NotNil(t, c.Passkey) { - assert.True(t, c.Passkey.Enabled) - } - if assert.NotNil(t, c.Webauthn) { - assert.Equal(t, "Supabase CLI", c.Webauthn.RpDisplayName) - assert.Equal(t, "localhost", c.Webauthn.RpId) - assert.Equal(t, []string{ - "http://127.0.0.1:3000", - "https://localhost:3000", - }, c.Webauthn.RpOrigins) - } - }) - - t.Run("ignores remote settings when local passkey config is undefined", func(t *testing.T) { - c := newWithDefaults() - // Run test - c.FromRemoteAuthConfig(v1API.AuthConfigResponseOutput{ - PasskeyEnabled: true, - WebauthnRpDisplayName: nullable.NewNullableWithValue("Supabase CLI"), - WebauthnRpId: nullable.NewNullableWithValue("localhost"), - WebauthnRpOrigins: nullable.NewNullableWithValue("http://127.0.0.1:3000"), - }) - // Check result - assert.Nil(t, c.Passkey) - assert.Nil(t, c.Webauthn) - }) -} - -func TestPasskeyDiff(t *testing.T) { - t.Run("ignores undefined config", func(t *testing.T) { - c := newWithDefaults() - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - PasskeyEnabled: true, - WebauthnRpDisplayName: nullable.NewNullableWithValue("Supabase CLI"), - WebauthnRpId: nullable.NewNullableWithValue("localhost"), - WebauthnRpOrigins: nullable.NewNullableWithValue("http://127.0.0.1:3000"), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) -} - -func TestHookDiff(t *testing.T) { - t.Run("local and remote enabled", func(t *testing.T) { - c := newWithDefaults() - c.Hook = hook{ - BeforeUserCreated: &hookConfig{ - Enabled: true, - URI: "http://example.com", - Secrets: Secret{ - Value: "test-secret", - SHA256: "ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252", - }, - }, - CustomAccessToken: &hookConfig{ - Enabled: true, - URI: "http://example.com", - Secrets: Secret{ - Value: "test-secret", - SHA256: "ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252", - }, - }, - SendSMS: &hookConfig{ - Enabled: true, - URI: "http://example.com", - Secrets: Secret{ - Value: "test-secret", - SHA256: "ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252", - }, - }, - SendEmail: &hookConfig{ - Enabled: true, - URI: "https://example.com", - Secrets: Secret{ - Value: "test-secret", - SHA256: "ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252", - }, - }, - MFAVerificationAttempt: &hookConfig{ - Enabled: true, - URI: "https://example.com", - Secrets: Secret{ - Value: "test-secret", - SHA256: "ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252", - }, - }, - PasswordVerificationAttempt: &hookConfig{ - Enabled: true, - URI: "pg-functions://verifyPassword", - }, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - HookBeforeUserCreatedEnabled: nullable.NewNullableWithValue(true), - HookBeforeUserCreatedUri: nullable.NewNullableWithValue("http://example.com"), - HookBeforeUserCreatedSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - HookCustomAccessTokenEnabled: nullable.NewNullableWithValue(true), - HookCustomAccessTokenUri: nullable.NewNullableWithValue("http://example.com"), - HookCustomAccessTokenSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - HookSendSmsEnabled: nullable.NewNullableWithValue(true), - HookSendSmsUri: nullable.NewNullableWithValue("http://example.com"), - HookSendSmsSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - HookSendEmailEnabled: nullable.NewNullableWithValue(true), - HookSendEmailUri: nullable.NewNullableWithValue("https://example.com"), - HookSendEmailSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - HookMfaVerificationAttemptEnabled: nullable.NewNullableWithValue(true), - HookMfaVerificationAttemptUri: nullable.NewNullableWithValue("https://example.com"), - HookMfaVerificationAttemptSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - HookPasswordVerificationAttemptEnabled: nullable.NewNullableWithValue(true), - HookPasswordVerificationAttemptUri: nullable.NewNullableWithValue("pg-functions://verifyPassword"), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) - - t.Run("local disabled remote enabled", func(t *testing.T) { - c := newWithDefaults() - c.Hook = hook{ - BeforeUserCreated: &hookConfig{ - Enabled: false, - }, - CustomAccessToken: &hookConfig{ - Enabled: false, - }, - SendSMS: &hookConfig{ - Enabled: false, - URI: "https://example.com", - Secrets: Secret{Value: "test-secret"}, - }, - SendEmail: &hookConfig{ - Enabled: false, - }, - MFAVerificationAttempt: &hookConfig{ - Enabled: false, - URI: "pg-functions://postgres/public/verifyMFA", - }, - PasswordVerificationAttempt: nil, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - HookBeforeUserCreatedEnabled: nullable.NewNullableWithValue(true), - HookBeforeUserCreatedUri: nullable.NewNullableWithValue("http://example.com"), - HookBeforeUserCreatedSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - HookCustomAccessTokenEnabled: nullable.NewNullableWithValue(true), - HookCustomAccessTokenUri: nullable.NewNullableWithValue("http://example.com"), - HookCustomAccessTokenSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - HookSendSmsEnabled: nullable.NewNullableWithValue(true), - HookSendSmsUri: nullable.NewNullableWithValue("https://example.com"), - HookSendSmsSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - HookSendEmailEnabled: nullable.NewNullableWithValue(true), - HookSendEmailUri: nullable.NewNullableWithValue("pg-functions://postgres/public/sendEmail"), - HookMfaVerificationAttemptEnabled: nullable.NewNullableWithValue(true), - HookMfaVerificationAttemptUri: nullable.NewNullableWithValue("pg-functions://postgres/public/verifyMFA"), - HookPasswordVerificationAttemptEnabled: nullable.NewNullableWithValue(true), - HookPasswordVerificationAttemptUri: nullable.NewNullableWithValue("https://example.com"), - HookPasswordVerificationAttemptSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - }) - // Check error - assert.NoError(t, err) - assertSnapshotEqual(t, diff) - }) - - t.Run("local enabled remote disabled", func(t *testing.T) { - c := newWithDefaults() - c.Hook = hook{ - BeforeUserCreated: &hookConfig{ - Enabled: true, - URI: "http://example.com", - Secrets: Secret{ - Value: "test-secret", - SHA256: "ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252", - }, - }, - CustomAccessToken: &hookConfig{ - Enabled: true, - URI: "http://example.com", - Secrets: Secret{ - Value: "test-secret", - SHA256: "ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252", - }, - }, - SendSMS: &hookConfig{ - Enabled: true, - URI: "https://example.com", - Secrets: Secret{ - Value: "test-secret", - SHA256: "ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252", - }, - }, - SendEmail: &hookConfig{ - Enabled: true, - URI: "pg-functions://postgres/public/sendEmail", - }, - MFAVerificationAttempt: &hookConfig{ - Enabled: true, - URI: "pg-functions://postgres/public/verifyMFA", - }, - PasswordVerificationAttempt: nil, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - HookBeforeUserCreatedEnabled: nullable.NewNullableWithValue(false), - HookBeforeUserCreatedUri: nullable.NewNullableWithValue("pg-functions://postgres/public/beforeUserCreated"), - HookCustomAccessTokenEnabled: nullable.NewNullableWithValue(false), - HookCustomAccessTokenUri: nullable.NewNullableWithValue("pg-functions://postgres/public/customToken"), - HookSendSmsEnabled: nullable.NewNullableWithValue(false), - HookSendSmsUri: nullable.NewNullableWithValue("https://example.com"), - HookSendSmsSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - HookSendEmailEnabled: nullable.NewNullableWithValue(false), - HookSendEmailUri: nullable.NewNullableWithValue("https://example.com"), - HookSendEmailSecrets: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - HookMfaVerificationAttemptEnabled: nullable.NewNullableWithValue(false), - HookMfaVerificationAttemptUri: nullable.NewNullableWithValue("pg-functions://postgres/public/verifyMFA"), - HookPasswordVerificationAttemptEnabled: nullable.NewNullableWithValue(false), - }) - // Check error - assert.NoError(t, err) - assertSnapshotEqual(t, diff) - }) - - t.Run("local and remote disabled", func(t *testing.T) { - c := newWithDefaults() - c.Hook = hook{ - BeforeUserCreated: &hookConfig{Enabled: false}, - CustomAccessToken: &hookConfig{Enabled: false}, - SendSMS: &hookConfig{Enabled: false}, - SendEmail: &hookConfig{Enabled: false}, - MFAVerificationAttempt: &hookConfig{Enabled: false}, - PasswordVerificationAttempt: &hookConfig{Enabled: false}, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - HookBeforeUserCreatedEnabled: nullable.NewNullableWithValue(false), - HookCustomAccessTokenEnabled: nullable.NewNullableWithValue(false), - HookSendSmsEnabled: nullable.NewNullableWithValue(false), - HookSendEmailEnabled: nullable.NewNullableWithValue(false), - HookMfaVerificationAttemptEnabled: nullable.NewNullableWithValue(false), - HookPasswordVerificationAttemptEnabled: nullable.NewNullableWithValue(false), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) -} - -func TestMfaDiff(t *testing.T) { - t.Run("local and remote enabled", func(t *testing.T) { - c := newWithDefaults() - c.MFA = mfa{ - TOTP: factorTypeConfiguration{ - EnrollEnabled: true, - VerifyEnabled: true, - }, - Phone: phoneFactorTypeConfiguration{ - factorTypeConfiguration: factorTypeConfiguration{ - EnrollEnabled: true, - VerifyEnabled: true, - }, - OtpLength: 6, - Template: "Your code is {{ .Code }}", - MaxFrequency: 5 * time.Second, - }, - WebAuthn: factorTypeConfiguration{ - EnrollEnabled: true, - VerifyEnabled: true, - }, - MaxEnrolledFactors: 10, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - MfaMaxEnrolledFactors: nullable.NewNullableWithValue(10), - MfaTotpEnrollEnabled: nullable.NewNullableWithValue(true), - MfaTotpVerifyEnabled: nullable.NewNullableWithValue(true), - MfaPhoneEnrollEnabled: nullable.NewNullableWithValue(true), - MfaPhoneVerifyEnabled: nullable.NewNullableWithValue(true), - MfaPhoneOtpLength: 6, - MfaPhoneTemplate: nullable.NewNullableWithValue("Your code is {{ .Code }}"), - MfaPhoneMaxFrequency: nullable.NewNullableWithValue(5), - MfaWebAuthnEnrollEnabled: nullable.NewNullableWithValue(true), - MfaWebAuthnVerifyEnabled: nullable.NewNullableWithValue(true), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) - - t.Run("local enabled and disabled", func(t *testing.T) { - c := newWithDefaults() - c.MFA = mfa{ - TOTP: factorTypeConfiguration{ - EnrollEnabled: false, - VerifyEnabled: false, - }, - Phone: phoneFactorTypeConfiguration{ - factorTypeConfiguration: factorTypeConfiguration{ - EnrollEnabled: true, - VerifyEnabled: true, - }, - }, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - MfaMaxEnrolledFactors: nullable.NewNullableWithValue(10), - MfaTotpEnrollEnabled: nullable.NewNullableWithValue(false), - MfaTotpVerifyEnabled: nullable.NewNullableWithValue(false), - MfaPhoneEnrollEnabled: nullable.NewNullableWithValue(false), - MfaPhoneVerifyEnabled: nullable.NewNullableWithValue(false), - MfaPhoneOtpLength: 6, - MfaPhoneTemplate: nullable.NewNullableWithValue("Your code is {{ .Code }}"), - MfaPhoneMaxFrequency: nullable.NewNullableWithValue(5), - MfaWebAuthnEnrollEnabled: nullable.NewNullableWithValue(false), - MfaWebAuthnVerifyEnabled: nullable.NewNullableWithValue(false), - }) - // Check error - assert.NoError(t, err) - assertSnapshotEqual(t, diff) - }) - - t.Run("local and remote disabled", func(t *testing.T) { - c := newWithDefaults() - c.MFA = mfa{ - MaxEnrolledFactors: 10, - Phone: phoneFactorTypeConfiguration{ - OtpLength: 6, - Template: "Your code is {{ .Code }}", - MaxFrequency: 5 * time.Second, - }, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - MfaMaxEnrolledFactors: nullable.NewNullableWithValue(10), - MfaTotpEnrollEnabled: nullable.NewNullableWithValue(false), - MfaTotpVerifyEnabled: nullable.NewNullableWithValue(false), - MfaPhoneEnrollEnabled: nullable.NewNullableWithValue(false), - MfaPhoneVerifyEnabled: nullable.NewNullableWithValue(false), - MfaPhoneOtpLength: 6, - MfaPhoneTemplate: nullable.NewNullableWithValue("Your code is {{ .Code }}"), - MfaPhoneMaxFrequency: nullable.NewNullableWithValue(5), - MfaWebAuthnEnrollEnabled: nullable.NewNullableWithValue(false), - MfaWebAuthnVerifyEnabled: nullable.NewNullableWithValue(false), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) -} - -func TestEmailDiff(t *testing.T) { - t.Run("local enabled remote enabled", func(t *testing.T) { - c := newWithDefaults() - c.Email = email{ - EnableSignup: true, - DoubleConfirmChanges: true, - EnableConfirmations: true, - SecurePasswordChange: true, - Template: map[string]emailTemplate{ - "invite": { - Subject: cast.Ptr("invite-subject"), - Content: cast.Ptr("invite-content"), - }, - "confirmation": { - Subject: cast.Ptr("confirmation-subject"), - Content: cast.Ptr("confirmation-content"), - }, - "recovery": { - Subject: cast.Ptr("recovery-subject"), - Content: cast.Ptr("recovery-content"), - }, - "magic_link": { - Subject: cast.Ptr("magic-link-subject"), - Content: cast.Ptr("magic-link-content"), - }, - "email_change": { - Subject: cast.Ptr("email-change-subject"), - Content: cast.Ptr("email-change-content"), - }, - "reauthentication": { - Subject: cast.Ptr("reauthentication-subject"), - Content: cast.Ptr("reauthentication-content"), - }, - }, - Notification: map[string]notification{ - "password_changed": { - Enabled: true, - emailTemplate: emailTemplate{ - Subject: cast.Ptr("password-changed-subject"), - Content: cast.Ptr("password-changed-content"), - }, - }, - "email_changed": { - Enabled: true, - emailTemplate: emailTemplate{ - Subject: cast.Ptr("email-changed-subject"), - Content: cast.Ptr("email-changed-content"), - }, - }, - "phone_changed": { - Enabled: true, - emailTemplate: emailTemplate{ - Subject: cast.Ptr("phone-changed-subject"), - Content: cast.Ptr("phone-changed-content"), - }, - }, - "identity_linked": { - Enabled: true, - emailTemplate: emailTemplate{ - Subject: cast.Ptr("identity-linked-subject"), - Content: cast.Ptr("identity-linked-content"), - }, - }, - "identity_unlinked": { - Enabled: true, - emailTemplate: emailTemplate{ - Subject: cast.Ptr("identity-unlinked-subject"), - Content: cast.Ptr("identity-unlinked-content"), - }, - }, - "mfa_factor_enrolled": { - Enabled: true, - emailTemplate: emailTemplate{ - Subject: cast.Ptr("mfa-enrolled-subject"), - Content: cast.Ptr("mfa-enrolled-content"), - }, - }, - "mfa_factor_unenrolled": { - Enabled: true, - emailTemplate: emailTemplate{ - Subject: cast.Ptr("mfa-unenrolled-subject"), - Content: cast.Ptr("mfa-unenrolled-content"), - }, - }, - }, - Smtp: &smtp{ - Enabled: true, - Host: "smtp.sendgrid.net", - Port: 587, - User: "apikey", - Pass: Secret{ - Value: "test-key", - SHA256: "ed64b7695a606bc6ab4fcb41fe815b5ddf1063ccbc87afe1fa89756635db520e", - }, - AdminEmail: openapi_types.Email("admin@email.com"), - SenderName: "Admin", - }, - MaxFrequency: time.Second, - OtpLength: 6, - OtpExpiry: 3600, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - ExternalEmailEnabled: nullable.NewNullableWithValue(true), - MailerSecureEmailChangeEnabled: nullable.NewNullableWithValue(true), - MailerAutoconfirm: nullable.NewNullableWithValue(false), - MailerOtpLength: nullable.NewNullableWithValue(6), - MailerOtpExp: 3600, - SecurityUpdatePasswordRequireReauthentication: nullable.NewNullableWithValue(true), - SmtpHost: nullable.NewNullableWithValue("smtp.sendgrid.net"), - SmtpPort: nullable.NewNullableWithValue("587"), - SmtpUser: nullable.NewNullableWithValue("apikey"), - SmtpPass: nullable.NewNullableWithValue("ed64b7695a606bc6ab4fcb41fe815b5ddf1063ccbc87afe1fa89756635db520e"), - SmtpAdminEmail: nullable.NewNullableWithValue(openapi_types.Email("admin@email.com")), - SmtpSenderName: nullable.NewNullableWithValue("Admin"), - SmtpMaxFrequency: nullable.NewNullableWithValue(1), - // Custom templates - MailerSubjectsInvite: nullable.NewNullableWithValue("invite-subject"), - MailerTemplatesInviteContent: nullable.NewNullableWithValue("invite-content"), - MailerSubjectsConfirmation: nullable.NewNullableWithValue("confirmation-subject"), - MailerTemplatesConfirmationContent: nullable.NewNullableWithValue("confirmation-content"), - MailerSubjectsRecovery: nullable.NewNullableWithValue("recovery-subject"), - MailerTemplatesRecoveryContent: nullable.NewNullableWithValue("recovery-content"), - MailerSubjectsMagicLink: nullable.NewNullableWithValue("magic-link-subject"), - MailerTemplatesMagicLinkContent: nullable.NewNullableWithValue("magic-link-content"), - MailerSubjectsEmailChange: nullable.NewNullableWithValue("email-change-subject"), - MailerTemplatesEmailChangeContent: nullable.NewNullableWithValue("email-change-content"), - MailerSubjectsReauthentication: nullable.NewNullableWithValue("reauthentication-subject"), - MailerTemplatesReauthenticationContent: nullable.NewNullableWithValue("reauthentication-content"), - // Notifications - MailerNotificationsPasswordChangedEnabled: nullable.NewNullableWithValue(true), - MailerSubjectsPasswordChangedNotification: nullable.NewNullableWithValue("password-changed-subject"), - MailerTemplatesPasswordChangedNotificationContent: nullable.NewNullableWithValue("password-changed-content"), - MailerNotificationsEmailChangedEnabled: nullable.NewNullableWithValue(true), - MailerSubjectsEmailChangedNotification: nullable.NewNullableWithValue("email-changed-subject"), - MailerTemplatesEmailChangedNotificationContent: nullable.NewNullableWithValue("email-changed-content"), - MailerNotificationsPhoneChangedEnabled: nullable.NewNullableWithValue(true), - MailerSubjectsPhoneChangedNotification: nullable.NewNullableWithValue("phone-changed-subject"), - MailerTemplatesPhoneChangedNotificationContent: nullable.NewNullableWithValue("phone-changed-content"), - MailerNotificationsIdentityLinkedEnabled: nullable.NewNullableWithValue(true), - MailerSubjectsIdentityLinkedNotification: nullable.NewNullableWithValue("identity-linked-subject"), - MailerTemplatesIdentityLinkedNotificationContent: nullable.NewNullableWithValue("identity-linked-content"), - MailerNotificationsIdentityUnlinkedEnabled: nullable.NewNullableWithValue(true), - MailerSubjectsIdentityUnlinkedNotification: nullable.NewNullableWithValue("identity-unlinked-subject"), - MailerTemplatesIdentityUnlinkedNotificationContent: nullable.NewNullableWithValue("identity-unlinked-content"), - MailerNotificationsMfaFactorEnrolledEnabled: nullable.NewNullableWithValue(true), - MailerSubjectsMfaFactorEnrolledNotification: nullable.NewNullableWithValue("mfa-enrolled-subject"), - MailerTemplatesMfaFactorEnrolledNotificationContent: nullable.NewNullableWithValue("mfa-enrolled-content"), - MailerNotificationsMfaFactorUnenrolledEnabled: nullable.NewNullableWithValue(true), - MailerSubjectsMfaFactorUnenrolledNotification: nullable.NewNullableWithValue("mfa-unenrolled-subject"), - MailerTemplatesMfaFactorUnenrolledNotificationContent: nullable.NewNullableWithValue("mfa-unenrolled-content"), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) - - t.Run("local enabled remote disabled", func(t *testing.T) { - c := newWithDefaults() - c.Email = email{ - EnableSignup: true, - DoubleConfirmChanges: true, - EnableConfirmations: true, - SecurePasswordChange: true, - Template: map[string]emailTemplate{ - "invite": { - Subject: cast.Ptr("invite-subject"), - Content: cast.Ptr("invite-content"), - }, - "confirmation": { - Subject: cast.Ptr("confirmation-subject"), - }, - "recovery": { - Content: cast.Ptr("recovery-content"), - }, - "magic_link": { - Subject: cast.Ptr("magic-link-subject"), - Content: cast.Ptr("magic-link-content"), - }, - "email_change": { - Subject: cast.Ptr("email-change-subject"), - Content: cast.Ptr("email-change-content"), - }, - "reauthentication": { - Subject: cast.Ptr(""), - Content: cast.Ptr(""), - }, - }, - Notification: map[string]notification{ - "password_changed": { - Enabled: true, - emailTemplate: emailTemplate{ - Subject: cast.Ptr("password-changed-subject"), - Content: cast.Ptr("password-changed-content"), - }, - }, - "email_changed": { - Enabled: true, - emailTemplate: emailTemplate{ - Subject: cast.Ptr("email-changed-subject"), - Content: cast.Ptr("email-changed-content"), - }, - }, - "phone_changed": { - Enabled: true, - emailTemplate: emailTemplate{ - Subject: cast.Ptr("phone-changed-subject"), - Content: cast.Ptr("phone-changed-content"), - }, - }, - "identity_linked": { - Enabled: true, - emailTemplate: emailTemplate{ - Subject: cast.Ptr("identity-linked-subject"), - Content: cast.Ptr("identity-linked-content"), - }, - }, - "identity_unlinked": { - Enabled: true, - }, - "mfa_factor_enrolled": { - Enabled: true, - }, - "mfa_factor_unenrolled": { - Enabled: true, - }, - }, - Smtp: &smtp{ - Enabled: true, - Host: "smtp.sendgrid.net", - Port: 587, - User: "apikey", - Pass: Secret{ - Value: "test-key", - SHA256: "ed64b7695a606bc6ab4fcb41fe815b5ddf1063ccbc87afe1fa89756635db520e", - }, - AdminEmail: openapi_types.Email("admin@email.com"), - SenderName: "Admin", - }, - MaxFrequency: time.Second, - OtpLength: 8, - OtpExpiry: 86400, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - ExternalEmailEnabled: nullable.NewNullableWithValue(false), - MailerSecureEmailChangeEnabled: nullable.NewNullableWithValue(false), - MailerAutoconfirm: nullable.NewNullableWithValue(true), - MailerOtpLength: nullable.NewNullableWithValue(6), - MailerOtpExp: 3600, - SecurityUpdatePasswordRequireReauthentication: nullable.NewNullableWithValue(false), - SmtpMaxFrequency: nullable.NewNullableWithValue(60), - // Custom templates - MailerTemplatesConfirmationContent: nullable.NewNullableWithValue("confirmation-content"), - MailerSubjectsRecovery: nullable.NewNullableWithValue("recovery-subject"), - MailerSubjectsMagicLink: nullable.NewNullableWithValue("magic-link-subject"), - MailerTemplatesEmailChangeContent: nullable.NewNullableWithValue("email-change-content"), - // Notifications - MailerNotificationsPasswordChangedEnabled: nullable.NewNullableWithValue(false), - MailerSubjectsPasswordChangedNotification: nullable.NewNullableWithValue("password-changed-subject"), - MailerTemplatesPasswordChangedNotificationContent: nullable.NewNullableWithValue("password-changed-content"), - MailerNotificationsEmailChangedEnabled: nullable.NewNullableWithValue(false), - MailerSubjectsEmailChangedNotification: nullable.NewNullableWithValue("email-changed-subject"), - MailerNotificationsPhoneChangedEnabled: nullable.NewNullableWithValue(false), - MailerTemplatesPhoneChangedNotificationContent: nullable.NewNullableWithValue("phone-changed-content"), - MailerNotificationsIdentityLinkedEnabled: nullable.NewNullableWithValue(false), - MailerNotificationsIdentityUnlinkedEnabled: nullable.NewNullableWithValue(false), - MailerSubjectsIdentityUnlinkedNotification: nullable.NewNullableWithValue("identity-unlinked-subject"), - MailerTemplatesIdentityUnlinkedNotificationContent: nullable.NewNullableWithValue("identity-unlinked-content"), - MailerNotificationsMfaFactorEnrolledEnabled: nullable.NewNullableWithValue(false), - MailerSubjectsMfaFactorEnrolledNotification: nullable.NewNullableWithValue("mfa-enrolled-subject"), - MailerNotificationsMfaFactorUnenrolledEnabled: nullable.NewNullableWithValue(false), - }) - // Check error - assert.NoError(t, err) - assertSnapshotEqual(t, diff) - }) - - t.Run("local disabled remote enabled", func(t *testing.T) { - c := newWithDefaults() - c.Email = email{ - EnableConfirmations: false, - Template: map[string]emailTemplate{ - "invite": {}, - "confirmation": {}, - "recovery": {}, - "magic_link": {}, - "email_change": {}, - "reauthentication": {}, - }, - Notification: map[string]notification{ - "password_changed": {}, - "email_changed": {}, - "phone_changed": {}, - "identity_linked": {}, - "identity_unlinked": {}, - "mfa_factor_enrolled": {}, - "mfa_factor_unenrolled": {}, - }, - MaxFrequency: time.Minute, - OtpLength: 8, - OtpExpiry: 86400, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - ExternalEmailEnabled: nullable.NewNullableWithValue(true), - MailerSecureEmailChangeEnabled: nullable.NewNullableWithValue(true), - MailerAutoconfirm: nullable.NewNullableWithValue(false), - MailerOtpLength: nullable.NewNullableWithValue(6), - MailerOtpExp: 3600, - SecurityUpdatePasswordRequireReauthentication: nullable.NewNullableWithValue(true), - SmtpHost: nullable.NewNullableWithValue("smtp.sendgrid.net"), - SmtpPort: nullable.NewNullableWithValue("587"), - SmtpUser: nullable.NewNullableWithValue("apikey"), - SmtpPass: nullable.NewNullableWithValue("ed64b7695a606bc6ab4fcb41fe815b5ddf1063ccbc87afe1fa89756635db520e"), - SmtpAdminEmail: nullable.NewNullableWithValue(openapi_types.Email("admin@email.com")), - SmtpSenderName: nullable.NewNullableWithValue("Admin"), - SmtpMaxFrequency: nullable.NewNullableWithValue(1), - // Custom templates - MailerSubjectsInvite: nullable.NewNullableWithValue("invite-subject"), - MailerTemplatesInviteContent: nullable.NewNullableWithValue("invite-content"), - MailerSubjectsConfirmation: nullable.NewNullableWithValue("confirmation-subject"), - MailerTemplatesConfirmationContent: nullable.NewNullableWithValue("confirmation-content"), - MailerSubjectsRecovery: nullable.NewNullableWithValue("recovery-subject"), - MailerTemplatesRecoveryContent: nullable.NewNullableWithValue("recovery-content"), - MailerSubjectsMagicLink: nullable.NewNullableWithValue("magic-link-subject"), - MailerTemplatesMagicLinkContent: nullable.NewNullableWithValue("magic-link-content"), - MailerSubjectsEmailChange: nullable.NewNullableWithValue("email-change-subject"), - MailerTemplatesEmailChangeContent: nullable.NewNullableWithValue("email-change-content"), - MailerSubjectsReauthentication: nullable.NewNullableWithValue("reauthentication-subject"), - MailerTemplatesReauthenticationContent: nullable.NewNullableWithValue("reauthentication-content"), - // Notifications - MailerNotificationsPasswordChangedEnabled: nullable.NewNullableWithValue(true), - MailerSubjectsPasswordChangedNotification: nullable.NewNullableWithValue("password-changed-subject"), - MailerTemplatesPasswordChangedNotificationContent: nullable.NewNullableWithValue("password-changed-content"), - MailerNotificationsEmailChangedEnabled: nullable.NewNullableWithValue(true), - MailerSubjectsEmailChangedNotification: nullable.NewNullableWithValue("email-changed-subject"), - MailerNotificationsPhoneChangedEnabled: nullable.NewNullableWithValue(true), - MailerTemplatesPhoneChangedNotificationContent: nullable.NewNullableWithValue("phone-changed-content"), - MailerNotificationsIdentityLinkedEnabled: nullable.NewNullableWithValue(true), - MailerNotificationsIdentityUnlinkedEnabled: nullable.NewNullableWithValue(true), - MailerSubjectsIdentityUnlinkedNotification: nullable.NewNullableWithValue("identity-unlinked-subject"), - MailerTemplatesIdentityUnlinkedNotificationContent: nullable.NewNullableWithValue("identity-unlinked-content"), - MailerNotificationsMfaFactorEnrolledEnabled: nullable.NewNullableWithValue(true), - MailerSubjectsMfaFactorEnrolledNotification: nullable.NewNullableWithValue("mfa-enrolled-subject"), - MailerTemplatesMfaFactorEnrolledNotificationContent: nullable.NewNullableWithValue("mfa-enrolled-content"), - MailerNotificationsMfaFactorUnenrolledEnabled: nullable.NewNullableWithValue(true), - MailerSubjectsMfaFactorUnenrolledNotification: nullable.NewNullableWithValue("mfa-unenrolled-subject"), - MailerTemplatesMfaFactorUnenrolledNotificationContent: nullable.NewNullableWithValue("mfa-unenrolled-content"), - }) - // Check error - assert.NoError(t, err) - assertSnapshotEqual(t, diff) - }) - - t.Run("local disabled remote disabled", func(t *testing.T) { - c := newWithDefaults() - c.Email = email{ - EnableConfirmations: false, - Template: map[string]emailTemplate{ - "invite": {}, - "confirmation": {}, - "recovery": {}, - "magic_link": {}, - "email_change": {}, - "reauthentication": {}, - }, - Notification: map[string]notification{ - "password_changed": {}, - "email_changed": {}, - "phone_changed": {}, - "identity_linked": {}, - "identity_unlinked": {}, - "mfa_factor_enrolled": {}, - "mfa_factor_unenrolled": {}, - }, - Smtp: &smtp{ - Enabled: false, - Host: "smtp.sendgrid.net", - Port: 587, - User: "apikey", - Pass: Secret{ - Value: "test-key", - SHA256: "ed64b7695a606bc6ab4fcb41fe815b5ddf1063ccbc87afe1fa89756635db520e", - }, - AdminEmail: openapi_types.Email("admin@email.com"), - SenderName: "Admin", - }, - MaxFrequency: time.Minute, - OtpLength: 6, - OtpExpiry: 3600, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - ExternalEmailEnabled: nullable.NewNullableWithValue(false), - MailerSecureEmailChangeEnabled: nullable.NewNullableWithValue(false), - MailerAutoconfirm: nullable.NewNullableWithValue(true), - MailerOtpLength: nullable.NewNullableWithValue(6), - MailerOtpExp: 3600, - SecurityUpdatePasswordRequireReauthentication: nullable.NewNullableWithValue(false), - SmtpMaxFrequency: nullable.NewNullableWithValue(60), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) -} - -func TestSmsDiff(t *testing.T) { - t.Run("local enabled remote enabled", func(t *testing.T) { - c := newWithDefaults() - c.Sms = sms{ - EnableSignup: true, - EnableConfirmations: true, - Template: "Your code is {{ .Code }}", - TestOTP: map[string]string{"123": "456"}, - MaxFrequency: time.Minute, - Twilio: twilioConfig{ - Enabled: true, - AccountSid: "test-account", - MessageServiceSid: "test-service", - AuthToken: Secret{ - Value: "test-token", - SHA256: "c84443bc59b92caef8ec8500ff443584793756749523811eb333af2bbc74fc88", - }, - }, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - ExternalPhoneEnabled: nullable.NewNullableWithValue(true), - SmsAutoconfirm: nullable.NewNullableWithValue(true), - SmsMaxFrequency: nullable.NewNullableWithValue(60), - SmsOtpExp: nullable.NewNullableWithValue(3600), - SmsOtpLength: 6, - SmsProvider: nullable.NewNullableWithValue(v1API.AuthConfigResponseOutputSmsProviderTwilio), - SmsTemplate: nullable.NewNullableWithValue("Your code is {{ .Code }}"), - SmsTestOtp: nullable.NewNullableWithValue("123=456"), - SmsTestOtpValidUntil: nullable.NewNullableWithValue(time.Date(2050, 1, 1, 1, 0, 0, 0, time.UTC)), - SmsTwilioAccountSid: nullable.NewNullableWithValue("test-account"), - SmsTwilioAuthToken: nullable.NewNullableWithValue("c84443bc59b92caef8ec8500ff443584793756749523811eb333af2bbc74fc88"), - SmsTwilioContentSid: nullable.NewNullableWithValue("test-content"), - SmsTwilioMessageServiceSid: nullable.NewNullableWithValue("test-service"), - // Extra configs returned from api can be ignored - SmsMessagebirdAccessKey: nullable.NewNullableWithValue("test-messagebird-key"), - SmsMessagebirdOriginator: nullable.NewNullableWithValue("test-messagebird-originator"), - SmsTextlocalApiKey: nullable.NewNullableWithValue("test-textlocal-key"), - SmsTextlocalSender: nullable.NewNullableWithValue("test-textlocal-sencer"), - SmsTwilioVerifyAccountSid: nullable.NewNullableWithValue("test-verify-account"), - SmsTwilioVerifyAuthToken: nullable.NewNullableWithValue("test-verify-token"), - SmsTwilioVerifyMessageServiceSid: nullable.NewNullableWithValue("test-verify-service"), - SmsVonageApiKey: nullable.NewNullableWithValue("test-vonage-key"), - SmsVonageApiSecret: nullable.NewNullableWithValue("test-vonage-secret"), - SmsVonageFrom: nullable.NewNullableWithValue("test-vonage-from"), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) - - t.Run("local disabled remote enabled", func(t *testing.T) { - c := newWithDefaults() - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - ExternalPhoneEnabled: nullable.NewNullableWithValue(true), - SmsAutoconfirm: nullable.NewNullableWithValue(true), - SmsMaxFrequency: nullable.NewNullableWithValue(60), - SmsOtpExp: nullable.NewNullableWithValue(3600), - SmsOtpLength: 6, - SmsProvider: nullable.NewNullableWithValue(v1API.AuthConfigResponseOutputSmsProviderTwilio), - SmsTemplate: nullable.NewNullableWithValue("Your code is {{ .Code }}"), - SmsTestOtp: nullable.NewNullableWithValue("123=456,456=123"), - SmsTestOtpValidUntil: nullable.NewNullableWithValue(time.Date(2050, 1, 1, 1, 0, 0, 0, time.UTC)), - SmsTwilioAccountSid: nullable.NewNullableWithValue("test-account"), - SmsTwilioAuthToken: nullable.NewNullableWithValue("c84443bc59b92caef8ec8500ff443584793756749523811eb333af2bbc74fc88"), - SmsTwilioContentSid: nullable.NewNullableWithValue("test-content"), - SmsTwilioMessageServiceSid: nullable.NewNullableWithValue("test-service"), - }) - // Check error - assert.NoError(t, err) - assertSnapshotEqual(t, diff) - }) - - t.Run("local enabled remote disabled", func(t *testing.T) { - c := newWithDefaults() - c.Sms = sms{ - EnableSignup: true, - EnableConfirmations: true, - Template: "Your code is {{ .Code }}", - TestOTP: map[string]string{"123": "456"}, - MaxFrequency: time.Minute, - Messagebird: messagebirdConfig{ - Enabled: true, - Originator: "test-originator", - AccessKey: Secret{ - Value: "test-access-key", - SHA256: "ab60d03fc809fb02dae838582f3ddc13d1d6cb32ffba77c4b969dd3caa496f13", - }, - }, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - ExternalPhoneEnabled: nullable.NewNullableWithValue(false), - SmsAutoconfirm: nullable.NewNullableWithValue(false), - SmsMaxFrequency: nullable.NewNullableWithValue(0), - SmsOtpExp: nullable.NewNullableWithValue(3600), - SmsOtpLength: 6, - SmsProvider: nullable.NewNullableWithValue(v1API.AuthConfigResponseOutputSmsProviderTwilio), - SmsTemplate: nullable.NewNullableWithValue(""), - SmsTwilioAccountSid: nullable.NewNullableWithValue("test-account"), - SmsTwilioAuthToken: nullable.NewNullableWithValue("c84443bc59b92caef8ec8500ff443584793756749523811eb333af2bbc74fc88"), - SmsTwilioContentSid: nullable.NewNullableWithValue("test-content"), - SmsTwilioMessageServiceSid: nullable.NewNullableWithValue("test-service"), - }) - // Check error - assert.NoError(t, err) - assertSnapshotEqual(t, diff) - }) - - t.Run("local disabled remote disabled", func(t *testing.T) { - c := newWithDefaults() - c.Sms = sms{ - EnableSignup: false, - EnableConfirmations: true, - Template: "Your code is {{ .Code }}", - TestOTP: map[string]string{"123": "456"}, - MaxFrequency: time.Minute, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - ExternalPhoneEnabled: nullable.NewNullableWithValue(false), - SmsAutoconfirm: nullable.NewNullableWithValue(true), - SmsMaxFrequency: nullable.NewNullableWithValue(60), - SmsOtpExp: nullable.NewNullableWithValue(3600), - SmsOtpLength: 6, - SmsTemplate: nullable.NewNullableWithValue("Your code is {{ .Code }}"), - SmsTestOtp: nullable.NewNullableWithValue("123=456"), - SmsTestOtpValidUntil: nullable.NewNullableWithValue(time.Date(2050, 1, 1, 1, 0, 0, 0, time.UTC)), - SmsProvider: nullable.NewNullableWithValue(v1API.AuthConfigResponseOutputSmsProviderMessagebird), - SmsMessagebirdAccessKey: nullable.NewNullableWithValue("test-messagebird-key"), - SmsMessagebirdOriginator: nullable.NewNullableWithValue("test-messagebird-originator"), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) - - t.Run("enable sign up without provider", func(t *testing.T) { - // This is not a valid config because platform requires a SMS provider. - // For consistency, we handle this in config.Load and emit a warning. - c := newWithDefaults() - c.Sms.EnableSignup = true - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - ExternalPhoneEnabled: nullable.NewNullableWithValue(false), - SmsProvider: nullable.NewNullableWithValue(v1API.AuthConfigResponseOutputSmsProviderTwilio), - }) - // Check error - assert.NoError(t, err) - assertSnapshotEqual(t, diff) - }) - - t.Run("enable provider without sign up", func(t *testing.T) { - c := newWithDefaults() - c.Sms.Messagebird.Enabled = true - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - ExternalPhoneEnabled: nullable.NewNullableWithValue(false), - SmsProvider: nullable.NewNullableWithValue(v1API.AuthConfigResponseOutputSmsProviderMessagebird), - SmsMessagebirdAccessKey: nullable.NewNullableWithValue(""), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) -} - -func TestExternalDiff(t *testing.T) { - t.Run("local and remote enabled", func(t *testing.T) { - c := newWithDefaults() - c.External = map[string]provider{ - "apple": {Enabled: true}, - "azure": {Enabled: true}, - "bitbucket": {Enabled: true}, - "discord": {Enabled: true}, - "facebook": {Enabled: true}, - "figma": {Enabled: true}, - "github": {Enabled: true}, - "gitlab": {Enabled: true}, - "google": {Enabled: true}, - "kakao": {Enabled: true}, - "keycloak": {Enabled: true}, - "linkedin_oidc": {Enabled: true}, - "notion": {Enabled: true}, - "slack_oidc": {Enabled: true}, - "spotify": {Enabled: true}, - "twitch": {Enabled: true}, - "twitter": {Enabled: true}, - "x": {Enabled: true}, - "workos": {Enabled: true}, - "zoom": {Enabled: true}, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - ExternalAppleAdditionalClientIds: nullable.NewNullableWithValue(""), - ExternalAppleClientId: nullable.NewNullableWithValue(""), - ExternalAppleEnabled: nullable.NewNullableWithValue(true), - ExternalAppleSecret: nullable.NewNullableWithValue(""), - ExternalAzureClientId: nullable.NewNullableWithValue(""), - ExternalAzureEnabled: nullable.NewNullableWithValue(true), - ExternalAzureSecret: nullable.NewNullableWithValue(""), - ExternalAzureUrl: nullable.NewNullableWithValue(""), - ExternalBitbucketClientId: nullable.NewNullableWithValue(""), - ExternalBitbucketEnabled: nullable.NewNullableWithValue(true), - ExternalBitbucketSecret: nullable.NewNullableWithValue(""), - ExternalDiscordClientId: nullable.NewNullableWithValue(""), - ExternalDiscordEnabled: nullable.NewNullableWithValue(true), - ExternalDiscordSecret: nullable.NewNullableWithValue(""), - ExternalFacebookClientId: nullable.NewNullableWithValue(""), - ExternalFacebookEnabled: nullable.NewNullableWithValue(true), - ExternalFacebookSecret: nullable.NewNullableWithValue(""), - ExternalFigmaClientId: nullable.NewNullableWithValue(""), - ExternalFigmaEnabled: nullable.NewNullableWithValue(true), - ExternalFigmaSecret: nullable.NewNullableWithValue(""), - ExternalGithubClientId: nullable.NewNullableWithValue(""), - ExternalGithubEnabled: nullable.NewNullableWithValue(true), - ExternalGithubSecret: nullable.NewNullableWithValue(""), - ExternalGitlabClientId: nullable.NewNullableWithValue(""), - ExternalGitlabEnabled: nullable.NewNullableWithValue(true), - ExternalGitlabSecret: nullable.NewNullableWithValue(""), - ExternalGitlabUrl: nullable.NewNullableWithValue(""), - ExternalGoogleAdditionalClientIds: nullable.NewNullableWithValue(""), - ExternalGoogleClientId: nullable.NewNullableWithValue(""), - ExternalGoogleEnabled: nullable.NewNullableWithValue(true), - ExternalGoogleSecret: nullable.NewNullableWithValue(""), - ExternalGoogleSkipNonceCheck: nullable.NewNullableWithValue(false), - ExternalKakaoClientId: nullable.NewNullableWithValue(""), - ExternalKakaoEnabled: nullable.NewNullableWithValue(true), - ExternalKakaoSecret: nullable.NewNullableWithValue(""), - ExternalKeycloakClientId: nullable.NewNullableWithValue(""), - ExternalKeycloakEnabled: nullable.NewNullableWithValue(true), - ExternalKeycloakSecret: nullable.NewNullableWithValue(""), - ExternalKeycloakUrl: nullable.NewNullableWithValue(""), - ExternalLinkedinOidcClientId: nullable.NewNullableWithValue(""), - ExternalLinkedinOidcEnabled: nullable.NewNullableWithValue(true), - ExternalLinkedinOidcSecret: nullable.NewNullableWithValue(""), - ExternalNotionClientId: nullable.NewNullableWithValue(""), - ExternalNotionEnabled: nullable.NewNullableWithValue(true), - ExternalNotionSecret: nullable.NewNullableWithValue(""), - ExternalSlackOidcClientId: nullable.NewNullableWithValue(""), - ExternalSlackOidcEnabled: nullable.NewNullableWithValue(true), - ExternalSlackOidcSecret: nullable.NewNullableWithValue(""), - ExternalSpotifyClientId: nullable.NewNullableWithValue(""), - ExternalSpotifyEnabled: nullable.NewNullableWithValue(true), - ExternalSpotifySecret: nullable.NewNullableWithValue(""), - ExternalTwitchClientId: nullable.NewNullableWithValue(""), - ExternalTwitchEnabled: nullable.NewNullableWithValue(true), - ExternalTwitchSecret: nullable.NewNullableWithValue(""), - ExternalTwitterClientId: nullable.NewNullableWithValue(""), - ExternalTwitterEnabled: nullable.NewNullableWithValue(true), - ExternalTwitterSecret: nullable.NewNullableWithValue(""), - ExternalXClientId: nullable.NewNullableWithValue(""), - ExternalXEnabled: nullable.NewNullableWithValue(true), - ExternalXSecret: nullable.NewNullableWithValue(""), - ExternalWorkosClientId: nullable.NewNullableWithValue(""), - ExternalWorkosEnabled: nullable.NewNullableWithValue(true), - ExternalWorkosSecret: nullable.NewNullableWithValue(""), - ExternalWorkosUrl: nullable.NewNullableWithValue(""), - ExternalZoomClientId: nullable.NewNullableWithValue(""), - ExternalZoomEnabled: nullable.NewNullableWithValue(true), - ExternalZoomSecret: nullable.NewNullableWithValue(""), - // Deprecated fields should be ignored - ExternalSlackClientId: nullable.NewNullableWithValue(""), - ExternalSlackEnabled: nullable.NewNullableWithValue(true), - ExternalSlackSecret: nullable.NewNullableWithValue(""), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) - - t.Run("local enabled and disabled", func(t *testing.T) { - c := newWithDefaults() - c.External = map[string]provider{ - "apple": { - Enabled: true, - ClientId: "test-client-1,test-client-2", - Secret: Secret{ - Value: "test-secret", - SHA256: "ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252", - }, - }, - "azure": { - Enabled: true, - ClientId: "test-client-1", - Secret: Secret{ - Value: "test-secret", - SHA256: "ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252", - }, - }, - "bitbucket": {}, - "discord": {}, - "facebook": {}, - "figma": {}, - "github": {}, - "gitlab": {}, - "google": { - Enabled: false, - ClientId: "test-client-2", - Secret: Secret{Value: "env(test_secret)"}, - SkipNonceCheck: false, - }, - // "kakao": {}, - "keycloak": {}, - "linkedin_oidc": {}, - "notion": {}, - "slack_oidc": {}, - "spotify": {}, - "twitch": {}, - "twitter": {}, - "x": {}, - "workos": {}, - "zoom": {}, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - ExternalAppleAdditionalClientIds: nullable.NewNullableWithValue("test-client-2"), - ExternalAppleClientId: nullable.NewNullableWithValue("test-client-1"), - ExternalAppleEnabled: nullable.NewNullableWithValue(false), - ExternalAppleSecret: nullable.NewNullableWithValue("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - ExternalGoogleAdditionalClientIds: nullable.NewNullableWithValue("test-client-2"), - ExternalGoogleClientId: nullable.NewNullableWithValue("test-client-1"), - ExternalGoogleEnabled: nullable.NewNullableWithValue(true), - ExternalGoogleSecret: nullable.NewNullableWithValue("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), - ExternalGoogleSkipNonceCheck: nullable.NewNullableWithValue(true), - ExternalKakaoClientId: nullable.NewNullableWithValue("test-client-2"), - ExternalKakaoEnabled: nullable.NewNullableWithValue(true), - ExternalKakaoSecret: nullable.NewNullableWithValue("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), - }) - // Check error - assert.NoError(t, err) - assertSnapshotEqual(t, diff) - }) - - t.Run("local and remote disabled", func(t *testing.T) { - c := newWithDefaults() - c.External = map[string]provider{ - "apple": {}, - "azure": {}, - "bitbucket": {}, - "discord": {}, - "facebook": {}, - "figma": {}, - "github": {}, - "gitlab": {}, - "google": {}, - "kakao": {}, - "keycloak": {}, - "linkedin_oidc": {}, - "notion": {}, - "slack_oidc": {}, - "spotify": {}, - "twitch": {}, - "twitter": {}, - "x": {}, - "workos": {}, - "zoom": {}, - } - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - ExternalAppleEnabled: nullable.NewNullableWithValue(false), - ExternalAzureEnabled: nullable.NewNullableWithValue(false), - ExternalBitbucketEnabled: nullable.NewNullableWithValue(false), - ExternalDiscordEnabled: nullable.NewNullableWithValue(false), - ExternalFacebookEnabled: nullable.NewNullableWithValue(false), - ExternalFigmaEnabled: nullable.NewNullableWithValue(false), - ExternalGithubEnabled: nullable.NewNullableWithValue(false), - ExternalGitlabEnabled: nullable.NewNullableWithValue(false), - ExternalGoogleEnabled: nullable.NewNullableWithValue(false), - ExternalGoogleSkipNonceCheck: nullable.NewNullableWithValue(false), - ExternalKakaoEnabled: nullable.NewNullableWithValue(false), - ExternalKeycloakEnabled: nullable.NewNullableWithValue(false), - ExternalLinkedinOidcEnabled: nullable.NewNullableWithValue(false), - ExternalNotionEnabled: nullable.NewNullableWithValue(false), - ExternalSlackOidcEnabled: nullable.NewNullableWithValue(false), - ExternalSpotifyEnabled: nullable.NewNullableWithValue(false), - ExternalTwitchEnabled: nullable.NewNullableWithValue(false), - ExternalTwitterEnabled: nullable.NewNullableWithValue(false), - ExternalXEnabled: nullable.NewNullableWithValue(false), - ExternalWorkosEnabled: nullable.NewNullableWithValue(false), - ExternalZoomEnabled: nullable.NewNullableWithValue(false), - // Deprecated fields should be ignored - ExternalSlackEnabled: nullable.NewNullableWithValue(false), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) -} - -func TestRateLimitsDiff(t *testing.T) { - t.Run("local and remote rate limits match", func(t *testing.T) { - // Setup auth with rate limits - c := newWithDefaults() - c.RateLimit.AnonymousUsers = 20 - c.RateLimit.TokenRefresh = 30 - c.RateLimit.SignInSignUps = 40 - c.RateLimit.TokenVerifications = 50 - c.RateLimit.EmailSent = 25 - c.RateLimit.SmsSent = 35 - c.Email.Smtp = &smtp{Enabled: true} - // Run test - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - RateLimitAnonymousUsers: nullable.NewNullableWithValue(20), - RateLimitTokenRefresh: nullable.NewNullableWithValue(30), - RateLimitOtp: nullable.NewNullableWithValue(40), - RateLimitVerify: nullable.NewNullableWithValue(50), - RateLimitEmailSent: nullable.NewNullableWithValue(25), - RateLimitSmsSent: nullable.NewNullableWithValue(35), - SmtpHost: nullable.NewNullableWithValue(""), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) - - t.Run("local and remote rate limits differ", func(t *testing.T) { - // Setup auth with rate limits - c := newWithDefaults() - c.RateLimit.AnonymousUsers = 20 - c.RateLimit.TokenRefresh = 30 - c.RateLimit.SignInSignUps = 40 - c.RateLimit.TokenVerifications = 50 - c.RateLimit.EmailSent = 25 - c.RateLimit.SmsSent = 35 - c.Email.Smtp = &smtp{Enabled: true} - // Run test with different remote values - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - RateLimitAnonymousUsers: nullable.NewNullableWithValue(10), // Different value - RateLimitTokenRefresh: nullable.NewNullableWithValue(30), - RateLimitOtp: nullable.NewNullableWithValue(45), // Different value - RateLimitVerify: nullable.NewNullableWithValue(50), - RateLimitEmailSent: nullable.NewNullableWithValue(15), // Different value - RateLimitSmsSent: nullable.NewNullableWithValue(55), // Different value - SmtpHost: nullable.NewNullableWithValue(""), - }) - // Check error - assert.NoError(t, err) - // Compare with snapshot - assertSnapshotEqual(t, diff) - }) - - t.Run("ignores email rate limit when smtp is disabled", func(t *testing.T) { - // Setup auth without rate limits - c := newWithDefaults() - c.RateLimit.EmailSent = 25 - // Run test with remote rate limits - diff, err := c.DiffWithRemote(v1API.AuthConfigResponseOutput{ - RateLimitEmailSent: nullable.NewNullableWithValue(15), - SmtpHost: nullable.NewNullableWithValue(""), - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, string(diff)) - }) -} diff --git a/apps/cli-go/pkg/config/config.go b/apps/cli-go/pkg/config/config.go deleted file mode 100644 index 5c2eca797e..0000000000 --- a/apps/cli-go/pkg/config/config.go +++ /dev/null @@ -1,1909 +0,0 @@ -package config - -import ( - "bytes" - "context" - _ "embed" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "io/fs" - "maps" - "net" - "net/http" - "net/url" - "os" - "path" - "path/filepath" - "regexp" - "slices" - "sort" - "strconv" - "strings" - "text/template" - "time" - - "github.com/BurntSushi/toml" - "github.com/docker/go-units" - "github.com/go-errors/errors" - "github.com/go-viper/mapstructure/v2" - "github.com/joho/godotenv" - "github.com/spf13/viper" - "github.com/supabase/cli/pkg/cast" - "github.com/supabase/cli/pkg/fetcher" - "golang.org/x/mod/semver" -) - -// Type for turning human-friendly bytes string ("5MB", "32kB") into an int64 during toml decoding. -type sizeInBytes int64 - -func (s *sizeInBytes) UnmarshalText(text []byte) error { - size, err := units.RAMInBytes(string(text)) - if err == nil { - *s = sizeInBytes(size) - } - return err -} - -func (s sizeInBytes) MarshalText() (text []byte, err error) { - return []byte(units.BytesSize(float64(s))), nil -} - -type LogflareBackend string - -const ( - LogflarePostgres LogflareBackend = "postgres" - LogflareBigQuery LogflareBackend = "bigquery" -) - -func (b *LogflareBackend) UnmarshalText(text []byte) error { - allowed := []LogflareBackend{LogflarePostgres, LogflareBigQuery} - if *b = LogflareBackend(text); !slices.Contains(allowed, *b) { - return errors.Errorf("must be one of %v", allowed) - } - return nil -} - -type AddressFamily string - -const ( - AddressIPv6 AddressFamily = "IPv6" - AddressIPv4 AddressFamily = "IPv4" -) - -func (f *AddressFamily) UnmarshalText(text []byte) error { - allowed := []AddressFamily{AddressIPv6, AddressIPv4} - if *f = AddressFamily(text); !slices.Contains(allowed, *f) { - return errors.Errorf("must be one of %v", allowed) - } - return nil -} - -type RequestPolicy string - -const ( - PolicyPerWorker RequestPolicy = "per_worker" - PolicyOneshot RequestPolicy = "oneshot" -) - -func (p *RequestPolicy) UnmarshalText(text []byte) error { - allowed := []RequestPolicy{PolicyPerWorker, PolicyOneshot} - if *p = RequestPolicy(text); !slices.Contains(allowed, *p) { - return errors.Errorf("must be one of %v", allowed) - } - return nil -} - -type Glob []string - -type globOptions struct { - skipEmptyGlobs bool - errorOnAllSkipped bool -} - -type GlobOption func(*globOptions) - -func WithSkipEmptyGlobs() GlobOption { - return func(o *globOptions) { - o.skipEmptyGlobs = true - } -} - -func WithErrorOnAllSkippedGlobs() GlobOption { - return func(o *globOptions) { - o.errorOnAllSkipped = true - } -} - -func (g Glob) Files(fsys fs.FS, options ...GlobOption) ([]string, error) { - return g.files(fsys, nil, options...) -} - -// SQLFiles matches glob patterns and expands directory matches recursively to -// SQL files. Pattern order is preserved, and directory contents are sorted for -// deterministic application. -func (g Glob) SQLFiles(fsys fs.FS, options ...GlobOption) ([]string, error) { - return g.files(fsys, func(path string, entry fs.DirEntry) bool { - return entry.Type().IsRegular() && filepath.Ext(path) == ".sql" - }, options...) -} - -// Match the glob patterns in the given FS to get a deduplicated -// array of all migrations files to apply in the declared order. -func (g Glob) files(fsys fs.FS, expandDir func(string, fs.DirEntry) bool, options ...GlobOption) ([]string, error) { - opts := globOptions{} - for _, apply := range options { - apply(&opts) - } - var result []string - var allErrors []error - var skipped []string - set := make(map[string]struct{}) - for _, pattern := range g { - // Glob expects / as path separator on windows - matches, err := fs.Glob(fsys, filepath.ToSlash(pattern)) - if err != nil { - allErrors = append(allErrors, errors.Errorf("failed to glob files: %w", err)) - } else if len(matches) == 0 { - if opts.skipEmptyGlobs && hasGlobMeta(pattern) { - skipped = append(skipped, pattern) - continue - } - allErrors = append(allErrors, errors.Errorf("no files matched pattern: %s", pattern)) - } - sort.Strings(matches) - // Remove duplicates - for _, item := range matches { - fp := filepath.ToSlash(item) - if expandDir != nil { - info, err := fs.Stat(fsys, fp) - if err != nil { - allErrors = append(allErrors, errors.Errorf("failed to stat matched file: %w", err)) - continue - } - if info.IsDir() { - files, err := walkMatchedDir(fsys, fp, expandDir) - if err != nil { - allErrors = append(allErrors, err) - continue - } - for _, file := range files { - if _, exists := set[file]; !exists { - set[file] = struct{}{} - result = append(result, file) - } - } - continue - } - } - if _, exists := set[fp]; !exists { - set[fp] = struct{}{} - result = append(result, fp) - } - } - } - if opts.errorOnAllSkipped && len(result) == 0 && len(skipped) > 0 { - for _, pattern := range skipped { - allErrors = append(allErrors, errors.Errorf("no files matched pattern: %s", pattern)) - } - } - return result, errors.Join(allErrors...) -} - -func walkMatchedDir(fsys fs.FS, dir string, include func(string, fs.DirEntry) bool) ([]string, error) { - var files []string - if err := fs.WalkDir(fsys, dir, func(path string, entry fs.DirEntry, err error) error { - if err != nil { - return err - } - if include(path, entry) { - files = append(files, filepath.ToSlash(path)) - } - return nil - }); err != nil { - return nil, errors.Errorf("failed to walk matched directory: %w", err) - } - sort.Strings(files) - return files, nil -} - -func hasGlobMeta(pattern string) bool { - return strings.ContainsAny(pattern, `*?[`) -} - -// We follow these rules when adding new config: -// 1. Update init_config.toml (and init_config.test.toml) with the new key, default value, and comments to explain usage. -// 2. Update config struct with new field and toml tag (spelled in snake_case). -// 3. Add custom field validations to LoadConfigFS function for eg. integer range checks. -// -// If you are adding new user defined secrets, such as OAuth provider secret, the default value in -// init_config.toml should be an env var substitution. For example, -// -// > secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" -// -// Default values for internal configs should be added to `var Config` initializer. -type ( - // Common config fields between our "base" config and any "remote" branch specific - baseConfig struct { - ProjectId string `toml:"project_id" json:"project_id"` - Hostname string `toml:"-" json:"-"` - Api api `toml:"api" json:"api"` - Db db `toml:"db" json:"db"` - Realtime realtime `toml:"realtime" json:"realtime"` - Studio studio `toml:"studio" json:"studio"` - Inbucket inbucket `toml:"local_smtp" json:"local_smtp"` - Storage storage `toml:"storage" json:"storage"` - Auth auth `toml:"auth" json:"auth"` - EdgeRuntime edgeRuntime `toml:"edge_runtime" json:"edge_runtime"` - Functions FunctionConfig `toml:"functions" json:"functions"` - Analytics analytics `toml:"analytics" json:"analytics"` - Experimental experimental `toml:"experimental" json:"experimental"` - // Compute is parsed but never read here. The [compute] section is owned by - // the TS CLI; this field exists only so a config the TS schema accepts does - // not trip UnmarshalExact in the delegated Go child (`flags.LoadConfig`). - // The json tag is the one that matters: the decoder runs with - // dc.TagName = "json". Omitted from toml so Go never emits the section. - Compute map[string]any `toml:"-" json:"compute"` - } - - config struct { - baseConfig - Remotes map[string]baseConfig `toml:"remotes" json:"remotes"` - } - - realtime struct { - Enabled bool `toml:"enabled" json:"enabled"` - Image string `toml:"-" json:"-"` - IpVersion AddressFamily `toml:"ip_version" json:"ip_version"` - MaxHeaderLength uint `toml:"max_header_length" json:"max_header_length"` - TenantId string `toml:"-" json:"-"` - EncryptionKey string `toml:"-" json:"-"` - SecretKeyBase string `toml:"-" json:"-"` - } - - studio struct { - Enabled bool `toml:"enabled" json:"enabled"` - Image string `toml:"-" json:"-"` - Port uint16 `toml:"port" json:"port"` - ApiUrl string `toml:"api_url" json:"api_url"` - OpenaiApiKey Secret `toml:"openai_api_key" json:"openai_api_key"` - PgmetaImage string `toml:"-" json:"-"` - } - - inbucket struct { - Enabled bool `toml:"enabled" json:"enabled"` - Image string `toml:"-" json:"-"` - Port uint16 `toml:"port" json:"port"` - SmtpPort uint16 `toml:"smtp_port" json:"smtp_port"` - Pop3Port uint16 `toml:"pop3_port" json:"pop3_port"` - AdminEmail string `toml:"admin_email" json:"admin_email"` - SenderName string `toml:"sender_name" json:"sender_name"` - } - - edgeRuntime struct { - Enabled bool `toml:"enabled" json:"enabled"` - Image string `toml:"-" json:"-"` - Policy RequestPolicy `toml:"policy" json:"policy"` - InspectorPort uint16 `toml:"inspector_port" json:"inspector_port"` - Secrets SecretsConfig `toml:"secrets" json:"secrets"` - DenoVersion uint `toml:"deno_version" json:"deno_version"` - } - - SecretsConfig map[string]Secret - FunctionConfig map[string]function - - function struct { - Enabled bool `toml:"enabled" json:"enabled"` - VerifyJWT *bool `toml:"verify_jwt" json:"verify_jwt"` - ImportMap string `toml:"import_map" json:"import_map"` - Entrypoint string `toml:"entrypoint" json:"entrypoint"` - StaticFiles Glob `toml:"static_files" json:"static_files"` - } - - analytics struct { - Enabled bool `toml:"enabled" json:"enabled"` - Image string `toml:"-" json:"-"` - VectorImage string `toml:"-" json:"-"` - Port uint16 `toml:"port" json:"port"` - Backend LogflareBackend `toml:"backend" json:"backend"` - GcpProjectId string `toml:"gcp_project_id" json:"gcp_project_id"` - GcpProjectNumber string `toml:"gcp_project_number" json:"gcp_project_number"` - GcpJwtPath string `toml:"gcp_jwt_path" json:"gcp_jwt_path"` - ApiKey string `toml:"-" json:"-"` - // Deprecated together with syslog - VectorPort uint16 `toml:"vector_port" json:"vector_port"` - } - - webhooks struct { - Enabled bool `toml:"enabled" json:"enabled"` - } - - PgDeltaConfig struct { - Enabled bool `toml:"enabled" json:"enabled"` - DeclarativeSchemaPath string `toml:"declarative_schema_path" json:"declarative_schema_path"` - FormatOptions string `toml:"format_options" json:"format_options"` - } - - inspect struct { - Rules []rule `toml:"rules" json:"rules"` - } - - rule struct { - Query string `toml:"query" json:"query"` - Name string `toml:"name" json:"name"` - Pass string `toml:"pass" json:"pass"` - Fail string `toml:"fail" json:"fail"` - } - - experimental struct { - OrioleDBVersion string `toml:"orioledb_version" json:"orioledb_version"` - S3Host string `toml:"s3_host" json:"s3_host"` - S3Region string `toml:"s3_region" json:"s3_region"` - S3AccessKey string `toml:"s3_access_key" json:"s3_access_key"` - S3SecretKey string `toml:"s3_secret_key" json:"s3_secret_key"` - Stack bool `toml:"-" json:"stack"` - Compute bool `toml:"-" json:"compute"` - Webhooks *webhooks `toml:"webhooks" json:"webhooks"` - PgDelta *PgDeltaConfig `toml:"pgdelta" json:"pgdelta"` - Inspect inspect `toml:"inspect" json:"inspect"` - // PgDeltaInitEnabled drives the [experimental.pgdelta] enabled value rendered - // by Eject. It is true only for the supabase init scaffold so freshly generated - // projects opt into pg-delta, and false when Eject feeds mergeDefaultValues so - // existing configs without the section keep resolving to migra (non-breaking). - PgDeltaInitEnabled bool `toml:"-" json:"-"` - } -) - -func (a *auth) Clone() auth { - copy := *a - if copy.Captcha != nil { - capt := *a.Captcha - copy.Captcha = &capt - } - if copy.Passkey != nil { - passkey := *a.Passkey - copy.Passkey = &passkey - } - if copy.Webauthn != nil { - webauthn := *a.Webauthn - webauthn.RpOrigins = slices.Clone(a.Webauthn.RpOrigins) - copy.Webauthn = &webauthn - } - copy.External = maps.Clone(a.External) - if a.Email.Smtp != nil { - mailer := *a.Email.Smtp - copy.Email.Smtp = &mailer - } - copy.Email.Template = maps.Clone(a.Email.Template) - copy.Email.Notification = maps.Clone(a.Email.Notification) - if a.Hook.MFAVerificationAttempt != nil { - hook := *a.Hook.MFAVerificationAttempt - copy.Hook.MFAVerificationAttempt = &hook - } - if a.Hook.PasswordVerificationAttempt != nil { - hook := *a.Hook.PasswordVerificationAttempt - copy.Hook.PasswordVerificationAttempt = &hook - } - if a.Hook.CustomAccessToken != nil { - hook := *a.Hook.CustomAccessToken - copy.Hook.CustomAccessToken = &hook - } - if a.Hook.SendSMS != nil { - hook := *a.Hook.SendSMS - copy.Hook.SendSMS = &hook - } - if a.Hook.SendEmail != nil { - hook := *a.Hook.SendEmail - copy.Hook.SendEmail = &hook - } - if a.Hook.BeforeUserCreated != nil { - hook := *a.Hook.BeforeUserCreated - copy.Hook.BeforeUserCreated = &hook - } - copy.Sms.TestOTP = maps.Clone(a.Sms.TestOTP) - return copy -} - -func (s *storage) Clone() storage { - copy := *s - copy.Buckets = maps.Clone(s.Buckets) - copy.AnalyticsBuckets.Buckets = maps.Clone(s.AnalyticsBuckets.Buckets) - copy.VectorBuckets.Buckets = maps.Clone(s.VectorBuckets.Buckets) - if s.ImageTransformation != nil { - img := *s.ImageTransformation - copy.ImageTransformation = &img - } - if s.S3Protocol != nil { - s3 := *s.S3Protocol - copy.S3Protocol = &s3 - } - return copy -} - -func (c *baseConfig) Clone() baseConfig { - copy := *c - copy.Db.Vault = maps.Clone(c.Db.Vault) - copy.Storage = c.Storage.Clone() - copy.EdgeRuntime.Secrets = maps.Clone(c.EdgeRuntime.Secrets) - copy.Functions = maps.Clone(c.Functions) - copy.Auth = c.Auth.Clone() - if c.Experimental.Webhooks != nil { - webhooks := *c.Experimental.Webhooks - copy.Experimental.Webhooks = &webhooks - } - if c.Experimental.PgDelta != nil { - pgDelta := *c.Experimental.PgDelta - copy.Experimental.PgDelta = &pgDelta - } - return copy -} - -type Config *config - -type ConfigEditor func(Config) - -func WithHostname(hostname string) ConfigEditor { - return func(c Config) { - c.Hostname = hostname - } -} - -func NewConfig(editors ...ConfigEditor) config { - initial := config{baseConfig: baseConfig{ - Hostname: "127.0.0.1", - Api: api{ - Image: Images.Postgrest, - KongImage: Images.Kong, - Tls: tlsKong{ - CertContent: kongCert, - KeyContent: kongKey, - }, - }, - Db: db{ - Image: Images.Pg, - Password: "postgres", - RootKey: Secret{ - Value: "d4dc5b6d4a1d6a10b2c1e76112c994d65db7cec380572cc1839624d4be3fa275", - }, - Pooler: pooler{ - Image: Images.Supavisor, - TenantId: "pooler-dev", - EncryptionKey: "12345678901234567890123456789032", - SecretKeyBase: "EAx3IQ/wRG1v47ZD4NE4/9RzBI8Jmil3x0yhcW4V2NHBP6c2iPIzwjofi2Ep4HIG", - }, - Migrations: migrations{ - Enabled: true, - }, - Seed: seed{ - Enabled: true, - SqlPaths: []string{"seed.sql"}, - }, - }, - Realtime: realtime{ - Image: Images.Realtime, - IpVersion: AddressIPv4, - MaxHeaderLength: 4096, - TenantId: "realtime-dev", - EncryptionKey: "supabaserealtime", - SecretKeyBase: "EAx3IQ/wRG1v47ZD4NE4/9RzBI8Jmil3x0yhcW4V2NHBP6c2iPIzwjofi2Ep4HIG", - }, - Storage: storage{ - Image: Images.Storage, - ImgProxyImage: Images.ImgProxy, - S3Credentials: storageS3Credentials{ - AccessKeyId: "625729a08b95bf1b7ff351a663f3a23c", - SecretAccessKey: "850181e4652dd023b7a98c58ae0d2d34bd487ee0cc3254aed6eda37307425907", - Region: "local", - }, - }, - Auth: auth{ - Image: Images.Gotrue, - Email: email{ - Template: map[string]emailTemplate{}, - Notification: map[string]notification{}, - }, - Sms: sms{ - TestOTP: map[string]string{}, - }, - External: map[string]provider{}, - SigningKeys: []JWK{{ - KeyType: "EC", - KeyID: "b81269f1-21d8-4f2e-b719-c2240a840d90", - Use: "sig", - KeyOps: []string{"sign", "verify"}, - Algorithm: "ES256", - Extractable: cast.Ptr(true), - Curve: "P-256", - X: "M5Sjqn5zwC9Kl1zVfUUGvv9boQjCGd45G8sdopBExB4", - Y: "P6IXMvA2WYXSHSOMTBH2jsw_9rrzGy89FjPf6oOsIxQ", - PrivateExponent: "dIhR8wywJlqlua4y_yMq2SLhlFXDZJBCvFrY1DCHyVU", - }}, - }, - Inbucket: inbucket{ - Image: Images.Inbucket, - AdminEmail: "admin@email.com", - SenderName: "Admin", - }, - Studio: studio{ - Image: Images.Studio, - PgmetaImage: Images.Pgmeta, - }, - Analytics: analytics{ - Image: Images.Logflare, - VectorImage: Images.Vector, - ApiKey: "api-key", - // Defaults to bigquery for backwards compatibility with existing config.toml - Backend: LogflareBigQuery, - }, - EdgeRuntime: edgeRuntime{ - Image: Images.EdgeRuntime, - }, - }} - for _, apply := range editors { - apply(&initial) - } - return initial -} - -func (c *config) AuthExternalURL() string { - return c.Auth.GetExternalURL(c.Api.ExternalUrl) -} - -var ( - //go:embed templates/certs/kong.local.crt - kongCert []byte - //go:embed templates/certs/kong.local.key - kongKey []byte - - //go:embed templates/config.toml - initConfigEmbed string - initConfigTemplate = template.Must(template.New("initConfig").Parse(initConfigEmbed)) - - invalidProjectId = regexp.MustCompile("[^a-zA-Z0-9_.-]+") - refPattern = regexp.MustCompile(`^[a-z]{20}$`) -) - -func (c *config) Eject(w io.Writer) error { - // Defaults to current directory name as project id - if len(c.ProjectId) == 0 { - cwd, err := os.Getwd() - if err != nil { - return errors.Errorf("failed to get working directory: %w", err) - } - c.ProjectId = filepath.Base(cwd) - } - c.ProjectId = sanitizeProjectId(c.ProjectId) - // TODO: templatize all fields eventually - if err := initConfigTemplate.Option("missingkey=error").Execute(w, c); err != nil { - return errors.Errorf("failed to initialise config: %w", err) - } - return nil -} - -// Loads custom config file to struct fields tagged with toml. -func (c *config) loadFromFile(filename string, fsys fs.FS) error { - v := viper.NewWithOptions( - viper.ExperimentalBindStruct(), - viper.EnvKeyReplacer(strings.NewReplacer(".", "_")), - ) - fileConfig := viper.New() - v.SetEnvPrefix("SUPABASE") - v.AutomaticEnv() - if err := c.mergeDefaultValues(v); err != nil { - return err - } else if err := mergeFileConfig(v, filename, fsys); err != nil { - return err - } else if err := mergeFileConfig(fileConfig, filename, fsys); err != nil { - return err - } - v = normalizeDeprecatedSMTPConfig(v, fileConfig) - v = normalizeDeprecatedOrioleDBConfig(v, fileConfig) - // Find [remotes.*] block to override base config - idToName := map[string]string{} - for name, remote := range v.GetStringMap("remotes") { - projectId := v.GetString(fmt.Sprintf("remotes.%s.project_id", name)) - // Track remote project_id to check for duplication - if other, exists := idToName[projectId]; exists { - return errors.Errorf("duplicate project_id for [remotes.%s] and %s", name, other) - } - idToName[projectId] = fmt.Sprintf("[remotes.%s]", name) - if projectId == c.ProjectId { - fmt.Fprintln(os.Stderr, "Loading config override:", idToName[projectId]) - if err := mergeRemoteConfig(v, remote.(map[string]any)); err != nil { - return err - } - } - } - return c.load(v) -} - -func normalizeDeprecatedSMTPConfig(v, fileConfig *viper.Viper) *viper.Viper { - settings := v.AllSettings() - changed := false - if fileConfig.IsSet("inbucket") { - fmt.Fprintln(os.Stderr, `WARN: config section [inbucket] is deprecated. Please use [local_smtp] instead.`) - renameDeprecatedSMTP(settings, !fileConfig.IsSet("local_smtp")) - changed = true - } - if remotes, ok := settings["remotes"].(map[string]any); ok { - for name, raw := range remotes { - remote, ok := raw.(map[string]any) - if !ok || !fileConfig.IsSet(fmt.Sprintf("remotes.%s.inbucket", name)) { - continue - } - fmt.Fprintf( - os.Stderr, - "WARN: config section [remotes.%s.inbucket] is deprecated. Please use [remotes.%s.local_smtp] instead.\n", - name, - name, - ) - renameDeprecatedSMTP(remote, !fileConfig.IsSet(fmt.Sprintf("remotes.%s.local_smtp", name))) - changed = true - } - } - if !changed { - return v - } - return rebuildViperFromSettings(settings, v) -} - -// rebuildViperFromSettings rebuilds a viper instance from rewritten settings so a -// now-removed deprecated key does not trip UnmarshalExact. It preserves the -// env-binding options from the original instance, otherwise SUPABASE_-prefixed -// env overrides bound via ExperimentalBindStruct would be silently dropped. -func rebuildViperFromSettings(settings map[string]any, original *viper.Viper) *viper.Viper { - u := viper.NewWithOptions( - viper.ExperimentalBindStruct(), - viper.EnvKeyReplacer(strings.NewReplacer(".", "_")), - ) - u.SetEnvPrefix("SUPABASE") - u.AutomaticEnv() - if err := u.MergeConfigMap(settings); err != nil { - return original - } - return u -} - -func normalizeDeprecatedOrioleDBConfig(v, fileConfig *viper.Viper) *viper.Viper { - settings := v.AllSettings() - changed := promoteDeprecatedOrioleDBVersion(settings, fileConfig, "", "experimental.orioledb_version", "db.orioledb_version") - if remotes, ok := settings["remotes"].(map[string]any); ok { - for name, raw := range remotes { - remote, ok := raw.(map[string]any) - if !ok { - continue - } - legacyKey := fmt.Sprintf("remotes.%s.experimental.orioledb_version", name) - dbKey := fmt.Sprintf("remotes.%s.db.orioledb_version", name) - if promoteDeprecatedOrioleDBVersion(remote, fileConfig, name, legacyKey, dbKey) { - changed = true - } - } - } - if !changed { - return v - } - return rebuildViperFromSettings(settings, v) -} - -// promoteDeprecatedOrioleDBVersion promotes a non-empty legacy `experimental.orioledb_version` -// onto `db.orioledb_version` in settings when the latter is absent or empty in the user's -// file (the template always writes `db.orioledb_version = ""`, so an unset value is -// indistinguishable from an explicit empty string), and warns on stderr whenever the legacy -// value is non-empty. remoteName is empty for the top-level config, or a `[remotes.*]` name -// for the warning message. -func promoteDeprecatedOrioleDBVersion(settings map[string]any, fileConfig *viper.Viper, remoteName, legacyKey, dbKey string) bool { - legacyVal := fileConfig.GetString(legacyKey) - if legacyVal == "" { - return false - } - if fileConfig.GetString(dbKey) == "" { - db, ok := settings["db"].(map[string]any) - if !ok { - db = map[string]any{} - settings["db"] = db - } - db["orioledb_version"] = legacyVal - } - if remoteName == "" { - fmt.Fprintln(os.Stderr, "WARN: experimental.orioledb_version is deprecated. Please use db.orioledb_version instead.") - } else { - fmt.Fprintf( - os.Stderr, - "WARN: remotes.%s.experimental.orioledb_version is deprecated. Please use remotes.%s.db.orioledb_version instead.\n", - remoteName, - remoteName, - ) - } - return true -} - -// renameDeprecatedSMTP removes the deprecated `inbucket` key from settings. When -// promote is true (no explicit `local_smtp` is present), the inbucket values are -// deep-merged over any existing `local_smtp` defaults so a partial `[inbucket]` -// section keeps the template defaults it omits (e.g. `enabled = true`). -func renameDeprecatedSMTP(settings map[string]any, promote bool) { - inbucket, ok := settings["inbucket"] - delete(settings, "inbucket") - if !ok || !promote { - return - } - if existing, ok := settings["local_smtp"].(map[string]any); ok { - if override, ok := inbucket.(map[string]any); ok { - mergeConfigMaps(existing, override) - return - } - } - settings["local_smtp"] = inbucket -} - -// mergeConfigMaps deep-merges src into dst, overwriting leaf values while -// recursing into nested maps, mirroring viper's own config merge semantics. -func mergeConfigMaps(dst, src map[string]any) { - for k, val := range src { - if srcMap, ok := val.(map[string]any); ok { - if dstMap, ok := dst[k].(map[string]any); ok { - mergeConfigMaps(dstMap, srcMap) - continue - } - } - dst[k] = val - } -} - -func (c *config) mergeDefaultValues(v *viper.Viper) error { - v.SetConfigType("toml") - var buf bytes.Buffer - if err := c.Eject(&buf); err != nil { - return err - } else if err := v.MergeConfig(&buf); err != nil { - return errors.Errorf("failed to merge default values: %w", err) - } - return nil -} - -func mergeFileConfig(v *viper.Viper, filename string, fsys fs.FS) error { - if ext := filepath.Ext(filename); len(ext) > 0 { - v.SetConfigType(ext[1:]) - } - f, err := fsys.Open(filename) - if errors.Is(err, os.ErrNotExist) { - return nil - } else if err != nil { - return errors.Errorf("failed to read file config: %w", err) - } - defer f.Close() - if err := v.MergeConfig(f); err != nil { - return errors.Errorf("failed to merge file config: %w", err) - } - return nil -} - -func mergeRemoteConfig(v *viper.Viper, remote map[string]any) error { - u := viper.New() - if err := u.MergeConfigMap(remote); err != nil { - return errors.Errorf("failed to merge remote config: %w", err) - } - for _, k := range u.AllKeys() { - v.Set(k, u.Get(k)) - } - if key := "db.seed.enabled"; !u.IsSet(key) { - v.Set(key, false) - } - return nil -} - -func (c *config) load(v *viper.Viper) error { - // Set default values for [functions.*] when config struct is empty - for key, value := range v.GetStringMap("functions") { - if _, ok := value.(map[string]any); !ok { - // Leave validation to decode hook - continue - } - if k := fmt.Sprintf("functions.%s.enabled", key); !v.IsSet(k) { - v.Set(k, true) - } - } - // Set default values when [auth.email.smtp] is defined - if smtp := v.GetStringMap("auth.email.smtp"); len(smtp) > 0 { - if _, exists := smtp["enabled"]; !exists { - v.Set("auth.email.smtp.enabled", true) - } - } - if err := v.UnmarshalExact(c, func(dc *mapstructure.DecoderConfig) { - dc.TagName = "json" - dc.Squash = true - dc.ZeroFields = true - dc.DecodeHook = c.newDecodeHook(LoadEnvHook, ValidateFunctionsHook) - }); err != nil { - return errors.Errorf("failed to parse config: %w", err) - } - // Manually parse config to map - c.Storage.AnalyticsBuckets.Buckets = map[string]struct{}{} - for key := range v.GetStringMap("storage.analytics.buckets") { - c.Storage.AnalyticsBuckets.Buckets[key] = struct{}{} - } - c.Storage.VectorBuckets.Buckets = map[string]struct{}{} - for key := range v.GetStringMap("storage.vector.buckets") { - c.Storage.VectorBuckets.Buckets[key] = struct{}{} - } - // Convert keys to upper case: https://github.com/spf13/viper/issues/1014 - secrets := make(SecretsConfig, len(c.EdgeRuntime.Secrets)) - for k, v := range c.EdgeRuntime.Secrets { - secrets[strings.ToUpper(k)] = v - } - c.EdgeRuntime.Secrets = secrets - return nil -} - -func (c *config) newDecodeHook(fs ...mapstructure.DecodeHookFunc) mapstructure.DecodeHookFunc { - fs = append(fs, - mapstructure.StringToTimeDurationHookFunc(), - mapstructure.StringToIPHookFunc(), - mapstructure.StringToSliceHookFunc(","), - mapstructure.TextUnmarshallerHookFunc(), - DecryptSecretHookFunc(c.ProjectId), - ) - return mapstructure.ComposeDecodeHookFunc(fs...) -} - -func (c *config) Load(path string, fsys fs.FS, overrides ...ConfigEditor) error { - builder := NewPathBuilder(path) - // Load secrets from .env file - if err := loadNestedEnv(builder.SupabaseDirPath); err != nil { - return err - } - if err := c.loadFromFile(builder.ConfigPath, fsys); err != nil { - return err - } - // TODO: move linked pooler connection string elsewhere - if connString, err := fs.ReadFile(fsys, builder.PoolerUrlPath); err == nil && len(connString) > 0 { - c.Db.Pooler.ConnectionString = string(connString) - } - if len(c.Api.ExternalUrl) == 0 { - // Update external api url - apiUrl := url.URL{Host: net.JoinHostPort(c.Hostname, - strconv.FormatUint(uint64(c.Api.Port), 10), - )} - if c.Api.Tls.Enabled { - apiUrl.Scheme = "https" - } else { - apiUrl.Scheme = "http" - } - c.Api.ExternalUrl = apiUrl.String() - } - if len(c.Auth.ExternalUrl) == 0 { - c.Auth.ExternalUrl = c.AuthExternalURL() - } - // Set default JWT issuer if not configured - if len(c.Auth.JwtIssuer) == 0 { - c.Auth.JwtIssuer = c.Auth.ExternalUrl - } - // Update image versions - switch c.Db.MajorVersion { - case 13: - c.Db.Image = Images.Pg15 - case 14: - c.Db.Image = Images.Pg14 - case 15: - c.Db.Image = Images.Pg15 - } - if c.Db.MajorVersion > 14 { - if version, err := fs.ReadFile(fsys, builder.PostgresVersionPath); err == nil { - // Only replace image if postgres version is above 15.1.0.55 - if i := strings.IndexByte(c.Db.Image, ':'); VersionCompare(c.Db.Image[i+1:], "15.1.0.55") >= 0 { - c.Db.Image = replaceImageTag(Images.Pg, string(version)) - } - } - if version, err := fs.ReadFile(fsys, builder.RestVersionPath); err == nil && len(version) > 0 { - c.Api.Image = replaceImageTag(Images.Postgrest, string(version)) - } - if version, err := fs.ReadFile(fsys, builder.GotrueVersionPath); err == nil && len(version) > 0 { - c.Auth.Image = replaceImageTag(Images.Gotrue, string(version)) - } - } - if version, err := fs.ReadFile(fsys, builder.StorageVersionPath); err == nil && len(version) > 0 { - c.Storage.Image = replaceImageTag(Images.Storage, string(version)) - } - if version, err := fs.ReadFile(fsys, builder.StorageMigrationPath); err == nil && len(version) > 0 { - c.Storage.TargetMigration = strings.TrimSpace(string(version)) - } - if version, err := fs.ReadFile(fsys, builder.EdgeRuntimeVersionPath); err == nil && len(version) > 0 { - c.EdgeRuntime.Image = replaceImageTag(Images.EdgeRuntime, string(version)) - } - if version, err := fs.ReadFile(fsys, builder.PoolerVersionPath); err == nil && len(version) > 0 { - c.Db.Pooler.Image = replaceImageTag(Images.Supavisor, string(version)) - } - if version, err := fs.ReadFile(fsys, builder.RealtimeVersionPath); err == nil && len(version) > 0 { - c.Realtime.Image = replaceImageTag(Images.Realtime, string(version)) - } - if version, err := fs.ReadFile(fsys, builder.StudioVersionPath); err == nil && len(version) > 0 { - c.Studio.Image = replaceImageTag(Images.Studio, string(version)) - } - if version, err := fs.ReadFile(fsys, builder.PgmetaVersionPath); err == nil && len(version) > 0 { - c.Studio.PgmetaImage = replaceImageTag(Images.Pgmeta, string(version)) - } - if version, err := fs.ReadFile(fsys, builder.LogflareVersionPath); err == nil && len(version) > 0 { - c.Analytics.Image = replaceImageTag(Images.Logflare, string(version)) - } - // TODO: replace derived config resolution with viper decode hooks - if err := c.resolve(builder, fsys); err != nil { - return err - } - for _, apply := range overrides { - apply(c) - } - return c.Validate(fsys) -} - -func VersionCompare(a, b string) int { - var pA, pB string - if vA := strings.Split(a, "."); len(vA) > 3 { - a = strings.Join(vA[:3], ".") - pA = strings.TrimLeft(strings.Join(vA[3:], "."), "0") - } - if vB := strings.Split(b, "."); len(vB) > 3 { - b = strings.Join(vB[:3], ".") - pB = strings.TrimLeft(strings.Join(vB[3:], "."), "0") - } - if r := semver.Compare("v"+a, "v"+b); r != 0 { - return r - } - return semver.Compare("v"+pA, "v"+pB) -} - -func (c *baseConfig) resolve(builder pathBuilder, fsys fs.FS) error { - // Update content paths - for name, tmpl := range c.Auth.Email.Template { - // FIXME: only email template is relative to repo directory - cwd := filepath.Dir(builder.SupabaseDirPath) - if len(tmpl.ContentPath) > 0 && !filepath.IsAbs(tmpl.ContentPath) { - tmpl.ContentPath = filepath.Join(cwd, tmpl.ContentPath) - } - c.Auth.Email.Template[name] = tmpl - } - for name, tmpl := range c.Auth.Email.Notification { - if len(tmpl.ContentPath) > 0 && !filepath.IsAbs(tmpl.ContentPath) { - tmpl.ContentPath = filepath.Join(builder.SupabaseDirPath, tmpl.ContentPath) - } - c.Auth.Email.Notification[name] = tmpl - } - // Update fallback configs - for name, bucket := range c.Storage.Buckets { - if bucket.FileSizeLimit == 0 { - bucket.FileSizeLimit = c.Storage.FileSizeLimit - } - if len(bucket.ObjectsPath) > 0 && !filepath.IsAbs(bucket.ObjectsPath) { - bucket.ObjectsPath = filepath.Join(builder.SupabaseDirPath, bucket.ObjectsPath) - } - c.Storage.Buckets[name] = bucket - } - // Resolve signing keys path for cross-platform compatibility - if len(c.Auth.SigningKeysPath) > 0 && !filepath.IsAbs(c.Auth.SigningKeysPath) { - c.Auth.SigningKeysPath = filepath.Join(builder.SupabaseDirPath, c.Auth.SigningKeysPath) - } - // Resolve functions config - for slug, function := range c.Functions { - if len(function.Entrypoint) == 0 { - function.Entrypoint = filepath.Join(builder.FunctionsDir, slug, "index.ts") - } else if !filepath.IsAbs(function.Entrypoint) { - // Append supabase/ because paths in configs are specified relative to config.toml - function.Entrypoint = filepath.Join(builder.SupabaseDirPath, function.Entrypoint) - } - if len(function.ImportMap) == 0 { - functionDir := filepath.Dir(function.Entrypoint) - denoJsonPath := filepath.Join(functionDir, "deno.json") - denoJsoncPath := filepath.Join(functionDir, "deno.jsonc") - if _, err := fs.Stat(fsys, denoJsonPath); err == nil { - function.ImportMap = denoJsonPath - } else if _, err := fs.Stat(fsys, denoJsoncPath); err == nil { - function.ImportMap = denoJsoncPath - } - // Functions may not use import map so we don't set a default value - } else if !filepath.IsAbs(function.ImportMap) { - function.ImportMap = filepath.Join(builder.SupabaseDirPath, function.ImportMap) - } - for i, val := range function.StaticFiles { - if len(val) > 0 && !filepath.IsAbs(val) { - function.StaticFiles[i] = filepath.Join(builder.SupabaseDirPath, val) - } - } - c.Functions[slug] = function - } - // Resolve TLS config - if c.Api.Enabled && c.Api.Tls.Enabled { - if len(c.Api.Tls.CertPath) > 0 { - c.Api.Tls.CertPath = path.Join(builder.SupabaseDirPath, c.Api.Tls.CertPath) - } - if len(c.Api.Tls.KeyPath) > 0 { - c.Api.Tls.KeyPath = path.Join(builder.SupabaseDirPath, c.Api.Tls.KeyPath) - } - } - // Resolve database config - if c.Db.Seed.Enabled { - for i, pattern := range c.Db.Seed.SqlPaths { - if len(pattern) > 0 && !filepath.IsAbs(pattern) { - c.Db.Seed.SqlPaths[i] = path.Join(builder.SupabaseDirPath, pattern) - } - } - } - for i, pattern := range c.Db.Migrations.SchemaPaths { - if len(pattern) > 0 && !filepath.IsAbs(pattern) { - c.Db.Migrations.SchemaPaths[i] = path.Join(builder.SupabaseDirPath, pattern) - } - } - if c.Experimental.PgDelta != nil && - len(c.Experimental.PgDelta.DeclarativeSchemaPath) > 0 && - !filepath.IsAbs(c.Experimental.PgDelta.DeclarativeSchemaPath) { - c.Experimental.PgDelta.DeclarativeSchemaPath = path.Join(builder.SupabaseDirPath, c.Experimental.PgDelta.DeclarativeSchemaPath) - } - return nil -} - -func (c *config) Validate(fsys fs.FS) error { - if c.ProjectId == "" { - return errors.New("Missing required field in config: project_id") - } else if sanitized := sanitizeProjectId(c.ProjectId); sanitized != c.ProjectId { - fmt.Fprintln(os.Stderr, "WARN: project_id field in config is invalid. Auto-fixing to", sanitized) - c.ProjectId = sanitized - } - // Since remote config is merged to base, we only need to validate the project_id field. - for name, remote := range c.Remotes { - if !refPattern.MatchString(remote.ProjectId) { - return errors.Errorf("Invalid config for remotes.%s.project_id. Must be like: abcdefghijklmnopqrst", name) - } - } - // Validate api config - if c.Api.AutoExposeNewTables != nil && *c.Api.AutoExposeNewTables { - fmt.Fprintln(os.Stderr, "WARN: api.auto_expose_new_tables is deprecated and will be removed on 2026-10-30. Remove the field or set it to false to adopt the new default of revoking Data API privileges on new entities in the public schema.") - } - if c.Api.Enabled { - if c.Api.Port == 0 { - return errors.New("Missing required field in config: api.port") - } - if c.Api.Tls.Enabled { - var err error - if len(c.Api.Tls.CertPath) > 0 { - if len(c.Api.Tls.KeyPath) == 0 { - return errors.New("Missing required field in config: api.tls.key_path") - } - if c.Api.Tls.CertContent, err = fs.ReadFile(fsys, c.Api.Tls.CertPath); err != nil { - return errors.Errorf("failed to read TLS cert: %w", err) - } - } - if len(c.Api.Tls.KeyPath) > 0 { - if len(c.Api.Tls.CertPath) == 0 { - return errors.New("Missing required field in config: api.tls.cert_path") - } - if c.Api.Tls.KeyContent, err = fs.ReadFile(fsys, c.Api.Tls.KeyPath); err != nil { - return errors.Errorf("failed to read TLS key: %w", err) - } - } - } - } - // Validate db config - if c.Db.Port == 0 { - return errors.New("Missing required field in config: db.port") - } - switch c.Db.MajorVersion { - case 0: - return errors.New("Missing required field in config: db.major_version") - case 12: - return errors.New("Postgres version 12.x is unsupported. To use the CLI, either start a new project or follow project migration steps here: https://supabase.com/docs/guides/database#migrating-between-projects.") - case 13, 14: - case 15, 17: - if len(c.Db.OrioleDBVersion) > 0 { - if VersionCompare(c.Db.OrioleDBVersion, "15.1.1.13") > 0 { - c.Db.Image = fmt.Sprintf("supabase/postgres:%s-orioledb", c.Db.OrioleDBVersion) - } else { - c.Db.Image = "supabase/postgres:orioledb-" + c.Db.OrioleDBVersion - } - if err := assertEnvLoaded(c.Experimental.S3Host); err != nil { - return err - } - if err := assertEnvLoaded(c.Experimental.S3Region); err != nil { - return err - } - if err := assertEnvLoaded(c.Experimental.S3AccessKey); err != nil { - return err - } - if err := assertEnvLoaded(c.Experimental.S3SecretKey); err != nil { - return err - } - } - default: - return errors.Errorf("Failed reading config: Invalid %s: %v.", "db.major_version", c.Db.MajorVersion) - } - // Validate storage config - for name := range c.Storage.Buckets { - if err := ValidateBucketName(name); err != nil { - return err - } - } - // Validate studio config - if c.Studio.Enabled { - if c.Studio.Port == 0 { - return errors.New("Missing required field in config: studio.port") - } - if parsed, err := url.Parse(c.Studio.ApiUrl); err != nil { - return errors.Errorf("Invalid config for studio.api_url: %w", err) - } else if parsed.Host == "" || parsed.Host == c.Hostname { - c.Studio.ApiUrl = c.Api.ExternalUrl - } - } - // Validate smtp config - if c.Inbucket.Enabled { - if c.Inbucket.Port == 0 { - return errors.New("Missing required field in config: local_smtp.port") - } - } - // Validate auth config - if c.Auth.Enabled { - if c.Auth.SiteUrl == "" { - return errors.New("Missing required field in config: auth.site_url") - } - if err := assertEnvLoaded(c.Auth.SiteUrl); err != nil { - return err - } - for i, url := range c.Auth.AdditionalRedirectUrls { - if err := assertEnvLoaded(url); err != nil { - return errors.Errorf("Invalid config for auth.additional_redirect_urls[%d]: %v", i, err) - } - } - if c.Auth.Captcha != nil && c.Auth.Captcha.Enabled { - if len(c.Auth.Captcha.Provider) == 0 { - return errors.New("Missing required field in config: auth.captcha.provider") - } - if len(c.Auth.Captcha.Secret.Value) == 0 { - return errors.Errorf("Missing required field in config: auth.captcha.secret") - } - if err := assertEnvLoaded(c.Auth.Captcha.Secret.Value); err != nil { - return err - } - } - if len(c.Auth.SigningKeysPath) > 0 { - if f, err := fsys.Open(c.Auth.SigningKeysPath); err != nil { - return errors.Errorf("failed to read signing keys: %w", err) - } else if c.Auth.SigningKeys, err = fetcher.ParseJSON[[]JWK](f); err != nil { - return errors.Errorf("failed to decode signing keys: %w", err) - } - } - if c.Auth.Passkey != nil && c.Auth.Passkey.Enabled { - if c.Auth.Webauthn == nil { - return errors.New("Missing required config section: auth.webauthn (required when auth.passkey.enabled is true)") - } - if len(c.Auth.Webauthn.RpId) == 0 { - return errors.New("Missing required field in config: auth.webauthn.rp_id") - } - if len(c.Auth.Webauthn.RpOrigins) == 0 { - return errors.New("Missing required field in config: auth.webauthn.rp_origins") - } - if err := assertEnvLoaded(c.Auth.Webauthn.RpId); err != nil { - return errors.Errorf("Invalid config for auth.webauthn.rp_id: %v", err) - } - for i, origin := range c.Auth.Webauthn.RpOrigins { - if err := assertEnvLoaded(origin); err != nil { - return errors.Errorf("Invalid config for auth.webauthn.rp_origins[%d]: %v", i, err) - } - } - } - if err := c.Auth.Hook.validate(); err != nil { - return err - } - if err := c.Auth.MFA.validate(); err != nil { - return err - } - if err := c.Auth.Email.validate(fsys); err != nil { - return err - } - if err := c.Auth.Sms.validate(); err != nil { - return err - } - if err := c.Auth.External.validate(); err != nil { - return err - } - if err := c.Auth.ThirdParty.validate(); err != nil { - return err - } - } - if err := c.Auth.generateAPIKeys(); err != nil { - return err - } - // Validate functions config - for name := range c.Functions { - if err := ValidateFunctionSlug(name); err != nil { - return err - } - } - switch c.EdgeRuntime.DenoVersion { - case 0: - return errors.New("Missing required field in config: edge_runtime.deno_version") - case 1: - c.EdgeRuntime.Image = deno1 - case 2: - break - default: - return errors.Errorf("Failed reading config: Invalid %s: %v.", "edge_runtime.deno_version", c.EdgeRuntime.DenoVersion) - } - // Validate logflare config - if c.Analytics.Enabled { - if c.Analytics.Backend == LogflareBigQuery { - if len(c.Analytics.GcpProjectId) == 0 { - return errors.New("Missing required field in config: analytics.gcp_project_id") - } - if len(c.Analytics.GcpProjectNumber) == 0 { - return errors.New("Missing required field in config: analytics.gcp_project_number") - } - if len(c.Analytics.GcpJwtPath) == 0 { - return errors.New("Path to GCP Service Account Key must be provided in config, relative to config.toml: analytics.gcp_jwt_path") - } - } - } - if err := c.Experimental.validate(); err != nil { - return err - } - return nil -} - -func assertEnvLoaded(s string) error { - if matches := envPattern.FindStringSubmatch(s); len(matches) > 1 { - fmt.Fprintln(os.Stderr, "WARN: environment variable is unset:", matches[1]) - } - return nil -} - -func truncateText(text string, maxLen int) string { - if len(text) > maxLen { - return text[:maxLen] - } - return text -} - -const maxProjectIdLength = 40 - -func sanitizeProjectId(src string) string { - // A valid project ID must only contain alphanumeric and special characters _.- - sanitized := invalidProjectId.ReplaceAllString(src, "_") - // It must also start with an alphanumeric character - sanitized = strings.TrimLeft(sanitized, "_.-") - // Truncate sanitized ID to 40 characters since docker hostnames cannot exceed - // 63 characters, and we need to save space for padding supabase_*_edge_runtime. - return truncateText(sanitized, maxProjectIdLength) -} - -func loadNestedEnv(basePath string) error { - repoDir, err := os.Getwd() - if err != nil { - return errors.Errorf("failed to get repo directory: %w", err) - } - if !filepath.IsAbs(basePath) { - basePath = filepath.Join(repoDir, basePath) - } - env := os.Getenv("SUPABASE_ENV") - for cwd := basePath; cwd != filepath.Dir(repoDir); cwd = filepath.Dir(cwd) { - if err := os.Chdir(cwd); err != nil && !errors.Is(err, os.ErrNotExist) { - return errors.Errorf("failed to change directory: %w", err) - } - if err := loadDefaultEnv(env); err != nil { - return err - } - } - if err := os.Chdir(repoDir); err != nil { - return errors.Errorf("failed to restore directory: %w", err) - } - return nil -} - -func loadDefaultEnv(env string) error { - if env == "" { - env = "development" - } - filenames := []string{".env." + env + ".local"} - if env != "test" { - filenames = append(filenames, ".env.local") - } - filenames = append(filenames, ".env."+env, ".env") - for _, path := range filenames { - if err := loadEnvIfExists(path); err != nil { - return err - } - } - return nil -} - -func loadEnvIfExists(path string) error { - if err := godotenv.Load(path); err != nil && !errors.Is(err, os.ErrNotExist) { - // If DEBUG=1, return the error as is for full debugability - if viper.GetBool("DEBUG") { - return errors.Errorf("failed to load %s: %w", path, err) - } - msg := err.Error() - switch { - case strings.HasPrefix(msg, "unexpected character"): - // Try to extract the character, fallback to generic - start := strings.Index(msg, "unexpected character \"") - if start != -1 { - start += len("unexpected character \"") - end := strings.Index(msg[start:], "\"") - if end != -1 { - char := msg[start : start+end] - return errors.Errorf("failed to parse environment file: %s (unexpected character '%s' in variable name)", path, char) - } - } - return errors.Errorf("failed to parse environment file: %s (unexpected character in variable name)", path) - case strings.HasPrefix(msg, "unterminated quoted value"): - return errors.Errorf("failed to parse environment file: %s (unterminated quoted value)", path) - // If the error message contains newlines, there is a high chance that the actual content of the - // dotenv file is being leaked. In such cases, we return a generic error to avoid unwanted leaks in the logs - case strings.Contains(msg, "\n"): - return errors.Errorf("failed to parse environment file: %s (syntax error)", path) - default: - return errors.Errorf("failed to load %s: %w", path, err) - } - } - return nil -} - -func (e *email) validate(fsys fs.FS) (err error) { - for name, tmpl := range e.Template { - if len(tmpl.ContentPath) == 0 { - if tmpl.Content != nil { - return errors.Errorf("Invalid config for auth.email.template.%s.content: please use content_path instead", name) - } - continue - } - if content, err := fs.ReadFile(fsys, tmpl.ContentPath); err != nil { - return errors.Errorf("Invalid config for auth.email.template.%s.content_path: %w", name, err) - } else { - tmpl.Content = cast.Ptr(string(content)) - } - e.Template[name] = tmpl - } - for name, tmpl := range e.Notification { - if !tmpl.Enabled { - continue - } - if len(tmpl.ContentPath) == 0 { - if tmpl.Content != nil { - return errors.Errorf("Invalid config for auth.email.notification.%s.content: please use content_path instead", name) - } - continue - } - if content, err := fs.ReadFile(fsys, tmpl.ContentPath); err != nil { - return errors.Errorf("Invalid config for auth.email.notification.%s.content_path: %w", name, err) - } else { - tmpl.Content = cast.Ptr(string(content)) - } - e.Notification[name] = tmpl - } - if e.Smtp != nil && e.Smtp.Enabled { - if len(e.Smtp.Host) == 0 { - return errors.New("Missing required field in config: auth.email.smtp.host") - } - if e.Smtp.Port == 0 { - return errors.New("Missing required field in config: auth.email.smtp.port") - } - if len(e.Smtp.User) == 0 { - return errors.New("Missing required field in config: auth.email.smtp.user") - } - if len(e.Smtp.Pass.Value) == 0 { - return errors.New("Missing required field in config: auth.email.smtp.pass") - } - if len(e.Smtp.AdminEmail) == 0 { - return errors.New("Missing required field in config: auth.email.smtp.admin_email") - } - if err := assertEnvLoaded(e.Smtp.Pass.Value); err != nil { - return err - } - } - return nil -} - -func (s *sms) validate() (err error) { - switch { - case s.Twilio.Enabled: - if len(s.Twilio.AccountSid) == 0 { - return errors.New("Missing required field in config: auth.sms.twilio.account_sid") - } - if len(s.Twilio.MessageServiceSid) == 0 { - return errors.New("Missing required field in config: auth.sms.twilio.message_service_sid") - } - if len(s.Twilio.AuthToken.Value) == 0 { - return errors.New("Missing required field in config: auth.sms.twilio.auth_token") - } - if err := assertEnvLoaded(s.Twilio.AuthToken.Value); err != nil { - return err - } - case s.TwilioVerify.Enabled: - if len(s.TwilioVerify.AccountSid) == 0 { - return errors.New("Missing required field in config: auth.sms.twilio_verify.account_sid") - } - if len(s.TwilioVerify.MessageServiceSid) == 0 { - return errors.New("Missing required field in config: auth.sms.twilio_verify.message_service_sid") - } - if len(s.TwilioVerify.AuthToken.Value) == 0 { - return errors.New("Missing required field in config: auth.sms.twilio_verify.auth_token") - } - if err := assertEnvLoaded(s.TwilioVerify.AuthToken.Value); err != nil { - return err - } - case s.Messagebird.Enabled: - if len(s.Messagebird.Originator) == 0 { - return errors.New("Missing required field in config: auth.sms.messagebird.originator") - } - if len(s.Messagebird.AccessKey.Value) == 0 { - return errors.New("Missing required field in config: auth.sms.messagebird.access_key") - } - if err := assertEnvLoaded(s.Messagebird.AccessKey.Value); err != nil { - return err - } - case s.Textlocal.Enabled: - if len(s.Textlocal.Sender) == 0 { - return errors.New("Missing required field in config: auth.sms.textlocal.sender") - } - if len(s.Textlocal.ApiKey.Value) == 0 { - return errors.New("Missing required field in config: auth.sms.textlocal.api_key") - } - if err := assertEnvLoaded(s.Textlocal.ApiKey.Value); err != nil { - return err - } - case s.Vonage.Enabled: - if len(s.Vonage.From) == 0 { - return errors.New("Missing required field in config: auth.sms.vonage.from") - } - if len(s.Vonage.ApiKey) == 0 { - return errors.New("Missing required field in config: auth.sms.vonage.api_key") - } - if len(s.Vonage.ApiSecret.Value) == 0 { - return errors.New("Missing required field in config: auth.sms.vonage.api_secret") - } - if err := assertEnvLoaded(s.Vonage.ApiKey); err != nil { - return err - } - if err := assertEnvLoaded(s.Vonage.ApiSecret.Value); err != nil { - return err - } - case s.EnableSignup: - s.EnableSignup = false - fmt.Fprintln(os.Stderr, "WARN: no SMS provider is enabled. Disabling phone login") - } - return nil -} - -func (e external) validate() (err error) { - for _, ext := range []string{"linkedin", "slack"} { - if e[ext].Enabled { - fmt.Fprintf(os.Stderr, `WARN: disabling deprecated "%[1]s" provider. Please use [auth.external.%[1]s_oidc] instead\n`, ext) - } - delete(e, ext) - } - for ext, provider := range e { - if !provider.Enabled { - continue - } - if provider.ClientId == "" { - return errors.Errorf("Missing required field in config: auth.external.%s.client_id", ext) - } - if !slices.Contains([]string{"apple", "google"}, ext) && len(provider.Secret.Value) == 0 { - return errors.Errorf("Missing required field in config: auth.external.%s.secret", ext) - } - if err := assertEnvLoaded(provider.ClientId); err != nil { - return err - } - if err := assertEnvLoaded(provider.Secret.Value); err != nil { - return err - } - if err := assertEnvLoaded(provider.RedirectUri); err != nil { - return err - } - if err := assertEnvLoaded(provider.Url); err != nil { - return err - } - e[ext] = provider - } - return nil -} - -func (h *hook) validate() error { - if hook := h.MFAVerificationAttempt; hook != nil { - if err := hook.validate("mfa_verification_attempt"); err != nil { - return err - } - } - if hook := h.PasswordVerificationAttempt; hook != nil { - if err := hook.validate("password_verification_attempt"); err != nil { - return err - } - } - if hook := h.CustomAccessToken; hook != nil { - if err := hook.validate("custom_access_token"); err != nil { - return err - } - } - if hook := h.SendSMS; hook != nil { - if err := hook.validate("send_sms"); err != nil { - return err - } - } - if hook := h.SendEmail; hook != nil { - if err := h.SendEmail.validate("send_email"); err != nil { - return err - } - } - if hook := h.BeforeUserCreated; hook != nil { - if err := hook.validate("before_user_created"); err != nil { - return err - } - } - return nil -} - -var hookSecretPattern = regexp.MustCompile(`^v1,whsec_[A-Za-z0-9+/=]{32,88}$`) - -func (h *hookConfig) validate(hookType string) (err error) { - // If not enabled do nothing - if !h.Enabled { - return nil - } - if h.URI == "" { - return errors.Errorf("Missing required field in config: auth.hook.%s.uri", hookType) - } - parsed, err := url.Parse(h.URI) - if err != nil { - return errors.Errorf("failed to parse template url: %w", err) - } - switch strings.ToLower(parsed.Scheme) { - case "http", "https": - if len(h.Secrets.Value) == 0 { - return errors.Errorf("Missing required field in config: auth.hook.%s.secrets", hookType) - } else if err := assertEnvLoaded(h.Secrets.Value); err != nil { - return err - } - for secret := range strings.SplitSeq(h.Secrets.Value, "|") { - if !hookSecretPattern.MatchString(secret) { - return errors.Errorf(`Invalid hook config: auth.hook.%s.secrets must be formatted as "v1,whsec_" with a minimum length of 32 characters.`, hookType) - } - } - case "pg-functions": - if len(h.Secrets.Value) > 0 { - return errors.Errorf("Invalid hook config: auth.hook.%s.secrets is unsupported for pg-functions URI", hookType) - } - default: - return errors.Errorf("Invalid hook config: auth.hook.%s.uri should be a HTTP, HTTPS, or pg-functions URI", hookType) - } - return nil -} - -func (m *mfa) validate() error { - if m.TOTP.EnrollEnabled && !m.TOTP.VerifyEnabled { - return errors.Errorf("Invalid MFA config: auth.mfa.totp.enroll_enabled requires verify_enabled") - } - if m.Phone.EnrollEnabled && !m.Phone.VerifyEnabled { - return errors.Errorf("Invalid MFA config: auth.mfa.phone.enroll_enabled requires verify_enabled") - } - if m.WebAuthn.EnrollEnabled && !m.WebAuthn.VerifyEnabled { - return errors.Errorf("Invalid MFA config: auth.mfa.web_authn.enroll_enabled requires verify_enabled") - } - return nil -} - -// TODO: use field tag validator instead -var funcSlugPattern = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_-]*$`) - -func ValidateFunctionSlug(slug string) error { - if !funcSlugPattern.MatchString(slug) { - return errors.Errorf(`Invalid Function name: %s. Must start with at least one letter, and only include alphanumeric characters, underscores, and hyphens. (%s)`, slug, funcSlugPattern.String()) - } - return nil -} - -// Ref: https://github.com/supabase/storage/blob/master/src/storage/limits.ts#L59 -var bucketNamePattern = regexp.MustCompile(`^(\w|!|-|\.|\*|'|\(|\)| |&|\$|@|=|;|:|\+|,|\?)*$`) - -func ValidateBucketName(name string) error { - if !bucketNamePattern.MatchString(name) { - return errors.Errorf("Invalid Bucket name: %s. Only lowercase letters, numbers, dots, hyphens, and spaces are allowed. (%s)", name, bucketNamePattern.String()) - } - return nil -} - -func (f *tpaFirebase) issuerURL() string { - return fmt.Sprintf("https://securetoken.google.com/%s", f.ProjectID) -} - -func (f *tpaFirebase) validate() error { - if f.ProjectID == "" { - return errors.New("Invalid config: auth.third_party.firebase is enabled but without a project_id.") - } - - return nil -} - -func (a *tpaAuth0) issuerURL() string { - if a.TenantRegion != "" { - return fmt.Sprintf("https://%s.%s.auth0.com", a.Tenant, a.TenantRegion) - } - - return fmt.Sprintf("https://%s.auth0.com", a.Tenant) -} - -func (a *tpaAuth0) validate() error { - if a.Tenant == "" { - return errors.New("Invalid config: auth.third_party.auth0 is enabled but without a tenant.") - } - - return nil -} - -func (c *tpaCognito) issuerURL() string { - return fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s", c.UserPoolRegion, c.UserPoolID) -} - -func (c *tpaCognito) validate() (err error) { - if c.UserPoolID == "" { - return errors.New("Invalid config: auth.third_party.cognito is enabled but without a user_pool_id.") - } else if err := assertEnvLoaded(c.UserPoolID); err != nil { - return err - } - - if c.UserPoolRegion == "" { - return errors.New("Invalid config: auth.third_party.cognito is enabled but without a user_pool_region.") - } else if err := assertEnvLoaded(c.UserPoolRegion); err != nil { - return err - } - - return nil -} - -var clerkDomainPattern = regexp.MustCompile("^(clerk([.][a-z0-9-]+){2,}|([a-z0-9-]+[.])+clerk[.]accounts[.]dev)$") - -func (c *tpaClerk) issuerURL() string { - return fmt.Sprintf("https://%s", c.Domain) -} - -func (c *tpaClerk) validate() (err error) { - if c.Domain == "" { - return errors.New("Invalid config: auth.third_party.clerk is enabled but without a domain.") - } else if err := assertEnvLoaded(c.Domain); err != nil { - return err - } - - if !clerkDomainPattern.MatchString(c.Domain) { - return errors.New("Invalid config: auth.third_party.clerk has invalid domain, it usually is like clerk.example.com or example.clerk.accounts.dev. Check https://clerk.com/setup/supabase on how to find the correct value.") - } - - return nil -} - -func (w *tpaWorkOs) validate() error { - if w.IssuerUrl == "" { - return errors.New("Invalid config: auth.third_party.workos is enabled but without a issuer_url.") - } - return nil -} - -func (w *tpaWorkOs) issuerURL() string { - return w.IssuerUrl -} - -func (tpa *thirdParty) validate() error { - enabled := 0 - - if tpa.Firebase.Enabled { - enabled += 1 - - if err := tpa.Firebase.validate(); err != nil { - return err - } - } - - if tpa.Auth0.Enabled { - enabled += 1 - - if err := tpa.Auth0.validate(); err != nil { - return err - } - } - - if tpa.Cognito.Enabled { - enabled += 1 - - if err := tpa.Cognito.validate(); err != nil { - return err - } - } - - if tpa.Clerk.Enabled { - enabled += 1 - - if err := tpa.Clerk.validate(); err != nil { - return err - } - } - - if tpa.WorkOs.Enabled { - enabled += 1 - - if err := tpa.WorkOs.validate(); err != nil { - return err - } - } - - if enabled > 1 { - return errors.New("Invalid config: Only one third_party provider allowed to be enabled at a time.") - } - - return nil -} - -func (tpa *thirdParty) IssuerURL() string { - if tpa.Firebase.Enabled { - return tpa.Firebase.issuerURL() - } - - if tpa.Auth0.Enabled { - return tpa.Auth0.issuerURL() - } - - if tpa.Cognito.Enabled { - return tpa.Cognito.issuerURL() - } - - if tpa.Clerk.Enabled { - return tpa.Clerk.issuerURL() - } - - if tpa.WorkOs.Enabled { - return tpa.WorkOs.issuerURL() - } - - return "" -} - -type ( - remoteJWKS struct { - Keys []json.RawMessage `json:"keys"` - } - - oidcConfiguration struct { - JWKSURI string `json:"jwks_uri"` - } - - secretJWK struct { - KeyType string `json:"kty"` - KeyBase64URL string `json:"k"` - } -) - -// ResolveJWKS creates the JWKS from the JWT secret and Third-Party Auth -// configs by resolving the JWKS via the OIDC discovery URL. -// It always returns a JWKS string, except when there's an error fetching. -func (a *auth) ResolveJWKS(ctx context.Context) (string, error) { - var jwks remoteJWKS - - if issuerURL := a.ThirdParty.IssuerURL(); issuerURL != "" { - discoveryURL := issuerURL + "/.well-known/openid-configuration" - - t := &http.Client{Timeout: 10 * time.Second} - client := fetcher.NewFetcher( - discoveryURL, - fetcher.WithHTTPClient(t), - fetcher.WithExpectedStatus(http.StatusOK), - ) - - resp, err := client.Send(ctx, http.MethodGet, "", nil) - if err != nil { - return "", err - } - - oidcConfig, err := fetcher.ParseJSON[oidcConfiguration](resp.Body) - if err != nil { - return "", err - } - - if oidcConfig.JWKSURI == "" { - return "", fmt.Errorf("auth.third_party: OIDC configuration at URL %q does not expose a jwks_uri property", discoveryURL) - } - - client = fetcher.NewFetcher( - oidcConfig.JWKSURI, - fetcher.WithHTTPClient(t), - fetcher.WithExpectedStatus(http.StatusOK), - ) - - resp, err = client.Send(ctx, http.MethodGet, "", nil) - if err != nil { - return "", err - } - - rJWKS, err := fetcher.ParseJSON[remoteJWKS](resp.Body) - if err != nil { - return "", err - } - - if len(rJWKS.Keys) == 0 { - return "", fmt.Errorf("auth.third_party: JWKS at URL %q as discovered from %q does not contain any JWK keys", oidcConfig.JWKSURI, discoveryURL) - } - - jwks.Keys = append(jwks.Keys, rJWKS.Keys...) - } - - // Convert each signing key to public-only version - for _, key := range a.SigningKeys { - publicKeyEncoded, err := json.Marshal(key.ToPublicJWK()) - if err != nil { - return "", errors.Errorf("failed to marshal public key: %w", err) - } - jwks.Keys = append(jwks.Keys, json.RawMessage(publicKeyEncoded)) - } - // Fallback to JWT_SECRET for backward compatibility - if len(a.SigningKeysPath) == 0 { - jwtSecret := secretJWK{ - KeyType: "oct", - KeyBase64URL: base64.RawURLEncoding.EncodeToString([]byte(a.JwtSecret.Value)), - } - - secretJWKEncoded, err := json.Marshal(&jwtSecret) - if err != nil { - return "", errors.Errorf("failed to marshal secret jwk: %w", err) - } - - jwks.Keys = append(jwks.Keys, json.RawMessage(secretJWKEncoded)) - } - - jwksEncoded, err := json.Marshal(jwks) - if err != nil { - return "", errors.Errorf("failed to marshal jwks keys: %w", err) - } - - return string(jwksEncoded), nil -} - -func (c *baseConfig) GetServiceImages() []string { - return []string{ - c.Db.Image, - c.Auth.Image, - c.Api.Image, - c.Realtime.Image, - c.Storage.Image, - c.EdgeRuntime.Image, - c.Studio.Image, - c.Studio.PgmetaImage, - c.Analytics.Image, - c.Db.Pooler.Image, - } -} - -// Retrieve the final base config to use taking into account the remotes override -// Pre: config must be loaded after setting config.ProjectID = "ref" -func (c *config) GetRemoteByProjectRef(projectRef string) (baseConfig, error) { - base := c.Clone() - for _, remote := range c.Remotes { - if remote.ProjectId == projectRef { - base.ProjectId = projectRef - return base, nil - } - } - return base, errors.Errorf("no remote found for project_id: %s", projectRef) -} - -func ToTomlBytes(config any) ([]byte, error) { - var buf bytes.Buffer - enc := toml.NewEncoder(&buf) - enc.Indent = "" - if err := enc.Encode(config); err != nil { - return nil, errors.Errorf("failed to marshal toml config: %w", err) - } - return buf.Bytes(), nil -} - -func (e *experimental) validate() error { - if e.Webhooks != nil && !e.Webhooks.Enabled { - return errors.Errorf("Webhooks cannot be deactivated. [experimental.webhooks] enabled can either be true or left undefined") - } - if e.PgDelta != nil && len(e.PgDelta.FormatOptions) > 0 && !json.Valid([]byte(e.PgDelta.FormatOptions)) { - return errors.Errorf("Invalid config for experimental.pgdelta.format_options: must be valid JSON") - } - return nil -} diff --git a/apps/cli-go/pkg/config/config_test.go b/apps/cli-go/pkg/config/config_test.go deleted file mode 100644 index 0f51bf5153..0000000000 --- a/apps/cli-go/pkg/config/config_test.go +++ /dev/null @@ -1,1265 +0,0 @@ -package config - -import ( - "bytes" - _ "embed" - "fmt" - "io" - "os" - "path" - "strings" - "testing" - fs "testing/fstest" - - "github.com/BurntSushi/toml" - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -//go:embed testdata/config.toml -var testInitConfigEmbed []byte - -func TestConfigParsing(t *testing.T) { - t.Run("classic config file", func(t *testing.T) { - config := NewConfig() - // Run test - var buf bytes.Buffer - require.NoError(t, config.Eject(&buf)) - file := fs.MapFile{Data: buf.Bytes()} - fsys := fs.MapFS{"config.toml": &file} - // Check error - assert.NoError(t, config.Load("config.toml", fsys)) - }) - - t.Run("optional config file", func(t *testing.T) { - config := NewConfig() - // Run test - err := config.Load("", fs.MapFS{}) - // Check error - assert.NoError(t, err) - assert.True(t, config.Storage.VectorBuckets.Enabled) - }) - - t.Run("auth external url defaults from api external url", func(t *testing.T) { - config := NewConfig() - require.NoError(t, config.Load("", fs.MapFS{})) - - assert.Equal(t, strings.TrimRight(config.Api.ExternalUrl, "/")+"/auth/v1", config.Auth.ExternalUrl) - assert.Equal(t, config.Auth.ExternalUrl, config.Auth.JwtIssuer) - }) - - t.Run("auth external url and jwt issuer preserve explicit overrides", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "config.toml": &fs.MapFile{Data: []byte(` -[api] -external_url = "https://api.example.com/" - -[auth] -site_url = "https://app.example.com" -external_url = "https://auth.example.com/custom/" -jwt_issuer = "https://issuer.example.com/custom/" -`)}, - } - - require.NoError(t, config.Load("config.toml", fsys)) - assert.Equal(t, "https://auth.example.com/custom/", config.Auth.ExternalUrl) - assert.Equal(t, "https://issuer.example.com/custom/", config.Auth.JwtIssuer) - }) - - t.Run("config file with environment variables", func(t *testing.T) { - config := NewConfig() - // Setup in-memory fs - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: testInitConfigEmbed}, - "supabase/templates/invite.html": &fs.MapFile{}, - "supabase/templates/password_changed_notification.html": &fs.MapFile{}, - "supabase/signing_keys.json": &fs.MapFile{Data: []byte("[]")}, - "certs/my-cert.pem": &fs.MapFile{}, - "certs/my-key.pem": &fs.MapFile{}, - } - // Run test - t.Setenv("TWILIO_AUTH_TOKEN", "token") - t.Setenv("AZURE_CLIENT_ID", "hello") - t.Setenv("AZURE_SECRET", "this is cool") - t.Setenv("AUTH_SEND_SMS_SECRETS", "v1,whsec_aWxpa2VzdXBhYmFzZXZlcnltdWNoYW5kaWhvcGV5b3Vkb3Rvbw==") - t.Setenv("SENDGRID_API_KEY", "sendgrid") - t.Setenv("AUTH_CALLBACK_URL", "http://localhost:3000/auth/callback") - assert.NoError(t, config.Load("", fsys)) - // Check error - assert.Equal(t, "hello", config.Auth.External["azure"].ClientId) - assert.Equal(t, "this is cool", config.Auth.External["azure"].Secret.Value) - assert.Equal(t, []string{ - "https://127.0.0.1:3000", - "http://localhost:3000/auth/callback", - }, config.Auth.AdditionalRedirectUrls) - }) - - t.Run("config file with environment variables fails when unset", func(t *testing.T) { - config := NewConfig() - // Setup in-memory fs - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: testInitConfigEmbed}, - } - // Run test - assert.Error(t, config.Load("", fsys)) - }) - t.Run("config file with passkey and webauthn settings", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[auth] -enabled = true -site_url = "http://127.0.0.1:3000" -[auth.passkey] -enabled = true -[auth.webauthn] -rp_display_name = "Supabase CLI" -rp_id = "localhost" -rp_origins = ["http://127.0.0.1:3000", "https://localhost:3000"] -`)}, - } - // Run test - assert.NoError(t, config.Load("", fsys)) - // Check result - if assert.NotNil(t, config.Auth.Passkey) { - assert.True(t, config.Auth.Passkey.Enabled) - } - if assert.NotNil(t, config.Auth.Webauthn) { - assert.Equal(t, "Supabase CLI", config.Auth.Webauthn.RpDisplayName) - assert.Equal(t, "localhost", config.Auth.Webauthn.RpId) - assert.Equal(t, []string{ - "http://127.0.0.1:3000", - "https://localhost:3000", - }, config.Auth.Webauthn.RpOrigins) - } - }) - - t.Run("webauthn section without passkey loads successfully", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[auth] -enabled = true -site_url = "http://127.0.0.1:3000" -[auth.webauthn] -rp_display_name = "Supabase CLI" -rp_id = "localhost" -rp_origins = ["http://127.0.0.1:3000"] -`)}, - } - // Run test - assert.NoError(t, config.Load("", fsys)) - // Check result - assert.Nil(t, config.Auth.Passkey) - if assert.NotNil(t, config.Auth.Webauthn) { - assert.Equal(t, "localhost", config.Auth.Webauthn.RpId) - } - }) - - t.Run("passkey enabled requires webauthn section", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[auth] -enabled = true -site_url = "http://127.0.0.1:3000" -[auth.passkey] -enabled = true -`)}, - } - // Run test - err := config.Load("", fsys) - // Check result - assert.ErrorContains(t, err, "Missing required config section: auth.webauthn") - }) - - t.Run("passkey enabled requires rp_id", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[auth] -enabled = true -site_url = "http://127.0.0.1:3000" -[auth.passkey] -enabled = true -[auth.webauthn] -rp_origins = ["http://127.0.0.1:3000"] -`)}, - } - // Run test - err := config.Load("", fsys) - // Check result - assert.ErrorContains(t, err, "Missing required field in config: auth.webauthn.rp_id") - }) - - t.Run("passkey enabled requires rp_origins", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[auth] -enabled = true -site_url = "http://127.0.0.1:3000" -[auth.passkey] -enabled = true -[auth.webauthn] -rp_id = "localhost" -`)}, - } - // Run test - err := config.Load("", fsys) - // Check result - assert.ErrorContains(t, err, "Missing required field in config: auth.webauthn.rp_origins") - }) - - t.Run("parses experimental pgdelta config", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[experimental.pgdelta] -enabled = true -declarative_schema_path = "./db/decl" -format_options = "{\"keywordCase\":\"upper\",\"indent\":2}" -`)}, - } - - require.NoError(t, config.Load("", fsys)) - require.NotNil(t, config.Experimental.PgDelta) - assert.True(t, config.Experimental.PgDelta.Enabled) - assert.Equal(t, path.Join("supabase", "db", "decl"), config.Experimental.PgDelta.DeclarativeSchemaPath) - assert.Equal(t, `{"keywordCase":"upper","indent":2}`, config.Experimental.PgDelta.FormatOptions) - }) - - t.Run("rejects invalid experimental pgdelta format options", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[experimental.pgdelta] -format_options = "not-json" -`)}, - } - - err := config.Load("", fsys) - assert.ErrorContains(t, err, "experimental.pgdelta.format_options") - }) - - t.Run("init scaffold opts into pgdelta", func(t *testing.T) { - config := NewConfig() - // supabase init renders the scaffold with the pg-delta opt-in flag set - config.Experimental.PgDeltaInitEnabled = true - var buf bytes.Buffer - require.NoError(t, config.Eject(&buf)) - fsys := fs.MapFS{"supabase/config.toml": &fs.MapFile{Data: buf.Bytes()}} - // Reloading the generated config resolves to pg-delta - require.NoError(t, config.Load("", fsys)) - require.NotNil(t, config.Experimental.PgDelta) - assert.True(t, config.Experimental.PgDelta.Enabled) - }) - - t.Run("absent pgdelta section falls back to migra", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[experimental] -orioledb_version = "" -`)}, - } - - // The default ejected by mergeDefaultValues keeps pg-delta disabled, so a config - // without the section resolves to migra (PgDelta is non-nil only for version pinning). - require.NoError(t, config.Load("", fsys)) - require.NotNil(t, config.Experimental.PgDelta) - assert.False(t, config.Experimental.PgDelta.Enabled) - }) - - t.Run("explicit enabled false restores migra", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[experimental.pgdelta] -enabled = false -`)}, - } - - require.NoError(t, config.Load("", fsys)) - require.NotNil(t, config.Experimental.PgDelta) - assert.False(t, config.Experimental.PgDelta.Enabled) - }) - - // [compute] is owned by the TS CLI, but the published JSON schema advertises it, - // so a user can hand-write it today. Every Go-delegated path goes through - // config.Load, and UnmarshalExact rejects keys baseConfig does not model — so the - // section has to at least parse here, in both base and remote position. - t.Run("accepts the TS-owned compute section", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -project_id = "test" - -[compute.api] -runtime = "node" - -[remotes.prod] -project_id = "bvikqvbczudanvggcord" - -[remotes.prod.compute.api] -instances = 3 -`)}, - } - - assert.NoError(t, config.Load("", fsys)) - }) - - for _, tt := range []struct { - name string - configData string - projectID string - want bool - }{ - { - name: "base true", - configData: "[experimental]\nstack = true\n", - want: true, - }, - { - name: "base false", - configData: "[experimental]\nstack = false\n", - want: false, - }, - { - name: "remote true", - configData: "[remotes.prod]\nproject_id = \"abcdefghijklmnopqrst\"\n[remotes.prod.experimental]\nstack = true\n", - projectID: "abcdefghijklmnopqrst", - want: true, - }, - { - name: "remote false", - configData: "[remotes.prod]\nproject_id = \"abcdefghijklmnopqrst\"\n[remotes.prod.experimental]\nstack = false\n", - projectID: "abcdefghijklmnopqrst", - want: false, - }, - } { - t.Run("accepts experimental stack "+tt.name, func(t *testing.T) { - t.Setenv("SUPABASE_EXPERIMENTAL_STACK", "") - config := NewConfig() - config.ProjectId = tt.projectID - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(tt.configData)}, - } - - require.NoError(t, config.Load("", fsys)) - assert.Equal(t, tt.want, config.Experimental.Stack) - }) - } - - for _, tt := range []struct { - name string - configData string - projectID string - want bool - }{ - { - name: "base true", - configData: "[experimental]\ncompute = true\n", - want: true, - }, - { - name: "base false", - configData: "[experimental]\ncompute = false\n", - want: false, - }, - { - name: "remote true", - configData: "[remotes.prod]\nproject_id = \"abcdefghijklmnopqrst\"\n[remotes.prod.experimental]\ncompute = true\n", - projectID: "abcdefghijklmnopqrst", - want: true, - }, - { - name: "remote false", - configData: "[remotes.prod]\nproject_id = \"abcdefghijklmnopqrst\"\n[remotes.prod.experimental]\ncompute = false\n", - projectID: "abcdefghijklmnopqrst", - want: false, - }, - } { - t.Run("accepts experimental compute "+tt.name, func(t *testing.T) { - t.Setenv("SUPABASE_EXPERIMENTAL_COMPUTE", "") - config := NewConfig() - config.ProjectId = tt.projectID - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(tt.configData)}, - } - - require.NoError(t, config.Load("", fsys)) - assert.Equal(t, tt.want, config.Experimental.Compute) - }) - } - - t.Run("does not emit experimental stack or compute", func(t *testing.T) { - config := NewConfig() - config.Experimental.Stack = true - config.Experimental.Compute = true - - encodedToml, err := ToTomlBytes(config.Experimental) - require.NoError(t, err) - var encoded map[string]any - _, err = toml.Decode(string(encodedToml), &encoded) - require.NoError(t, err) - assert.NotContains(t, encoded, "stack") - assert.NotContains(t, encoded, "compute") - - var buf bytes.Buffer - require.NoError(t, config.Eject(&buf)) - var rendered map[string]any - _, err = toml.Decode(buf.String(), &rendered) - require.NoError(t, err) - experimental, ok := rendered["experimental"].(map[string]any) - if assert.True(t, ok) { - assert.NotContains(t, experimental, "stack") - assert.NotContains(t, experimental, "compute") - } - }) -} - -func TestRemoteOverride(t *testing.T) { - t.Run("load staging override", func(t *testing.T) { - config := NewConfig() - config.ProjectId = "bvikqvbczudanvggcord" - // Setup in-memory fs - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: testInitConfigEmbed}, - "supabase/templates/invite.html": &fs.MapFile{}, - "supabase/templates/password_changed_notification.html": &fs.MapFile{}, - "certs/my-cert.pem": &fs.MapFile{}, - "certs/my-key.pem": &fs.MapFile{}, - "supabase/signing_keys.json": &fs.MapFile{Data: []byte("[]")}, - } - // Run test - t.Setenv("SUPABASE_AUTH_SITE_URL", "http://preview.com") - t.Setenv("AUTH_SEND_SMS_SECRETS", "v1,whsec_aWxpa2VzdXBhYmFzZXZlcnltdWNoYW5kaWhvcGV5b3Vkb3Rvbw==") - assert.NoError(t, config.Load("", fsys)) - // Check error - assert.True(t, config.Db.Seed.Enabled) - assert.Equal(t, "http://preview.com", config.Auth.SiteUrl) - assert.Equal(t, []string{"image/png"}, config.Storage.Buckets["images"].AllowedMimeTypes) - }) - - t.Run("load production override", func(t *testing.T) { - config := NewConfig() - config.ProjectId = "vpefcjyosynxeiebfscx" - // Setup in-memory fs - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: testInitConfigEmbed}, - "supabase/templates/invite.html": &fs.MapFile{}, - "supabase/templates/password_changed_notification.html": &fs.MapFile{}, - "certs/my-cert.pem": &fs.MapFile{}, - "certs/my-key.pem": &fs.MapFile{}, - "supabase/signing_keys.json": &fs.MapFile{Data: []byte("[]")}, - } - // Run test - t.Setenv("SUPABASE_AUTH_SITE_URL", "http://preview.com") - t.Setenv("AUTH_SEND_SMS_SECRETS", "v1,whsec_aWxpa2VzdXBhYmFzZXZlcnltdWNoYW5kaWhvcGV5b3Vkb3Rvbw==") - assert.NoError(t, config.Load("", fsys)) - // Check error - assert.False(t, config.Db.Seed.Enabled) - assert.Equal(t, "http://feature-auth-branch.com/", config.Auth.SiteUrl) - assert.Equal(t, false, config.Auth.External["azure"].Enabled) - assert.Equal(t, "nope", config.Auth.External["azure"].ClientId) - }) - - t.Run("config file with remotes", func(t *testing.T) { - config := NewConfig() - // Setup in-memory fs - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: testInitConfigEmbed}, - "supabase/templates/invite.html": &fs.MapFile{}, - "supabase/templates/password_changed_notification.html": &fs.MapFile{}, - "certs/my-cert.pem": &fs.MapFile{}, - "certs/my-key.pem": &fs.MapFile{}, - "supabase/signing_keys.json": &fs.MapFile{Data: []byte("[]")}, - } - // Run test - t.Setenv("TWILIO_AUTH_TOKEN", "token") - t.Setenv("AZURE_CLIENT_ID", "hello") - t.Setenv("AZURE_SECRET", "this is cool") - t.Setenv("AUTH_SEND_SMS_SECRETS", "v1,whsec_aWxpa2VzdXBhYmFzZXZlcnltdWNoYW5kaWhvcGV5b3Vkb3Rvbw==") - t.Setenv("SENDGRID_API_KEY", "sendgrid") - t.Setenv("AUTH_CALLBACK_URL", "http://localhost:3000/auth/callback") - assert.NoError(t, config.Load("", fsys)) - // Check the default value in the config - assert.Equal(t, "http://127.0.0.1:3000", config.Auth.SiteUrl) - assert.Equal(t, true, config.Auth.EnableSignup) - assert.Equal(t, true, config.Auth.External["azure"].Enabled) - assert.Equal(t, []string{"image/png", "image/jpeg"}, config.Storage.Buckets["images"].AllowedMimeTypes) - // Check the values for remotes override - production, ok := config.Remotes["production"] - assert.True(t, ok) - staging, ok := config.Remotes["staging"] - assert.True(t, ok) - // Check the values for production override - assert.Equal(t, "vpefcjyosynxeiebfscx", production.ProjectId) - assert.Equal(t, "http://feature-auth-branch.com/", production.Auth.SiteUrl) - assert.Equal(t, false, production.Auth.EnableSignup) - assert.Equal(t, false, production.Auth.External["azure"].Enabled) - assert.Equal(t, "nope", production.Auth.External["azure"].ClientId) - // Check seed should be disabled by default for remote configs - assert.Equal(t, false, production.Db.Seed.Enabled) - // Check the values for the staging override - assert.Equal(t, "bvikqvbczudanvggcord", staging.ProjectId) - assert.Equal(t, []string{"image/png"}, staging.Storage.Buckets["images"].AllowedMimeTypes) - assert.Equal(t, true, staging.Db.Seed.Enabled) - }) -} - -func TestFileSizeLimitConfigParsing(t *testing.T) { - t.Run("test file size limit parsing number", func(t *testing.T) { - var testConfig config - _, err := toml.Decode(` - [storage] - file_size_limit = 5000000 - `, &testConfig) - if assert.NoError(t, err) { - assert.Equal(t, sizeInBytes(5000000), testConfig.Storage.FileSizeLimit) - } - }) - - t.Run("test file size limit parsing bytes unit", func(t *testing.T) { - var testConfig config - _, err := toml.Decode(` - [storage] - file_size_limit = "5MB" - `, &testConfig) - if assert.NoError(t, err) { - assert.Equal(t, sizeInBytes(5242880), testConfig.Storage.FileSizeLimit) - } - }) - - t.Run("test file size limit parsing binary bytes unit", func(t *testing.T) { - var testConfig config - _, err := toml.Decode(` - [storage] - file_size_limit = "5MiB" - `, &testConfig) - if assert.NoError(t, err) { - assert.Equal(t, sizeInBytes(5242880), testConfig.Storage.FileSizeLimit) - } - }) - - t.Run("test file size limit parsing string number", func(t *testing.T) { - var testConfig config - _, err := toml.Decode(` - [storage] - file_size_limit = "5000000" - `, &testConfig) - if assert.NoError(t, err) { - assert.Equal(t, sizeInBytes(5000000), testConfig.Storage.FileSizeLimit) - } - }) - - t.Run("test file size limit parsing bad datatype", func(t *testing.T) { - var testConfig config - _, err := toml.Decode(` - [storage] - file_size_limit = [] - `, &testConfig) - assert.Error(t, err) - assert.Equal(t, sizeInBytes(0), testConfig.Storage.FileSizeLimit) - }) - - t.Run("test file size limit parsing bad string data", func(t *testing.T) { - var testConfig config - _, err := toml.Decode(` - [storage] - file_size_limit = "foobar" - `, &testConfig) - assert.Error(t, err) - assert.Equal(t, sizeInBytes(0), testConfig.Storage.FileSizeLimit) - }) -} - -func TestSanitizeProjectI(t *testing.T) { - // Preserves valid consecutive characters - assert.Equal(t, "abc", sanitizeProjectId("abc")) - assert.Equal(t, "a..b_c", sanitizeProjectId("a..b_c")) - // Removes leading special characters - assert.Equal(t, "abc", sanitizeProjectId("_abc")) - assert.Equal(t, "abc", sanitizeProjectId("_@abc")) - // Replaces consecutive invalid characters with a single _ - assert.Equal(t, "a_bc-", sanitizeProjectId("a@@bc-")) - // Truncates to less than 40 characters - sanitized := strings.Repeat("a", maxProjectIdLength) - assert.Equal(t, sanitized, sanitizeProjectId(sanitized+"bb")) -} - -const ( - defaultAnonKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0" - defaultServiceRoleKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU" -) - -func TestSigningJWT(t *testing.T) { - t.Run("signs default anon key", func(t *testing.T) { - anonToken := CustomClaims{Role: "anon"}.NewToken() - signed, err := anonToken.SignedString([]byte(defaultJwtSecret)) - assert.NoError(t, err) - assert.Equal(t, defaultAnonKey, signed) - }) - - t.Run("signs default service_role key", func(t *testing.T) { - serviceToken := CustomClaims{Role: "service_role"}.NewToken() - signed, err := serviceToken.SignedString([]byte(defaultJwtSecret)) - assert.NoError(t, err) - assert.Equal(t, defaultServiceRoleKey, signed) - }) -} - -func TestValidateHookURI(t *testing.T) { - tests := []struct { - hookConfig - name string - errorMsg string - }{ - { - name: "valid http URL", - hookConfig: hookConfig{ - Enabled: true, - URI: "http://example.com", - Secrets: Secret{Value: "v1,whsec_aWxpa2VzdXBhYmFzZXZlcnltdWNoYW5kaWhvcGV5b3Vkb3Rvbw=="}, - }, - }, - { - name: "valid https URL", - hookConfig: hookConfig{ - Enabled: true, - URI: "https://example.com", - Secrets: Secret{Value: "v1,whsec_aWxpa2VzdXBhYmFzZXZlcnltdWNoYW5kaWhvcGV5b3Vkb3Rvbw=="}, - }, - }, - { - name: "valid pg-functions URI", - hookConfig: hookConfig{ - Enabled: true, - URI: "pg-functions://functionName", - }, - }, - { - name: "invalid URI with unsupported scheme", - hookConfig: hookConfig{ - Enabled: true, - URI: "ftp://example.com", - Secrets: Secret{Value: "v1,whsec_aWxpa2VzdXBhYmFzZXZlcnltdWNoYW5kaWhvcGV5b3Vkb3Rvbw=="}, - }, - errorMsg: "Invalid hook config: auth.hook.invalid URI with unsupported scheme.uri should be a HTTP, HTTPS, or pg-functions URI", - }, - { - name: "invalid URI with parsing error", - hookConfig: hookConfig{ - Enabled: true, - URI: "http://a b.com", - Secrets: Secret{Value: "v1,whsec_aWxpa2VzdXBhYmFzZXZlcnltdWNoYW5kaWhvcGV5b3Vkb3Rvbw=="}, - }, - errorMsg: "failed to parse template url: parse \"http://a b.com\": invalid character \" \" in host name", - }, - { - name: "valid http URL with missing secrets", - hookConfig: hookConfig{ - Enabled: true, - URI: "http://example.com", - }, - errorMsg: "Missing required field in config: auth.hook.valid http URL with missing secrets.secrets", - }, - { - name: "valid pg-functions URI with unsupported secrets", - hookConfig: hookConfig{ - Enabled: true, - URI: "pg-functions://functionName", - Secrets: Secret{Value: "test-secret"}, - }, - errorMsg: "Invalid hook config: auth.hook.valid pg-functions URI with unsupported secrets.secrets is unsupported for pg-functions URI", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := tt.validate(tt.name) - if len(tt.errorMsg) > 0 { - assert.Error(t, err, "Expected an error for %v", tt.name) - assert.EqualError(t, err, tt.errorMsg, "Expected error message does not match for %v", tt.name) - } else { - assert.NoError(t, err, "Expected no error for %v", tt.name) - } - }) - } -} - -func TestGlobFiles(t *testing.T) { - t.Run("returns seed files matching patterns", func(t *testing.T) { - // Setup in-memory fs - fsys := fs.MapFS{ - "supabase/seeds/seed1.sql": &fs.MapFile{Data: []byte("INSERT INTO table1 VALUES (1);")}, - "supabase/seeds/seed2.sql": &fs.MapFile{Data: []byte("INSERT INTO table2 VALUES (2);")}, - "supabase/seeds/seed3.sql": &fs.MapFile{Data: []byte("INSERT INTO table2 VALUES (2);")}, - "supabase/seeds/another.sql": &fs.MapFile{Data: []byte("INSERT INTO table2 VALUES (2);")}, - "supabase/seeds/ignore.sql": &fs.MapFile{Data: []byte("INSERT INTO table3 VALUES (3);")}, - } - // Mock config patterns - g := Glob{ - "supabase/seeds/seed[12].sql", - "supabase/seeds/ano*.sql", - } - // Run test - files, err := g.Files(fsys) - // Check error - assert.NoError(t, err) - // Validate files - assert.ElementsMatch(t, []string{ - "supabase/seeds/seed1.sql", - "supabase/seeds/seed2.sql", - "supabase/seeds/another.sql", - }, files) - }) - - t.Run("returns seed files matching patterns skip duplicates", func(t *testing.T) { - // Setup in-memory fs - fsys := fs.MapFS{ - "supabase/seeds/seed1.sql": &fs.MapFile{Data: []byte("INSERT INTO table1 VALUES (1);")}, - "supabase/seeds/seed2.sql": &fs.MapFile{Data: []byte("INSERT INTO table2 VALUES (2);")}, - "supabase/seeds/seed3.sql": &fs.MapFile{Data: []byte("INSERT INTO table2 VALUES (2);")}, - "supabase/seeds/another.sql": &fs.MapFile{Data: []byte("INSERT INTO table2 VALUES (2);")}, - "supabase/seeds/ignore.sql": &fs.MapFile{Data: []byte("INSERT INTO table3 VALUES (3);")}, - } - // Mock config patterns - g := Glob{ - "supabase/seeds/seed[12].sql", - "supabase/seeds/ano*.sql", - "supabase/seeds/seed*.sql", - } - // Run test - files, err := g.Files(fsys) - // Check error - assert.NoError(t, err) - // Validate files - assert.ElementsMatch(t, []string{ - "supabase/seeds/seed1.sql", - "supabase/seeds/seed2.sql", - "supabase/seeds/another.sql", - "supabase/seeds/seed3.sql", - }, files) - }) - - t.Run("returns error on invalid pattern", func(t *testing.T) { - // Setup in-memory fs - fsys := fs.MapFS{} - // Mock config patterns - g := Glob{"[*!#@D#"} - // Run test - files, err := g.Files(fsys) - // Check error - assert.ErrorIs(t, err, path.ErrBadPattern) - // The resuling seed list should be empty - assert.Empty(t, files) - }) - - t.Run("returns empty list if no files match", func(t *testing.T) { - // Setup in-memory fs - fsys := fs.MapFS{} - // Mock config patterns - g := Glob{"seeds/*.sql"} - // Run test - files, err := g.Files(fsys) - // Check error - assert.ErrorContains(t, err, "no files matched") - // Validate files - assert.Empty(t, files) - }) - - t.Run("skips empty globs when configured", func(t *testing.T) { - fsys := fs.MapFS{ - "supabase/schemas/tables/players.sql": &fs.MapFile{}, - } - g := Glob{ - "supabase/schemas/tables/*.sql", - "supabase/schemas/materialized_views/*.sql", - } - - files, err := g.Files(fsys, WithSkipEmptyGlobs()) - - assert.NoError(t, err) - assert.Equal(t, []string{"supabase/schemas/tables/players.sql"}, files) - }) - - t.Run("errors when all skipped globs are empty and configured to fail", func(t *testing.T) { - fsys := fs.MapFS{} - g := Glob{ - "supabase/schemas/tables/*.sql", - "supabase/schemas/materialized_views/*.sql", - } - - files, err := g.Files(fsys, WithSkipEmptyGlobs(), WithErrorOnAllSkippedGlobs()) - - assert.ErrorContains(t, err, "no files matched pattern") - assert.Empty(t, files) - }) -} - -func TestGlobSQLFiles(t *testing.T) { - t.Run("expands directory entries in declared order", func(t *testing.T) { - fsys := fs.MapFS{ - "supabase/schemas/z_function.sql": &fs.MapFile{Data: []byte("select 1;")}, - "supabase/schemas/tables/a_table.sql": &fs.MapFile{Data: []byte("select 2;")}, - "supabase/schemas/tables/nested/b_table.sql": &fs.MapFile{Data: []byte("select 3;")}, - "supabase/schemas/tables/readme.md": &fs.MapFile{Data: []byte("ignored")}, - } - g := Glob{ - "supabase/schemas/z_function.sql", - "supabase/schemas/tables", - } - - files, err := g.SQLFiles(fsys) - - assert.NoError(t, err) - assert.Equal(t, []string{ - "supabase/schemas/z_function.sql", - "supabase/schemas/tables/a_table.sql", - "supabase/schemas/tables/nested/b_table.sql", - }, files) - }) - - t.Run("deduplicates explicit files and directory matches", func(t *testing.T) { - fsys := fs.MapFS{ - "supabase/database/a.sql": &fs.MapFile{Data: []byte("select 1;")}, - "supabase/database/b.sql": &fs.MapFile{Data: []byte("select 2;")}, - } - g := Glob{ - "supabase/database/a.sql", - "supabase/database", - "supabase/database/*.sql", - } - - files, err := g.SQLFiles(fsys) - - assert.NoError(t, err) - assert.Equal(t, []string{ - "supabase/database/a.sql", - "supabase/database/b.sql", - }, files) - }) -} - -func TestLoadFunctionImportMap(t *testing.T) { - t.Run("uses deno.json as import map when present", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` - project_id = "bvikqvbczudanvggcord" - [functions.hello] - `)}, - "supabase/functions/hello/deno.json": &fs.MapFile{}, - "supabase/functions/hello/index.ts": &fs.MapFile{}, - } - // Run test - assert.NoError(t, config.Load("", fsys)) - // Check that deno.json was set as import map - assert.Equal(t, "supabase/functions/hello/deno.json", config.Functions["hello"].ImportMap) - assert.Nil(t, config.Functions["hello"].VerifyJWT) - }) - - t.Run("uses deno.jsonc as import map when present", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` - project_id = "bvikqvbczudanvggcord" - [functions.hello] - `)}, - "supabase/functions/hello/deno.jsonc": &fs.MapFile{}, - "supabase/functions/hello/index.ts": &fs.MapFile{}, - } - // Run test - assert.NoError(t, config.Load("", fsys)) - // Check that deno.jsonc was set as import map - assert.Equal(t, "supabase/functions/hello/deno.jsonc", config.Functions["hello"].ImportMap) - }) - - t.Run("config.toml takes precedence over deno.json", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` - project_id = "bvikqvbczudanvggcord" - [functions] - hello.import_map = "custom_import_map.json" - `)}, - "supabase/functions/hello/deno.json": &fs.MapFile{}, - "supabase/functions/hello/index.ts": &fs.MapFile{}, - } - // Run test - assert.NoError(t, config.Load("", fsys)) - // Check that config.toml takes precedence over deno.json - assert.Equal(t, "supabase/custom_import_map.json", config.Functions["hello"].ImportMap) - }) -} - -func TestLoadFunctionErrorMessageParsing(t *testing.T) { - t.Run("returns error for array-style function config", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` - project_id = "bvikqvbczudanvggcord" - [[functions]] - name = "hello" - verify_jwt = true - `)}, - } - // Run test - err := config.Load("", fsys) - // Check error contains both decode errors - assert.ErrorContains(t, err, invalidFunctionsConfigFormat) - }) - - t.Run("returns error with function slug for invalid non-existent field", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` - project_id = "bvikqvbczudanvggcord" - [functions.hello] - unknown_field = true - `)}, - } - // Run test - err := config.Load("", fsys) - // Check error contains both decode errors - assert.ErrorContains(t, err, "'functions[hello]' has invalid keys: unknown_field") - }) - - t.Run("returns error with function slug for invalid field value", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` - project_id = "bvikqvbczudanvggcord" - [functions.hello] - verify_jwt = "not-a-bool" - `)}, - } - // Run test - err := config.Load("", fsys) - // Check error contains both decode errors - assert.ErrorContains(t, err, `'functions[hello].verify_jwt' cannot parse value as 'bool': strconv.ParseBool: invalid syntax`) - }) - - t.Run("returns error for unknown function fields", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` - project_id = "bvikqvbczudanvggcord" - [functions] - name = "hello" - verify_jwt = true - `)}, - } - // Run test - err := config.Load("", fsys) - assert.ErrorContains(t, err, `'functions[name]' expected a map or struct, got "string"`) - assert.ErrorContains(t, err, `'functions[verify_jwt]' expected a map or struct, got "bool"`) - }) -} - -func TestLoadEnvIfExists(t *testing.T) { - t.Run("returns nil when file does not exist", func(t *testing.T) { - err := loadEnvIfExists("nonexistent.env") - assert.NoError(t, err) - }) - - t.Run("returns raw error when file exists but is malformed and DEBUG=1", func(t *testing.T) { - // Set DEBUG=1 - t.Setenv("DEBUG", "1") - viper.AutomaticEnv() - - // Create a temporary file with malformed content - tmpFile, err := os.CreateTemp("", "test-*.env") - require.NoError(t, err) - defer os.Remove(tmpFile.Name()) - - // Write malformed content - _, err = tmpFile.WriteString("[invalid]\nvalue=secret_value\n") - require.NoError(t, err) - require.NoError(t, tmpFile.Close()) - - // Test loading the malformed file - err = loadEnvIfExists(tmpFile.Name()) - // Should contain the raw error, including the secret value - assert.ErrorContains(t, err, "unexpected character") - assert.ErrorContains(t, err, "secret_value") - }) - - t.Run("returns error when file exists but is malformed invalid character", func(t *testing.T) { - // Create a temporary file with malformed content - tmpFile, err := os.CreateTemp("", "test-*.env") - require.NoError(t, err) - defer os.Remove(tmpFile.Name()) - - // Write malformed content - _, err = tmpFile.WriteString("[invalid]\nvalue=secret_value\n") - require.NoError(t, err) - require.NoError(t, tmpFile.Close()) - - // Test loading the malformed file - err = loadEnvIfExists(tmpFile.Name()) - assert.ErrorContains(t, err, fmt.Sprintf("failed to parse environment file: %s (unexpected character '[' in variable name)", tmpFile.Name())) - assert.NotContains(t, err.Error(), "secret_value") - }) - - t.Run("returns error when file exists but is malformed unterminated quotes", func(t *testing.T) { - // Create a temporary file with malformed content - tmpFile, err := os.CreateTemp("", "test-*.env") - require.NoError(t, err) - defer os.Remove(tmpFile.Name()) - - // Write malformed content - _, err = tmpFile.WriteString("value=\"secret_value\n") - require.NoError(t, err) - require.NoError(t, tmpFile.Close()) - - // Test loading the malformed file - err = loadEnvIfExists(tmpFile.Name()) - assert.ErrorContains(t, err, fmt.Sprintf("failed to parse environment file: %s (unterminated quoted value)", tmpFile.Name())) - assert.NotContains(t, err.Error(), "secret_value") - }) - - t.Run("loads valid env file successfully", func(t *testing.T) { - // Create a temporary file with valid content - tmpFile, err := os.CreateTemp("", "test-*.env") - require.NoError(t, err) - defer os.Remove(tmpFile.Name()) - - // Write valid content - _, err = tmpFile.WriteString("TEST_KEY=test_value\nANOTHER_KEY=another_value") - require.NoError(t, err) - require.NoError(t, tmpFile.Close()) - - // Test loading the valid file - err = loadEnvIfExists(tmpFile.Name()) - assert.NoError(t, err) - - // Verify environment variables were loaded - assert.Equal(t, "test_value", os.Getenv("TEST_KEY")) - assert.Equal(t, "another_value", os.Getenv("ANOTHER_KEY")) - - // Clean up environment variables - os.Unsetenv("TEST_KEY") - os.Unsetenv("ANOTHER_KEY") - }) -} - -func TestVersionCompare(t *testing.T) { - var testcase = []struct { - a string - b string - r int - }{ - {"15.1.0.55", "15.1.0.55", 0}, - {"15.8.1.085", "15.1.0.55", 1}, - {"15.1.0.55", "15.8.1.085", -1}, - {"17.4.1.005", "17.4.1.005", 0}, - {"17.4.1.030", "17.4.1.005", 1}, - {"17.4.1.005", "17.4.1.030", -1}, - {"15.8.1", "15.8.1", 0}, - {"17", "15.8", 1}, - {"14", "15.8", -1}, - {"oriole-17", "oriole-17", 0}, - {"17", "oriole-17", 1}, - {"oriole-17", "17", -1}, - } - - for _, tt := range testcase { - t.Run(fmt.Sprintf("%s vs %s", tt.a, tt.b), func(t *testing.T) { - result := VersionCompare(tt.a, tt.b) - assert.Equal(t, tt.r, result) - }) - } -} - -func TestDeprecatedSMTPConfig(t *testing.T) { - t.Run("maps deprecated [inbucket] to local_smtp", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[inbucket] -enabled = true -port = 12345 -`)}, - } - require.NoError(t, config.Load("", fsys)) - assert.True(t, config.Inbucket.Enabled) - assert.Equal(t, uint16(12345), config.Inbucket.Port) - }) - - t.Run("keeps template defaults for a partial [inbucket] section", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[inbucket] -port = 9999 -`)}, - } - require.NoError(t, config.Load("", fsys)) - // enabled is omitted by the user; the template default (true) must survive - // the inbucket -> local_smtp rewrite via deep merge instead of collapsing - // to the zero value. - assert.True(t, config.Inbucket.Enabled) - assert.Equal(t, uint16(9999), config.Inbucket.Port) - }) - - t.Run("prefers explicit [local_smtp] over deprecated [inbucket]", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[inbucket] -enabled = true -port = 11111 - -[local_smtp] -enabled = true -port = 22222 -`)}, - } - require.NoError(t, config.Load("", fsys)) - assert.Equal(t, uint16(22222), config.Inbucket.Port) - }) - - t.Run("normalizes deprecated [remotes.*.inbucket]", func(t *testing.T) { - config := NewConfig() - config.ProjectId = "abcdefghijklmnopqrst" - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[remotes.staging] -project_id = "abcdefghijklmnopqrst" - -[remotes.staging.inbucket] -enabled = true -port = 33333 -`)}, - } - require.NoError(t, config.Load("", fsys)) - assert.Equal(t, uint16(33333), config.Inbucket.Port) - }) - - t.Run("preserves env overrides when rewriting [inbucket]", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[inbucket] -enabled = true -port = 12345 -`)}, - } - // Env overrides are applied via ExperimentalBindStruct at unmarshal time, not - // captured by AllSettings(). Rebuilding the viper without those options while - // rewriting [inbucket] would silently drop this override. - t.Setenv("SUPABASE_AUTH_SITE_URL", "http://env-override.example/") - require.NoError(t, config.Load("", fsys)) - assert.Equal(t, "http://env-override.example/", config.Auth.SiteUrl) - assert.Equal(t, uint16(12345), config.Inbucket.Port) - }) -} - -func TestDeprecatedOrioleDBVersionConfig(t *testing.T) { - captureStderr := func(t *testing.T, run func()) string { - t.Helper() - r, w, err := os.Pipe() - require.NoError(t, err) - defer r.Close() - defer w.Close() - orig := os.Stderr - os.Stderr = w - defer func() { os.Stderr = orig }() - run() - require.NoError(t, w.Close()) - var out bytes.Buffer - _, err = io.Copy(&out, r) - require.NoError(t, err) - return out.String() - } - - t.Run("promotes deprecated [experimental] orioledb_version to [db]", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[experimental] -orioledb_version = "15.1.0.150" -`)}, - } - stderr := captureStderr(t, func() { - require.NoError(t, config.Load("", fsys)) - }) - assert.Equal(t, "15.1.0.150", config.Db.OrioleDBVersion) - assert.Contains(t, stderr, "WARN: experimental.orioledb_version is deprecated. Please use db.orioledb_version instead.") - }) - - t.Run("does not warn when [experimental] orioledb_version is empty", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[experimental] -orioledb_version = "" -`)}, - } - stderr := captureStderr(t, func() { - require.NoError(t, config.Load("", fsys)) - }) - assert.Equal(t, "", config.Db.OrioleDBVersion) - assert.NotContains(t, stderr, "orioledb_version is deprecated") - }) - - t.Run("promotes deprecated [experimental] orioledb_version when [db] is explicitly empty", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[experimental] -orioledb_version = "x" - -[db] -orioledb_version = "" -`)}, - } - stderr := captureStderr(t, func() { - require.NoError(t, config.Load("", fsys)) - }) - assert.Equal(t, "x", config.Db.OrioleDBVersion) - assert.Contains(t, stderr, "WARN: experimental.orioledb_version is deprecated. Please use db.orioledb_version instead.") - }) - - t.Run("prefers explicit [db] orioledb_version over deprecated [experimental]", func(t *testing.T) { - config := NewConfig() - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[experimental] -orioledb_version = "15.1.0.150" - -[db] -orioledb_version = "15.1.1.13" -`)}, - } - stderr := captureStderr(t, func() { - require.NoError(t, config.Load("", fsys)) - }) - assert.Equal(t, "15.1.1.13", config.Db.OrioleDBVersion) - assert.Contains(t, stderr, "WARN: experimental.orioledb_version is deprecated. Please use db.orioledb_version instead.") - }) - - t.Run("normalizes deprecated [remotes.*.experimental] orioledb_version", func(t *testing.T) { - config := NewConfig() - config.ProjectId = "abcdefghijklmnopqrst" - fsys := fs.MapFS{ - "supabase/config.toml": &fs.MapFile{Data: []byte(` -[remotes.staging] -project_id = "abcdefghijklmnopqrst" - -[remotes.staging.experimental] -orioledb_version = "15.1.0.150" -`)}, - } - stderr := captureStderr(t, func() { - require.NoError(t, config.Load("", fsys)) - }) - assert.Equal(t, "15.1.0.150", config.Db.OrioleDBVersion) - assert.Contains(t, stderr, "WARN: remotes.staging.experimental.orioledb_version is deprecated. Please use remotes.staging.db.orioledb_version instead.") - }) -} diff --git a/apps/cli-go/pkg/config/constants.go b/apps/cli-go/pkg/config/constants.go deleted file mode 100644 index 93bb482f69..0000000000 --- a/apps/cli-go/pkg/config/constants.go +++ /dev/null @@ -1,77 +0,0 @@ -package config - -import ( - _ "embed" - "regexp" - - "github.com/go-errors/errors" - "github.com/go-viper/mapstructure/v2" -) - -const ( - deno1 = "supabase/edge-runtime:v1.68.4" -) - -type images struct { - Pg string `mapstructure:"pg"` - // PG13/15 and PG14 come from the Dockerfile's generated `pg15`/hand-pinned `pg14` stages - // (the single version table), not hardcoded constants. - Pg15 string `mapstructure:"pg15"` - Pg14 string `mapstructure:"pg14"` - // Append to Services when adding new dependencies below - Kong string `mapstructure:"kong"` - Inbucket string `mapstructure:"mailpit"` - Postgrest string `mapstructure:"postgrest"` - Pgmeta string `mapstructure:"pgmeta"` - Studio string `mapstructure:"studio"` - ImgProxy string `mapstructure:"imgproxy"` - EdgeRuntime string `mapstructure:"edgeruntime"` - Vector string `mapstructure:"vector"` - Supavisor string `mapstructure:"supavisor"` - Gotrue string `mapstructure:"gotrue"` - Realtime string `mapstructure:"realtime"` - Storage string `mapstructure:"storage"` - Logflare string `mapstructure:"logflare"` - // Append to Jobs when adding new dependencies below - Differ string `mapstructure:"differ"` - Migra string `mapstructure:"migra"` - PgProve string `mapstructure:"pgprove"` -} - -var ( - //go:embed templates/Dockerfile - dockerImage string - imagePattern = regexp.MustCompile(`(?i)FROM\s+([^\s]+)\s+AS\s+([^\s]+)`) - Images images -) - -func init() { - matches := imagePattern.FindAllStringSubmatch(dockerImage, -1) - result := make(map[string]string, len(matches)) - for _, m := range matches { - if len(m) == 3 { - result[m[2]] = m[1] - } - } - if err := mapstructure.Decode(result, &Images); err != nil { - panic(errors.Errorf("failed to decode images: %w", err)) - } -} - -func (s images) Services() []string { - return []string{ - s.Gotrue, - s.Realtime, - s.Storage, - s.ImgProxy, - s.Kong, - s.Inbucket, - s.Postgrest, - s.Pgmeta, - s.Studio, - s.EdgeRuntime, - s.Logflare, - s.Vector, - s.Supavisor, - } -} diff --git a/apps/cli-go/pkg/config/constants_test.go b/apps/cli-go/pkg/config/constants_test.go deleted file mode 100644 index 1f8badef39..0000000000 --- a/apps/cli-go/pkg/config/constants_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package config - -import ( - "strings" - "testing" - - "github.com/go-viper/mapstructure/v2" - "github.com/stretchr/testify/assert" -) - -func TestParseImage(t *testing.T) { - output := map[string]string{} - err := mapstructure.Decode(Images, &output) - assert.NoError(t, err) - assert.Len(t, output, strings.Count(dockerImage, "FROM")) - for _, v := range output { - assert.NotEmpty(t, v) - } -} diff --git a/apps/cli-go/pkg/config/db.go b/apps/cli-go/pkg/config/db.go deleted file mode 100644 index 821c0a554b..0000000000 --- a/apps/cli-go/pkg/config/db.go +++ /dev/null @@ -1,269 +0,0 @@ -package config - -import ( - "bytes" - "slices" - "time" - - "github.com/go-errors/errors" - v1API "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" - "github.com/supabase/cli/pkg/diff" -) - -type PoolMode string - -const ( - TransactionMode PoolMode = "transaction" - SessionMode PoolMode = "session" -) - -func (m *PoolMode) UnmarshalText(text []byte) error { - allowed := []PoolMode{TransactionMode, SessionMode} - if *m = PoolMode(text); !slices.Contains(allowed, *m) { - return errors.Errorf("must be one of %v", allowed) - } - return nil -} - -type SessionReplicationRole string - -const ( - SessionReplicationRoleOrigin SessionReplicationRole = "origin" - SessionReplicationRoleReplica SessionReplicationRole = "replica" - SessionReplicationRoleLocal SessionReplicationRole = "local" -) - -func (r *SessionReplicationRole) UnmarshalText(text []byte) error { - allowed := []SessionReplicationRole{SessionReplicationRoleOrigin, SessionReplicationRoleReplica, SessionReplicationRoleLocal} - if *r = SessionReplicationRole(text); !slices.Contains(allowed, *r) { - return errors.Errorf("must be one of %v", allowed) - } - return nil -} - -type ( - settings struct { - EffectiveCacheSize *string `toml:"effective_cache_size" json:"effective_cache_size"` - LogicalDecodingWorkMem *string `toml:"logical_decoding_work_mem" json:"logical_decoding_work_mem"` - MaintenanceWorkMem *string `toml:"maintenance_work_mem" json:"maintenance_work_mem"` - MaxConnections *uint `toml:"max_connections" json:"max_connections"` - MaxLocksPerTransaction *uint `toml:"max_locks_per_transaction" json:"max_locks_per_transaction"` - MaxParallelMaintenanceWorkers *uint `toml:"max_parallel_maintenance_workers" json:"max_parallel_maintenance_workers"` - MaxParallelWorkers *uint `toml:"max_parallel_workers" json:"max_parallel_workers"` - MaxParallelWorkersPerGather *uint `toml:"max_parallel_workers_per_gather" json:"max_parallel_workers_per_gather"` - MaxReplicationSlots *uint `toml:"max_replication_slots" json:"max_replication_slots"` - MaxSlotWalKeepSize *string `toml:"max_slot_wal_keep_size" json:"max_slot_wal_keep_size"` - MaxStandbyArchiveDelay *string `toml:"max_standby_archive_delay" json:"max_standby_archive_delay"` - MaxStandbyStreamingDelay *string `toml:"max_standby_streaming_delay" json:"max_standby_streaming_delay"` - MaxWalSize *string `toml:"max_wal_size" json:"max_wal_size"` - MaxWalSenders *uint `toml:"max_wal_senders" json:"max_wal_senders"` - MaxWorkerProcesses *uint `toml:"max_worker_processes" json:"max_worker_processes"` - SessionReplicationRole *SessionReplicationRole `toml:"session_replication_role" json:"session_replication_role"` - SharedBuffers *string `toml:"shared_buffers" json:"shared_buffers"` - StatementTimeout *string `toml:"statement_timeout" json:"statement_timeout"` - TrackActivityQuerySize *string `toml:"track_activity_query_size" json:"track_activity_query_size"` - TrackCommitTimestamp *bool `toml:"track_commit_timestamp" json:"track_commit_timestamp"` - WalKeepSize *string `toml:"wal_keep_size" json:"wal_keep_size"` - WalSenderTimeout *string `toml:"wal_sender_timeout" json:"wal_sender_timeout"` - WorkMem *string `toml:"work_mem" json:"work_mem"` - } - - networkRestrictions struct { - Enabled bool `toml:"enabled" json:"enabled"` - AllowedCidrs []string `toml:"allowed_cidrs" json:"allowed_cidrs"` - AllowedCidrsV6 []string `toml:"allowed_cidrs_v6" json:"allowed_cidrs_v6"` - } - - sslEnforcement struct { - Enabled bool `toml:"enabled" json:"enabled"` - } - - db struct { - Image string `toml:"-" json:"-"` - Port uint16 `toml:"port" json:"port"` - ShadowPort uint16 `toml:"shadow_port" json:"shadow_port"` - HealthTimeout time.Duration `toml:"health_timeout" json:"health_timeout"` - MajorVersion uint `toml:"major_version" json:"major_version"` - OrioleDBVersion string `toml:"orioledb_version" json:"orioledb_version"` - Password string `toml:"-" json:"-"` - RootKey Secret `toml:"root_key" json:"root_key"` - Pooler pooler `toml:"pooler" json:"pooler"` - Migrations migrations `toml:"migrations" json:"migrations"` - Seed seed `toml:"seed" json:"seed"` - Settings settings `toml:"settings" json:"settings"` - NetworkRestrictions networkRestrictions `toml:"network_restrictions" json:"network_restrictions"` - SslEnforcement *sslEnforcement `toml:"ssl_enforcement" json:"ssl_enforcement"` - Vault map[string]Secret `toml:"vault" json:"vault"` - } - - migrations struct { - Enabled bool `toml:"enabled" json:"enabled"` - SchemaPaths Glob `toml:"schema_paths" json:"schema_paths"` - } - - seed struct { - Enabled bool `toml:"enabled" json:"enabled"` - SqlPaths Glob `toml:"sql_paths" json:"sql_paths"` - } - - pooler struct { - Enabled bool `toml:"enabled" json:"enabled"` - Image string `toml:"-" json:"-"` - Port uint16 `toml:"port" json:"port"` - PoolMode PoolMode `toml:"pool_mode" json:"pool_mode"` - DefaultPoolSize uint `toml:"default_pool_size" json:"default_pool_size"` - MaxClientConn uint `toml:"max_client_conn" json:"max_client_conn"` - ConnectionString string `toml:"-" json:"-"` - TenantId string `toml:"-" json:"-"` - EncryptionKey string `toml:"-" json:"-"` - SecretKeyBase string `toml:"-" json:"-"` - } -) - -func (a *settings) ToUpdatePostgresConfigBody() v1API.UpdatePostgresConfigBody { - body := v1API.UpdatePostgresConfigBody{} - - // Parameters that require restart - body.MaxConnections = cast.UintToIntPtr(a.MaxConnections) - body.MaxWorkerProcesses = cast.UintToIntPtr(a.MaxWorkerProcesses) - body.MaxParallelWorkers = cast.UintToIntPtr(a.MaxParallelWorkers) - body.MaxWalSenders = cast.UintToIntPtr(a.MaxWalSenders) - body.MaxReplicationSlots = cast.UintToIntPtr(a.MaxReplicationSlots) - body.SharedBuffers = a.SharedBuffers - - // Parameters that can be changed without restart - body.EffectiveCacheSize = a.EffectiveCacheSize - body.LogicalDecodingWorkMem = a.LogicalDecodingWorkMem - body.MaintenanceWorkMem = a.MaintenanceWorkMem - body.MaxLocksPerTransaction = cast.UintToIntPtr(a.MaxLocksPerTransaction) - body.MaxParallelMaintenanceWorkers = cast.UintToIntPtr(a.MaxParallelMaintenanceWorkers) - body.MaxParallelWorkersPerGather = cast.UintToIntPtr(a.MaxParallelWorkersPerGather) - body.MaxSlotWalKeepSize = a.MaxSlotWalKeepSize - body.MaxStandbyArchiveDelay = a.MaxStandbyArchiveDelay - body.MaxStandbyStreamingDelay = a.MaxStandbyStreamingDelay - body.MaxWalSize = a.MaxWalSize - body.SessionReplicationRole = (*v1API.UpdatePostgresConfigBodySessionReplicationRole)(a.SessionReplicationRole) - body.StatementTimeout = a.StatementTimeout - body.TrackActivityQuerySize = a.TrackActivityQuerySize - body.TrackCommitTimestamp = a.TrackCommitTimestamp - body.WalKeepSize = a.WalKeepSize - body.WalSenderTimeout = a.WalSenderTimeout - body.WorkMem = a.WorkMem - return body -} - -func (a *settings) FromRemotePostgresConfig(remoteConfig v1API.PostgresConfigResponseOutput) { - a.EffectiveCacheSize = remoteConfig.EffectiveCacheSize - a.LogicalDecodingWorkMem = remoteConfig.LogicalDecodingWorkMem - a.MaintenanceWorkMem = remoteConfig.MaintenanceWorkMem - a.MaxConnections = cast.IntToUintPtr(remoteConfig.MaxConnections) - a.MaxLocksPerTransaction = cast.IntToUintPtr(remoteConfig.MaxLocksPerTransaction) - a.MaxParallelMaintenanceWorkers = cast.IntToUintPtr(remoteConfig.MaxParallelMaintenanceWorkers) - a.MaxParallelWorkers = cast.IntToUintPtr(remoteConfig.MaxParallelWorkers) - a.MaxParallelWorkersPerGather = cast.IntToUintPtr(remoteConfig.MaxParallelWorkersPerGather) - a.MaxReplicationSlots = cast.IntToUintPtr(remoteConfig.MaxReplicationSlots) - a.MaxSlotWalKeepSize = remoteConfig.MaxSlotWalKeepSize - a.MaxStandbyArchiveDelay = remoteConfig.MaxStandbyArchiveDelay - a.MaxStandbyStreamingDelay = remoteConfig.MaxStandbyStreamingDelay - a.MaxWalSenders = cast.IntToUintPtr(remoteConfig.MaxWalSenders) - a.MaxWalSize = remoteConfig.MaxWalSize - a.MaxWorkerProcesses = cast.IntToUintPtr(remoteConfig.MaxWorkerProcesses) - a.SessionReplicationRole = (*SessionReplicationRole)(remoteConfig.SessionReplicationRole) - a.SharedBuffers = remoteConfig.SharedBuffers - a.StatementTimeout = remoteConfig.StatementTimeout - a.TrackActivityQuerySize = remoteConfig.TrackActivityQuerySize - a.TrackCommitTimestamp = remoteConfig.TrackCommitTimestamp - a.WalKeepSize = remoteConfig.WalKeepSize - a.WalSenderTimeout = remoteConfig.WalSenderTimeout - a.WorkMem = remoteConfig.WorkMem -} - -const pgConfHeader = "\n# supabase [db.settings] configuration\n" - -// create a valid string to append to /etc/postgresql/postgresql.conf -func (a *settings) ToPostgresConfig() string { - // Assuming postgres settings is always a flat struct, we can serialise - // using toml, then replace double quotes with single. - data, _ := ToTomlBytes(*a) - body := bytes.ReplaceAll(data, []byte{'"'}, []byte{'\''}) - return pgConfHeader + string(body) -} - -func (a *settings) DiffWithRemote(remoteConfig v1API.PostgresConfigResponseOutput) ([]byte, error) { - copy := *a - // Convert the config values into easily comparable remoteConfig values - currentValue, err := ToTomlBytes(copy) - if err != nil { - return nil, err - } - copy.FromRemotePostgresConfig(remoteConfig) - remoteCompare, err := ToTomlBytes(copy) - if err != nil { - return nil, err - } - return diff.Diff("remote[db.settings]", remoteCompare, "local[db.settings]", currentValue), nil -} - -func (n networkRestrictions) ToUpdateNetworkRestrictionsBody() v1API.V1UpdateNetworkRestrictionsJSONRequestBody { - body := v1API.V1UpdateNetworkRestrictionsJSONRequestBody{ - DbAllowedCidrs: &n.AllowedCidrs, - DbAllowedCidrsV6: &n.AllowedCidrsV6, - } - return body -} - -func (n *networkRestrictions) FromRemoteNetworkRestrictions(remoteConfig v1API.NetworkRestrictionsResponseOutput) { - if !n.Enabled { - return - } - if remoteConfig.Config.DbAllowedCidrs != nil { - n.AllowedCidrs = *remoteConfig.Config.DbAllowedCidrs - } - if remoteConfig.Config.DbAllowedCidrsV6 != nil { - n.AllowedCidrsV6 = *remoteConfig.Config.DbAllowedCidrsV6 - } -} - -func (n *networkRestrictions) DiffWithRemote(remoteConfig v1API.NetworkRestrictionsResponseOutput) ([]byte, error) { - copy := *n - // Convert the config values into easily comparable remoteConfig values - currentValue, err := ToTomlBytes(copy) - if err != nil { - return nil, err - } - copy.FromRemoteNetworkRestrictions(remoteConfig) - remoteCompare, err := ToTomlBytes(copy) - if err != nil { - return nil, err - } - return diff.Diff("remote[db.network_restrictions]", remoteCompare, "local[db.network_restrictions]", currentValue), nil -} - -func (s sslEnforcement) ToUpdateSslEnforcementBody() v1API.V1UpdateSslEnforcementConfigJSONRequestBody { - body := v1API.V1UpdateSslEnforcementConfigJSONRequestBody{} - body.RequestedConfig.Database = s.Enabled - return body -} - -func (s *sslEnforcement) FromRemoteSslEnforcement(remoteConfig v1API.SslEnforcementResponseOutput) { - if s == nil { - return - } - s.Enabled = remoteConfig.CurrentConfig.Database -} - -func (s *sslEnforcement) DiffWithRemote(remoteConfig v1API.SslEnforcementResponseOutput) ([]byte, error) { - copy := *s - // Convert the config values into easily comparable remoteConfig values - currentValue, err := ToTomlBytes(copy) - if err != nil { - return nil, err - } - copy.FromRemoteSslEnforcement(remoteConfig) - remoteCompare, err := ToTomlBytes(copy) - if err != nil { - return nil, err - } - return diff.Diff("remote[db.ssl_enforcement]", remoteCompare, "local[db.ssl_enforcement]", currentValue), nil -} diff --git a/apps/cli-go/pkg/config/db_test.go b/apps/cli-go/pkg/config/db_test.go deleted file mode 100644 index 2e23ec9563..0000000000 --- a/apps/cli-go/pkg/config/db_test.go +++ /dev/null @@ -1,281 +0,0 @@ -package config - -import ( - "testing" - - "github.com/stretchr/testify/assert" - v1API "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" -) - -func TestDbSettingsToUpdatePostgresConfigBody(t *testing.T) { - t.Run("converts all fields correctly", func(t *testing.T) { - db := &db{ - Settings: settings{ - EffectiveCacheSize: cast.Ptr("4GB"), - MaxConnections: cast.Ptr(uint(100)), - SharedBuffers: cast.Ptr("1GB"), - StatementTimeout: cast.Ptr("30s"), - SessionReplicationRole: cast.Ptr(SessionReplicationRoleReplica), - }, - } - - body := db.Settings.ToUpdatePostgresConfigBody() - - assert.Equal(t, "4GB", *body.EffectiveCacheSize) - assert.Equal(t, 100, *body.MaxConnections) - assert.Equal(t, "1GB", *body.SharedBuffers) - assert.Equal(t, "30s", *body.StatementTimeout) - assert.Equal(t, v1API.UpdatePostgresConfigBodySessionReplicationRoleReplica, *body.SessionReplicationRole) - }) - - t.Run("handles empty fields", func(t *testing.T) { - db := &db{} - - body := db.Settings.ToUpdatePostgresConfigBody() - - assert.Nil(t, body.EffectiveCacheSize) - assert.Nil(t, body.MaxConnections) - assert.Nil(t, body.SharedBuffers) - assert.Nil(t, body.StatementTimeout) - assert.Nil(t, body.SessionReplicationRole) - }) -} - -func TestDbSettingsDiff(t *testing.T) { - t.Run("detects differences", func(t *testing.T) { - db := &db{ - Settings: settings{ - EffectiveCacheSize: cast.Ptr("4GB"), - MaxConnections: cast.Ptr(uint(100)), - SharedBuffers: cast.Ptr("1GB"), - }, - } - - remoteConfig := v1API.PostgresConfigResponseOutput{ - EffectiveCacheSize: cast.Ptr("8GB"), - MaxConnections: cast.Ptr(200), - SharedBuffers: cast.Ptr("2GB"), - } - - diff, err := db.Settings.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - - assertSnapshotEqual(t, diff) - }) - - t.Run("handles no differences", func(t *testing.T) { - db := &db{ - Settings: settings{ - EffectiveCacheSize: cast.Ptr("4GB"), - MaxConnections: cast.Ptr(uint(100)), - SharedBuffers: cast.Ptr("1GB"), - }, - } - - remoteConfig := v1API.PostgresConfigResponseOutput{ - EffectiveCacheSize: cast.Ptr("4GB"), - MaxConnections: cast.Ptr(100), - SharedBuffers: cast.Ptr("1GB"), - } - - diff, err := db.Settings.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - - assert.Empty(t, diff) - }) - - t.Run("handles multiple schemas and search paths with spaces", func(t *testing.T) { - db := &db{ - Settings: settings{ - EffectiveCacheSize: cast.Ptr("4GB"), - MaxConnections: cast.Ptr(uint(100)), - SharedBuffers: cast.Ptr("1GB"), - }, - } - - remoteConfig := v1API.PostgresConfigResponseOutput{ - EffectiveCacheSize: cast.Ptr("4GB"), - MaxConnections: cast.Ptr(100), - SharedBuffers: cast.Ptr("1GB"), - } - - diff, err := db.Settings.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - - assert.Empty(t, diff) - }) - - t.Run("handles api disabled on remote side", func(t *testing.T) { - db := &db{ - Settings: settings{ - EffectiveCacheSize: cast.Ptr("4GB"), - MaxConnections: cast.Ptr(uint(100)), - SharedBuffers: cast.Ptr("1GB"), - }, - } - - remoteConfig := v1API.PostgresConfigResponseOutput{ - // All fields are nil to simulate disabled API - } - - diff, err := db.Settings.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - - assertSnapshotEqual(t, diff) - }) - - t.Run("handles api disabled on local side", func(t *testing.T) { - db := &db{ - Settings: settings{ - // All fields are nil to simulate disabled API - }, - } - - remoteConfig := v1API.PostgresConfigResponseOutput{ - EffectiveCacheSize: cast.Ptr("4GB"), - MaxConnections: cast.Ptr(100), - SharedBuffers: cast.Ptr("1GB"), - } - - diff, err := db.Settings.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - - assertSnapshotEqual(t, diff) - }) -} - -func TestSettingsToPostgresConfig(t *testing.T) { - t.Run("Only set values should appear", func(t *testing.T) { - settings := settings{ - MaxConnections: cast.Ptr(uint(100)), - MaxLocksPerTransaction: cast.Ptr(uint(64)), - SharedBuffers: cast.Ptr("128MB"), - WorkMem: cast.Ptr("4MB"), - } - got := settings.ToPostgresConfig() - - assert.Contains(t, got, "max_connections = 100") - assert.Contains(t, got, "max_locks_per_transaction = 64") - assert.Contains(t, got, "shared_buffers = '128MB'") - assert.Contains(t, got, "work_mem = '4MB'") - - assert.NotContains(t, got, "effective_cache_size") - assert.NotContains(t, got, "maintenance_work_mem") - assert.NotContains(t, got, "max_parallel_workers") - }) - - t.Run("SessionReplicationRole should be handled correctly", func(t *testing.T) { - settings := settings{ - SessionReplicationRole: cast.Ptr(SessionReplicationRoleOrigin), - } - got := settings.ToPostgresConfig() - - assert.Contains(t, got, "session_replication_role = 'origin'") - }) - - t.Run("Empty settings should result in empty string", func(t *testing.T) { - settings := settings{} - got := settings.ToPostgresConfig() - - assert.Equal(t, got, "\n# supabase [db.settings] configuration\n") - assert.NotContains(t, got, "=") - }) -} - -func TestNetworkRestrictionsFromRemote(t *testing.T) { - t.Run("converts from remote config with restrictions", func(t *testing.T) { - ipv4Cidrs := []string{"192.168.1.0/24"} - ipv6Cidrs := []string{"2001:db8::/32"} - remoteConfig := v1API.NetworkRestrictionsResponseOutput{} - remoteConfig.Config.DbAllowedCidrs = &ipv4Cidrs - remoteConfig.Config.DbAllowedCidrsV6 = &ipv6Cidrs - nr := networkRestrictions{Enabled: true} - nr.FromRemoteNetworkRestrictions(remoteConfig) - assert.ElementsMatch(t, ipv4Cidrs, nr.AllowedCidrs) - assert.ElementsMatch(t, ipv6Cidrs, nr.AllowedCidrsV6) - }) - - t.Run("converts from remote config with allow all", func(t *testing.T) { - ipv4Cidrs := []string{"0.0.0.0/0"} - ipv6Cidrs := []string{"::/0"} - remoteConfig := v1API.NetworkRestrictionsResponseOutput{} - remoteConfig.Config.DbAllowedCidrs = &ipv4Cidrs - remoteConfig.Config.DbAllowedCidrsV6 = &ipv6Cidrs - nr := networkRestrictions{Enabled: true} - nr.FromRemoteNetworkRestrictions(remoteConfig) - assert.ElementsMatch(t, ipv4Cidrs, nr.AllowedCidrs) - assert.ElementsMatch(t, ipv6Cidrs, nr.AllowedCidrsV6) - }) - - t.Run("ignores locally disabled network restrictions", func(t *testing.T) { - remoteConfig := v1API.NetworkRestrictionsResponseOutput{} - remoteConfig.Config.DbAllowedCidrs = &[]string{"192.168.1.0/24"} - remoteConfig.Config.DbAllowedCidrsV6 = &[]string{"2001:db8::/32"} - nr := networkRestrictions{} - nr.FromRemoteNetworkRestrictions(remoteConfig) - assert.False(t, nr.Enabled) - assert.Empty(t, nr.AllowedCidrs) - assert.Empty(t, nr.AllowedCidrsV6) - }) -} - -func TestNetworkRestrictionsDiff(t *testing.T) { - t.Run("detects differences", func(t *testing.T) { - local := networkRestrictions{ - Enabled: true, - AllowedCidrs: []string{"192.168.1.0/24"}, - AllowedCidrsV6: []string{"2001:db8::/32"}, - } - remoteConfig := v1API.NetworkRestrictionsResponseOutput{} - remoteConfig.Config.DbAllowedCidrs = &[]string{"10.0.0.0/8"} - remoteConfig.Config.DbAllowedCidrsV6 = &[]string{"fd00::/8"} - diff, err := local.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - assert.Contains(t, string(diff), "-allowed_cidrs = [\"10.0.0.0/8\"]") - assert.Contains(t, string(diff), "+allowed_cidrs = [\"192.168.1.0/24\"]") - assert.Contains(t, string(diff), "-allowed_cidrs_v6 = [\"fd00::/8\"]") - assert.Contains(t, string(diff), "+allowed_cidrs_v6 = [\"2001:db8::/32\"]") - }) - - t.Run("no differences", func(t *testing.T) { - local := networkRestrictions{ - Enabled: true, - AllowedCidrs: []string{"192.168.1.0/24"}, - AllowedCidrsV6: []string{"2001:db8::/32"}, - } - remoteConfig := v1API.NetworkRestrictionsResponseOutput{} - remoteConfig.Config.DbAllowedCidrs = &local.AllowedCidrs - remoteConfig.Config.DbAllowedCidrsV6 = &local.AllowedCidrsV6 - diff, err := local.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - assert.Empty(t, diff) - }) - - t.Run("both have no restrictions - disabled vs allow all", func(t *testing.T) { - local := networkRestrictions{} - remoteConfig := v1API.NetworkRestrictionsResponseOutput{} - remoteConfig.Config.DbAllowedCidrs = &[]string{"0.0.0.0/0"} - remoteConfig.Config.DbAllowedCidrsV6 = &[]string{"::/0"} - diff, err := local.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - assert.Empty(t, diff) - }) - - t.Run("local disallow all, remote allow all", func(t *testing.T) { - local := networkRestrictions{ - Enabled: true, - AllowedCidrs: []string{}, - AllowedCidrsV6: []string{}, - } - remoteConfig := v1API.NetworkRestrictionsResponseOutput{} - remoteConfig.Config.DbAllowedCidrs = &[]string{"0.0.0.0/0"} - remoteConfig.Config.DbAllowedCidrsV6 = &[]string{"::/0"} - diff, err := local.DiffWithRemote(remoteConfig) - assert.NoError(t, err) - assert.Contains(t, string(diff), "-allowed_cidrs = [\"0.0.0.0/0\"]") - assert.Contains(t, string(diff), "+allowed_cidrs = []") - assert.Contains(t, string(diff), "-allowed_cidrs_v6 = [\"::/0\"]") - assert.Contains(t, string(diff), "+allowed_cidrs_v6 = []") - }) -} diff --git a/apps/cli-go/pkg/config/decode_hooks.go b/apps/cli-go/pkg/config/decode_hooks.go deleted file mode 100644 index 265ec872e9..0000000000 --- a/apps/cli-go/pkg/config/decode_hooks.go +++ /dev/null @@ -1,50 +0,0 @@ -package config - -import ( - "os" - "reflect" - "regexp" - - "github.com/go-errors/errors" -) - -var envPattern = regexp.MustCompile(`^env\((.*)\)$`) - -// LoadEnvHook is a mapstructure decode hook that loads environment variables -// from strings formatted as env(VAR_NAME). -func LoadEnvHook(f reflect.Kind, t reflect.Kind, data any) (any, error) { - if f != reflect.String { - return data, nil - } - value := data.(string) - if matches := envPattern.FindStringSubmatch(value); len(matches) > 1 { - if env := os.Getenv(matches[1]); len(env) > 0 { - value = env - } - } - return value, nil -} - -const invalidFunctionsConfigFormat = `Invalid functions config format. Functions should be configured as: - -[functions.] -field = value - -Example: -[functions.hello] -verify_jwt = true` - -// ValidateFunctionsHook is a mapstructure decode hook that validates the functions config format. -func ValidateFunctionsHook(f reflect.Type, t reflect.Type, data any) (any, error) { - // Only handle FunctionConfig type - if t != reflect.TypeOf(FunctionConfig{}) { - return data, nil - } - - // Check if source is not a map - if f.Kind() != reflect.Map { - return nil, errors.New(invalidFunctionsConfigFormat) - } - - return data, nil -} diff --git a/apps/cli-go/pkg/config/secret.go b/apps/cli-go/pkg/config/secret.go deleted file mode 100644 index 8fa7f811b9..0000000000 --- a/apps/cli-go/pkg/config/secret.go +++ /dev/null @@ -1,109 +0,0 @@ -package config - -import ( - "encoding/base64" - "os" - "reflect" - "strings" - - ecies "github.com/ecies/go/v2" - "github.com/go-errors/errors" - "github.com/go-viper/mapstructure/v2" -) - -type Secret struct { - Value string - SHA256 string -} - -const HASHED_PREFIX = "hash:" - -func (s Secret) MarshalText() (text []byte, err error) { - if len(s.SHA256) == 0 { - return []byte{}, nil - } - return []byte(HASHED_PREFIX + s.SHA256), nil -} - -const ENCRYPTED_PREFIX = "encrypted:" - -func (s *Secret) Decrypt(keys []string) error { - if !strings.HasPrefix(s.Value, ENCRYPTED_PREFIX) { - return nil - } - if len(keys) == 0 { - return errors.New(errMissingKey) - } - var err error - for _, k := range keys { - // Use the first private key that successfully decrypts the secret - if s.Value, err = decrypt(k, s.Value); err == nil { - break - } - } - // If we didn't break, none of the keys worked - return err -} - -var errMissingKey = errors.New("missing private key") - -// Decrypt secret values following dotenvx convention: -// https://github.com/dotenvx/dotenvx/blob/main/src/lib/helpers/decryptKeyValue.js -func decrypt(key, value string) (string, error) { - if len(key) == 0 { - return value, errors.New(errMissingKey) - } - // Verify private key exists - privateKey, err := ecies.NewPrivateKeyFromHex(key) - if err != nil { - return value, errors.Errorf("failed to hex decode private key: %w", err) - } - // Verify ciphertext is base64 encoded - encoded := value[len(ENCRYPTED_PREFIX):] - ciphertext, err := base64.StdEncoding.DecodeString(encoded) - if err != nil { - return value, errors.Errorf("failed to base64 decode secret: %w", err) - } - // Return decrypted value - plaintext, err := ecies.Decrypt(privateKey, ciphertext) - if err != nil { - return value, errors.Errorf("failed to decrypt secret: %w", err) - } - return string(plaintext), nil -} - -const PRIVATE_KEY_PREFIX = "DOTENV_PRIVATE_KEY" - -func DecryptSecretHookFunc(hashKey string) mapstructure.DecodeHookFunc { - // Get all env vars and filter for private keys - var privateKeys []string - for _, env := range os.Environ() { - kv := strings.SplitN(env, "=", 2) - if kv[0] == PRIVATE_KEY_PREFIX || strings.HasPrefix(kv[0], PRIVATE_KEY_PREFIX+"_") { - privateKeys = append(privateKeys, strToArr(kv[1])...) - } - } - return func(f reflect.Type, t reflect.Type, data any) (any, error) { - if f.Kind() != reflect.String { - return data, nil - } - var result Secret - if t != reflect.TypeOf(result) { - return data, nil - } - result.Value = data.(string) - if len(result.Value) == 0 { - return result, nil - } - // Unloaded env() references should be returned verbatim without hashing - if envPattern.MatchString(result.Value) { - return result, nil - } - if err := result.Decrypt(privateKeys); err != nil { - return result, err - } - // Decrypted values should be hashed - result.SHA256 = sha256Hmac(hashKey, result.Value) - return result, nil - } -} diff --git a/apps/cli-go/pkg/config/secret_test.go b/apps/cli-go/pkg/config/secret_test.go deleted file mode 100644 index a83d07666b..0000000000 --- a/apps/cli-go/pkg/config/secret_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package config - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestDecryptSecret(t *testing.T) { - key := "7fd7210cef8f331ee8c55897996aaaafd853a2b20a4dc73d6d75759f65d2a7eb" - value := "encrypted:BKiXH15AyRzeohGyUrmB6cGjSklCrrBjdesQlX1VcXo/Xp20Bi2gGZ3AlIqxPQDmjVAALnhZamKnuY73l8Dz1P+BYiZUgxTSLzdCvdYUyVbNekj2UudbdUizBViERtZkuQwZHIv/" - - t.Run("decrypts secret value", func(t *testing.T) { - // Run test - plaintext, err := decrypt(key, value) - // Check error - assert.NoError(t, err) - assert.Equal(t, "value", plaintext) - }) - - t.Run("throws error on missing key", func(t *testing.T) { - // Run test - plaintext, err := decrypt("", value) - // Check error - assert.ErrorIs(t, err, errMissingKey) - assert.Equal(t, value, plaintext) - }) - - t.Run("throws error on non-hex key", func(t *testing.T) { - // Run test - plaintext, err := decrypt("invalid", value) - // Check error - assert.ErrorContains(t, err, "failed to hex decode private key: cannot decode hex string") - assert.Equal(t, value, plaintext) - }) - - t.Run("throws error on non-base64 value", func(t *testing.T) { - // Run test - plaintext, err := decrypt(key, "encrypted:invalid") - // Check error - assert.ErrorContains(t, err, "failed to base64 decode secret: illegal base64 data at input byte 4") - assert.Equal(t, "encrypted:invalid", plaintext) - }) - - t.Run("throws error on empty ciphertext", func(t *testing.T) { - // Run test - plaintext, err := decrypt(key, "encrypted:") - // Check error - assert.ErrorContains(t, err, "failed to decrypt secret: invalid length of message") - assert.Equal(t, "encrypted:", plaintext) - }) -} diff --git a/apps/cli-go/pkg/config/storage.go b/apps/cli-go/pkg/config/storage.go deleted file mode 100644 index a5d39ef080..0000000000 --- a/apps/cli-go/pkg/config/storage.go +++ /dev/null @@ -1,170 +0,0 @@ -package config - -import ( - v1API "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" - "github.com/supabase/cli/pkg/diff" -) - -type ( - storage struct { - Enabled bool `toml:"enabled" json:"enabled"` - Image string `toml:"-" json:"-"` - TargetMigration string `toml:"-" json:"-"` - ImgProxyImage string `toml:"-" json:"-"` - FileSizeLimit sizeInBytes `toml:"file_size_limit" json:"file_size_limit"` - ImageTransformation *imageTransformation `toml:"image_transformation" json:"image_transformation"` - S3Protocol *s3Protocol `toml:"s3_protocol" json:"s3_protocol"` - S3Credentials storageS3Credentials `toml:"-" json:"-"` - Buckets BucketConfig `toml:"buckets" json:"buckets"` - AnalyticsBuckets analyticsBuckets `toml:"analytics" json:"analytics"` - VectorBuckets vectorBuckets `toml:"vector" json:"vector"` - } - - imageTransformation struct { - Enabled bool `toml:"enabled" json:"enabled"` - } - - analyticsBuckets struct { - Enabled bool `toml:"enabled" json:"enabled"` - MaxNamespaces uint `toml:"max_namespaces" json:"max_namespaces"` - MaxTables uint `toml:"max_tables" json:"max_tables"` - MaxCatalogs uint `toml:"max_catalogs" json:"max_catalogs"` - Buckets map[string]struct{} `toml:"buckets" json:"buckets"` - } - - vectorBuckets struct { - Enabled bool `toml:"enabled" json:"enabled"` - MaxBuckets uint `toml:"max_buckets" json:"max_buckets"` - MaxIndexes uint `toml:"max_indexes" json:"max_indexes"` - Buckets map[string]struct{} `toml:"buckets" json:"buckets"` - } - - s3Protocol struct { - Enabled bool `toml:"enabled" json:"enabled"` - } - - storageS3Credentials struct { - AccessKeyId string `toml:"-" json:"-"` - SecretAccessKey string `toml:"-" json:"-"` - Region string `toml:"-" json:"-"` - } - - BucketConfig map[string]bucket - - bucket struct { - Public *bool `toml:"public" json:"public"` - FileSizeLimit sizeInBytes `toml:"file_size_limit" json:"file_size_limit"` - AllowedMimeTypes []string `toml:"allowed_mime_types" json:"allowed_mime_types"` - ObjectsPath string `toml:"objects_path" json:"objects_path"` - } -) - -func (s *storage) ToUpdateStorageConfigBody() v1API.UpdateStorageConfigBody { - body := v1API.UpdateStorageConfigBody{ - FileSizeLimit: cast.Ptr(int64(s.FileSizeLimit)), - Features: &struct { - IcebergCatalog *struct { - Enabled bool `json:"enabled"` - MaxCatalogs int `json:"maxCatalogs"` - MaxNamespaces int `json:"maxNamespaces"` - MaxTables int `json:"maxTables"` - } `json:"icebergCatalog,omitempty"` - ImageTransformation *struct { - Enabled bool `json:"enabled"` - } `json:"imageTransformation,omitempty"` - PurgeCache *struct { - Enabled bool `json:"enabled"` - } `json:"purgeCache,omitempty"` - S3Protocol *struct { - Enabled bool `json:"enabled"` - } `json:"s3Protocol,omitempty"` - VectorBuckets *struct { - Enabled bool `json:"enabled"` - MaxBuckets int `json:"maxBuckets"` - MaxIndexes int `json:"maxIndexes"` - } `json:"vectorBuckets,omitempty"` - }{}, - } - // When local config is not set, we assume platform defaults should not change - if s.ImageTransformation != nil { - body.Features.ImageTransformation = &struct { - Enabled bool `json:"enabled"` - }{ - Enabled: s.ImageTransformation.Enabled, - } - } - // Disabling analytics and vector buckets means leaving platform values unchanged - if s.AnalyticsBuckets.Enabled { - body.Features.IcebergCatalog = &struct { - Enabled bool `json:"enabled"` - MaxCatalogs int `json:"maxCatalogs"` - MaxNamespaces int `json:"maxNamespaces"` - MaxTables int `json:"maxTables"` - }{ - Enabled: true, - MaxNamespaces: cast.UintToInt(s.AnalyticsBuckets.MaxNamespaces), - MaxTables: cast.UintToInt(s.AnalyticsBuckets.MaxTables), - MaxCatalogs: cast.UintToInt(s.AnalyticsBuckets.MaxCatalogs), - } - } - if s.VectorBuckets.Enabled { - body.Features.VectorBuckets = &struct { - Enabled bool `json:"enabled"` - MaxBuckets int `json:"maxBuckets"` - MaxIndexes int `json:"maxIndexes"` - }{ - Enabled: true, - MaxBuckets: cast.UintToInt(s.VectorBuckets.MaxBuckets), - MaxIndexes: cast.UintToInt(s.VectorBuckets.MaxIndexes), - } - } - if s.S3Protocol != nil { - body.Features.S3Protocol = &struct { - Enabled bool `json:"enabled"` - }{ - Enabled: s.S3Protocol.Enabled, - } - } - return body -} - -func (s *storage) FromRemoteStorageConfig(remoteConfig v1API.StorageConfigResponseOutput) { - s.FileSizeLimit = sizeInBytes(remoteConfig.FileSizeLimit) - if value, err := remoteConfig.MigrationVersion.Get(); err == nil { - s.TargetMigration = value - } - // When local config is not set, we assume platform defaults should not change - if s.ImageTransformation != nil { - s.ImageTransformation.Enabled = remoteConfig.Features.ImageTransformation.Enabled - } - if s.AnalyticsBuckets.Enabled { - s.AnalyticsBuckets.Enabled = remoteConfig.Features.IcebergCatalog.Enabled - s.AnalyticsBuckets.MaxNamespaces = cast.IntToUint(remoteConfig.Features.IcebergCatalog.MaxNamespaces) - s.AnalyticsBuckets.MaxTables = cast.IntToUint(remoteConfig.Features.IcebergCatalog.MaxTables) - s.AnalyticsBuckets.MaxCatalogs = cast.IntToUint(remoteConfig.Features.IcebergCatalog.MaxCatalogs) - } - if s.VectorBuckets.Enabled { - s.VectorBuckets.Enabled = remoteConfig.Features.VectorBuckets.Enabled - s.VectorBuckets.MaxBuckets = cast.IntToUint(remoteConfig.Features.VectorBuckets.MaxBuckets) - s.VectorBuckets.MaxIndexes = cast.IntToUint(remoteConfig.Features.VectorBuckets.MaxIndexes) - } - if s.S3Protocol != nil { - s.S3Protocol.Enabled = remoteConfig.Features.S3Protocol.Enabled - } -} - -func (s *storage) DiffWithRemote(remoteConfig v1API.StorageConfigResponseOutput) ([]byte, error) { - copy := s.Clone() - // Convert the config values into easily comparable remoteConfig values - currentValue, err := ToTomlBytes(copy) - if err != nil { - return nil, err - } - copy.FromRemoteStorageConfig(remoteConfig) - remoteCompare, err := ToTomlBytes(copy) - if err != nil { - return nil, err - } - return diff.Diff("remote[storage]", remoteCompare, "local[storage]", currentValue), nil -} diff --git a/apps/cli-go/pkg/config/storage_test.go b/apps/cli-go/pkg/config/storage_test.go deleted file mode 100644 index 859bb9f413..0000000000 --- a/apps/cli-go/pkg/config/storage_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package config - -import ( - "testing" - - "github.com/oapi-codegen/nullable" - "github.com/stretchr/testify/assert" - v1API "github.com/supabase/cli/pkg/api" -) - -// TargetMigration carries `toml:"-" json:"-"`, so DiffWithRemote cannot observe -// it: the clone it compares is serialized to TOML. These assert the mapping -// directly instead. -func TestStorageMigrationVersionMapping(t *testing.T) { - t.Run("adopts the remote migration version when the platform reports one", func(t *testing.T) { - s := storage{TargetMigration: "local-metadata"} - s.FromRemoteStorageConfig(v1API.StorageConfigResponseOutput{ - MigrationVersion: nullable.NewNullableWithValue("remote-metadata"), - }) - assert.Equal(t, "remote-metadata", s.TargetMigration) - }) - - t.Run("keeps the local migration version when the platform reports null", func(t *testing.T) { - s := storage{TargetMigration: "local-metadata"} - s.FromRemoteStorageConfig(v1API.StorageConfigResponseOutput{ - MigrationVersion: nullable.NewNullNullable[string](), - }) - assert.Equal(t, "local-metadata", s.TargetMigration) - }) - - t.Run("keeps the local migration version when the platform omits the field", func(t *testing.T) { - s := storage{TargetMigration: "local-metadata"} - s.FromRemoteStorageConfig(v1API.StorageConfigResponseOutput{}) - assert.Equal(t, "local-metadata", s.TargetMigration) - }) -} diff --git a/apps/cli-go/pkg/config/templates/Dockerfile b/apps/cli-go/pkg/config/templates/Dockerfile deleted file mode 100644 index b11cc9690a..0000000000 --- a/apps/cli-go/pkg/config/templates/Dockerfile +++ /dev/null @@ -1,47 +0,0 @@ -# The tag pinned below is generated from packages/stack/src/Artifacts.ts by -# apps/cli/scripts/render-service-dockerfile.ts, for these aliases only: pg, pg15, mailpit, -# postgrest, pgmeta, studio, imgproxy, edgeruntime, vector, supavisor, gotrue, realtime, storage, -# logflare. Do not hand-edit those tags; a CI drift check enforces this. Run the generator after -# a catalog update. Everything else in this file (this comment, kong, pg14, the one-shot job -# images) is hand- or Dependabot-managed. Keep a line between image lines: git conflicts on edits to -# adjacent lines, and catalog PRs for neighbouring aliases are often open at the same time. -FROM supabase/postgres:17.11.0.004 AS pg - -FROM supabase/postgres:15.19.0.004 AS pg15 -# Postgres 14 has no slim build; bumped by hand (Dependabot ignores supabase/postgres). -FROM supabase/postgres:14.1.0.89 AS pg14 -# New always-on service alias: extend SERVICE_IMAGE_ALIASES in -# shared/services/services.shared.ts and DOCKERFILE_ALIAS_BY_SERVICE in -# commands/start/start.gates.ts. -FROM library/kong:2.8.1 AS kong - -FROM axllent/mailpit:v1.31.3 AS mailpit - -FROM postgrest/postgrest:v16.4 AS postgrest - -FROM supabase/postgres-meta:v0.100.0 AS pgmeta - -FROM supabase/studio:2026.10.05-sha-94b8b06 AS studio - -FROM darthsim/imgproxy:v3.26.0 AS imgproxy - -FROM supabase/edge-runtime:v1.77.4 AS edgeruntime - -FROM timberio/vector:0.58.0-alpine AS vector - -FROM supabase/supavisor:2.9.13 AS supavisor - -FROM supabase/gotrue:v2.197.0 AS gotrue - -FROM supabase/realtime:v2.142.0 AS realtime - -FROM supabase/storage-api:v1.80.0 AS storage - -FROM supabase/logflare:1.52.0 AS logflare -# One-shot job images (not started as long-running containers); differ's alias -# is resolved via dockerfileServiceImage("differ") in commands/db/diff/pgadmin-diff.ts. -FROM supabase/pgadmin-schema-diff:cli-0.0.5 AS differ - -FROM supabase/migra:3.0.1663481299 AS migra - -FROM supabase/pg_prove:3.36 AS pgprove diff --git a/apps/cli-go/pkg/config/templates/certs/kong.local.crt b/apps/cli-go/pkg/config/templates/certs/kong.local.crt deleted file mode 100644 index 0a7b2dc53c..0000000000 --- a/apps/cli-go/pkg/config/templates/certs/kong.local.crt +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFJTCCAw2gAwIBAgIURUQAFIOhttOg0D12xZvx+pScX4cwDQYJKoZIhvcNAQEL -BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDcwNTE0MjI1NFoXDTM0MDcw -MzE0MjI1NFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEAyL79RrCl78/XutDm7tVNyIJNZqbNSfX+3AsnP1dQc37Q -7QkxmNJ2EmfklTLyvY1A+CY/o+3S1blK+O8lm40yTHP+GC30InKS7lSnCUNqSrHy -DaGNShtphqJoao9vo69/anI6I2tKc8VQ2tebvMVzmjrsvGtWcU4ciyzbUterKQas -Iin8Aw5gDCLswVNZnl6wUvlcgbLmiTLPaJKnNsbYcBJxST2SFxY5q5YWzlugm9Q0 -3913XB64rVoKAyQ86Yabl4UXyVXknyJcbpfnsmn6ORxnZJebnuU0O9y8ys/E9hGw -5rkVTm7QjzsbczMYOgvpPbR1miyDQaZ1bGyj9SmJo1H5e12xO0RNi+awuMnpt3cB -8NeTsZctOgnEQB1Gidq2XcYV48e2OGJTNtVVVPu1UKCNeJur1AfLNPRz8fJq06A+ -Xy5xeRNGFsaeYRt/IO+/CZUOHcY/vR55i8PeeTj9cB1f9KjfmQVeOt25widxrhFK -RkpRgA1ofQ4O8/gPSOcnroQgSs2Whr9RXIF71uSLJyZU0IBxru0cznuEpXxkh7oK -Fb6t/n1SZB2WEQXHh5iG49fw7MT29CzNpJIP1ApT3LKasHZHf4yBn9ZwVAagCGSL -W7kMDhIUhOfVwpyfkXYHzQE2aaCdQ9IfZy9ybO/Y8sbUg6B5i5t8xODutv+fmd0C -AwEAAaNvMG0wHQYDVR0OBBYEFGtw+BogdJ6GJRCe+BHGQdA0f85XMB8GA1UdIwQY -MBaAFGtw+BogdJ6GJRCe+BHGQdA0f85XMA8GA1UdEwEB/wQFMAMBAf8wGgYDVR0R -BBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4ICAQCO9ITlkXil -+dLTddpSq06UuRTYLx8vWGLnd8uEoUg4qlLKi/hKnqTEP9dePw+QeuIM+aLjvvXY -bP4SqBOUi192OohQs7ZPCpjXizmabDNl4/Z+qyiGvZK/N0gWPEaTn6tzeQ03hECK -vY8lRrAP7xLrEYmA6unjfaj3inNUJeqL26i3SIAq/koK4jPm1F/AzwslNH00MJld -xKormGzvNJtaZPr5xrtuLQAmeCotE6av+R5k9zmSYa25Nel50JTk31fNiuUFmECD -qop+Yqfzw9/UkObDWw+g1+N3uHlH9CB2RvfkB2fW9SFMsznjQlq+MyQCwKFs699Q -rNMeHbUEwovDEcPUaLc3d5gCk/dvH9aR8mVLEcs7lMyHSsiG1Co1QXg6UjpMCrIV -Tvk+MrSQVVxw0rKS4H7ZEhfC2PfEVe1az24HrrPWJDZKnRhv1Ohj4jsxkBSprXsu -3qxcMCr7yr4hjsu9BsKtmvRfHTrMOwY1WtZBcl7hjgAItfMs29Fd4Tkn0aZs5f+n -GSwG5BeJL3kfEKwOjw8j+EAqRBu/7mbcoUyErnuLK7FimU4jtL8VX03n6nTgLJzS -xjUTaplgerrazAvV31vMaiINoaGM0RDnGjSCgZT4Va33noqo3qVJJOnWpheFmaXq -bUMQtwrAWTt12NECJt1nWAT4VWhfDoBE7A== ------END CERTIFICATE----- diff --git a/apps/cli-go/pkg/config/templates/certs/kong.local.key b/apps/cli-go/pkg/config/templates/certs/kong.local.key deleted file mode 100644 index 9b71aab612..0000000000 --- a/apps/cli-go/pkg/config/templates/certs/kong.local.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDIvv1GsKXvz9e6 -0Obu1U3Igk1mps1J9f7cCyc/V1BzftDtCTGY0nYSZ+SVMvK9jUD4Jj+j7dLVuUr4 -7yWbjTJMc/4YLfQicpLuVKcJQ2pKsfINoY1KG2mGomhqj2+jr39qcjoja0pzxVDa -15u8xXOaOuy8a1ZxThyLLNtS16spBqwiKfwDDmAMIuzBU1meXrBS+VyBsuaJMs9o -kqc2xthwEnFJPZIXFjmrlhbOW6Cb1DTf3XdcHritWgoDJDzphpuXhRfJVeSfIlxu -l+eyafo5HGdkl5ue5TQ73LzKz8T2EbDmuRVObtCPOxtzMxg6C+k9tHWaLINBpnVs -bKP1KYmjUfl7XbE7RE2L5rC4yem3dwHw15Oxly06CcRAHUaJ2rZdxhXjx7Y4YlM2 -1VVU+7VQoI14m6vUB8s09HPx8mrToD5fLnF5E0YWxp5hG38g778JlQ4dxj+9HnmL -w955OP1wHV/0qN+ZBV463bnCJ3GuEUpGSlGADWh9Dg7z+A9I5yeuhCBKzZaGv1Fc -gXvW5IsnJlTQgHGu7RzOe4SlfGSHugoVvq3+fVJkHZYRBceHmIbj1/DsxPb0LM2k -kg/UClPcspqwdkd/jIGf1nBUBqAIZItbuQwOEhSE59XCnJ+RdgfNATZpoJ1D0h9n -L3Js79jyxtSDoHmLm3zE4O62/5+Z3QIDAQABAoICAAnou5HYuGgtB0YWd7/EUbGb -FP0DMND8zpbICijrQ3JgCSoaMRONF/zymervd+H5bgKRyMRrweOA4P2GuIGEJ750 -X0+MPSfSJgSTsycW59FGFV+s4M+OqNfXfnX8HJOk3xI/DzWeXy91xvb2e56G1J0B -WZw3ZC31oB0SmsTtFmrRBIAvOFxiQkV3F963IY5auDNwUaupTA3rrydHTe+7QwXN -M0BdRK/VDgW1Q+ztwvDOOcnvIawvbAhKkIH1MPiKB3YkQLdUgROF27At2WcKKirV -hxkFDs56G+j0jXEpblyDJQtOiYm0mKDpVwKJvCFpYxsDpGZ22gfMkrFGb5VzdnK9 -6iMyz6eGCKBPd0I66nynZSfZwf+E3n0kGruBAEkTtGztyBxa1EUNul6uzv+Jw3No -o6cb5S7d/+4gFe6g1GoLMuWwtZyC7uXm0aZa64VgY713qkr1dDa5VHjLSs9+o2Jy -LeV6t/9/gcSn7uqtT6kQomYtpYVuEevKt/Bwj/r9Lc+KfPwOUsl5h0zUMw7c+1cL -NfCLadH1lpjB7AIiZwmKyGII4yiiIHSCZU8AKizJUvkW4TaPbtBhLkK6YYOJM+CY -DondkhlnyINaDKyH247Bfl0Ju2yl7IS3s3KUHZGbK1dBc5wC1jW7cp7yA9ZGxk8X -+5NIuozgp6Qd0swh/7XVAoIBAQD3wjJdMgqiuhveVY1tOpKa9mwdW/dSyY421Dvi -XUOEhgWPyheu78aahF3qrhY5yqX4oh643R4qQrok+7wphigLelMboqqb9qM/NL8R -m4jLvlHygHUQhrF8aGjRAKzfnOU0djpCLK1TJrvkxdZfo1ngzxfE9vo4OwtLcRuZ -GMWZNTthUyXEIkw6e4yoJvpk+cb7+TvTnVMjuks5tqI4OgXQ0fYmRixImRg/6PTC -QgsbG3FFxs71vrVJfcFmeymjq0gXHnviMDAgbqjnnHdqhsIauA0cByBfAQWrujUK -N9sTy/Fwni3j8Lxin5HxP8cZxkr2p7ynG98sfGMZFoqGn8JLAoIBAQDPbHRfJz69 -5Os82zumYXfNQw0pfiKn8Jve+dw1i/gSzj8wb98sRZ9w9scY5KBFH1tG5UcWdYtv -UJnUc1OO4mKmVpzrsQjS/qsGTPj2WkXEy3icoyJcDMFw6LBhctNHBwjtKL8Fe+ez -EPHR+Sy7WuvJphZhm817VQXFK5AAmfYJbraI7B9Qm7uP7o1+pv94+886BxWi6Kqm -wlEz+YhCQNPol5Lzth9/a3Ty0cwkaF5hG844Zu+feNWPdT7+nPs4QMWtCuQXD7cY -/UVjyUp8R5lL35woVqTyh2DUbawnAgvy5VR/q6nvnnorBSS5CCEokFTbcC2+5g2s -cquTixmnnzt3AoIBAEEAOApvWI782NbqByLDHTd+szq4qBiWPLNx1ww6f75DEy54 -TOJekfJ0C9fIOJiM7CJhFkJV8NYRj5ioWuNiCRuJykgT00L74DRy+DX8YlVh50oC -HbRqQiJrmiRLUw+PsCqlYf9+jTKllOIebXHF/4yViJzqVxdtljcCw8TU1PKvHpcY -I25juTAWvf5Xy5W2Wlg9OU75lZhoBdhsa/7oaiIxE61S98dMMyqBScW1YgiImJ0/ -3YhnslRHo/fpFDGWfGFkZAghsF6x6apqSIIwPLiZ1Qanb/y83eoB/mxh3x5AgWWU -emG1BRey2a07mFbQyVYRB9On4lNfhvhMC6fqPiUCggEBAIWznv+GyIaW/JrppfWF -dje1NNK+H5CpI5AC9tL9OhbFIy4RroVXC4NBTuzY0gY8RBXazIvJkOtbxQyQmhfM -DVKzGctC/jEjPxJ8oy4I2lgT39b3wLoc7sPc+XSXE2EBQ9u7HAZo690c2wNB3neW -D86XZG3ehAEvuNxzTAtXMqS53Kc9rKHFAI2yczpkYuCVT+pTjrrHxthfAQDDo/Uw -3Gdc7LhzeLIljejHGxOMwulNhwwMLgFZXi9uoAB3J+gGuGTmVEgZNSjCeOvtYDnj -3QhvRrUpxIEgimXjm8g5JYKYGHODL7LpME5yrk0m1FjVUB1yncojeETkVXxxeUP2 -+9kCggEBAMcsbVgvNVwt7bBf1zuw/AiXX4e3Lc2019M/z9SFsu6WfA+OgNMM90lQ -Eqq6XGhlNSDw+Zz8JDPq5RIM3rGlKFWAFSQ+h/4q67LgIDTOKjk7EXpbt/bJIFud -xyiG6uvMSX35OZ5vLsBvv2AzkzR5x2v9kcKD1b5eWn6haEXMOX/X0yGHux9vRylw -zEW1IOA5+2FZvZhwLy+F3XxY3Vrf04mWGyCxiBy4aMvbmp0BcWBg+tRLOmc6h7rX -2Bi8TfeE+GnmJkezD44MH/S+EEfafOkiDqoDCxxgYpJzXiE6DZNEUGdpUm87hdx4 -MviBG5FCtAAJp+5btxy72dIKBYBVUJ8= ------END PRIVATE KEY----- diff --git a/apps/cli-go/pkg/config/templates/config.toml b/apps/cli-go/pkg/config/templates/config.toml deleted file mode 100644 index c26043d5c9..0000000000 --- a/apps/cli-go/pkg/config/templates/config.toml +++ /dev/null @@ -1,416 +0,0 @@ -# For detailed configuration reference documentation, visit: -# https://supabase.com/docs/guides/local-development/cli/config -# A string used to distinguish different Supabase projects on the same host. Defaults to the -# working directory name when running `supabase init`. -project_id = "{{ .ProjectId }}" - -[api] -enabled = true -# Port to use for the API URL. -port = 54321 -# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API -# endpoints. `public` and `graphql_public` schemas are included by default. -schemas = ["public", "graphql_public"] -# Extra schemas to add to the search_path of every request. -extra_search_path = ["public", "extensions"] -# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size -# for accidental or malicious requests. -max_rows = 1000 -# Controls whether new tables, views, sequences and functions created in the `public` schema by -# `postgres` are reachable through the Data API roles (`anon`, `authenticated`, `service_role`) -# without explicit GRANTs. When unset, new entities are NOT auto-exposed, matching the new cloud -# default. Set to `true` to keep the legacy behaviour of auto-exposing new entities; this is -# deprecated and the field is removed on 2026-10-30 once the always-revoked behaviour is permanent. -# auto_expose_new_tables = true - -[api.tls] -# Enable HTTPS endpoints locally using a self-signed certificate. -enabled = false -# Paths to self-signed certificate pair. -# cert_path = "../certs/my-cert.pem" -# key_path = "../certs/my-key.pem" - -[db] -# Port to use for the local database URL. -port = 54322 -# Port used by db diff command to initialize the shadow database. -shadow_port = 54320 -# Maximum amount of time to wait for health check when starting the local database. -health_timeout = "2m" -# The database major version to use. This has to be the same as your remote database's. Run `SHOW -# server_version;` on the remote database to check. -major_version = 17 -# OrioleDB version to use as the Postgres storage engine. Leave empty to use the default engine. -orioledb_version = "{{ .Db.OrioleDBVersion }}" - -[db.pooler] -enabled = false -# Port to use for the local connection pooler. -port = 54329 -# Specifies when a server connection can be reused by other clients. -# Configure one of the supported pooler modes: `transaction`, `session`. -pool_mode = "transaction" -# How many server connections to allow per user/database pair. -default_pool_size = 20 -# Maximum number of client connections allowed. -max_client_conn = 100 - -# [db.vault] -# secret_key = "env(SECRET_VALUE)" - -[db.migrations] -# If disabled, migrations will be skipped during a db push or reset. -enabled = true -# Specifies an ordered list of schema files, directories, or glob patterns that describe your database. -# Supports paths relative to supabase directory: "./schemas/*.sql", "./database". -schema_paths = [] - -[db.seed] -# If enabled, seeds the database after migrations during a db reset. -enabled = true -# Specifies an ordered list of seed files to load during db reset. -# Supports glob patterns relative to supabase directory: "./seeds/*.sql" -sql_paths = ["./seed.sql"] - -[db.network_restrictions] -# Enable management of network restrictions. -enabled = false -# List of IPv4 CIDR blocks allowed to connect to the database. -# Defaults to allow all IPv4 connections. Set empty array to block all IPs. -allowed_cidrs = ["0.0.0.0/0"] -# List of IPv6 CIDR blocks allowed to connect to the database. -# Defaults to allow all IPv6 connections. Set empty array to block all IPs. -allowed_cidrs_v6 = ["::/0"] - -# Uncomment to reject non-secure connections to the database. -# [db.ssl_enforcement] -# enabled = true - -[realtime] -enabled = true -# Bind realtime via either IPv4 or IPv6. (default: IPv4) -# ip_version = "IPv6" -# The maximum length in bytes of HTTP request headers. (default: 4096) -# max_header_length = 4096 - -[studio] -enabled = true -# Port to use for Supabase Studio. -port = 54323 -# External URL of the API server that frontend connects to. -api_url = "http://127.0.0.1" -# OpenAI API Key to use for Supabase AI in the Supabase Studio. -openai_api_key = "env(OPENAI_API_KEY)" - -# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they -# are monitored, and you can view the emails that would have been sent from the web interface. -[local_smtp] -enabled = true -# Port to use for the email testing server web interface. -port = 54324 -# Uncomment to expose additional ports for testing user applications that send emails. -# smtp_port = 54325 -# pop3_port = 54326 -# admin_email = "admin@email.com" -# sender_name = "Admin" - -[storage] -enabled = true -# The maximum file size allowed (e.g. "5MB", "500KB"). -file_size_limit = "50MiB" - -# Uncomment to configure local storage buckets -# [storage.buckets.images] -# public = false -# file_size_limit = "50MiB" -# allowed_mime_types = ["image/png", "image/jpeg"] -# objects_path = "./images" - -# Allow connections via S3 compatible clients -[storage.s3_protocol] -enabled = true - -# Image transformation API is available to Supabase Pro plan. -# [storage.image_transformation] -# enabled = true - -# Store analytical data in S3 for running ETL jobs over Iceberg Catalog -# This feature is only available on the hosted platform. -[storage.analytics] -enabled = false -max_namespaces = 5 -max_tables = 10 -max_catalogs = 2 - -# Analytics Buckets is available to Supabase Pro plan. -# [storage.analytics.buckets.my-warehouse] - -# Store vector embeddings in S3 for large and durable datasets -[storage.vector] -enabled = true -max_buckets = 10 -max_indexes = 5 - -# Vector Buckets is available to Supabase Pro plan. -# [storage.vector.buckets.documents-openai] - -[auth] -enabled = true -# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used -# in emails. -site_url = "http://127.0.0.1:3000" -# The public URL that Auth serves on. Defaults to the API external URL with `/auth/v1` appended. -# external_url = "" -# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. -additional_redirect_urls = ["https://127.0.0.1:3000"] -# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week). -jwt_expiry = 3600 -# JWT issuer URL. If not set, defaults to auth.external_url. -# jwt_issuer = "" -# Path to JWT signing key. DO NOT commit your signing keys file to git. -# signing_keys_path = "./signing_keys.json" -# If disabled, the refresh token will never expire. -enable_refresh_token_rotation = true -# Allows refresh tokens to be reused after expiry, up to the specified interval in seconds. -# Requires enable_refresh_token_rotation = true. -refresh_token_reuse_interval = 10 -# Allow/disallow new user signups to your project. -enable_signup = true -# Allow/disallow anonymous sign-ins to your project. -enable_anonymous_sign_ins = false -# Allow/disallow testing manual linking of accounts -enable_manual_linking = false -# Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more. -minimum_password_length = 6 -# Passwords that do not meet the following requirements will be rejected as weak. Supported values -# are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols` -password_requirements = "" - -# Configure passkey sign-ins. -# [auth.passkey] -# enabled = false - -# Configure WebAuthn relying party settings (required when passkey is enabled). -# [auth.webauthn] -# rp_display_name = "Supabase" -# rp_id = "localhost" -# rp_origins = ["http://127.0.0.1:3000"] - -[auth.rate_limit] -# Number of emails that can be sent per hour. Requires auth.email.smtp to be enabled. -email_sent = 2 -# Number of SMS messages that can be sent per hour. Requires auth.sms to be enabled. -sms_sent = 30 -# Number of anonymous sign-ins that can be made per hour per IP address. Requires enable_anonymous_sign_ins = true. -anonymous_users = 30 -# Number of sessions that can be refreshed in a 5 minute interval per IP address. -token_refresh = 150 -# Number of sign up and sign-in requests that can be made in a 5 minute interval per IP address (excludes anonymous users). -sign_in_sign_ups = 30 -# Number of OTP / Magic link verifications that can be made in a 5 minute interval per IP address. -token_verifications = 30 -# Number of Web3 logins that can be made in a 5 minute interval per IP address. -web3 = 30 - -# Configure one of the supported captcha providers: `hcaptcha`, `turnstile`. -# [auth.captcha] -# enabled = true -# provider = "hcaptcha" -# secret = "" - -[auth.email] -# Allow/disallow new user signups via email to your project. -enable_signup = true -# If enabled, a user will be required to confirm any email change on both the old, and new email -# addresses. If disabled, only the new email is required to confirm. -double_confirm_changes = true -# If enabled, users need to confirm their email address before signing in. -enable_confirmations = false -# If enabled, users will need to reauthenticate or have logged in recently to change their password. -secure_password_change = false -# Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email. -max_frequency = "1s" -# Number of characters used in the email OTP. -otp_length = 6 -# Number of seconds before the email OTP expires (defaults to 1 hour). -otp_expiry = 3600 - -# Use a production-ready SMTP server -# [auth.email.smtp] -# enabled = true -# host = "smtp.sendgrid.net" -# port = 587 -# user = "apikey" -# pass = "env(SENDGRID_API_KEY)" -# admin_email = "admin@email.com" -# sender_name = "Admin" - -# Uncomment to customize email template -# [auth.email.template.invite] -# subject = "You have been invited" -# content_path = "./supabase/templates/invite.html" - -# Uncomment to customize notification email template -# [auth.email.notification.password_changed] -# enabled = true -# subject = "Your password has been changed" -# content_path = "./templates/password_changed_notification.html" - -[auth.sms] -# Allow/disallow new user signups via SMS to your project. -enable_signup = false -# If enabled, users need to confirm their phone number before signing in. -enable_confirmations = false -# Template for sending OTP to users -template = "Your code is {{ `{{ .Code }}` }}" -# Controls the minimum amount of time that must pass before sending another sms otp. -max_frequency = "5s" - -# Use pre-defined map of phone number to OTP for testing. -# [auth.sms.test_otp] -# 4152127777 = "123456" - -# Configure logged in session timeouts. -# [auth.sessions] -# Force log out after the specified duration. -# timebox = "24h" -# Force log out if the user has been inactive longer than the specified duration. -# inactivity_timeout = "8h" - -# This hook runs before a new user is created and allows developers to reject the request based on the incoming user object. -# [auth.hook.before_user_created] -# enabled = true -# uri = "pg-functions://postgres/auth/before-user-created-hook" - -# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used. -# [auth.hook.custom_access_token] -# enabled = true -# uri = "pg-functions:////" - -# Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`. -[auth.sms.twilio] -enabled = false -account_sid = "" -message_service_sid = "" -# DO NOT commit your Twilio auth token to git. Use environment variable substitution instead: -auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)" - -# Multi-factor-authentication is available to Supabase Pro plan. -[auth.mfa] -# Control how many MFA factors can be enrolled at once per user. -max_enrolled_factors = 10 - -# Control MFA via App Authenticator (TOTP) -[auth.mfa.totp] -enroll_enabled = false -verify_enabled = false - -# Configure MFA via Phone Messaging -[auth.mfa.phone] -enroll_enabled = false -verify_enabled = false -otp_length = 6 -template = "Your code is {{ `{{ .Code }}` }}" -max_frequency = "5s" - -# Configure MFA via WebAuthn -# [auth.mfa.web_authn] -# enroll_enabled = true -# verify_enabled = true - -# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, -# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`, -# `twitter`, `x`, `slack`, `spotify`, `workos`, `zoom`. -[auth.external.apple] -enabled = false -client_id = "" -# DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: -secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" -# Overrides the default auth callback URL derived from auth.external_url. -redirect_uri = "" -# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, -# or any other third-party OIDC providers. -url = "" -# If enabled, the nonce check will be skipped. Required for local sign in with Google auth. -skip_nonce_check = false -# If enabled, it will allow the user to successfully authenticate when the provider does not return an email address. -email_optional = false - -# Allow Solana wallet holders to sign in to your project via the Sign in with Solana (SIWS, EIP-4361) standard. -# You can configure "web3" rate limit in the [auth.rate_limit] section and set up [auth.captcha] if self-hosting. -[auth.web3.solana] -enabled = false - -# Use Firebase Auth as a third-party provider alongside Supabase Auth. -[auth.third_party.firebase] -enabled = false -# project_id = "my-firebase-project" - -# Use Auth0 as a third-party provider alongside Supabase Auth. -[auth.third_party.auth0] -enabled = false -# tenant = "my-auth0-tenant" -# tenant_region = "us" - -# Use AWS Cognito (Amplify) as a third-party provider alongside Supabase Auth. -[auth.third_party.aws_cognito] -enabled = false -# user_pool_id = "my-user-pool-id" -# user_pool_region = "us-east-1" - -# Use Clerk as a third-party provider alongside Supabase Auth. -[auth.third_party.clerk] -enabled = false -# Obtain from https://clerk.com/setup/supabase -# domain = "example.clerk.accounts.dev" - -# OAuth server configuration -[auth.oauth_server] -# Enable OAuth server functionality -enabled = false -# Path for OAuth consent flow UI -authorization_url_path = "/oauth/consent" -# Allow dynamic client registration -allow_dynamic_registration = false - -[edge_runtime] -enabled = true -# Supported request policies: `oneshot`, `per_worker`. -# `per_worker` (default) — enables hot reload during local development. -# `oneshot` — fallback mode if hot reload causes issues (e.g. in large repos or with symlinks). -policy = "per_worker" -# Port to attach the Chrome inspector for debugging edge functions. -inspector_port = 8083 -# The Deno major version to use. -deno_version = 2 - -# [edge_runtime.secrets] -# secret_key = "env(SECRET_VALUE)" - -[analytics] -enabled = true -port = 54327 -# Configure one of the supported backends: `postgres`, `bigquery`. -backend = "postgres" - -# Experimental features may be deprecated any time -[experimental] -# Configures S3 bucket URL, eg. .s3-.amazonaws.com -s3_host = "env(S3_HOST)" -# Configures S3 bucket region, eg. us-east-1 -s3_region = "env(S3_REGION)" -# Configures AWS_ACCESS_KEY_ID for S3 bucket -s3_access_key = "env(S3_ACCESS_KEY)" -# Configures AWS_SECRET_ACCESS_KEY for S3 bucket -s3_secret_key = "env(S3_SECRET_KEY)" - -# pg-delta is the schema diff engine for db diff / db pull / db remote commit. -# Set enabled = false to fall back to the legacy migra engine. -[experimental.pgdelta] -enabled = {{ .Experimental.PgDeltaInitEnabled }} -# Directory under `supabase/` where declarative files are written. -# declarative_schema_path = "./schemas" -# JSON string passed through to pg-delta SQL formatting. When omitted, SQL is -# formatted with uppercase keywords, indent 2, max width 180, trailing commas, -# and column/key alignment. Set to "null" to emit raw, unformatted SQL. -# format_options = "{\"keywordCase\":\"upper\",\"indent\":2,\"maxWidth\":180,\"commaStyle\":\"trailing\"}" diff --git a/apps/cli-go/pkg/config/testdata/TestApiDiff/detects_differences.diff b/apps/cli-go/pkg/config/testdata/TestApiDiff/detects_differences.diff deleted file mode 100644 index 5d0e9603ef..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestApiDiff/detects_differences.diff +++ /dev/null @@ -1,14 +0,0 @@ -diff remote[api] local[api] ---- remote[api] -+++ local[api] -@@ -1,7 +1,7 @@ - enabled = true --schemas = ["public"] --extra_search_path = ["public"] --max_rows = 500 -+schemas = ["public", "private"] -+extra_search_path = ["extensions", "public"] -+max_rows = 1000 - port = 0 - external_url = "" - diff --git a/apps/cli-go/pkg/config/testdata/TestApiDiff/handles_api_disabled_on_local_side.diff b/apps/cli-go/pkg/config/testdata/TestApiDiff/handles_api_disabled_on_local_side.diff deleted file mode 100644 index 67a5ccd5ca..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestApiDiff/handles_api_disabled_on_local_side.diff +++ /dev/null @@ -1,9 +0,0 @@ -diff remote[api] local[api] ---- remote[api] -+++ local[api] -@@ -1,4 +1,4 @@ --enabled = true -+enabled = false - schemas = ["public"] - extra_search_path = ["public"] - max_rows = 500 diff --git a/apps/cli-go/pkg/config/testdata/TestApiDiff/handles_api_disabled_on_remote_side.diff b/apps/cli-go/pkg/config/testdata/TestApiDiff/handles_api_disabled_on_remote_side.diff deleted file mode 100644 index 200d36c403..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestApiDiff/handles_api_disabled_on_remote_side.diff +++ /dev/null @@ -1,9 +0,0 @@ -diff remote[api] local[api] ---- remote[api] -+++ local[api] -@@ -1,4 +1,4 @@ --enabled = false -+enabled = true - schemas = ["public", "private"] - extra_search_path = ["extensions", "public"] - max_rows = 500 diff --git a/apps/cli-go/pkg/config/testdata/TestAuthDiff/local_enabled_and_disabled.diff b/apps/cli-go/pkg/config/testdata/TestAuthDiff/local_enabled_and_disabled.diff deleted file mode 100644 index 21094fdd40..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestAuthDiff/local_enabled_and_disabled.diff +++ /dev/null @@ -1,30 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -1,16 +1,16 @@ - enabled = false --site_url = "" -+site_url = "http://127.0.0.1:3000" - external_url = "" --additional_redirect_urls = ["https://127.0.0.1:3000", "https://ref.supabase.co"] --jwt_expiry = 0 -+additional_redirect_urls = ["https://127.0.0.1:3000"] -+jwt_expiry = 3600 - jwt_issuer = "" --enable_refresh_token_rotation = true --refresh_token_reuse_interval = 0 --enable_manual_linking = true --enable_signup = true --enable_anonymous_sign_ins = true --minimum_password_length = 8 --password_requirements = "letters_digits" -+enable_refresh_token_rotation = false -+refresh_token_reuse_interval = 10 -+enable_manual_linking = false -+enable_signup = false -+enable_anonymous_sign_ins = false -+minimum_password_length = 6 -+password_requirements = "lower_upper_letters_digits_symbols" - signing_keys_path = "" - publishable_key = "" - secret_key = "" diff --git a/apps/cli-go/pkg/config/testdata/TestCaptchaDiff/local_disabled_remote_enabled.diff b/apps/cli-go/pkg/config/testdata/TestCaptchaDiff/local_disabled_remote_enabled.diff deleted file mode 100644 index 2df3d18a24..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestCaptchaDiff/local_disabled_remote_enabled.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -28,7 +28,7 @@ - web3 = 0 - - [captcha] --enabled = true -+enabled = false - provider = "turnstile" - secret = "hash:ed64b7695a606bc6ab4fcb41fe815b5ddf1063ccbc87afe1fa89756635db520e" - diff --git a/apps/cli-go/pkg/config/testdata/TestCaptchaDiff/local_enabled_remote_disabled.diff b/apps/cli-go/pkg/config/testdata/TestCaptchaDiff/local_enabled_remote_disabled.diff deleted file mode 100644 index ea98e88092..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestCaptchaDiff/local_enabled_remote_disabled.diff +++ /dev/null @@ -1,16 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -28,9 +28,9 @@ - web3 = 0 - - [captcha] --enabled = false --provider = "hcaptcha" --secret = "hash:ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252" -+enabled = true -+provider = "turnstile" -+secret = "hash:ed64b7695a606bc6ab4fcb41fe815b5ddf1063ccbc87afe1fa89756635db520e" - - [hook] - diff --git a/apps/cli-go/pkg/config/testdata/TestDbSettingsDiff/detects_differences.diff b/apps/cli-go/pkg/config/testdata/TestDbSettingsDiff/detects_differences.diff deleted file mode 100644 index 12aeb50ae3..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestDbSettingsDiff/detects_differences.diff +++ /dev/null @@ -1,10 +0,0 @@ -diff remote[db.settings] local[db.settings] ---- remote[db.settings] -+++ local[db.settings] -@@ -1,3 +1,3 @@ --effective_cache_size = "8GB" --max_connections = 200 --shared_buffers = "2GB" -+effective_cache_size = "4GB" -+max_connections = 100 -+shared_buffers = "1GB" diff --git a/apps/cli-go/pkg/config/testdata/TestDbSettingsDiff/handles_api_disabled_on_local_side.diff b/apps/cli-go/pkg/config/testdata/TestDbSettingsDiff/handles_api_disabled_on_local_side.diff deleted file mode 100644 index 392143fd13..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestDbSettingsDiff/handles_api_disabled_on_local_side.diff +++ /dev/null @@ -1,7 +0,0 @@ -diff remote[db.settings] local[db.settings] ---- remote[db.settings] -+++ local[db.settings] -@@ -1,3 +0,0 @@ --effective_cache_size = "4GB" --max_connections = 100 --shared_buffers = "1GB" diff --git a/apps/cli-go/pkg/config/testdata/TestDbSettingsDiff/handles_api_disabled_on_remote_side.diff b/apps/cli-go/pkg/config/testdata/TestDbSettingsDiff/handles_api_disabled_on_remote_side.diff deleted file mode 100644 index 9382425387..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestDbSettingsDiff/handles_api_disabled_on_remote_side.diff +++ /dev/null @@ -1,7 +0,0 @@ -diff remote[db.settings] local[db.settings] ---- remote[db.settings] -+++ local[db.settings] -@@ -0,0 +1,3 @@ -+effective_cache_size = "4GB" -+max_connections = 100 -+shared_buffers = "1GB" diff --git a/apps/cli-go/pkg/config/testdata/TestEmailDiff/local_disabled_remote_enabled.diff b/apps/cli-go/pkg/config/testdata/TestEmailDiff/local_disabled_remote_enabled.diff deleted file mode 100644 index d341bab3f4..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestEmailDiff/local_disabled_remote_enabled.diff +++ /dev/null @@ -1,57 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -49,13 +49,13 @@ - inactivity_timeout = "0s" - - [email] --enable_signup = true --double_confirm_changes = true --enable_confirmations = true --secure_password_change = true --max_frequency = "1s" --otp_length = 6 --otp_expiry = 3600 -+enable_signup = false -+double_confirm_changes = false -+enable_confirmations = false -+secure_password_change = false -+max_frequency = "1m0s" -+otp_length = 8 -+otp_expiry = 86400 - [email.template] - [email.template.confirmation] - content_path = "" -@@ -71,25 +71,25 @@ - content_path = "" - [email.notification] - [email.notification.email_changed] --enabled = true -+enabled = false - content_path = "" - [email.notification.identity_linked] --enabled = true -+enabled = false - content_path = "" - [email.notification.identity_unlinked] --enabled = true -+enabled = false - content_path = "" - [email.notification.mfa_factor_enrolled] --enabled = true -+enabled = false - content_path = "" - [email.notification.mfa_factor_unenrolled] --enabled = true -+enabled = false - content_path = "" - [email.notification.password_changed] --enabled = true -+enabled = false - content_path = "" - [email.notification.phone_changed] --enabled = true -+enabled = false - content_path = "" - - [sms] diff --git a/apps/cli-go/pkg/config/testdata/TestEmailDiff/local_enabled_remote_disabled.diff b/apps/cli-go/pkg/config/testdata/TestEmailDiff/local_enabled_remote_disabled.diff deleted file mode 100644 index c3a49ddf02..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestEmailDiff/local_enabled_remote_disabled.diff +++ /dev/null @@ -1,99 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -49,62 +49,74 @@ - inactivity_timeout = "0s" - - [email] --enable_signup = false --double_confirm_changes = false --enable_confirmations = false --secure_password_change = false --max_frequency = "1m0s" --otp_length = 6 --otp_expiry = 3600 -+enable_signup = true -+double_confirm_changes = true -+enable_confirmations = true -+secure_password_change = true -+max_frequency = "1s" -+otp_length = 8 -+otp_expiry = 86400 - [email.template] - [email.template.confirmation] -+subject = "confirmation-subject" - content_path = "" - [email.template.email_change] -+subject = "email-change-subject" - content = "email-change-content" - content_path = "" - [email.template.invite] -+subject = "invite-subject" -+content = "invite-content" - content_path = "" - [email.template.magic_link] - subject = "magic-link-subject" -+content = "magic-link-content" - content_path = "" - [email.template.reauthentication] -+subject = "" -+content = "" - content_path = "" - [email.template.recovery] -+content = "recovery-content" - content_path = "" - [email.notification] - [email.notification.email_changed] --enabled = false -+enabled = true - subject = "email-changed-subject" -+content = "email-changed-content" - content_path = "" - [email.notification.identity_linked] --enabled = false -+enabled = true -+subject = "identity-linked-subject" -+content = "identity-linked-content" - content_path = "" - [email.notification.identity_unlinked] --enabled = false -+enabled = true - content_path = "" - [email.notification.mfa_factor_enrolled] --enabled = false -+enabled = true - content_path = "" - [email.notification.mfa_factor_unenrolled] --enabled = false -+enabled = true - content_path = "" - [email.notification.password_changed] --enabled = false -+enabled = true - subject = "password-changed-subject" - content = "password-changed-content" - content_path = "" - [email.notification.phone_changed] --enabled = false -+enabled = true -+subject = "phone-changed-subject" - content = "phone-changed-content" - content_path = "" - [email.smtp] --enabled = false --host = "" --port = 0 --user = "" --pass = "" --admin_email = "" --sender_name = "" -+enabled = true -+host = "smtp.sendgrid.net" -+port = 587 -+user = "apikey" -+pass = "hash:ed64b7695a606bc6ab4fcb41fe815b5ddf1063ccbc87afe1fa89756635db520e" -+admin_email = "admin@email.com" -+sender_name = "Admin" - - [sms] - enable_signup = false diff --git a/apps/cli-go/pkg/config/testdata/TestExternalDiff/local_enabled_and_disabled.diff b/apps/cli-go/pkg/config/testdata/TestExternalDiff/local_enabled_and_disabled.diff deleted file mode 100644 index 54ca03e506..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestExternalDiff/local_enabled_and_disabled.diff +++ /dev/null @@ -1,34 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -89,7 +89,7 @@ - - [external] - [external.apple] --enabled = false -+enabled = true - client_id = "test-client-1,test-client-2" - secret = "hash:ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252" - url = "" -@@ -97,9 +97,9 @@ - skip_nonce_check = false - email_optional = false - [external.azure] --enabled = false --client_id = "" --secret = "" -+enabled = true -+client_id = "test-client-1" -+secret = "hash:ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252" - url = "" - redirect_uri = "" - skip_nonce_check = false -@@ -153,7 +153,7 @@ - skip_nonce_check = false - email_optional = false - [external.google] --enabled = true -+enabled = false - client_id = "test-client-2" - secret = "" - url = "" diff --git a/apps/cli-go/pkg/config/testdata/TestHookDiff/local_disabled_remote_enabled.diff b/apps/cli-go/pkg/config/testdata/TestHookDiff/local_disabled_remote_enabled.diff deleted file mode 100644 index d0b4dcc30e..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestHookDiff/local_disabled_remote_enabled.diff +++ /dev/null @@ -1,32 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -29,23 +29,23 @@ - - [hook] - [hook.mfa_verification_attempt] --enabled = true -+enabled = false - uri = "pg-functions://postgres/public/verifyMFA" - secrets = "" - [hook.custom_access_token] --enabled = true -+enabled = false - uri = "" - secrets = "" - [hook.send_sms] --enabled = true -+enabled = false - uri = "https://example.com" - secrets = "" - [hook.send_email] --enabled = true -+enabled = false - uri = "" - secrets = "" - [hook.before_user_created] --enabled = true -+enabled = false - uri = "" - secrets = "" - diff --git a/apps/cli-go/pkg/config/testdata/TestHookDiff/local_enabled_remote_disabled.diff b/apps/cli-go/pkg/config/testdata/TestHookDiff/local_enabled_remote_disabled.diff deleted file mode 100644 index 3c3364d17b..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestHookDiff/local_enabled_remote_disabled.diff +++ /dev/null @@ -1,39 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -29,25 +29,25 @@ - - [hook] - [hook.mfa_verification_attempt] --enabled = false -+enabled = true - uri = "pg-functions://postgres/public/verifyMFA" - secrets = "" - [hook.custom_access_token] --enabled = false --uri = "pg-functions://postgres/public/customToken" --secrets = "" -+enabled = true -+uri = "http://example.com" -+secrets = "hash:ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252" - [hook.send_sms] --enabled = false -+enabled = true - uri = "https://example.com" - secrets = "hash:ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252" - [hook.send_email] --enabled = false --uri = "https://example.com" -+enabled = true -+uri = "pg-functions://postgres/public/sendEmail" - secrets = "" - [hook.before_user_created] --enabled = false --uri = "pg-functions://postgres/public/beforeUserCreated" --secrets = "" -+enabled = true -+uri = "http://example.com" -+secrets = "hash:ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252" - - [mfa] - max_enrolled_factors = 0 diff --git a/apps/cli-go/pkg/config/testdata/TestMfaDiff/local_enabled_and_disabled.diff b/apps/cli-go/pkg/config/testdata/TestMfaDiff/local_enabled_and_disabled.diff deleted file mode 100644 index ec1f6ebe90..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestMfaDiff/local_enabled_and_disabled.diff +++ /dev/null @@ -1,26 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -30,16 +30,16 @@ - [hook] - - [mfa] --max_enrolled_factors = 10 -+max_enrolled_factors = 0 - [mfa.totp] - enroll_enabled = false - verify_enabled = false - [mfa.phone] --enroll_enabled = false --verify_enabled = false --otp_length = 6 --template = "Your code is {{ .Code }}" --max_frequency = "5s" -+enroll_enabled = true -+verify_enabled = true -+otp_length = 0 -+template = "" -+max_frequency = "0s" - [mfa.web_authn] - enroll_enabled = false - verify_enabled = false diff --git a/apps/cli-go/pkg/config/testdata/TestRateLimitsDiff/local_and_remote_rate_limits_differ.diff b/apps/cli-go/pkg/config/testdata/TestRateLimitsDiff/local_and_remote_rate_limits_differ.diff deleted file mode 100644 index 9663c1438b..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestRateLimitsDiff/local_and_remote_rate_limits_differ.diff +++ /dev/null @@ -1,20 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -19,12 +19,12 @@ - service_role_key = "" - - [rate_limit] --anonymous_users = 10 -+anonymous_users = 20 - token_refresh = 30 --sign_in_sign_ups = 45 -+sign_in_sign_ups = 40 - token_verifications = 50 --email_sent = 15 --sms_sent = 55 -+email_sent = 25 -+sms_sent = 35 - web3 = 0 - - [hook] diff --git a/apps/cli-go/pkg/config/testdata/TestSmsDiff/enable_sign_up_without_provider.diff b/apps/cli-go/pkg/config/testdata/TestSmsDiff/enable_sign_up_without_provider.diff deleted file mode 100644 index 2e44496fd3..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestSmsDiff/enable_sign_up_without_provider.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -58,7 +58,7 @@ - otp_expiry = 0 - - [sms] --enable_signup = false -+enable_signup = true - enable_confirmations = false - template = "" - max_frequency = "0s" diff --git a/apps/cli-go/pkg/config/testdata/TestSmsDiff/local_disabled_remote_enabled.diff b/apps/cli-go/pkg/config/testdata/TestSmsDiff/local_disabled_remote_enabled.diff deleted file mode 100644 index 385f49fb83..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestSmsDiff/local_disabled_remote_enabled.diff +++ /dev/null @@ -1,30 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -58,12 +58,12 @@ - otp_expiry = 0 - - [sms] --enable_signup = true --enable_confirmations = true --template = "Your code is {{ .Code }}" --max_frequency = "1m0s" -+enable_signup = false -+enable_confirmations = false -+template = "" -+max_frequency = "0s" - [sms.twilio] --enabled = true -+enabled = false - account_sid = "" - message_service_sid = "" - auth_token = "" -@@ -86,8 +86,6 @@ - api_key = "" - api_secret = "" - [sms.test_otp] --123 = "456" --456 = "123" - - [web3] - [web3.solana] diff --git a/apps/cli-go/pkg/config/testdata/TestSmsDiff/local_enabled_remote_disabled.diff b/apps/cli-go/pkg/config/testdata/TestSmsDiff/local_enabled_remote_disabled.diff deleted file mode 100644 index 3db44eabab..0000000000 --- a/apps/cli-go/pkg/config/testdata/TestSmsDiff/local_enabled_remote_disabled.diff +++ /dev/null @@ -1,42 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -58,12 +58,12 @@ - otp_expiry = 0 - - [sms] --enable_signup = false --enable_confirmations = false --template = "" --max_frequency = "0s" -+enable_signup = true -+enable_confirmations = true -+template = "Your code is {{ .Code }}" -+max_frequency = "1m0s" - [sms.twilio] --enabled = true -+enabled = false - account_sid = "" - message_service_sid = "" - auth_token = "" -@@ -73,9 +73,9 @@ - message_service_sid = "" - auth_token = "" - [sms.messagebird] --enabled = false --originator = "" --access_key = "" -+enabled = true -+originator = "test-originator" -+access_key = "hash:ab60d03fc809fb02dae838582f3ddc13d1d6cb32ffba77c4b969dd3caa496f13" - [sms.textlocal] - enabled = false - sender = "" -@@ -86,6 +86,7 @@ - api_key = "" - api_secret = "" - [sms.test_otp] -+123 = "456" - - [web3] - [web3.solana] diff --git a/apps/cli-go/pkg/config/testdata/config.toml b/apps/cli-go/pkg/config/testdata/config.toml deleted file mode 100644 index 4c6ffc0c80..0000000000 --- a/apps/cli-go/pkg/config/testdata/config.toml +++ /dev/null @@ -1,378 +0,0 @@ -# For detailed configuration reference documentation, visit: -# https://supabase.com/docs/guides/local-development/cli/config -# A string used to distinguish different Supabase projects on the same host. Defaults to the -# working directory name when running `supabase init`. -project_id = "test" - -[api] -enabled = true -# Port to use for the API URL. -port = 54321 -# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API -# endpoints. `public` and `graphql_public` schemas are included by default. -schemas = ["public", "graphql_public"] -# Extra schemas to add to the search_path of every request. public is always included. -extra_search_path = ["public", "extensions"] -# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size -# for accidental or malicious requests. -max_rows = 1000 - -[api.tls] -# Enable HTTPS endpoints locally using a self-signed certificate. -enabled = true -# Paths to self-signed certificate pair. -cert_path = "../certs/my-cert.pem" -key_path = "../certs/my-key.pem" - -[db] -# Port to use for the local database URL. -port = 54322 -# Port used by db diff command to initialize the shadow database. -shadow_port = 54320 -# Maximum amount of time to wait for health check when starting the local database. -health_timeout = "2m" -# The database major version to use. This has to be the same as your remote database's. Run `SHOW -# server_version;` on the remote database to check. -major_version = 17 -orioledb_version = "15.1.0.150" - -[db.migrations] -# If disabled, migrations will be skipped during a db push or reset. -enabled = true -# Specifies an ordered list of schema files that describe your database. -# Supports glob patterns relative to supabase directory: "./schemas/*.sql" -schema_paths = ["./schemas/*.sql"] - -[db.pooler] -enabled = true -# Port to use for the local connection pooler. -port = 54329 -# Specifies when a server connection can be reused by other clients. -# Configure one of the supported pooler modes: `transaction`, `session`. -pool_mode = "transaction" -# How many server connections to allow per user/database pair. -default_pool_size = 20 -# Maximum number of client connections allowed. -max_client_conn = 100 - -[db.vault] -test_key = "test_value" - -[db.seed] -# If enabled, seeds the database after migrations during a db reset. -enabled = true -# Specifies an ordered list of seed files to load during db reset. -# Supports glob patterns relative to supabase directory: "./seeds/*.sql" -sql_paths = ["./seed.sql"] - -[db.network_restrictions] -# Enable management of network restrictions. -enabled = true -# List of IPv4 CIDR blocks allowed to connect to the database. -# Defaults to allow all IPv4 connections. Set empty array to block all IPs. -allowed_cidrs = ["0.0.0.0/0"] -# List of IPv6 CIDR blocks allowed to connect to the database. -# Defaults to allow all IPv6 connections. Set empty array to block all IPs. -allowed_cidrs_v6 = ["::/0"] - -# Uncomment to reject non-secure connections to the database. -[db.ssl_enforcement] -enabled = true - -[realtime] -enabled = true -# Bind realtime via either IPv4 or IPv6. (default: IPv6) -ip_version = "IPv4" -# The maximum length in bytes of HTTP request headers. (default: 4096) -max_header_length = 8192 - -[studio] -enabled = true -# Port to use for Supabase Studio. -port = 54323 -# External URL of the API server that frontend connects to. -api_url = "http://127.0.0.1" -# OpenAI API Key to use for Supabase AI in the Supabase Studio. -openai_api_key = "env(OPENAI_API_KEY)" - -# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they -# are monitored, and you can view the emails that would have been sent from the web interface. -[local_smtp] -enabled = true -# Port to use for the email testing server web interface. -port = 54324 -# Uncomment to expose additional ports for testing user applications that send emails. -# smtp_port = 54325 -# pop3_port = 54326 -# admin_email = "admin@email.com" -# sender_name = "Admin" - -[storage] -enabled = true -# The maximum file size allowed (e.g. "5MB", "500KB"). -file_size_limit = "50MiB" - -# Uncomment to configure local storage buckets -[storage.buckets.images] -public = false -file_size_limit = "50MiB" -allowed_mime_types = ["image/png", "image/jpeg"] -objects_path = "./images" - -# Allow connections via S3 compatible clients -[storage.s3_protocol] -enabled = true - -# Image transformation API is available to Supabase Pro plan. -[storage.image_transformation] -enabled = true - -# Store analytical data in S3 for running ETL jobs over Iceberg Catalog -[storage.analytics] -enabled = true -max_namespaces = 5 -max_tables = 10 -max_catalogs = 2 - -# Analytics Buckets is available to Supabase Pro plan. -[storage.analytics.buckets.my-warehouse] - -# Store vector embeddings in S3 for large and durable datasets -[storage.vector] -enabled = true -max_buckets = 10 -max_indexes = 5 - -# Vector Buckets is available to Supabase Pro plan. -# [storage.vector.buckets.documents-openai] - -[auth] -enabled = true -# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used -# in emails. -site_url = "http://127.0.0.1:3000" -# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. -additional_redirect_urls = ["https://127.0.0.1:3000", "env(AUTH_CALLBACK_URL)"] -# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week). -jwt_expiry = 3600 -# Path to JWT signing key. DO NOT commit your signing keys file to git. -signing_keys_path = "./signing_keys.json" -# If disabled, the refresh token will never expire. -enable_refresh_token_rotation = true -# Allows refresh tokens to be reused after expiry, up to the specified interval in seconds. -# Requires enable_refresh_token_rotation = true. -refresh_token_reuse_interval = 10 -# Allow/disallow new user signups to your project. -enable_signup = true -# Allow/disallow anonymous sign-ins to your project. -enable_anonymous_sign_ins = true -# Allow/disallow testing manual linking of accounts -enable_manual_linking = true -# Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more. -minimum_password_length = 6 -# Passwords that do not meet the following requirements will be rejected as weak. Supported values -# are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols` -password_requirements = "" - -# OAuth server configuration -[auth.oauth_server] -# Enable OAuth server functionality -enabled = true -# Path for OAuth consent flow UI -authorization_url_path = "/oauth/consent" -# Allow dynamic client registration -allow_dynamic_registration = true - -[auth.rate_limit] -# Number of emails that can be sent per hour. Requires auth.email.smtp to be enabled. -email_sent = 2 -# Number of SMS messages that can be sent per hour. Requires auth.sms to be enabled. -sms_sent = 30 -# Number of anonymous sign-ins that can be made per hour per IP address. Requires enable_anonymous_sign_ins = true. -anonymous_users = 30 -# Number of sessions that can be refreshed in a 5 minute interval per IP address. -token_refresh = 150 -# Number of sign up and sign-in requests that can be made in a 5 minute interval per IP address (excludes anonymous users). -sign_in_sign_ups = 30 -# Number of OTP / Magic link verifications that can be made in a 5 minute interval per IP address. -token_verifications = 30 -# Number of Web3 logins that can be made in a 5 minute interval per IP address. -web3 = 30 - -# Configure one of the supported captcha providers: `hcaptcha`, `turnstile`. -[auth.captcha] -enabled = true -provider = "hcaptcha" -secret = "env(HCAPTCHA_SECRET)" - -[auth.email] -# Allow/disallow new user signups via email to your project. -enable_signup = true -# If enabled, a user will be required to confirm any email change on both the old, and new email -# addresses. If disabled, only the new email is required to confirm. -double_confirm_changes = true -# If enabled, users need to confirm their email address before signing in. -enable_confirmations = false -# If enabled, users will need to reauthenticate or have logged in recently to change their password. -secure_password_change = true -# Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email. -max_frequency = "1s" -# Number of characters used in the email OTP. -otp_length = 6 -# Number of seconds before the email OTP expires (defaults to 1 hour). -otp_expiry = 3600 - -# Use a production-ready SMTP server -[auth.email.smtp] -enabled = true -host = "smtp.sendgrid.net" -port = 587 -user = "apikey" -pass = "env(SENDGRID_API_KEY)" -admin_email = "admin@email.com" -sender_name = "Admin" - -# Uncomment to customize email template -[auth.email.template.invite] -subject = "You have been invited" -content_path = "./supabase/templates/invite.html" - -# Uncomment to customize notification email template -[auth.email.notification.password_changed] -enabled = true -subject = "Your password has been changed" -content_path = "./templates/password_changed_notification.html" - -[auth.sms] -# Allow/disallow new user signups via SMS to your project. -enable_signup = true -# If enabled, users need to confirm their phone number before signing in. -enable_confirmations = false -# Template for sending OTP to users -template = "Your code is {{ `{{ .Code }}` }}" -# Controls the minimum amount of time that must pass before sending another sms otp. -max_frequency = "5s" - -# Use pre-defined map of phone number to OTP for testing. -[auth.sms.test_otp] -4152127777 = "123456" - -# Configure logged in session timeouts. -[auth.sessions] -# Force log out after the specified duration. -timebox = "24h" -# Force log out if the user has been inactive longer than the specified duration. -inactivity_timeout = "8h" - -# This hook runs before a new user is created and allows developers to reject the request based on the incoming user object. -[auth.hook.before_user_created] -enabled = true -uri = "pg-functions://postgres/auth/before-user-created-hook" - -# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used. -[auth.hook.custom_access_token] -enabled = true -uri = "pg-functions://postgres/auth/custom-access-token-hook" - -[auth.hook.send_sms] -enabled = true -uri = "http://host.docker.internal/functions/v1/send_sms" -secrets = "env(AUTH_SEND_SMS_SECRETS)" - -# Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`. -[auth.sms.twilio] -enabled = true -account_sid = "account_sid" -message_service_sid = "message_service_sid" -# DO NOT commit your Twilio auth token to git. Use environment variable substitution instead: -auth_token = "env(TWILIO_AUTH_TOKEN)" - -# Multi-factor-authentication is available to Supabase Pro plan. -[auth.mfa] -max_enrolled_factors = 10 - -# Configure MFA via App Authenticator (TOTP) -[auth.mfa.totp] -enroll_enabled = true -verify_enabled = true - -# Configure MFA via Phone Messaging -[auth.mfa.phone] -enroll_enabled = true -verify_enabled = true -otp_length = 6 -template = "Your code is {{ `{{ .Code }}` }}" -max_frequency = "5s" - -# Configure MFA via Phone Messaging -[auth.mfa.web_authn] -enroll_enabled = true -verify_enabled = true - -# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, -# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`, -# `twitter`, `x`, `slack`, `spotify`, `workos`, `zoom`. -[auth.external.azure] -enabled = true -client_id = "env(AZURE_CLIENT_ID)" -secret = "env(AZURE_SECRET)" -# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, -# or any other third-party OIDC providers. -url = "https://login.microsoftonline.com/tenant" -# If enabled, the nonce check will be skipped. Required for local sign in with Google auth. -skip_nonce_check = true -# If enabled, it will allow the user to successfully authenticate when the provider does not return an email address. -email_optional = true - -# Allow Solana wallet holders to sign in to your project via the Sign in with Solana (SIWS, EIP-4361) standard. -# You can configure "web3" rate limit in the [auth.rate_limit] section and set up [auth.captcha] if self-hosting. -[auth.web3.solana] -enabled = true - -[edge_runtime] -enabled = true -# Configure one of the supported request policies: `oneshot`, `per_worker`. -# Use `oneshot` for hot reload, or `per_worker` for load testing. -policy = "per_worker" -inspector_port = 8083 -deno_version = 2 - -[edge_runtime.secrets] -test_key = "test_value" - -[analytics] -enabled = true -port = 54327 -# Configure one of the supported backends: `postgres`, `bigquery`. -backend = "postgres" - -# Experimental features may be deprecated any time -[experimental] -# Configures S3 bucket URL, eg. .s3-.amazonaws.com -s3_host = "orioledb.s3-accelerate.amazonaws.com" -# Configures S3 bucket region, eg. us-east-1 -s3_region = "ap-southeast-1" -# Configures AWS_ACCESS_KEY_ID for S3 bucket -s3_access_key = "" -# Configures AWS_SECRET_ACCESS_KEY for S3 bucket -s3_secret_key = "" - -[remotes.production] -project_id = "vpefcjyosynxeiebfscx" - -[remotes.production.auth] -site_url = "http://feature-auth-branch.com/" -enable_signup = false - -[remotes.production.auth.external.azure] -enabled = false -client_id = "nope" - -[remotes.staging] -project_id = "bvikqvbczudanvggcord" - -[remotes.staging.db.seed] -enabled = true - -[remotes.staging.storage.buckets.images] -allowed_mime_types = ["image/png"] diff --git a/apps/cli-go/pkg/config/updater.go b/apps/cli-go/pkg/config/updater.go deleted file mode 100644 index 6c7e7eecd1..0000000000 --- a/apps/cli-go/pkg/config/updater.go +++ /dev/null @@ -1,318 +0,0 @@ -package config - -import ( - "context" - "fmt" - "os" - - "github.com/go-errors/errors" - v1API "github.com/supabase/cli/pkg/api" -) - -type ConfigUpdater struct { - client v1API.ClientWithResponses -} - -func NewConfigUpdater(client v1API.ClientWithResponses) ConfigUpdater { - return ConfigUpdater{client: client} -} - -func (u *ConfigUpdater) UpdateRemoteConfig(ctx context.Context, remote baseConfig, filter ...func(string) bool) error { - if err := u.UpdateApiConfig(ctx, remote.ProjectId, remote.Api, filter...); err != nil { - return err - } - if err := u.UpdateDbConfig(ctx, remote.ProjectId, remote.Db, filter...); err != nil { - return err - } - if err := u.UpdateAuthConfig(ctx, remote.ProjectId, remote.Auth, filter...); err != nil { - return err - } - if err := u.UpdateStorageConfig(ctx, remote.ProjectId, remote.Storage, filter...); err != nil { - return err - } - if err := u.UpdateExperimentalConfig(ctx, remote.ProjectId, remote.Experimental, filter...); err != nil { - return err - } - return nil -} - -func (u *ConfigUpdater) UpdateApiConfig(ctx context.Context, projectRef string, c api, filter ...func(string) bool) error { - apiConfig, err := u.client.V1GetPostgrestServiceConfigWithResponse(ctx, projectRef) - if err != nil { - return errors.Errorf("failed to read API config: %w", err) - } else if apiConfig.JSON200 == nil { - return errors.Errorf("unexpected status %d: %s", apiConfig.StatusCode(), string(apiConfig.Body)) - } - apiDiff, err := c.DiffWithRemote(*apiConfig.JSON200) - if err != nil { - return err - } else if len(apiDiff) == 0 { - fmt.Fprintln(os.Stderr, "Remote API config is up to date.") - return nil - } - fmt.Fprintln(os.Stderr, "Updating API service with config:", string(apiDiff)) - for _, keep := range filter { - if !keep("api") { - return nil - } - } - if resp, err := u.client.V1UpdatePostgrestServiceConfigWithResponse(ctx, projectRef, c.ToUpdatePostgrestConfigBody()); err != nil { - return errors.Errorf("failed to update API config: %w", err) - } else if resp.JSON200 == nil { - return errors.Errorf("unexpected status %d: %s", resp.StatusCode(), string(resp.Body)) - } - return nil -} - -func (u *ConfigUpdater) UpdateDbSettingsConfig(ctx context.Context, projectRef string, s settings, filter ...func(string) bool) error { - dbConfig, err := u.client.V1GetPostgresConfigWithResponse(ctx, projectRef) - if err != nil { - return errors.Errorf("failed to read DB config: %w", err) - } else if dbConfig.JSON200 == nil { - return errors.Errorf("unexpected status %d: %s", dbConfig.StatusCode(), string(dbConfig.Body)) - } - dbDiff, err := s.DiffWithRemote(*dbConfig.JSON200) - if err != nil { - return err - } else if len(dbDiff) == 0 { - fmt.Fprintln(os.Stderr, "Remote DB config is up to date.") - return nil - } - fmt.Fprintln(os.Stderr, "Updating DB service with config:", string(dbDiff)) - for _, keep := range filter { - if !keep("db") { - return nil - } - } - updateBody := s.ToUpdatePostgresConfigBody() - if resp, err := u.client.V1UpdatePostgresConfigWithResponse(ctx, projectRef, updateBody); err != nil { - return errors.Errorf("failed to update DB config: %w", err) - } else if resp.JSON200 == nil { - return errors.Errorf("unexpected status %d: %s", resp.StatusCode(), string(resp.Body)) - } - return nil -} - -func (u *ConfigUpdater) UpdateDbConfig(ctx context.Context, projectRef string, c db, filter ...func(string) bool) error { - if err := u.UpdateDbSettingsConfig(ctx, projectRef, c.Settings, filter...); err != nil { - return err - } - if err := u.UpdateDbNetworkRestrictionsConfig(ctx, projectRef, c.NetworkRestrictions, filter...); err != nil { - return err - } - if c.SslEnforcement != nil { - return u.UpdateSslEnforcement(ctx, projectRef, *c.SslEnforcement, filter...) - } - return nil -} - -func (u *ConfigUpdater) UpdateDbNetworkRestrictionsConfig(ctx context.Context, projectRef string, n networkRestrictions, filter ...func(string) bool) error { - if !n.Enabled { - return nil - } - networkRestrictionsConfig, err := u.client.V1GetNetworkRestrictionsWithResponse(ctx, projectRef) - if err != nil { - return errors.Errorf("failed to read network restrictions config: %w", err) - } else if networkRestrictionsConfig.JSON200 == nil { - return errors.Errorf("unexpected status %d: %s", networkRestrictionsConfig.StatusCode(), string(networkRestrictionsConfig.Body)) - } - networkRestrictionsDiff, err := n.DiffWithRemote(*networkRestrictionsConfig.JSON200) - if err != nil { - return err - } else if len(networkRestrictionsDiff) == 0 { - fmt.Fprintln(os.Stderr, "Remote DB Network restrictions config is up to date.") - return nil - } - fmt.Fprintln(os.Stderr, "Updating network restrictions with config:", string(networkRestrictionsDiff)) - for _, keep := range filter { - if !keep("db") { - return nil - } - } - updateBody := n.ToUpdateNetworkRestrictionsBody() - if resp, err := u.client.V1UpdateNetworkRestrictionsWithResponse(ctx, projectRef, updateBody); err != nil { - return errors.Errorf("failed to update network restrictions config: %w", err) - } else if resp.JSON201 == nil { - return errors.Errorf("unexpected status %d: %s", resp.StatusCode(), string(resp.Body)) - } - return nil -} - -func (u *ConfigUpdater) UpdateSslEnforcement(ctx context.Context, projectRef string, s sslEnforcement, filter ...func(string) bool) error { - sslEnforcementConfig, err := u.client.V1GetSslEnforcementConfigWithResponse(ctx, projectRef) - if err != nil { - return errors.Errorf("failed to read SSL enforcement config: %w", err) - } else if sslEnforcementConfig.JSON200 == nil { - return errors.Errorf("unexpected status %d: %s", sslEnforcementConfig.StatusCode(), string(sslEnforcementConfig.Body)) - } - sslEnforcementDiff, err := s.DiffWithRemote(*sslEnforcementConfig.JSON200) - if err != nil { - return err - } else if len(sslEnforcementDiff) == 0 { - fmt.Fprintln(os.Stderr, "Remote DB SSL enforcement config is up to date.") - return nil - } - fmt.Fprintln(os.Stderr, "Updating SSL enforcement with config:", string(sslEnforcementDiff)) - for _, keep := range filter { - if !keep("db") { - return nil - } - } - updateBody := s.ToUpdateSslEnforcementBody() - if resp, err := u.client.V1UpdateSslEnforcementConfigWithResponse(ctx, projectRef, updateBody); err != nil { - return errors.Errorf("failed to update SSL enforcement config: %w", err) - } else if resp.JSON200 == nil { - return errors.Errorf("unexpected status %d: %s", resp.StatusCode(), string(resp.Body)) - } - return nil -} - -func (u *ConfigUpdater) UpdateAuthConfig(ctx context.Context, projectRef string, c auth, filter ...func(string) bool) error { - if !c.Enabled { - return nil - } - authConfig, err := u.client.V1GetAuthServiceConfigWithResponse(ctx, projectRef) - if err != nil { - return errors.Errorf("failed to read Auth config: %w", err) - } else if authConfig.JSON200 == nil { - return errors.Errorf("unexpected status %d: %s", authConfig.StatusCode(), string(authConfig.Body)) - } - authDiff, err := c.DiffWithRemote(*authConfig.JSON200, filter...) - if err != nil { - return err - } else if len(authDiff) == 0 { - fmt.Fprintln(os.Stderr, "Remote Auth config is up to date.") - return nil - } - fmt.Fprintln(os.Stderr, "Updating Auth service with config:", string(authDiff)) - for _, keep := range filter { - if !keep("auth") { - return nil - } - } - if resp, err := u.client.V1UpdateAuthServiceConfigWithResponse(ctx, projectRef, c.ToUpdateAuthConfigBody()); err != nil { - return errors.Errorf("failed to update Auth config: %w", err) - } else if status := resp.StatusCode(); status < 200 || status >= 300 { - return errors.Errorf("unexpected status %d: %s", status, string(resp.Body)) - } - return nil -} - -func (u *ConfigUpdater) UpdateSigningKeys(ctx context.Context, projectRef string, signingKeys []JWK, filter ...func(string) bool) error { - if len(signingKeys) == 0 { - return nil - } - resp, err := u.client.V1GetProjectSigningKeysWithResponse(ctx, projectRef) - if err != nil { - return errors.Errorf("failed to fetch signing keys: %w", err) - } else if resp.JSON200 == nil { - return errors.Errorf("unexpected status %d: %s", resp.StatusCode(), string(resp.Body)) - } - exists := map[string]struct{}{} - for _, k := range resp.JSON200.Keys { - if k.PublicJwk != nil { - exists[k.Id.String()] = struct{}{} - } - } - var toInsert []JWK - for _, k := range signingKeys { - if _, ok := exists[k.KeyID]; !ok { - toInsert = append(toInsert, k) - } - } - if len(toInsert) == 0 { - fmt.Fprintln(os.Stderr, "Remote JWT signing keys are up to date.") - return nil - } - fmt.Fprintln(os.Stderr, "JWT signing keys to insert:") - for _, k := range toInsert { - fmt.Fprintln(os.Stderr, " -", k.KeyID) - } - for _, keep := range filter { - if !keep("signing keys") { - return nil - } - } - for _, k := range toInsert { - body := v1API.CreateSigningKeyBody{ - Algorithm: v1API.CreateSigningKeyBodyAlgorithm(k.Algorithm), - PrivateJwk: &v1API.CreateSigningKeyBody_PrivateJwk{}, - } - switch k.Algorithm { - case AlgRS256: - body.PrivateJwk.FromCreateSigningKeyBodyPrivateJwk0(v1API.CreateSigningKeyBodyPrivateJwk0{ - D: k.PrivateExponent, - Dp: k.FirstFactorCRTExponent, - Dq: k.SecondFactorCRTExponent, - E: v1API.CreateSigningKeyBodyPrivateJwk0E(k.Exponent), - Kty: v1API.CreateSigningKeyBodyPrivateJwk0Kty(k.KeyType), - N: k.Modulus, - P: k.FirstPrimeFactor, - Q: k.SecondPrimeFactor, - Qi: k.FirstCRTCoefficient, - }) - case AlgES256: - body.PrivateJwk.FromCreateSigningKeyBodyPrivateJwk1(v1API.CreateSigningKeyBodyPrivateJwk1{ - Crv: v1API.CreateSigningKeyBodyPrivateJwk1Crv(k.Curve), - D: k.PrivateExponent, - Kty: v1API.CreateSigningKeyBodyPrivateJwk1Kty(k.KeyType), - X: k.X, - Y: k.Y, - }) - } - if resp, err := u.client.V1CreateProjectSigningKeyWithResponse(ctx, projectRef, body); err != nil { - return errors.Errorf("failed to add signing key: %w", err) - } else if status := resp.StatusCode(); status < 200 || status >= 300 { - return errors.Errorf("unexpected status %d: %s", status, string(resp.Body)) - } - } - return nil -} - -func (u *ConfigUpdater) UpdateStorageConfig(ctx context.Context, projectRef string, c storage, filter ...func(string) bool) error { - if !c.Enabled { - return nil - } - storageConfig, err := u.client.V1GetStorageConfigWithResponse(ctx, projectRef) - if err != nil { - return errors.Errorf("failed to read Storage config: %w", err) - } else if storageConfig.JSON200 == nil { - return errors.Errorf("unexpected status %d: %s", storageConfig.StatusCode(), string(storageConfig.Body)) - } - storageDiff, err := c.DiffWithRemote(*storageConfig.JSON200) - if err != nil { - return err - } else if len(storageDiff) == 0 { - fmt.Fprintln(os.Stderr, "Remote Storage config is up to date.") - return nil - } - fmt.Fprintln(os.Stderr, "Updating Storage service with config:", string(storageDiff)) - for _, keep := range filter { - if !keep("storage") { - return nil - } - } - if resp, err := u.client.V1UpdateStorageConfigWithResponse(ctx, projectRef, c.ToUpdateStorageConfigBody()); err != nil { - return errors.Errorf("failed to update Storage config: %w", err) - } else if status := resp.StatusCode(); status < 200 || status >= 300 { - return errors.Errorf("unexpected status %d: %s", status, string(resp.Body)) - } - return nil -} - -func (u *ConfigUpdater) UpdateExperimentalConfig(ctx context.Context, projectRef string, exp experimental, filter ...func(string) bool) error { - if exp.Webhooks != nil && exp.Webhooks.Enabled { - fmt.Fprintln(os.Stderr, "Enabling webhooks for project:", projectRef) - for _, keep := range filter { - if !keep("webhooks") { - return nil - } - } - if resp, err := u.client.V1EnableDatabaseWebhookWithResponse(ctx, projectRef); err != nil { - return errors.Errorf("failed to enable webhooks: %w", err) - } else if status := resp.StatusCode(); status < 200 || status >= 300 { - return errors.Errorf("unexpected enable webhook status %d: %s", status, string(resp.Body)) - } - } - return nil -} diff --git a/apps/cli-go/pkg/config/updater_test.go b/apps/cli-go/pkg/config/updater_test.go deleted file mode 100644 index 0f00aba3b8..0000000000 --- a/apps/cli-go/pkg/config/updater_test.go +++ /dev/null @@ -1,397 +0,0 @@ -package config - -import ( - "context" - "net/http" - "testing" - - "github.com/h2non/gock" - "github.com/oapi-codegen/nullable" - openapi_types "github.com/oapi-codegen/runtime/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - v1API "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" -) - -func TestUpdateApi(t *testing.T) { - server := "http://localhost" - client, err := v1API.NewClientWithResponses(server) - require.NoError(t, err) - - t.Run("updates remote config", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Setup mock server - defer gock.Off() - gock.New(server). - Get("/v1/projects/test-project/postgrest"). - Reply(http.StatusOK). - JSON(v1API.PostgrestConfigWithJWTSecretResponseOutput{}) - gock.New(server). - Patch("/v1/projects/test-project/postgrest"). - Reply(http.StatusOK). - JSON(v1API.PostgrestConfigWithJWTSecretResponseOutput{ - DbSchema: "public,graphql_public", - DbExtraSearchPath: "public,extensions", - MaxRows: 1000, - }) - // Run test - err := updater.UpdateApiConfig(context.Background(), "test-project", api{ - Enabled: true, - Schemas: []string{"public", "graphql_public"}, - ExtraSearchPath: []string{"public", "extensions"}, - MaxRows: 1000, - }) - // Check result - assert.NoError(t, err) - assert.True(t, gock.IsDone()) - }) - - t.Run("skips update if no diff", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Setup mock server - defer gock.Off() - gock.New(server). - Get("/v1/projects/test-project/postgrest"). - Reply(http.StatusOK). - JSON(v1API.PostgrestConfigWithJWTSecretResponseOutput{ - DbSchema: "", - DbExtraSearchPath: "public,extensions", - MaxRows: 1000, - }) - // Run test - err := updater.UpdateApiConfig(context.Background(), "test-project", api{}) - // Check result - assert.NoError(t, err) - assert.True(t, gock.IsDone()) - }) -} - -func TestUpdateDbConfig(t *testing.T) { - server := "http://localhost" - client, err := v1API.NewClientWithResponses(server) - require.NoError(t, err) - - t.Run("updates remote DB config", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Setup mock server - defer gock.Off() - gock.New(server). - Get("/v1/projects/test-project/config/database"). - Reply(http.StatusOK). - JSON(v1API.PostgresConfigResponseOutput{}) - gock.New(server). - Put("/v1/projects/test-project/config/database"). - Reply(http.StatusOK). - JSON(v1API.PostgresConfigResponseOutput{ - MaxConnections: cast.Ptr(cast.UintToInt(100)), - }) - // Run test - err := updater.UpdateDbConfig(context.Background(), "test-project", db{ - Settings: settings{ - MaxConnections: cast.Ptr(cast.IntToUint(100)), - }, - }) - // Check result - assert.NoError(t, err) - assert.True(t, gock.IsDone()) - }) - - t.Run("skips update if no diff in DB config", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Setup mock server - defer gock.Off() - gock.New(server). - Get("/v1/projects/test-project/config/database"). - Reply(http.StatusOK). - JSON(v1API.PostgresConfigResponseOutput{ - MaxConnections: cast.Ptr(cast.UintToInt(100)), - }) - // Run test - err := updater.UpdateDbConfig(context.Background(), "test-project", db{ - Settings: settings{ - MaxConnections: cast.Ptr(cast.IntToUint(100)), - }, - }) - // Check result - assert.NoError(t, err) - assert.True(t, gock.IsDone()) - }) -} - -func TestUpdateDbNetworkRestrictionsConfig(t *testing.T) { - server := "http://localhost" - client, err := v1API.NewClientWithResponses(server) - require.NoError(t, err) - - t.Run("skips update if disabled locally", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Run test - err := updater.UpdateDbNetworkRestrictionsConfig(context.Background(), "test-project", networkRestrictions{}) - // Check result - assert.NoError(t, err) - assert.False(t, gock.HasUnmatchedRequest()) - }) - - t.Run("returns error on 400 when enabled locally", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Setup mock server - defer gock.Off() - gock.New(server). - Get("/v1/projects/test-project/network-restrictions"). - Reply(http.StatusBadRequest). - JSON(map[string]any{ - "message": "project not allowed to set up network restrictions", - }) - // Run test - err := updater.UpdateDbNetworkRestrictionsConfig(context.Background(), "test-project", networkRestrictions{ - Enabled: true, - }) - // Check result - assert.Error(t, err) - assert.Contains(t, err.Error(), "unexpected status 400") - assert.True(t, gock.IsDone()) - }) -} - -func TestUpdateExperimentalConfig(t *testing.T) { - server := "http://localhost" - client, err := v1API.NewClientWithResponses(server) - require.NoError(t, err) - - t.Run("enables webhooks", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Setup mock server - defer gock.Off() - gock.New(server). - Post("/v1/projects/test-project/database/webhooks/enable"). - Reply(http.StatusOK). - JSON(map[string]any{}) - // Run test - err := updater.UpdateExperimentalConfig(context.Background(), "test-project", experimental{ - Webhooks: &webhooks{ - Enabled: true, - }, - }) - // Check result - assert.NoError(t, err) - assert.True(t, gock.IsDone()) - }) - - t.Run("skips update if webhooks not enabled", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Run test - err := updater.UpdateExperimentalConfig(context.Background(), "test-project", experimental{ - Webhooks: &webhooks{ - Enabled: false, - }, - }) - // Check result - assert.NoError(t, err) - assert.True(t, gock.IsDone()) - }) -} - -func TestUpdateAuthConfig(t *testing.T) { - server := "http://localhost" - client, err := v1API.NewClientWithResponses(server) - require.NoError(t, err) - - t.Run("updates remote Auth config", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Setup mock server - defer gock.Off() - gock.New(server). - Get("/v1/projects/test-project/config/auth"). - Reply(http.StatusOK). - JSON(v1API.AuthConfigResponseOutput{ - SiteUrl: nullable.NewNullableWithValue("http://localhost:3000"), - SmtpAdminEmail: nullable.NewNullableWithValue(openapi_types.Email("abc@example.com")), - }) - gock.New(server). - Patch("/v1/projects/test-project/config/auth"). - Reply(http.StatusOK) - // Run test - err := updater.UpdateAuthConfig(context.Background(), "test-project", auth{Enabled: true}) - // Check result - assert.NoError(t, err) - assert.True(t, gock.IsDone()) - }) - - t.Run("skips update if no diff in Auth config", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Setup mock server - defer gock.Off() - gock.New(server). - Get("/v1/projects/test-project/config/auth"). - Reply(http.StatusOK). - JSON(v1API.AuthConfigResponseOutput{ - SmtpAdminEmail: nullable.NewNullableWithValue(openapi_types.Email("abc@example.com")), - }) - // Run test - err := updater.UpdateAuthConfig(context.Background(), "test-project", auth{ - Enabled: true, - EnableSignup: true, - AdditionalRedirectUrls: []string{}, - Email: email{EnableConfirmations: true}, - Sms: sms{TestOTP: map[string]string{}}, - }) - // Check result - assert.NoError(t, err) - assert.True(t, gock.IsDone()) - }) - - t.Run("skips update if disabled locally", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Run test - err := updater.UpdateAuthConfig(context.Background(), "test-project", auth{}) - // Check result - assert.NoError(t, err) - }) -} - -func TestUpdateStorageConfig(t *testing.T) { - server := "http://localhost" - client, err := v1API.NewClientWithResponses(server) - require.NoError(t, err) - - t.Run("updates remote Storage config", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Setup mock server - defer gock.Off() - mockStorage := v1API.StorageConfigResponseOutput{ - FileSizeLimit: 100, - } - mockStorage.Features.ImageTransformation.Enabled = true - mockStorage.Features.S3Protocol.Enabled = true - gock.New(server). - Get("/v1/projects/test-project/config/storage"). - Reply(http.StatusOK). - JSON(mockStorage) - gock.New(server). - Patch("/v1/projects/test-project/config/storage"). - Reply(http.StatusOK) - // Run test - err := updater.UpdateStorageConfig(context.Background(), "test-project", storage{Enabled: true}) - // Check result - assert.NoError(t, err) - assert.True(t, gock.IsDone()) - }) - - t.Run("skips update if no diff in Storage config", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Setup mock server - defer gock.Off() - gock.New(server). - Get("/v1/projects/test-project/config/storage"). - Reply(http.StatusOK). - JSON(v1API.StorageConfigResponseOutput{}) - // Run test - err := updater.UpdateStorageConfig(context.Background(), "test-project", storage{Enabled: true}) - // Check result - assert.NoError(t, err) - assert.True(t, gock.IsDone()) - }) - - t.Run("skips update if disabled locally", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Run test - err := updater.UpdateStorageConfig(context.Background(), "test-project", storage{}) - // Check result - assert.NoError(t, err) - }) -} - -func TestUpdateRemoteConfig(t *testing.T) { - server := "http://localhost" - client, err := v1API.NewClientWithResponses(server) - require.NoError(t, err) - - t.Run("updates all configs", func(t *testing.T) { - updater := NewConfigUpdater(*client) - // Setup mock server - defer gock.Off() - // API config - gock.New(server). - Get("/v1/projects/test-project/postgrest"). - Reply(http.StatusOK). - JSON(v1API.PostgrestConfigWithJWTSecretResponseOutput{}) - gock.New(server). - Patch("/v1/projects/test-project/postgrest"). - Reply(http.StatusOK). - JSON(v1API.PostgrestConfigWithJWTSecretResponseOutput{ - DbSchema: "public", - MaxRows: 1000, - }) - // DB config - gock.New(server). - Get("/v1/projects/test-project/config/database"). - Reply(http.StatusOK). - JSON(v1API.PostgresConfigResponseOutput{}) - gock.New(server). - Put("/v1/projects/test-project/config/database"). - Reply(http.StatusOK). - JSON(v1API.PostgresConfigResponseOutput{ - MaxConnections: cast.Ptr(cast.UintToInt(100)), - }) - // Auth config - gock.New(server). - Get("/v1/projects/test-project/config/auth"). - Reply(http.StatusOK). - JSON(v1API.AuthConfigResponseOutput{ - SmtpAdminEmail: nullable.NewNullableWithValue(openapi_types.Email("abc@example.com")), - }) - gock.New(server). - Patch("/v1/projects/test-project/config/auth"). - Reply(http.StatusOK) - // Storage config - gock.New(server). - Get("/v1/projects/test-project/config/storage"). - Reply(http.StatusOK). - JSON(v1API.StorageConfigResponseOutput{}) - gock.New(server). - Patch("/v1/projects/test-project/config/storage"). - Reply(http.StatusOK) - // Experimental config - gock.New(server). - Post("/v1/projects/test-project/database/webhooks/enable"). - Reply(http.StatusOK). - JSON(map[string]any{}) - // Run test - err := updater.UpdateRemoteConfig(context.Background(), baseConfig{ - ProjectId: "test-project", - Api: api{ - Enabled: true, - Schemas: []string{"public", "private"}, - MaxRows: 1000, - }, - Db: db{ - Settings: settings{ - MaxConnections: cast.Ptr(cast.IntToUint(100)), - }, - }, - Auth: auth{ - Enabled: true, - SiteUrl: "http://localhost:3000", - }, - Storage: storage{ - Enabled: true, - FileSizeLimit: 100, - ImageTransformation: &imageTransformation{ - Enabled: true, - }, - S3Protocol: &s3Protocol{ - Enabled: true, - }, - }, - Experimental: experimental{ - Webhooks: &webhooks{ - Enabled: true, - }, - }, - }) - // Check result - assert.NoError(t, err) - assert.True(t, gock.IsDone()) - }) -} diff --git a/apps/cli-go/pkg/config/utils.go b/apps/cli-go/pkg/config/utils.go deleted file mode 100644 index 2bb6db6f4b..0000000000 --- a/apps/cli-go/pkg/config/utils.go +++ /dev/null @@ -1,116 +0,0 @@ -package config - -import ( - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "fmt" - "path/filepath" - "strings" -) - -type pathBuilder struct { - SupabaseDirPath string - ConfigPath string - GitIgnorePath string - TempDir string - ImportMapsDir string - ProjectRefPath string - PoolerUrlPath string - PostgresVersionPath string - GotrueVersionPath string - RestVersionPath string - StorageVersionPath string - StorageMigrationPath string - StudioVersionPath string - PgmetaVersionPath string - PoolerVersionPath string - RealtimeVersionPath string - EdgeRuntimeVersionPath string - LogflareVersionPath string - CliVersionPath string - CurrBranchPath string - SchemasDir string - MigrationsDir string - FunctionsDir string - FallbackImportMapPath string - FallbackEnvFilePath string - DbTestsDir string - CustomRolesPath string -} - -func NewPathBuilder(configPath string) pathBuilder { - if filepath.Base(configPath) == "." { - configPath = filepath.Join("supabase", "config.toml") - } - // TODO: make base path configurable from toml - base := filepath.Dir(configPath) - return pathBuilder{ - SupabaseDirPath: base, - ConfigPath: configPath, - GitIgnorePath: filepath.Join(base, ".gitignore"), - TempDir: filepath.Join(base, ".temp"), - ImportMapsDir: filepath.Join(base, ".temp", "import_maps"), - ProjectRefPath: filepath.Join(base, ".temp", "project-ref"), - PoolerUrlPath: filepath.Join(base, ".temp", "pooler-url"), - PostgresVersionPath: filepath.Join(base, ".temp", "postgres-version"), - GotrueVersionPath: filepath.Join(base, ".temp", "gotrue-version"), - RestVersionPath: filepath.Join(base, ".temp", "rest-version"), - StorageVersionPath: filepath.Join(base, ".temp", "storage-version"), - StorageMigrationPath: filepath.Join(base, ".temp", "storage-migration"), - EdgeRuntimeVersionPath: filepath.Join(base, ".temp", "edge-runtime-version"), - StudioVersionPath: filepath.Join(base, ".temp", "studio-version"), - PgmetaVersionPath: filepath.Join(base, ".temp", "pgmeta-version"), - PoolerVersionPath: filepath.Join(base, ".temp", "pooler-version"), - RealtimeVersionPath: filepath.Join(base, ".temp", "realtime-version"), - LogflareVersionPath: filepath.Join(base, ".temp", "logflare-version"), - CliVersionPath: filepath.Join(base, ".temp", "cli-latest"), - CurrBranchPath: filepath.Join(base, ".branches", "_current_branch"), - SchemasDir: filepath.Join(base, "schemas"), - MigrationsDir: filepath.Join(base, "migrations"), - FunctionsDir: filepath.Join(base, "functions"), - FallbackImportMapPath: filepath.Join(base, "functions", "import_map.json"), - FallbackEnvFilePath: filepath.Join(base, "functions", ".env"), - DbTestsDir: filepath.Join(base, "tests"), - CustomRolesPath: filepath.Join(base, "roles.sql"), - } -} - -func replaceImageTag(image string, tag string) string { - index := strings.IndexByte(image, ':') - return image[:index+1] + strings.TrimSpace(tag) -} - -func strToArr(v string) []string { - // Avoid returning [""] if v is empty - if len(v) == 0 { - return []string{} - } - return strings.Split(v, ",") -} - -func mapToEnv(input map[string]string) string { - var result []string - for k, v := range input { - kv := fmt.Sprintf("%s=%s", k, v) - result = append(result, kv) - } - return strings.Join(result, ",") -} - -func envToMap(input string) map[string]string { - env := strToArr(input) - result := make(map[string]string, len(env)) - for _, kv := range env { - if parts := strings.Split(kv, "="); len(parts) > 1 { - result[parts[0]] = parts[1] - } - } - return result -} - -func sha256Hmac(key, value string) string { - h := hmac.New(sha256.New, []byte(key)) - h.Write([]byte(value)) - return hex.EncodeToString(h.Sum(nil)) -} diff --git a/apps/cli-go/pkg/diff/diff.go b/apps/cli-go/pkg/diff/diff.go deleted file mode 100644 index e3a8e2d981..0000000000 --- a/apps/cli-go/pkg/diff/diff.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package diff - -import ( - "bytes" - "fmt" - "sort" - "strings" -) - -// A pair is a pair of values tracked for both the x and y side of a diff. -// It is typically a pair of line indexes. -type pair struct{ x, y int } - -// Diff returns an anchored diff of the two texts old and new -// in the “unified diff” format. If old and new are identical, -// Diff returns a nil slice (no output). -// -// Unix diff implementations typically look for a diff with -// the smallest number of lines inserted and removed, -// which can in the worst case take time quadratic in the -// number of lines in the texts. As a result, many implementations -// either can be made to run for a long time or cut off the search -// after a predetermined amount of work. -// -// In contrast, this implementation looks for a diff with the -// smallest number of “unique” lines inserted and removed, -// where unique means a line that appears just once in both old and new. -// We call this an “anchored diff” because the unique lines anchor -// the chosen matching regions. An anchored diff is usually clearer -// than a standard diff, because the algorithm does not try to -// reuse unrelated blank lines or closing braces. -// The algorithm also guarantees to run in O(n log n) time -// instead of the standard O(n²) time. -// -// Some systems call this approach a “patience diff,” named for -// the “patience sorting” algorithm, itself named for a solitaire card game. -// We avoid that name for two reasons. First, the name has been used -// for a few different variants of the algorithm, so it is imprecise. -// Second, the name is frequently interpreted as meaning that you have -// to wait longer (to be patient) for the diff, meaning that it is a slower algorithm, -// when in fact the algorithm is faster than the standard one. -func Diff(oldName string, old []byte, newName string, new []byte) []byte { - if bytes.Equal(old, new) { - return nil - } - x := lines(old) - y := lines(new) - - // Print diff header. - var out bytes.Buffer - fmt.Fprintf(&out, "diff %s %s\n", oldName, newName) - fmt.Fprintf(&out, "--- %s\n", oldName) - fmt.Fprintf(&out, "+++ %s\n", newName) - - // Loop over matches to consider, - // expanding each match to include surrounding lines, - // and then printing diff chunks. - // To avoid setup/teardown cases outside the loop, - // tgs returns a leading {0,0} and trailing {len(x), len(y)} pair - // in the sequence of matches. - var ( - done pair // printed up to x[:done.x] and y[:done.y] - chunk pair // start lines of current chunk - count pair // number of lines from each side in current chunk - ctext []string // lines for current chunk - ) - for _, m := range tgs(x, y) { - if m.x < done.x { - // Already handled scanning forward from earlier match. - continue - } - - // Expand matching lines as far as possible, - // establishing that x[start.x:end.x] == y[start.y:end.y]. - // Note that on the first (or last) iteration we may (or definitely do) - // have an empty match: start.x==end.x and start.y==end.y. - start := m - for start.x > done.x && start.y > done.y && x[start.x-1] == y[start.y-1] { - start.x-- - start.y-- - } - end := m - for end.x < len(x) && end.y < len(y) && x[end.x] == y[end.y] { - end.x++ - end.y++ - } - - // Emit the mismatched lines before start into this chunk. - // (No effect on first sentinel iteration, when start = {0,0}.) - for _, s := range x[done.x:start.x] { - ctext = append(ctext, "-"+s) - count.x++ - } - for _, s := range y[done.y:start.y] { - ctext = append(ctext, "+"+s) - count.y++ - } - - // If we're not at EOF and have too few common lines, - // the chunk includes all the common lines and continues. - const C = 3 // number of context lines - if (end.x < len(x) || end.y < len(y)) && - (end.x-start.x < C || (len(ctext) > 0 && end.x-start.x < 2*C)) { - for _, s := range x[start.x:end.x] { - ctext = append(ctext, " "+s) - count.x++ - count.y++ - } - done = end - continue - } - - // End chunk with common lines for context. - if len(ctext) > 0 { - n := min(end.x-start.x, C) - for _, s := range x[start.x : start.x+n] { - ctext = append(ctext, " "+s) - count.x++ - count.y++ - } - done = pair{start.x + n, start.y + n} - - // Format and emit chunk. - // Convert line numbers to 1-indexed. - // Special case: empty file shows up as 0,0 not 1,0. - if count.x > 0 { - chunk.x++ - } - if count.y > 0 { - chunk.y++ - } - fmt.Fprintf(&out, "@@ -%d,%d +%d,%d @@\n", chunk.x, count.x, chunk.y, count.y) - for _, s := range ctext { - out.WriteString(s) - } - count.x = 0 - count.y = 0 - ctext = ctext[:0] - } - - // If we reached EOF, we're done. - if end.x >= len(x) && end.y >= len(y) { - break - } - - // Otherwise start a new chunk. - chunk = pair{end.x - C, end.y - C} - for _, s := range x[chunk.x:end.x] { - ctext = append(ctext, " "+s) - count.x++ - count.y++ - } - done = end - } - - return out.Bytes() -} - -// lines returns the lines in the file x, including newlines. -// If the file does not end in a newline, one is supplied -// along with a warning about the missing newline. -func lines(x []byte) []string { - l := strings.SplitAfter(string(x), "\n") - if l[len(l)-1] == "" { - l = l[:len(l)-1] - } else { - // Treat last line as having a message about the missing newline attached, - // using the same text as BSD/GNU diff (including the leading backslash). - l[len(l)-1] += "\n\\ No newline at end of file\n" - } - return l -} - -// tgs returns the pairs of indexes of the longest common subsequence -// of unique lines in x and y, where a unique line is one that appears -// once in x and once in y. -// -// The longest common subsequence algorithm is as described in -// Thomas G. Szymanski, “A Special Case of the Maximal Common -// Subsequence Problem,” Princeton TR #170 (January 1975), -// available at https://research.swtch.com/tgs170.pdf. -func tgs(x, y []string) []pair { - // Count the number of times each string appears in a and b. - // We only care about 0, 1, many, counted as 0, -1, -2 - // for the x side and 0, -4, -8 for the y side. - // Using negative numbers now lets us distinguish positive line numbers later. - m := make(map[string]int) - for _, s := range x { - if c := m[s]; c > -2 { - m[s] = c - 1 - } - } - for _, s := range y { - if c := m[s]; c > -8 { - m[s] = c - 4 - } - } - - // Now unique strings can be identified by m[s] = -1+-4. - // - // Gather the indexes of those strings in x and y, building: - // xi[i] = increasing indexes of unique strings in x. - // yi[i] = increasing indexes of unique strings in y. - // inv[i] = index j such that x[xi[i]] = y[yi[j]]. - var xi, yi, inv []int - for i, s := range y { - if m[s] == -1+-4 { - m[s] = len(yi) - yi = append(yi, i) - } - } - for i, s := range x { - if j, ok := m[s]; ok && j >= 0 { - xi = append(xi, i) - inv = append(inv, j) - } - } - - // Apply Algorithm A from Szymanski's paper. - // In those terms, A = J = inv and B = [0, n). - // We add sentinel pairs {0,0}, and {len(x),len(y)} - // to the returned sequence, to help the processing loop. - J := inv - n := len(xi) - T := make([]int, n) - L := make([]int, n) - for i := range T { - T[i] = n + 1 - } - for i := range n { - k := sort.Search(n, func(k int) bool { - return T[k] >= J[i] - }) - T[k] = J[i] - L[i] = k + 1 - } - k := 0 - for _, v := range L { - if k < v { - k = v - } - } - seq := make([]pair, 2+k) - seq[1+k] = pair{len(x), len(y)} // sentinel at end - lastj := n - for i := n - 1; i >= 0; i-- { - if L[i] == k && J[i] < lastj { - seq[k] = pair{xi[i], yi[J[i]]} - k-- - } - } - seq[0] = pair{0, 0} // sentinel at start - return seq -} diff --git a/apps/cli-go/pkg/fetcher/gateway.go b/apps/cli-go/pkg/fetcher/gateway.go deleted file mode 100644 index 694882066b..0000000000 --- a/apps/cli-go/pkg/fetcher/gateway.go +++ /dev/null @@ -1,34 +0,0 @@ -package fetcher - -import ( - "net/http" - "strings" -) - -// NewServiceGateway returns a Fetcher for Supabase service-gateway calls (Kong, -// Storage, Auth, etc.). It deliberately omits http.Client.Timeout so streaming -// operations such as storage uploads are not capped under load. Connection -// setup is still bounded by the default transport's dial and TLS-handshake -// timeouts, and per-call deadlines should flow through the request context. -func NewServiceGateway(server, token string, overrides ...FetcherOption) *Fetcher { - opts := append([]FetcherOption{ - WithHTTPClient(&http.Client{}), - withAuthToken(token), - WithExpectedStatus(http.StatusOK), - }, overrides...) - return NewFetcher(server, opts...) -} - -func withAuthToken(token string) FetcherOption { - if strings.HasPrefix(token, "sb_") { - header := func(req *http.Request) { - req.Header.Add("apikey", token) - } - return WithRequestEditor(header) - } - header := func(req *http.Request) { - req.Header.Add("apikey", token) - req.Header.Add("Authorization", "Bearer "+token) - } - return WithRequestEditor(header) -} diff --git a/apps/cli-go/pkg/fetcher/http.go b/apps/cli-go/pkg/fetcher/http.go deleted file mode 100644 index 3ea4ac2fe2..0000000000 --- a/apps/cli-go/pkg/fetcher/http.go +++ /dev/null @@ -1,152 +0,0 @@ -package fetcher - -import ( - "bytes" - "context" - "encoding/json" - "io" - "net/http" - "net/url" - "slices" - "strings" - - "github.com/go-errors/errors" -) - -type Fetcher struct { - server string - client *http.Client - editors []RequestEditor - status []int -} - -type FetcherOption func(*Fetcher) - -func NewFetcher(server string, opts ...FetcherOption) *Fetcher { - api := &Fetcher{ - server: server, - client: http.DefaultClient, - } - for _, apply := range opts { - apply(api) - } - return api -} - -func WithHTTPClient(client *http.Client) FetcherOption { - return func(s *Fetcher) { - s.client = client - } -} - -func WithExpectedStatus(statusCode ...int) FetcherOption { - return func(s *Fetcher) { - s.status = statusCode - } -} - -func WithBearerToken(token string) FetcherOption { - addHeader := func(req *http.Request) { - req.Header.Add("Authorization", "Bearer "+token) - } - return WithRequestEditor(addHeader) -} - -func WithUserAgent(agent string) FetcherOption { - addHeader := func(req *http.Request) { - req.Header.Add("User-Agent", agent) - } - return WithRequestEditor(addHeader) -} - -func WithRequestEditor(fn RequestEditor) FetcherOption { - return func(s *Fetcher) { - s.editors = append(s.editors, fn) - } -} - -type RequestEditor func(req *http.Request) - -func (s *Fetcher) Send(ctx context.Context, method, path string, reqBody any, reqEditors ...RequestEditor) (*http.Response, error) { - body, ok := reqBody.(io.Reader) - if !ok && reqBody != nil { - var buf bytes.Buffer - enc := json.NewEncoder(&buf) - if err := enc.Encode(reqBody); err != nil { - return nil, errors.Errorf("failed to encode request body: %w", err) - } - reqEditors = append(reqEditors, func(req *http.Request) { - req.Header.Set("Content-Type", "application/json") - }) - body = &buf - } - // Creates request - req, err := http.NewRequestWithContext(ctx, method, s.server+path, body) - if err != nil { - return nil, errors.Errorf("failed to initialise http request: %w", err) - } - for _, apply := range s.editors { - apply(req) - } - for _, apply := range reqEditors { - apply(req) - } - // Sends request - resp, err := s.client.Do(req) - if err != nil { - if hint := localGatewayHint(s.server, err); len(hint) > 0 { - return nil, errors.Errorf("failed to execute http request: %w\n\n%s", err, hint) - } - return nil, errors.Errorf("failed to execute http request: %w", err) - } - if slices.Contains(s.status, resp.StatusCode) { - return resp, nil - } - // Reject unexpected status codes as error - if len(s.status) > 0 || resp.StatusCode >= http.StatusBadRequest { - defer resp.Body.Close() - data, err := io.ReadAll(resp.Body) - if err != nil { - return resp, errors.Errorf("Error status %d: %w", resp.StatusCode, err) - } - return resp, errors.Errorf("Error status %d: %s", resp.StatusCode, data) - } - return resp, nil -} - -func localGatewayHint(server string, err error) string { - if err == nil { - return "" - } - parsed, parseErr := url.Parse(server) - if parseErr != nil { - return "" - } - host := parsed.Hostname() - if host != "127.0.0.1" && host != "localhost" && host != "::1" { - return "" - } - message := err.Error() - if !strings.Contains(message, "malformed HTTP response") && - !strings.Contains(message, "Client.Timeout exceeded while awaiting headers") && - !strings.Contains(message, "context deadline exceeded") { - return "" - } - port := parsed.Port() - if len(port) == 0 { - return "" - } - return "The local Supabase API gateway did not return a valid HTTP response. " + - "Another process may be listening on the configured API port " + port + ". " + - "Check the port with `lsof -nP -iTCP:" + port + " -sTCP:LISTEN`, then stop the conflicting process or set a different `api.port` in supabase/config.toml." -} - -func ParseJSON[T any](r io.ReadCloser) (T, error) { - defer r.Close() - var data T - dec := json.NewDecoder(r) - if err := dec.Decode(&data); err != nil { - return data, errors.Errorf("failed to parse response body: %w", err) - } - return data, nil -} diff --git a/apps/cli-go/pkg/fetcher/http_test.go b/apps/cli-go/pkg/fetcher/http_test.go deleted file mode 100644 index a24a0fa303..0000000000 --- a/apps/cli-go/pkg/fetcher/http_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package fetcher - -import ( - "context" - "net" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestSendSuggestsApiPortConflictForMalformedLocalResponse(t *testing.T) { - listener, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - defer listener.Close() - - done := make(chan struct{}) - go func() { - defer close(done) - conn, err := listener.Accept() - if err != nil { - return - } - defer conn.Close() - _, _ = conn.Write([]byte(`{"type":"Tier1","version":"1.0"}`)) - }() - - api := NewFetcher("http://" + listener.Addr().String()) - _, err = api.Send(context.Background(), "GET", "/storage/v1/bucket", nil) - require.Error(t, err) - assert.Contains(t, err.Error(), "malformed HTTP response") - assert.Contains(t, err.Error(), "Another process may be listening on the configured API port") - assert.Contains(t, err.Error(), "lsof -nP -iTCP:") - assert.Contains(t, err.Error(), "api.port") - - <-done -} diff --git a/apps/cli-go/pkg/function/api.go b/apps/cli-go/pkg/function/api.go deleted file mode 100644 index df44432405..0000000000 --- a/apps/cli-go/pkg/function/api.go +++ /dev/null @@ -1,53 +0,0 @@ -package function - -import ( - "context" - "io" - - "github.com/supabase/cli/pkg/api" -) - -type EdgeRuntimeAPI struct { - project string - client api.ClientWithResponses - eszip EszipBundler - maxJobs uint -} - -type FunctionDeployMetadata struct { - EntrypointPath string `json:"entrypoint_path"` - ImportMapPath *string `json:"import_map_path,omitempty"` - Name *string `json:"name,omitempty"` - StaticPatterns *[]string `json:"static_patterns,omitempty"` - VerifyJwt *bool `json:"verify_jwt,omitempty"` - SHA256 string `json:"sha256,omitempty"` -} - -type EszipBundler interface { - Bundle(ctx context.Context, slug, entrypoint, importMap string, staticFiles []string, output io.Writer) (FunctionDeployMetadata, error) -} - -func NewEdgeRuntimeAPI(project string, client api.ClientWithResponses, opts ...withOption) EdgeRuntimeAPI { - result := EdgeRuntimeAPI{client: client, project: project} - for _, apply := range opts { - apply(&result) - } - if result.maxJobs == 0 { - result.maxJobs = 1 - } - return result -} - -type withOption func(*EdgeRuntimeAPI) - -func WithBundler(bundler EszipBundler) withOption { - return func(era *EdgeRuntimeAPI) { - era.eszip = bundler - } -} - -func WithMaxJobs(maxJobs uint) withOption { - return func(era *EdgeRuntimeAPI) { - era.maxJobs = maxJobs - } -} diff --git a/apps/cli-go/pkg/function/batch.go b/apps/cli-go/pkg/function/batch.go deleted file mode 100644 index 70d0f4298a..0000000000 --- a/apps/cli-go/pkg/function/batch.go +++ /dev/null @@ -1,170 +0,0 @@ -package function - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/hex" - "fmt" - "io" - "net/http" - "os" - "strings" - "time" - - "github.com/cenkalti/backoff/v4" - "github.com/docker/go-units" - "github.com/go-errors/errors" - "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" - "github.com/supabase/cli/pkg/config" -) - -const ( - eszipContentType = "application/vnd.denoland.eszip" - maxRetries = 3 -) - -func (s *EdgeRuntimeAPI) UpsertFunctions(ctx context.Context, functionConfig config.FunctionConfig, filter ...func(string) bool) error { - policy := backoff.WithContext(backoff.WithMaxRetries(backoff.NewExponentialBackOff(), maxRetries), ctx) - result, err := backoff.RetryWithData(func() ([]api.FunctionResponseOutput, error) { - resp, err := s.client.V1ListAllFunctionsWithResponse(ctx, s.project) - if err != nil { - return nil, errors.Errorf("failed to list functions: %w", err) - } else if resp.JSON200 == nil { - err = errors.Errorf("unexpected list functions status %d: %s", resp.StatusCode(), string(resp.Body)) - if resp.StatusCode() < http.StatusInternalServerError { - err = &backoff.PermanentError{Err: err} - } - return nil, err - } - return *resp.JSON200, nil - }, policy) - if err != nil { - return err - } - policy.Reset() - slugToIndex := make(map[string]int, len(result)) - for i, f := range result { - slugToIndex[f.Slug] = i - } - var toUpdate api.BulkUpdateFunctionBody -OUTER: - for slug, function := range functionConfig { - if !function.Enabled { - fmt.Fprintln(os.Stderr, "Skipping disabled Function:", slug) - continue - } - for _, keep := range filter { - if !keep(slug) { - continue OUTER - } - } - var body bytes.Buffer - meta, err := s.eszip.Bundle(ctx, slug, function.Entrypoint, function.ImportMap, function.StaticFiles, &body) - if errors.Is(err, ErrNoDeploy) { - fmt.Fprintln(os.Stderr, "Skipping undeployable Function:", slug) - continue - } else if err != nil { - return err - } - meta.VerifyJwt = function.VerifyJWT - bodyHash := sha256.Sum256(body.Bytes()) - meta.SHA256 = hex.EncodeToString(bodyHash[:]) - // Skip if function has not changed - if i, exists := slugToIndex[slug]; exists && i >= 0 && - result[i].EzbrSha256 != nil && *result[i].EzbrSha256 == meta.SHA256 && - (function.VerifyJWT == nil || result[i].VerifyJwt != nil && *result[i].VerifyJwt == *function.VerifyJWT) { - fmt.Fprintln(os.Stderr, "No change found in Function:", slug) - continue - } - // Update if function already exists - upsert := func() (api.BulkUpdateFunctionBody, error) { - if _, exists := slugToIndex[slug]; exists { - return s.updateFunction(ctx, slug, meta, bytes.NewReader(body.Bytes())) - } - return s.createFunction(ctx, slug, meta, bytes.NewReader(body.Bytes())) - } - functionSize := units.HumanSize(float64(body.Len())) - fmt.Fprintf(os.Stderr, "Deploying Function: %s (script size: %s)\n", slug, functionSize) - result, err := backoff.RetryNotifyWithData(upsert, policy, func(err error, d time.Duration) { - if strings.Contains(err.Error(), "Duplicated function slug") { - slugToIndex[slug] = -1 - } - }) - if err != nil { - return err - } - toUpdate = append(toUpdate, result...) - policy.Reset() - } - if len(toUpdate) > 1 { - if err := backoff.Retry(func() error { - if resp, err := s.client.V1BulkUpdateFunctionsWithResponse(ctx, s.project, toUpdate); err != nil { - return errors.Errorf("failed to bulk update: %w", err) - } else if resp.JSON200 == nil { - return errors.Errorf("unexpected bulk update status %d: %s", resp.StatusCode(), string(resp.Body)) - } - return nil - }, policy); err != nil { - return err - } - } - return nil -} - -func (s *EdgeRuntimeAPI) updateFunction(ctx context.Context, slug string, meta FunctionDeployMetadata, body io.Reader) (api.BulkUpdateFunctionBody, error) { - resp, err := s.client.V1UpdateAFunctionWithBodyWithResponse(ctx, s.project, slug, &api.V1UpdateAFunctionParams{ - VerifyJwt: cast.BoolToStringPtr(meta.VerifyJwt), - ImportMapPath: meta.ImportMapPath, - EntrypointPath: &meta.EntrypointPath, - EzbrSha256: &meta.SHA256, - }, eszipContentType, body) - if err != nil { - return api.BulkUpdateFunctionBody{}, errors.Errorf("failed to update function: %w", err) - } else if resp.JSON200 == nil { - return api.BulkUpdateFunctionBody{}, errors.Errorf("unexpected update function status %d: %s", resp.StatusCode(), string(resp.Body)) - } - return api.BulkUpdateFunctionBody{{ - Id: resp.JSON200.Id, - Name: resp.JSON200.Name, - Slug: resp.JSON200.Slug, - Version: resp.JSON200.Version, - EntrypointPath: resp.JSON200.EntrypointPath, - ImportMap: resp.JSON200.ImportMap, - ImportMapPath: resp.JSON200.ImportMapPath, - VerifyJwt: resp.JSON200.VerifyJwt, - Status: api.BulkUpdateFunctionBodyStatus(resp.JSON200.Status), - CreatedAt: &resp.JSON200.CreatedAt, - EzbrSha256: resp.JSON200.EzbrSha256, - }}, nil -} - -func (s *EdgeRuntimeAPI) createFunction(ctx context.Context, slug string, meta FunctionDeployMetadata, body io.Reader) (api.BulkUpdateFunctionBody, error) { - resp, err := s.client.V1CreateAFunctionWithBodyWithResponse(ctx, s.project, &api.V1CreateAFunctionParams{ - Slug: &slug, - Name: &slug, - VerifyJwt: cast.BoolToStringPtr(meta.VerifyJwt), - ImportMapPath: meta.ImportMapPath, - EntrypointPath: &meta.EntrypointPath, - EzbrSha256: &meta.SHA256, - }, eszipContentType, body) - if err != nil { - return api.BulkUpdateFunctionBody{}, errors.Errorf("failed to create function: %w", err) - } else if resp.JSON201 == nil { - return api.BulkUpdateFunctionBody{}, errors.Errorf("unexpected create function status %d: %s", resp.StatusCode(), string(resp.Body)) - } - return api.BulkUpdateFunctionBody{{ - Id: resp.JSON201.Id, - Name: resp.JSON201.Name, - Slug: resp.JSON201.Slug, - Version: resp.JSON201.Version, - EntrypointPath: resp.JSON201.EntrypointPath, - ImportMap: resp.JSON201.ImportMap, - ImportMapPath: resp.JSON201.ImportMapPath, - VerifyJwt: resp.JSON201.VerifyJwt, - Status: api.BulkUpdateFunctionBodyStatus(resp.JSON201.Status), - CreatedAt: &resp.JSON201.CreatedAt, - EzbrSha256: resp.JSON201.EzbrSha256, - }}, nil -} diff --git a/apps/cli-go/pkg/function/batch_test.go b/apps/cli-go/pkg/function/batch_test.go deleted file mode 100644 index 5bbd3a2fc1..0000000000 --- a/apps/cli-go/pkg/function/batch_test.go +++ /dev/null @@ -1,229 +0,0 @@ -package function - -import ( - "context" - "errors" - "io" - "net/http" - "testing" - - "github.com/h2non/gock" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" - "github.com/supabase/cli/pkg/config" -) - -type MockBundler struct { -} - -func (b *MockBundler) Bundle(ctx context.Context, slug, entrypoint, importMap string, staticFiles []string, output io.Writer) (FunctionDeployMetadata, error) { - if staticFiles == nil { - staticFiles = []string{} - } - return FunctionDeployMetadata{ - Name: &slug, - EntrypointPath: entrypoint, - ImportMapPath: &importMap, - StaticPatterns: &staticFiles, - }, nil -} - -func mockClient(t *testing.T) EdgeRuntimeAPI { - apiClient, err := api.NewClientWithResponses(mockApiHost) - require.NoError(t, err) - return NewEdgeRuntimeAPI(mockProject, *apiClient, func(era *EdgeRuntimeAPI) { - era.eszip = &MockBundler{} - }) -} - -const ( - mockApiHost = "https://api.supabase.com" - mockProject = "test-project" -) - -func TestUpsertFunctions(t *testing.T) { - client := mockClient(t) - - t.Run("deploys with bulk update", func(t *testing.T) { - // t.Cleanup(mockClock(100 * time.Millisecond)) - // Setup mock api - defer gock.OffAll() - gock.New(mockApiHost). - Get("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusOK). - JSON([]api.FunctionResponseOutput{{Slug: "test-a"}}) - gock.New(mockApiHost). - Patch("/v1/projects/" + mockProject + "/functions/test-a"). - Reply(http.StatusOK). - JSON(api.FunctionResponseOutput{Slug: "test-a"}) - gock.New(mockApiHost). - Post("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusCreated). - JSON(api.FunctionResponseOutput{Slug: "test-b"}) - gock.New(mockApiHost). - Put("/v1/projects/" + mockProject + "/functions"). - ReplyError(errors.New("network error")) - gock.New(mockApiHost). - Put("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusServiceUnavailable) - gock.New(mockApiHost). - Put("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusOK). - JSON(api.V1BulkUpdateFunctionsResponse{}) - // Run test - err := client.UpsertFunctions(context.Background(), config.FunctionConfig{ - "test-a": {Enabled: true}, - "test-b": {Enabled: true}, - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("skips disabled and unchanged", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New(mockApiHost). - Get("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusOK). - JSON([]api.FunctionResponseOutput{{ - Slug: "test-a", - VerifyJwt: cast.Ptr(true), - EzbrSha256: cast.Ptr("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), - }}) - // Run test - err := client.UpsertFunctions(context.Background(), config.FunctionConfig{ - "test-a": {Enabled: true, VerifyJWT: cast.Ptr(true)}, - "test-b": {Enabled: false}, - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("skips unchanged function when verify_jwt is not configured", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New(mockApiHost). - Get("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusOK). - JSON([]api.FunctionResponseOutput{{ - Slug: "test-a", - VerifyJwt: cast.Ptr(false), - EzbrSha256: cast.Ptr("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), - }}) - // Run test - err := client.UpsertFunctions(context.Background(), config.FunctionConfig{ - "test-a": {Enabled: true}, - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("handles concurrent deploy", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New(mockApiHost). - Get("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusOK). - JSON([]api.FunctionResponseOutput{}) - gock.New(mockApiHost). - Post("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusBadRequest). - BodyString("Duplicated function slug") - gock.New(mockApiHost). - Patch("/v1/projects/" + mockProject + "/functions/test"). - Reply(http.StatusOK). - JSON(api.FunctionResponseOutput{Slug: "test"}) - // Run test - err := client.UpsertFunctions(context.Background(), config.FunctionConfig{ - "test": {Enabled: true}, - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("retries on network failure", func(t *testing.T) { - // Setup mock api - defer gock.OffAll() - gock.New(mockApiHost). - Get("/v1/projects/" + mockProject + "/functions"). - ReplyError(errors.New("network error")) - gock.New(mockApiHost). - Get("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusServiceUnavailable) - gock.New(mockApiHost). - Get("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusBadRequest) - // Run test - err := client.UpsertFunctions(context.Background(), nil) - // Check error - assert.ErrorContains(t, err, "unexpected list functions status 400:") - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) -} - -func TestCreateFunction(t *testing.T) { - client := mockClient(t) - // Setup mock api - defer gock.OffAll() - gock.New(mockApiHost). - Get("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusOK). - JSON([]api.FunctionResponseOutput{}) - gock.New(mockApiHost). - Post("/v1/projects/" + mockProject + "/functions"). - ReplyError(errors.New("network error")) - gock.New(mockApiHost). - Post("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusServiceUnavailable) - gock.New(mockApiHost). - Post("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusCreated). - JSON(api.FunctionResponseOutput{Slug: "test"}) - // Run test - err := client.UpsertFunctions(context.Background(), config.FunctionConfig{ - "test": {Enabled: true}, - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) -} - -func TestUpdateFunction(t *testing.T) { - client := mockClient(t) - // Setup mock api - defer gock.OffAll() - gock.New(mockApiHost). - Get("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusOK). - JSON([]api.FunctionResponseOutput{{Slug: "test"}}) - gock.New(mockApiHost). - Patch("/v1/projects/" + mockProject + "/functions/test"). - ReplyError(errors.New("network error")) - gock.New(mockApiHost). - Patch("/v1/projects/" + mockProject + "/functions/test"). - Reply(http.StatusServiceUnavailable) - gock.New(mockApiHost). - Patch("/v1/projects/" + mockProject + "/functions/test"). - Reply(http.StatusOK). - JSON(api.FunctionResponseOutput{Slug: "test"}) - // Run test - err := client.UpsertFunctions(context.Background(), config.FunctionConfig{ - "test": {Enabled: true}, - }) - // Check error - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) -} diff --git a/apps/cli-go/pkg/function/bundle.go b/apps/cli-go/pkg/function/bundle.go deleted file mode 100644 index 599006aef8..0000000000 --- a/apps/cli-go/pkg/function/bundle.go +++ /dev/null @@ -1,153 +0,0 @@ -package function - -import ( - "context" - "fmt" - "io" - "io/fs" - "net/url" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - "github.com/andybalholm/brotli" - "github.com/go-errors/errors" - "github.com/supabase/cli/pkg/cast" -) - -type nativeBundler struct { - tempDir string - fsys fs.FS - timeout time.Duration - denoVersion uint -} - -func NewNativeBundler(tempDir string, fsys fs.FS, opts ...func(*nativeBundler)) EszipBundler { - b := &nativeBundler{ - tempDir: tempDir, - fsys: fsys, - denoVersion: 2, - } - for _, apply := range opts { - apply(b) - } - return b -} - -func WithTimeout(timeout time.Duration) func(*nativeBundler) { - return func(b *nativeBundler) { - b.timeout = timeout - } -} - -func WithDenoVersion(version uint) func(*nativeBundler) { - return func(b *nativeBundler) { - b.denoVersion = version - } -} - -var ( - // Use a package private variable to allow testing without gosec complaining about G204 - edgeRuntimeBin = "edge-runtime" - BundleFlags = []string{} -) - -func (b *nativeBundler) Bundle(ctx context.Context, slug, entrypoint, importMap string, staticFiles []string, output io.Writer) (FunctionDeployMetadata, error) { - meta := NewMetadata(slug, entrypoint, importMap, staticFiles) - outputPath := filepath.Join(b.tempDir, slug+".eszip") - // TODO: make edge runtime write to stdout - args := []string{"bundle", "--entrypoint", entrypoint, "--output", outputPath} - if len(importMap) > 0 && !ShouldUseDenoJsonDiscovery(entrypoint, importMap) { - args = append(args, "--import-map", importMap) - } - for _, staticFile := range staticFiles { - args = append(args, "--static", staticFile) - } - args = append(args, BundleFlags...) - if b.timeout > 0 { - timeoutCtx, cancel := context.WithTimeout(ctx, b.timeout) - defer cancel() // release resources if command exits before timeout - ctx = timeoutCtx - } - cmd := exec.CommandContext(ctx, edgeRuntimeBin, args...) - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - if fsys, ok := b.fsys.(fs.StatFS); ok && !ShouldUsePackageJsonDiscovery(entrypoint, importMap, fsys) { - cmd.Env = append(cmd.Environ(), "DENO_NO_PACKAGE_JSON=1") - } - if err := cmd.Run(); err != nil { - return meta, errors.Errorf("failed to bundle function: %w", err) - } - defer os.Remove(outputPath) - // Compress the output - eszipBytes, err := b.fsys.Open(outputPath) - if err != nil { - return meta, errors.Errorf("failed to open eszip: %w", err) - } - defer eszipBytes.Close() - return meta, Compress(eszipBytes, output) -} - -func ShouldUseDenoJsonDiscovery(entrypoint, importMap string) bool { - return isDeno(filepath.Base(importMap)) && filepath.Dir(importMap) == filepath.Dir(entrypoint) -} - -func ShouldUsePackageJsonDiscovery(entrypoint, importMap string, fsys fs.StatFS) bool { - if len(importMap) > 0 { - return false - } - packageJsonPath := filepath.Join(filepath.Dir(entrypoint), "package.json") - if _, err := fsys.Stat(packageJsonPath); errors.Is(err, os.ErrNotExist) { - return false - } - return true -} - -const compressedEszipMagicID = "EZBR" - -func Compress(r io.Reader, w io.Writer) error { - if _, err := fmt.Fprint(w, compressedEszipMagicID); err != nil { - return errors.Errorf("failed to append magic id: %w", err) - } - brw := brotli.NewWriter(w) - defer brw.Close() - if _, err := io.Copy(brw, r); err != nil { - return errors.Errorf("failed to compress eszip: %w", err) - } - return nil -} - -func NewMetadata(slug, entrypoint, importMap string, staticFiles []string) FunctionDeployMetadata { - meta := FunctionDeployMetadata{ - Name: &slug, - EntrypointPath: toFileURL(entrypoint), - } - if len(importMap) > 0 { - meta.ImportMapPath = cast.Ptr(toFileURL(importMap)) - } - files := make([]string, len(staticFiles)) - for i, sf := range staticFiles { - files[i] = toFileURL(sf) - } - meta.StaticPatterns = &files - return meta -} - -func toFileURL(hostPath string) string { - absHostPath, err := filepath.Abs(hostPath) - if err != nil { - return hostPath - } - // Convert to unix path because edge runtime only supports linux - unixPath := toUnixPath(absHostPath) - parsed := url.URL{Scheme: "file", Path: unixPath} - return parsed.String() -} - -func toUnixPath(absHostPath string) string { - prefix := filepath.VolumeName(absHostPath) - unixPath := filepath.ToSlash(absHostPath) - return strings.TrimPrefix(unixPath, prefix) -} diff --git a/apps/cli-go/pkg/function/bundle_test.go b/apps/cli-go/pkg/function/bundle_test.go deleted file mode 100644 index 1f16b87f47..0000000000 --- a/apps/cli-go/pkg/function/bundle_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package function - -import ( - "bytes" - "context" - "fmt" - "os" - "path/filepath" - "testing" - fs "testing/fstest" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/pkg/cast" -) - -func TestMain(m *testing.M) { - // Setup mock edge runtime binary - if len(os.Args) > 1 && os.Args[1] == "bundle" { - if msg := os.Getenv("TEST_BUNDLE_ERROR"); len(msg) > 0 { - fmt.Fprintln(os.Stderr, msg) - os.Exit(1) - } - os.Exit(0) - } - // Run test suite - os.Exit(m.Run()) -} - -func TestBundleFunction(t *testing.T) { - cwd, err := os.Getwd() - require.NoError(t, err) - edgeRuntimeBin, err = os.Executable() - require.NoError(t, err) - - t.Run("creates eszip bundle", func(t *testing.T) { - var body bytes.Buffer - // Setup in-memory fs - fsys := fs.MapFS{ - "hello.eszip": &fs.MapFile{}, - } - // Setup mock bundler - bundler := nativeBundler{fsys: fsys} - // Run test - meta, err := bundler.Bundle( - context.Background(), - "hello", - "hello/index.ts", - "hello/deno.json", - []string{"hello/data.pdf"}, - &body, - ) - // Check error - assert.NoError(t, err) - assert.Equal(t, compressedEszipMagicID+";", body.String()) - assert.Equal(t, cast.Ptr("hello"), meta.Name) - entrypoint := fmt.Sprintf("file://%s/hello/index.ts", filepath.ToSlash(cwd)) - assert.Equal(t, entrypoint, meta.EntrypointPath) - importMap := fmt.Sprintf("file://%s/hello/deno.json", filepath.ToSlash(cwd)) - assert.Equal(t, &importMap, meta.ImportMapPath) - staticFile := fmt.Sprintf("file://%s/hello/data.pdf", filepath.ToSlash(cwd)) - assert.Equal(t, cast.Ptr([]string{staticFile}), meta.StaticPatterns) - assert.Nil(t, meta.VerifyJwt) - }) - - t.Run("ignores empty value", func(t *testing.T) { - var body bytes.Buffer - // Setup in-memory fs - fsys := fs.MapFS{ - "hello.eszip": &fs.MapFile{}, - } - // Setup mock bundler - bundler := nativeBundler{fsys: fsys} - // Run test - meta, err := bundler.Bundle( - context.Background(), - "hello", - "hello/index.ts", - "", - nil, - &body, - ) - // Check error - assert.NoError(t, err) - assert.Equal(t, compressedEszipMagicID+";", body.String()) - assert.Equal(t, cast.Ptr("hello"), meta.Name) - entrypoint := fmt.Sprintf("file://%s/hello/index.ts", filepath.ToSlash(cwd)) - assert.Equal(t, entrypoint, meta.EntrypointPath) - assert.Nil(t, meta.ImportMapPath) - assert.NotNil(t, meta.StaticPatterns) - assert.Nil(t, meta.VerifyJwt) - }) -} diff --git a/apps/cli-go/pkg/function/deno.go b/apps/cli-go/pkg/function/deno.go deleted file mode 100644 index 6c98b367b7..0000000000 --- a/apps/cli-go/pkg/function/deno.go +++ /dev/null @@ -1,181 +0,0 @@ -package function - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/fs" - "net/url" - "os" - "path" - "path/filepath" - "regexp" - "strings" - - "github.com/go-errors/errors" - "github.com/tidwall/jsonc" -) - -type ImportMap struct { - Imports map[string]string `json:"imports"` - Scopes map[string]map[string]string `json:"scopes"` - // Fallback reference for deno.json - ImportMap string `json:"importMap"` -} - -func (m *ImportMap) LoadAsDeno(imPath string, fsys fs.FS, opts ...func(string, io.Reader) error) error { - if err := m.Load(imPath, fsys, opts...); err != nil { - return err - } - if name := path.Base(imPath); isDeno(name) && m.IsReference() { - imPath = path.Join(path.Dir(imPath), m.ImportMap) - if err := m.Load(imPath, fsys, opts...); err != nil { - return err - } - } - return nil -} - -func isDeno(name string) bool { - return strings.EqualFold(name, "deno.json") || - strings.EqualFold(name, "deno.jsonc") -} - -func (m *ImportMap) IsReference() bool { - // Ref: https://github.com/denoland/deno/blob/main/cli/schemas/config-file.v1.json#L273 - return len(m.Imports) == 0 && len(m.Scopes) == 0 && len(m.ImportMap) > 0 -} - -func (m *ImportMap) Load(imPath string, fsys fs.FS, opts ...func(string, io.Reader) error) error { - data, err := fs.ReadFile(fsys, filepath.FromSlash(imPath)) - if err != nil { - return errors.Errorf("failed to load import map: %w", err) - } - if err := m.Parse(data); err != nil { - return err - } - if err := m.Resolve(imPath); err != nil { - return err - } - for _, apply := range opts { - if err := apply(imPath, bytes.NewReader(data)); err != nil { - return err - } - } - return nil -} - -func (m *ImportMap) Parse(data []byte) error { - data = jsonc.ToJSONInPlace(data) - decoder := json.NewDecoder(bytes.NewReader(data)) - if err := decoder.Decode(&m); err != nil { - return errors.Errorf("failed to parse import map: %w", err) - } - return nil -} - -func (m *ImportMap) Resolve(imPath string) error { - // Resolve all paths relative to current file - for k, v := range m.Imports { - m.Imports[k] = resolveHostPath(imPath, v) - } - for module, mapping := range m.Scopes { - for k, v := range mapping { - m.Scopes[module][k] = resolveHostPath(imPath, v) - } - } - return nil -} - -func resolveHostPath(jsonPath, hostPath string) string { - // Leave absolute paths unchanged - if path.IsAbs(hostPath) { - return hostPath - } - // Leave URLs unchanged - if parsed, err := url.Parse(hostPath); err == nil && len(parsed.Scheme) > 0 { - return hostPath - } - resolved := path.Join(path.Dir(jsonPath), hostPath) - // Directory imports need to be suffixed with / - // Ref: https://deno.com/manual@v1.33.0/basics/import_maps - if strings.HasSuffix(hostPath, "/") { - resolved += "/" - } - // Relative imports must be prefixed with ./ or ../ - if !path.IsAbs(resolved) { - resolved = "./" + resolved - } - return resolved -} - -// Ref: https://regex101.com/r/DfBdJA/1 -var importPathPattern = regexp.MustCompile(`(?i)(?:import|export)\s+(?:type\s+)?(?:{[^{}]+}|.*?)\s*(?:from)?\s*['"](.*?)['"]|import\(\s*['"](.*?)['"]\)`) - -func (importMap *ImportMap) WalkImportPaths(srcPath string, readFile func(curr string, w io.Writer) error) error { - seen := map[string]struct{}{} - // DFS because it's more efficient to pop from end of array - q := make([]string, 1) - q[0] = srcPath - for len(q) > 0 { - curr := q[len(q)-1] - q = q[:len(q)-1] - // Assume no file is symlinked - if _, ok := seen[curr]; ok { - continue - } - seen[curr] = struct{}{} - // Read into memory for regex match later - var buf bytes.Buffer - if err := readFile(curr, &buf); errors.Is(err, os.ErrNotExist) { - fmt.Fprintln(os.Stderr, "WARN:", err) - continue - } else if err != nil { - return err - } - // Traverse all modules imported by the current source file - for _, matches := range importPathPattern.FindAllStringSubmatch(buf.String(), -1) { - if len(matches) < 3 { - continue - } - // Matches 'from' clause if present, else fallback to 'import' - mod := matches[1] - if len(mod) == 0 { - mod = matches[2] - } - mod = strings.TrimSpace(mod) - // Substitute kv from import map - substituted := false - for k, v := range importMap.Imports { - if strings.HasPrefix(mod, k) { - mod = v + mod[len(k):] - substituted = true - } - } - // Ignore URLs and directories, assuming no sloppy imports - // https://github.com/denoland/deno/issues/2506#issuecomment-2727635545 - if len(path.Ext(mod)) == 0 { - continue - } - // Deno import path must begin with one of these prefixes - if !isRelPath(mod) && !isAbsPath(mod) { - continue - } - if isRelPath(mod) && !substituted { - mod = path.Join(path.Dir(curr), mod) - } - // Cleans import path to help detect duplicates - q = append(q, path.Clean(mod)) - } - } - return nil -} - -func isRelPath(mod string) bool { - return strings.HasPrefix(mod, "./") || strings.HasPrefix(mod, "../") -} - -func isAbsPath(mod string) bool { - return strings.HasPrefix(mod, "/") -} diff --git a/apps/cli-go/pkg/function/deno_test.go b/apps/cli-go/pkg/function/deno_test.go deleted file mode 100644 index aa9677f5ad..0000000000 --- a/apps/cli-go/pkg/function/deno_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package function - -import ( - "embed" - "io" - "os" - "testing" - fs "testing/fstest" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -//go:embed testdata -var testImports embed.FS - -type MockFS struct { - mock.Mock -} - -func (m *MockFS) ReadFile(srcPath string, w io.Writer) error { - _ = m.Called(srcPath) - data, err := testImports.ReadFile(srcPath) - if err != nil { - return err - } - if _, err := w.Write(data); err != nil { - return err - } - return nil -} - -func TestImportPaths(t *testing.T) { - t.Run("iterates all import paths", func(t *testing.T) { - // Setup in-memory fs - fsys := MockFS{} - fsys.On("ReadFile", "/modules/my-module.ts").Once() - fsys.On("ReadFile", "testdata/modules/imports.ts").Once() - fsys.On("ReadFile", "testdata/geometries/Geometries.js").Once() - // Run test - im := ImportMap{} - err := im.WalkImportPaths("testdata/modules/imports.ts", fsys.ReadFile) - // Check error - assert.NoError(t, err) - fsys.AssertExpectations(t) - }) - - t.Run("iterates with import map", func(t *testing.T) { - // Setup in-memory fs - fsys := MockFS{} - fsys.On("ReadFile", "/modules/my-module.ts").Once() - fsys.On("ReadFile", "testdata/modules/imports.ts").Once() - fsys.On("ReadFile", "testdata/geometries/Geometries.js").Once() - fsys.On("ReadFile", "testdata/shared/whatever.ts").Once() - fsys.On("ReadFile", "testdata/shared/mod.ts").Once() - fsys.On("ReadFile", "testdata/nested/index.ts").Once() - // Setup deno.json - im := ImportMap{Imports: map[string]string{ - "module-name/": "../shared/", - }} - assert.NoError(t, im.Resolve("testdata/modules/deno.json")) - // Run test - err := im.WalkImportPaths("testdata/modules/imports.ts", fsys.ReadFile) - // Check error - assert.NoError(t, err) - fsys.AssertExpectations(t) - }) - - t.Run("resolves legacy import map", func(t *testing.T) { - // Setup in-memory fs - fsys := MockFS{} - fsys.On("ReadFile", "/modules/my-module.ts").Once() - fsys.On("ReadFile", "testdata/modules/imports.ts").Once() - fsys.On("ReadFile", "testdata/geometries/Geometries.js").Once() - fsys.On("ReadFile", "testdata/shared/whatever.ts").Once() - fsys.On("ReadFile", "testdata/shared/mod.ts").Once() - fsys.On("ReadFile", "testdata/nested/index.ts").Once() - // Setup legacy import map - im := ImportMap{Imports: map[string]string{ - "module-name/": "./shared/", - }} - assert.NoError(t, im.Resolve("testdata/import_map.json")) - // Run test - err := im.WalkImportPaths("testdata/modules/imports.ts", fsys.ReadFile) - // Check error - assert.NoError(t, err) - fsys.AssertExpectations(t) - }) - - t.Run("iterates multiline import type statements", func(t *testing.T) { - // This test verifies that multiline import type statements are correctly parsed. - // The (?:type\s+)? group consumes the type keyword so braced imports hit the {[^{}]+} branch. - // Setup in-memory fs - fsys := MockFS{} - fsys.On("ReadFile", "testdata/modules/import_types.ts").Once() - fsys.On("ReadFile", "testdata/types/database.ts").Once() - // Run test - im := ImportMap{} - err := im.WalkImportPaths("testdata/modules/import_types.ts", fsys.ReadFile) - // Check error - assert.NoError(t, err) - fsys.AssertExpectations(t) - }) -} - -func TestResolveImports(t *testing.T) { - t.Run("resolves relative directory", func(t *testing.T) { - imPath := "supabase/functions/import_map.json" - // Setup in-memory fs - fsys := fs.MapFS{ - imPath: &fs.MapFile{Data: []byte(`{ - "imports": { - "abs/": "/tmp/", - "root": "../../common", - "parent": "../tests", - "child": "child/", - "missing": "../missing" - } - }`)}, - "/tmp/": &fs.MapFile{}, - "common": &fs.MapFile{}, - "supabase/tests": &fs.MapFile{}, - "supabase/functions/child": &fs.MapFile{}, - } - // Run test - resolved := ImportMap{} - err := resolved.Load(imPath, fsys) - // Check error - assert.NoError(t, err) - assert.Equal(t, "/tmp/", resolved.Imports["abs/"]) - assert.Equal(t, "./common", resolved.Imports["root"]) - assert.Equal(t, "./supabase/tests", resolved.Imports["parent"]) - assert.Equal(t, "./supabase/functions/child/", resolved.Imports["child"]) - assert.Equal(t, "./supabase/missing", resolved.Imports["missing"]) - }) - - t.Run("resolves parent scopes", func(t *testing.T) { - imPath := "supabase/functions/import_map.json" - // Setup in-memory fs - fsys := fs.MapFS{ - imPath: &fs.MapFile{Data: []byte(`{ - "scopes": { - "my-scope": { - "my-mod": "https://deno.land" - } - } - }`)}, - } - // Run test - resolved := ImportMap{} - err := resolved.Load(imPath, fsys) - // Check error - assert.NoError(t, err) - assert.Equal(t, "https://deno.land", resolved.Scopes["my-scope"]["my-mod"]) - }) -} - -func TestResolveDeno(t *testing.T) { - t.Run("resolves deno.json", func(t *testing.T) { - imPath := "supabase/functions/slug/deno.json" - // Setup in-memory fs - fsys := fs.MapFS{ - imPath: &fs.MapFile{Data: []byte(`{ - "imports": { - "@mod": "./mod.ts" - }, - "importMap": "../../import_map.json" - }`)}, - "supabase/functions/slug/mod.ts": &fs.MapFile{}, - } - // Run test - resolved := ImportMap{} - err := resolved.LoadAsDeno(imPath, fsys) - // Check error - assert.NoError(t, err) - assert.Equal(t, "./supabase/functions/slug/mod.ts", resolved.Imports["@mod"]) - }) - - t.Run("resolves fallback imports", func(t *testing.T) { - imPath := "supabase/functions/slug/deno.json" - // Setup in-memory fs - fsys := fs.MapFS{ - imPath: &fs.MapFile{Data: []byte(`{ - "importMap": "../../import_map.json" - }`)}, - "supabase/import_map.json": &fs.MapFile{Data: []byte(`{ - "imports": { - "my-mod": "https://deno.land" - } - }`)}, - } - // Run test - resolved := ImportMap{} - err := resolved.LoadAsDeno(imPath, fsys) - // Check error - assert.NoError(t, err) - assert.Equal(t, "https://deno.land", resolved.Imports["my-mod"]) - }) - - t.Run("throws error on missing import", func(t *testing.T) { - imPath := "supabase/functions/slug/deno.jsonc" - // Setup in-memory fs - fsys := fs.MapFS{ - imPath: &fs.MapFile{Data: []byte(`{ - "importMap": "../../import_map.json" - }`)}, - } - // Run test - resolved := ImportMap{} - err := resolved.LoadAsDeno(imPath, fsys) - // Check error - assert.ErrorIs(t, err, os.ErrNotExist) - assert.Empty(t, resolved.Imports) - assert.Empty(t, resolved.Scopes) - }) -} diff --git a/apps/cli-go/pkg/function/deploy.go b/apps/cli-go/pkg/function/deploy.go deleted file mode 100644 index 38458d6cdb..0000000000 --- a/apps/cli-go/pkg/function/deploy.go +++ /dev/null @@ -1,304 +0,0 @@ -package function - -import ( - "context" - "encoding/json" - "fmt" - "io" - "io/fs" - "mime/multipart" - "net/http" - "os" - "path/filepath" - "strconv" - "time" - - "github.com/go-errors/errors" - "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" - "github.com/supabase/cli/pkg/config" - "github.com/supabase/cli/pkg/queue" -) - -var ErrNoDeploy = errors.New("All Functions are up to date.") - -const deployRateLimitMaxRetries = 8 - -func (s *EdgeRuntimeAPI) Deploy(ctx context.Context, functionConfig config.FunctionConfig, fsys fs.FS) error { - if s.eszip != nil { - return s.UpsertFunctions(ctx, functionConfig) - } - remoteFunctions, err := s.listRemoteFunctionsForVerifyJwt(ctx, functionConfig) - if err != nil { - return err - } - // Convert all paths in functions config to relative when using api deploy - var toDeploy []FunctionDeployMetadata - for slug, fc := range functionConfig { - if !fc.Enabled { - fmt.Fprintln(os.Stderr, "Skipping disabled Function:", slug) - continue - } - meta := FunctionDeployMetadata{ - Name: &slug, - EntrypointPath: toRelPath(fc.Entrypoint), - ImportMapPath: cast.Ptr(toRelPath(fc.ImportMap)), - VerifyJwt: fc.VerifyJWT, - } - if meta.VerifyJwt == nil { - if remote, ok := remoteFunctions[slug]; ok { - meta.VerifyJwt = remote.VerifyJwt - } - } - files := make([]string, len(fc.StaticFiles)) - for i, sf := range fc.StaticFiles { - files[i] = toRelPath(sf) - } - meta.StaticPatterns = &files - toDeploy = append(toDeploy, meta) - } - if len(toDeploy) == 0 { - return errors.New(ErrNoDeploy) - } else if len(toDeploy) == 1 { - param := api.V1DeployAFunctionParams{Slug: toDeploy[0].Name} - _, err := s.upload(ctx, param, toDeploy[0], fsys) - return err - } - return s.bulkUpload(ctx, toDeploy, fsys) -} - -func (s *EdgeRuntimeAPI) listRemoteFunctionsForVerifyJwt(ctx context.Context, functionConfig config.FunctionConfig) (map[string]api.FunctionResponseOutput, error) { - needsRemote := false - for _, fc := range functionConfig { - if fc.Enabled && fc.VerifyJWT == nil { - needsRemote = true - break - } - } - if !needsRemote { - return nil, nil - } - resp, err := s.client.V1ListAllFunctionsWithResponse(ctx, s.project) - if err != nil { - return nil, errors.Errorf("failed to list functions: %w", err) - } else if resp.JSON200 == nil { - return nil, errors.Errorf("unexpected list functions status %d: %s", resp.StatusCode(), string(resp.Body)) - } - remoteFunctions := make(map[string]api.FunctionResponseOutput, len(*resp.JSON200)) - for _, function := range *resp.JSON200 { - remoteFunctions[function.Slug] = function - } - return remoteFunctions, nil -} - -func toRelPath(fp string) string { - if filepath.IsAbs(fp) { - if cwd, err := os.Getwd(); err == nil { - if relPath, err := filepath.Rel(cwd, fp); err == nil { - fp = relPath - } - } - } - return filepath.ToSlash(fp) -} - -func (s *EdgeRuntimeAPI) bulkUpload(ctx context.Context, toDeploy []FunctionDeployMetadata, fsys fs.FS) error { - jq := queue.NewJobQueue(s.maxJobs) - toUpdate := make(api.BulkUpdateFunctionBody, len(toDeploy)) - // Bulk uploads only write the bundle and bump the version remotely, so the new metadata - // exists nowhere but this client until the final bulk update request persists it. Bailing - // out on the first upload error strands that metadata and makes every subsequent deploy - // fail with a version conflict, so we keep going and report the errors at the end. - // - // Upload errors are recorded per function index rather than surfaced through the job - // queue's error channel, so they are reported in input order regardless of completion - // order under concurrent --jobs, matching the TS port. - uploadErrs := make([]error, len(toDeploy)) - var queueErrs []error - for i, meta := range toDeploy { - param := api.V1DeployAFunctionParams{ - Slug: meta.Name, - BundleOnly: cast.Ptr("true"), - } - bundle := func() error { - fmt.Fprintln(os.Stderr, "Deploying Function:", *meta.Name) - resp, err := s.upload(ctx, param, meta, fsys) - if err != nil { - uploadErrs[i] = err - return nil - } - toUpdate[i].Id = resp.Id - toUpdate[i].Name = resp.Name - toUpdate[i].Slug = resp.Slug - toUpdate[i].Version = resp.Version - toUpdate[i].EntrypointPath = resp.EntrypointPath - toUpdate[i].ImportMap = resp.ImportMap - toUpdate[i].ImportMapPath = resp.ImportMapPath - toUpdate[i].VerifyJwt = resp.VerifyJwt - toUpdate[i].Status = api.BulkUpdateFunctionBodyStatus(resp.Status) - toUpdate[i].CreatedAt = resp.CreatedAt - return nil - } - queueErrs = append(queueErrs, jq.Put(bundle)) - } - queueErrs = append(queueErrs, jq.Collect()) - bundleErr := errors.Join(append(uploadErrs, queueErrs...)...) - // Failed uploads leave their slot zero valued, ie. without a function id. - uploaded := make(api.BulkUpdateFunctionBody, 0, len(toUpdate)) - for _, f := range toUpdate { - if len(f.Id) > 0 { - uploaded = append(uploaded, f) - } - } - if len(uploaded) == 0 { - return bundleErr - } - for attempt := 0; ; attempt++ { - resp, err := s.client.V1BulkUpdateFunctionsWithResponse(ctx, s.project, uploaded) - if err != nil { - return errors.Join(bundleErr, errors.Errorf("failed to bulk update: %w", err)) - } else if resp.JSON200 != nil { - return bundleErr - } else if resp.StatusCode() != http.StatusTooManyRequests || attempt >= deployRateLimitMaxRetries { - return errors.Join(bundleErr, errors.Errorf("unexpected bulk update status %d: %s", resp.StatusCode(), string(resp.Body))) - } else if err := waitForRateLimit(ctx, responseHeaders(resp.HTTPResponse), attempt, "bulk updating functions"); err != nil { - return errors.Join(bundleErr, err) - } - } -} - -func (s *EdgeRuntimeAPI) upload(ctx context.Context, param api.V1DeployAFunctionParams, meta FunctionDeployMetadata, fsys fs.FS) (*api.DeployFunctionResponseOutput, error) { - for attempt := 0; ; attempt++ { - resp, err := s.uploadOnce(ctx, param, meta, fsys) - if resp != nil && resp.JSON201 != nil { - return resp.JSON201, nil - } else if err != nil { - return nil, err - } else if resp.StatusCode() != http.StatusTooManyRequests || attempt >= deployRateLimitMaxRetries { - return nil, errors.Errorf("unexpected deploy status %d: %s", resp.StatusCode(), string(resp.Body)) - } else if err := waitForRateLimit(ctx, responseHeaders(resp.HTTPResponse), attempt, "deploying function "+cast.Val(meta.Name, "")); err != nil { - return nil, err - } - } -} - -func (s *EdgeRuntimeAPI) uploadOnce(ctx context.Context, param api.V1DeployAFunctionParams, meta FunctionDeployMetadata, fsys fs.FS) (*api.V1DeployAFunctionResponse, error) { - body, w := io.Pipe() - form := multipart.NewWriter(w) - ctx, cancel := context.WithCancelCause(ctx) - go func() { - defer w.Close() - defer form.Close() - if err := writeForm(form, meta, fsys); err != nil { - // Since we are streaming files to the POST request body, any errors - // should be propagated to the request context to cancel the upload. - cancel(err) - } - }() - resp, err := s.client.V1DeployAFunctionWithBodyWithResponse(ctx, s.project, ¶m, form.FormDataContentType(), body) - if cause := context.Cause(ctx); cause != ctx.Err() { - return nil, cause - } else if err != nil { - return nil, errors.Errorf("failed to deploy function: %w", err) - } - return resp, nil -} - -func waitForRateLimit(ctx context.Context, headers http.Header, attempt int, action string) error { - delay := rateLimitDelay(headers, attempt) - fmt.Fprintf(os.Stderr, "Rate limit exceeded while %s. Retrying in %s.\n", action, delay.Round(time.Second)) - timer := time.NewTimer(delay) - defer timer.Stop() - select { - case <-ctx.Done(): - return ctx.Err() - case <-timer.C: - return nil - } -} - -func responseHeaders(resp *http.Response) http.Header { - if resp == nil { - return nil - } - return resp.Header -} - -func rateLimitDelay(headers http.Header, attempt int) time.Duration { - if delay, ok := parseRateLimitDelay(headers.Get("Retry-After")); ok { - return delay - } - if delay, ok := parseRateLimitDelay(headers.Get("X-RateLimit-Reset")); ok { - return delay - } - delay := time.Second << min(attempt, 5) - return delay -} - -func parseRateLimitDelay(value string) (time.Duration, bool) { - if len(value) == 0 { - return 0, false - } - if seconds, err := strconv.Atoi(value); err == nil { - return max(time.Duration(seconds)*time.Second, 0), true - } - if retryAt, err := http.ParseTime(value); err == nil { - return max(time.Until(retryAt), 0), true - } - return 0, false -} - -func writeForm(form *multipart.Writer, meta FunctionDeployMetadata, fsys fs.FS) error { - m, err := form.CreateFormField("metadata") - if err != nil { - return errors.Errorf("failed to create metadata: %w", err) - } - enc := json.NewEncoder(m) - if err := enc.Encode(meta); err != nil { - return errors.Errorf("failed to encode metadata: %w", err) - } - uploadAsset := func(srcPath string, r io.Reader) error { - fmt.Fprintf(os.Stderr, "Uploading asset (%s): %s\n", *meta.Name, srcPath) - f, err := form.CreateFormFile("file", srcPath) - if err != nil { - return errors.Errorf("failed to create form: %w", err) - } - if _, err := io.Copy(f, r); err != nil { - return errors.Errorf("failed to write form: %w", err) - } - return nil - } - addFile := func(srcPath string, w io.Writer) error { - f, err := fsys.Open(filepath.FromSlash(srcPath)) - if err != nil { - return errors.Errorf("failed to read file: %w", err) - } - defer f.Close() - if fi, err := f.Stat(); err != nil { - return errors.Errorf("failed to stat file: %w", err) - } else if fi.IsDir() { - return errors.New("file path is a directory: " + srcPath) - } - r := io.TeeReader(f, w) - return uploadAsset(srcPath, r) - } - // Add import map - importMap := ImportMap{} - if imPath := cast.Val(meta.ImportMapPath, ""); len(imPath) > 0 { - if err := importMap.LoadAsDeno(imPath, fsys, uploadAsset); err != nil { - return err - } - } - // Add static files - patterns := config.Glob(cast.Val(meta.StaticPatterns, []string{})) - files, err := patterns.Files(fsys) - if err != nil { - fmt.Fprintln(os.Stderr, "WARN:", err) - } - for _, sfPath := range files { - if err := addFile(sfPath, io.Discard); err != nil { - return err - } - } - return importMap.WalkImportPaths(meta.EntrypointPath, addFile) -} diff --git a/apps/cli-go/pkg/function/deploy_test.go b/apps/cli-go/pkg/function/deploy_test.go deleted file mode 100644 index 06821e3ac1..0000000000 --- a/apps/cli-go/pkg/function/deploy_test.go +++ /dev/null @@ -1,448 +0,0 @@ -package function - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "io" - "mime/multipart" - "net/http" - "os" - "path" - "strings" - "testing" - fs "testing/fstest" - "time" - - "github.com/h2non/gock" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/cast" - "github.com/supabase/cli/pkg/config" -) - -func assertFormEqual(t *testing.T, actual []byte) { - snapshot := path.Join("testdata", path.Base(t.Name())+".form") - expected, err := testImports.ReadFile(snapshot) - if errors.Is(err, os.ErrNotExist) { - assert.NoError(t, os.WriteFile(snapshot, actual, 0600)) - } - assert.Equal(t, string(expected), string(actual)) -} - -// captureBody records the request body without consuming it, so tests can assert -// on the exact payload sent to a mocked endpoint. -func captureBody(out *[]byte) gock.MatchFunc { - return func(req *http.Request, _ *gock.Request) (bool, error) { - if req.Body == nil { - return true, nil - } - body, err := io.ReadAll(req.Body) - if err != nil { - return false, err - } - req.Body = io.NopCloser(bytes.NewReader(body)) - *out = body - return true, nil - } -} - -func mockFunctionList(functions ...api.FunctionResponseOutput) { - gock.New(mockApiHost). - Get("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusOK). - JSON(functions) -} - -func TestWriteForm(t *testing.T) { - t.Run("writes import map", func(t *testing.T) { - var buf bytes.Buffer - form := multipart.NewWriter(&buf) - require.NoError(t, form.SetBoundary("test")) - // Setup in-memory fs - fsys := testImports - // Run test - err := writeForm(form, FunctionDeployMetadata{ - Name: cast.Ptr("nested"), - VerifyJwt: cast.Ptr(true), - EntrypointPath: "testdata/nested/index.ts", - ImportMapPath: cast.Ptr("testdata/nested/deno.json"), - StaticPatterns: cast.Ptr([]string{"testdata/*/*.js"}), - }, fsys) - // Check error - assert.NoError(t, err) - assertFormEqual(t, buf.Bytes()) - }) - - t.Run("throws error on missing file", func(t *testing.T) { - var buf bytes.Buffer - form := multipart.NewWriter(&buf) - require.NoError(t, form.SetBoundary("test")) - // Setup in-memory fs - fsys := fs.MapFS{} - // Run test - err := writeForm(form, FunctionDeployMetadata{ - ImportMapPath: cast.Ptr("testdata/import_map.json"), - }, fsys) - // Check error - assert.ErrorIs(t, err, os.ErrNotExist) - }) - - t.Run("throws error on directory path", func(t *testing.T) { - var buf bytes.Buffer - form := multipart.NewWriter(&buf) - require.NoError(t, form.SetBoundary("test")) - // Setup in-memory fs - fsys := testImports - // Run test - err := writeForm(form, FunctionDeployMetadata{ - StaticPatterns: cast.Ptr([]string{"testdata"}), - }, fsys) - // Check error - assert.ErrorContains(t, err, "file path is a directory:") - }) -} - -func TestDeployAll(t *testing.T) { - apiClient, err := api.NewClientWithResponses(mockApiHost) - require.NoError(t, err) - client := NewEdgeRuntimeAPI(mockProject, *apiClient) - - t.Run("deploys single slug", func(t *testing.T) { - c := config.FunctionConfig{"demo": { - Enabled: true, - Entrypoint: "testdata/shared/whatever.ts", - }} - // Setup in-memory fs - fsys := testImports - // Setup mock api - defer gock.OffAll() - mockFunctionList() - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", "demo"). - Reply(http.StatusCreated). - JSON(api.DeployFunctionResponseOutput{}) - // Run test - err := client.Deploy(context.Background(), c, fsys) - // Check error - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("retries single slug after rate limit", func(t *testing.T) { - c := config.FunctionConfig{"demo": { - Enabled: true, - Entrypoint: "testdata/shared/whatever.ts", - }} - // Setup in-memory fs - fsys := testImports - // Setup mock api - defer gock.OffAll() - mockFunctionList() - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", "demo"). - Reply(http.StatusTooManyRequests). - SetHeader("Retry-After", "0"). - JSON(map[string]string{"message": "Too Many Requests"}) - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", "demo"). - Reply(http.StatusCreated). - JSON(api.DeployFunctionResponseOutput{}) - // Run test - err := client.Deploy(context.Background(), c, fsys) - // Check error - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("deploys multiple slugs", func(t *testing.T) { - c := config.FunctionConfig{ - "test-ts": { - Enabled: true, - Entrypoint: "testdata/shared/whatever.ts", - }, - "test-js": { - Enabled: true, - Entrypoint: "testdata/geometries/Geometries.js", - }, - } - // Setup in-memory fs - fsys := testImports - // Setup mock api - defer gock.OffAll() - mockFunctionList() - for slug := range c { - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", slug). - Reply(http.StatusCreated). - JSON(api.DeployFunctionResponseOutput{Id: slug}) - } - gock.New(mockApiHost). - Put("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusOK). - JSON(api.BulkUpdateFunctionResponseOutput{}) - // Run test - err := client.Deploy(context.Background(), c, fsys) - // Check error - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("retries bulk update after rate limit reset", func(t *testing.T) { - c := config.FunctionConfig{ - "test-ts": { - Enabled: true, - Entrypoint: "testdata/shared/whatever.ts", - }, - "test-js": { - Enabled: true, - Entrypoint: "testdata/geometries/Geometries.js", - }, - } - // Setup in-memory fs - fsys := testImports - // Setup mock api - defer gock.OffAll() - mockFunctionList() - for slug := range c { - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", slug). - Reply(http.StatusCreated). - JSON(api.DeployFunctionResponseOutput{Id: slug}) - } - gock.New(mockApiHost). - Put("/v1/projects/"+mockProject+"/functions"). - Reply(http.StatusTooManyRequests). - SetHeader("X-RateLimit-Reset", "0"). - JSON(map[string]string{"message": "Too Many Requests"}) - gock.New(mockApiHost). - Put("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusOK). - JSON(api.BulkUpdateFunctionResponseOutput{}) - // Run test - err := client.Deploy(context.Background(), c, fsys) - // Check error - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("bulk updates successful uploads when one fails", func(t *testing.T) { - c := config.FunctionConfig{ - "test-ts": { - Enabled: true, - Entrypoint: "testdata/shared/whatever.ts", - }, - "test-js": { - Enabled: true, - Entrypoint: "testdata/geometries/Geometries.js", - }, - } - // Setup in-memory fs - fsys := testImports - // Setup mock api - defer gock.OffAll() - mockFunctionList() - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", "test-ts"). - Reply(http.StatusCreated). - JSON(api.DeployFunctionResponseOutput{Id: "test-ts", Name: "test-ts", Slug: "test-ts"}) - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", "test-js"). - Reply(http.StatusConflict). - JSON(map[string]string{"message": "deployment already exists"}) - var bulkBody []byte - gock.New(mockApiHost). - Put("/v1/projects/" + mockProject + "/functions"). - AddMatcher(captureBody(&bulkBody)). - Reply(http.StatusOK). - JSON(api.BulkUpdateFunctionResponseOutput{}) - // Run test - err := client.Deploy(context.Background(), c, fsys) - // Check error - assert.ErrorContains(t, err, "unexpected deploy status 409") - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - var toUpdate api.BulkUpdateFunctionBody - require.NoError(t, json.Unmarshal(bulkBody, &toUpdate)) - require.Len(t, toUpdate, 1) - assert.Equal(t, "test-ts", toUpdate[0].Slug) - }) - - t.Run("skips bulk update when all uploads fail", func(t *testing.T) { - c := config.FunctionConfig{ - "test-ts": { - Enabled: true, - Entrypoint: "testdata/shared/whatever.ts", - }, - "test-js": { - Enabled: true, - Entrypoint: "testdata/geometries/Geometries.js", - }, - } - // Setup in-memory fs - fsys := testImports - // Setup mock api - defer gock.OffAll() - mockFunctionList() - for slug := range c { - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", slug). - Reply(http.StatusConflict). - JSON(map[string]string{"message": "deployment already exists"}) - } - // Run test - err := client.Deploy(context.Background(), c, fsys) - // Check error - assert.ErrorContains(t, err, "unexpected deploy status 409") - // No bulk update is mocked, so any PUT would show up as unmatched - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("reports upload and bulk update failures together", func(t *testing.T) { - c := config.FunctionConfig{ - "test-ts": { - Enabled: true, - Entrypoint: "testdata/shared/whatever.ts", - }, - "test-js": { - Enabled: true, - Entrypoint: "testdata/geometries/Geometries.js", - }, - } - // Setup in-memory fs - fsys := testImports - // Setup mock api - defer gock.OffAll() - mockFunctionList() - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", "test-ts"). - Reply(http.StatusCreated). - JSON(api.DeployFunctionResponseOutput{Id: "test-ts", Name: "test-ts", Slug: "test-ts"}) - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", "test-js"). - Reply(http.StatusConflict). - JSON(map[string]string{"message": "deployment already exists"}) - gock.New(mockApiHost). - Put("/v1/projects/" + mockProject + "/functions"). - Reply(http.StatusBadRequest). - JSON(map[string]string{"message": "bulk update rejected"}) - // Run test - err := client.Deploy(context.Background(), c, fsys) - // Check error - assert.ErrorContains(t, err, "unexpected deploy status 409") - assert.ErrorContains(t, err, "unexpected bulk update status 400") - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("reports concurrent upload failures in input order", func(t *testing.T) { - client2 := NewEdgeRuntimeAPI(mockProject, *apiClient, WithMaxJobs(2)) - toDeploy := []FunctionDeployMetadata{ - { - Name: cast.Ptr("first-fn"), - EntrypointPath: "testdata/shared/whatever.ts", - }, - { - Name: cast.Ptr("second-fn"), - EntrypointPath: "testdata/geometries/Geometries.js", - }, - } - fsys := testImports - // Setup mock api - defer gock.OffAll() - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", "first-fn"). - Reply(http.StatusConflict). - Delay(500 * time.Millisecond). - JSON(map[string]string{"message": "first failed"}) - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", "second-fn"). - Reply(http.StatusConflict). - JSON(map[string]string{"message": "second failed"}) - // Run test - err := client2.bulkUpload(context.Background(), toDeploy, fsys) - // Check error - require.Error(t, err) - // JSON replies are newline-terminated by json.Encoder, so filter out the - // resulting blank lines between joined errors before counting entries. - var lines []string - for _, line := range strings.Split(err.Error(), "\n") { - if len(line) > 0 { - lines = append(lines, line) - } - } - require.Len(t, lines, 2) - assert.Contains(t, lines[0], "first failed") - assert.Contains(t, lines[1], "second failed") - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("preserves remote verify_jwt when not configured", func(t *testing.T) { - c := config.FunctionConfig{"demo": { - Enabled: true, - Entrypoint: "testdata/shared/whatever.ts", - }} - // Setup in-memory fs - fsys := testImports - // Setup mock api - defer gock.OffAll() - mockFunctionList(api.FunctionResponseOutput{ - Id: "demo", - Name: "demo", - Slug: "demo", - VerifyJwt: cast.Ptr(false), - }) - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", "demo"). - BodyString(`"verify_jwt":false`). - Reply(http.StatusCreated). - JSON(api.DeployFunctionResponseOutput{}) - // Run test - err := client.Deploy(context.Background(), c, fsys) - // Check error - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("throws error on network failure", func(t *testing.T) { - errNetwork := errors.New("network") - c := config.FunctionConfig{"demo": {Enabled: true}} - // Setup in-memory fs - fsys := fs.MapFS{} - // Setup mock api - defer gock.OffAll() - mockFunctionList() - gock.New(mockApiHost). - Post("/v1/projects/"+mockProject+"/functions/deploy"). - MatchParam("slug", "demo"). - ReplyError(errNetwork) - // Run test - err := client.Deploy(context.Background(), c, fsys) - // Check error - assert.ErrorIs(t, err, errNetwork) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) -} diff --git a/apps/cli-go/pkg/function/testdata/geometries/Geometries.js b/apps/cli-go/pkg/function/testdata/geometries/Geometries.js deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/apps/cli-go/pkg/function/testdata/modules/import_types.ts b/apps/cli-go/pkg/function/testdata/modules/import_types.ts deleted file mode 100644 index d285ab0ee8..0000000000 --- a/apps/cli-go/pkg/function/testdata/modules/import_types.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Test file for multiline import type statements -// This pattern requires (?:type\s+)? to route braced imports into the {[^{}]+} branch - -// Multiline import type - should be matched by the regex -import type { - Database, - Json -} from '../types/database.ts' - -// Single line import type - should also work -import type { Database as DB } from '../types/database.ts' - -// Re-export type to verify export pattern -export type { Database } from '../types/database.ts' - -// Multiline export type -export type { - Json -} from '../types/database.ts' - -// Non-braced default type import - exercises the .*? branch on single-line -import type Database from '../types/database.ts' diff --git a/apps/cli-go/pkg/function/testdata/modules/imports.ts b/apps/cli-go/pkg/function/testdata/modules/imports.ts deleted file mode 100644 index d4d8cda7b3..0000000000 --- a/apps/cli-go/pkg/function/testdata/modules/imports.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { - Component -} from '@angular2/core'; -import defaultMember from "module-name"; -import * as name from "module-name "; -import { member } from " module-name"; -import { member as alias } from "module-name"; -import { member1 , member2 } from "module-name"; -import { member1 , member2 as alias2 , member3 as alias3 } from "module-name"; -import { - Component -} from '@angular2/core'; -import defaultMember from "$module-name"; -import defaultMember, { member, member } from "module-name"; -import defaultMember, * as name from "module-name"; - -import * as name from "module-name " -import { member } from " module-name" -import { member as alias } from "module-name" -import { member1 , member2 } from "module-name" -import { member1 , member2 as alias2 , member3 as alias3 } from "module-name" -import { - Component -} from '@angular2/core' -import defaultMember from "$module-name" -import defaultMember, { member, member } from "module-name" -import defaultMember, * as name from "module-name" - -import "module-name"; -import React from "react" -import { Field } from "redux-form" -import "module-name"; - -import { - PlaneBufferGeometry, - OctahedronGeometry, - TorusBufferGeometry -} from '../geometries/Geometries.js'; - -import { - PlaneBufferGeometry, - OctahedronGeometry, - TorusBufferGeometry -} from '../geometries/Geometries.js' - -import("module-name/whatever.ts"); -import("module-name/whatever.ts") - -import { Field } from "redux-form"; -import MultiContentListView from "./views/ListView"; -import MultiContentAddView from "./views/AddView"; -import MultiContentEditView from "./views/EditView"; - -import { Field } from "redux-form" -import MultiContentListView from "./views/ListView" -import MultiContentAddView from "./views/AddView" -import MultiContentEditView from "./views/EditView" - - -// JSX-like snippets are comments so this .ts fixture remains valid syntax. -// -// - -// *Add all needed dependency to this module -// *app requires those import modules to function - - -/** -* - *Add all needed dependency to this module - *app requires those import modules to function - * -**/ - -let modules = []; - - -import defaultExport from "module-name"; -import * as name from "module-name"; -import { export1 } from "module-name"; -import { export1 as alias1 } from "module-name"; -import { default as alias } from "module-name"; -import { export1, export2 } from "module-name"; -import { export1, export2 as alias2, /* … */ } from "module-name"; -import { "string name" as alias } from "module-name"; -import defaultExport, { export1, /* … */ } from "module-name"; -import defaultExport, * as name from "module-name"; -import "module-name"; -import { "a-b" as a } from "/modules/my-module.ts"; -import myDefault from "/modules/my-module.ts"; -import myDefault, * as myModule from "/modules/my-module.ts"; diff --git a/apps/cli-go/pkg/function/testdata/nested/deno.json b/apps/cli-go/pkg/function/testdata/nested/deno.json deleted file mode 100644 index bfba6fbb2c..0000000000 --- a/apps/cli-go/pkg/function/testdata/nested/deno.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "imports": { - "module": "jsr:@supabase/functions-js/edge-runtime.d.ts" - } -} diff --git a/apps/cli-go/pkg/function/testdata/nested/index.ts b/apps/cli-go/pkg/function/testdata/nested/index.ts deleted file mode 100644 index b0539a467f..0000000000 --- a/apps/cli-go/pkg/function/testdata/nested/index.ts +++ /dev/null @@ -1 +0,0 @@ -import "module"; diff --git a/apps/cli-go/pkg/function/testdata/shared/whatever.ts b/apps/cli-go/pkg/function/testdata/shared/whatever.ts deleted file mode 100644 index 879e79cb31..0000000000 --- a/apps/cli-go/pkg/function/testdata/shared/whatever.ts +++ /dev/null @@ -1,3 +0,0 @@ -import * as _ from "../nested/index.ts"; - -export * from "./mod.ts"; diff --git a/apps/cli-go/pkg/function/testdata/types/database.ts b/apps/cli-go/pkg/function/testdata/types/database.ts deleted file mode 100644 index 4c49d8e2d0..0000000000 --- a/apps/cli-go/pkg/function/testdata/types/database.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type Database = { - public: { - Tables: { - users: { - Row: { id: string; name: string } - } - } - } -} - -export type Json = string | number | boolean | null | { [key: string]: Json } | Json[] diff --git a/apps/cli-go/pkg/function/testdata/writes_import_map.form b/apps/cli-go/pkg/function/testdata/writes_import_map.form deleted file mode 100644 index 174a55f6b0..0000000000 --- a/apps/cli-go/pkg/function/testdata/writes_import_map.form +++ /dev/null @@ -1,25 +0,0 @@ ---test -Content-Disposition: form-data; name="metadata" - -{"entrypoint_path":"testdata/nested/index.ts","import_map_path":"testdata/nested/deno.json","name":"nested","static_patterns":["testdata/*/*.js"],"verify_jwt":true} - ---test -Content-Disposition: form-data; name="file"; filename="testdata/nested/deno.json" -Content-Type: application/octet-stream - -{ - "imports": { - "module": "jsr:@supabase/functions-js/edge-runtime.d.ts" - } -} - ---test -Content-Disposition: form-data; name="file"; filename="testdata/geometries/Geometries.js" -Content-Type: application/octet-stream - - ---test -Content-Disposition: form-data; name="file"; filename="testdata/nested/index.ts" -Content-Type: application/octet-stream - -import "module"; diff --git a/apps/cli-go/pkg/go.mod b/apps/cli-go/pkg/go.mod deleted file mode 100644 index 44a3f2acfd..0000000000 --- a/apps/cli-go/pkg/go.mod +++ /dev/null @@ -1,55 +0,0 @@ -module github.com/supabase/cli/pkg - -go 1.26.0 - -require ( - github.com/BurntSushi/toml v1.6.0 - github.com/andybalholm/brotli v1.2.3 - github.com/cenkalti/backoff/v4 v4.3.0 - github.com/docker/go-units v0.5.0 - github.com/ecies/go/v2 v2.0.11 - github.com/go-errors/errors v1.5.1 - github.com/go-viper/mapstructure/v2 v2.5.0 - github.com/golang-jwt/jwt/v5 v5.3.1 - github.com/h2non/gock v1.2.0 - github.com/jackc/pgconn v1.14.3 - github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 - github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 - github.com/jackc/pgproto3/v2 v2.3.3 - github.com/jackc/pgtype v1.14.4 - github.com/jackc/pgx/v4 v4.18.3 - github.com/joho/godotenv v1.5.1 - github.com/oapi-codegen/nullable v1.2.0 - github.com/oapi-codegen/runtime v1.7.0 - github.com/spf13/afero v1.15.0 - github.com/spf13/viper v1.21.0 - github.com/stretchr/testify v1.12.1 - github.com/tidwall/jsonc v0.3.3 - golang.org/x/mod v0.41.0 - google.golang.org/grpc v1.83.2 -) - -require ( - github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect - github.com/ethereum/go-ethereum v1.17.0 // indirect - github.com/fsnotify/fsnotify v1.10.1 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect - github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgio v1.0.0 // indirect - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/lib/pq v1.10.9 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/sagikazarmark/locafero v0.11.0 // indirect - github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect - github.com/spf13/cast v1.10.0 // indirect - github.com/spf13/pflag v1.0.10 // indirect - github.com/stretchr/objx v0.5.3 // indirect - github.com/subosito/gotenv v1.6.0 // indirect - go.yaml.in/yaml/v3 v3.0.5 // indirect - golang.org/x/crypto v0.55.0 // indirect - golang.org/x/sys v0.47.0 // indirect - golang.org/x/text v0.41.0 // indirect -) diff --git a/apps/cli-go/pkg/go.sum b/apps/cli-go/pkg/go.sum deleted file mode 100644 index b5fa53c40b..0000000000 --- a/apps/cli-go/pkg/go.sum +++ /dev/null @@ -1,298 +0,0 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= -github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= -github.com/andybalholm/brotli v1.2.3 h1:8H1qwOkl2LPfjf3YezB90JnCliZb6SInJ/OJkEbA5NQ= -github.com/andybalholm/brotli v1.2.3/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= -github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= -github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= -github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/ecies/go/v2 v2.0.11 h1:xYhtMdLiqNi02oLirFmLyNbVXw6250h3WM6zJryQdiM= -github.com/ecies/go/v2 v2.0.11/go.mod h1:LPRzoefP0Tam+1uesQOq3Gtb6M2OwlFUnXBTtBAKfDQ= -github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= -github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= -github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= -github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= -github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= -github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= -github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= -github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= -github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= -github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= -github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0= -github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= -github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= -github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8= -github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= -github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= -github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= -github.com/oapi-codegen/nullable v1.2.0 h1:VflFkDW980KhBPiFF7nWSyjg+r4Obqj8lXipV0UkP5w= -github.com/oapi-codegen/nullable v1.2.0/go.mod h1:KUZ3vUzkmEKY90ksAmit2+5juDIhIZhfDl+0PwOQlFY= -github.com/oapi-codegen/runtime v1.7.0 h1:t7358VYPvNbWJ9gdAkIK/smVeHpBf6yp8VTsaZsb/7k= -github.com/oapi-codegen/runtime v1.7.0/go.mod h1:GwV7hC2hviaMzj+ITfHVRESK5J2W/GefVwIND/bMGvU= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= -github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= -github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= -github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= -github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= -github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= -github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= -github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= -github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= -github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= -github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/tidwall/jsonc v0.3.3 h1:RVQqL3xFfDkKKXIDsrBiVQiEpBtxoKbmMXONb2H/y2w= -github.com/tidwall/jsonc v0.3.3/go.mod h1:dw+3CIxqHi+t8eFSpzzMlcVYxKp08UP5CD8/uSFCyJE= -github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= -github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= -go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= -golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M= -golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c= -golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= -golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= -google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/apps/cli-go/pkg/migration/apply.go b/apps/cli-go/pkg/migration/apply.go deleted file mode 100644 index e40b58be2e..0000000000 --- a/apps/cli-go/pkg/migration/apply.go +++ /dev/null @@ -1,77 +0,0 @@ -package migration - -import ( - "context" - "fmt" - "io/fs" - "os" - "path/filepath" - - "github.com/go-errors/errors" - "github.com/jackc/pgx/v4" -) - -var ( - ErrMissingRemote = errors.New("Found local migration files to be inserted before the last migration on remote database.") - ErrMissingLocal = errors.New("Remote migration versions not found in local migrations directory.") -) - -// Find unapplied local migrations older than the latest migration on -// remote, and remote migrations that are missing from local. -func FindPendingMigrations(localMigrations, remoteMigrations []string) ([]string, error) { - var unapplied, missing []string - i, j := 0, 0 - for i < len(remoteMigrations) && j < len(localMigrations) { - remote := remoteMigrations[i] - filename := filepath.Base(localMigrations[j]) - // Check if migration has been applied before, LoadLocalMigrations guarantees a match - local := migrateFilePattern.FindStringSubmatch(filename)[1] - if remote == local { - j++ - i++ - } else if remote < local { - missing = append(missing, remote) - i++ - } else { - // Include out-of-order local migrations - unapplied = append(unapplied, localMigrations[j]) - j++ - } - } - // Ensure all remote versions exist on local - if j == len(localMigrations) { - missing = append(missing, remoteMigrations[i:]...) - } - if len(missing) > 0 { - return missing, errors.New(ErrMissingLocal) - } - // Enforce migrations are applied in chronological order by default - if len(unapplied) > 0 { - return unapplied, errors.New(ErrMissingRemote) - } - pending := localMigrations[len(remoteMigrations):] - return pending, nil -} - -func ApplyMigrations(ctx context.Context, pending []string, conn *pgx.Conn, fsys fs.FS) error { - if len(pending) > 0 { - if err := CreateMigrationTable(ctx, conn); err != nil { - return err - } - } - for _, path := range pending { - filename := filepath.Base(path) - fmt.Fprintf(os.Stderr, "Applying migration %s...\n", filename) - // Reset all connection settings that might have been modified by another statement on the same connection - // eg: `SELECT pg_catalog.set_config('search_path', '', false);` - if _, err := conn.Exec(ctx, "RESET ALL"); err != nil { - return errors.Errorf("failed to reset connection state: %v", err) - } - if migration, err := NewMigrationFromFile(path, fsys); err != nil { - return err - } else if err := migration.ExecBatch(ctx, conn); err != nil { - return err - } - } - return nil -} diff --git a/apps/cli-go/pkg/migration/apply_test.go b/apps/cli-go/pkg/migration/apply_test.go deleted file mode 100644 index e6df97721f..0000000000 --- a/apps/cli-go/pkg/migration/apply_test.go +++ /dev/null @@ -1,200 +0,0 @@ -package migration - -import ( - "context" - "embed" - "os" - "testing" - fs "testing/fstest" - - "github.com/jackc/pgerrcode" - "github.com/stretchr/testify/assert" - "github.com/supabase/cli/pkg/pgtest" -) - -func TestPendingMigrations(t *testing.T) { - t.Run("finds pending migrations", func(t *testing.T) { - local := []string{ - "20221201000000_test.sql", - "20221201000001_test.sql", - "20221201000002_test.sql", - "20221201000003_test.sql", - } - remote := []string{ - "20221201000000", - "20221201000001", - } - // Run test - pending, err := FindPendingMigrations(local, remote) - // Check error - assert.NoError(t, err) - assert.ElementsMatch(t, local[2:], pending) - }) - - t.Run("throws error on missing local migration", func(t *testing.T) { - local := []string{} - remote := []string{"0"} - // Run test - pending, err := FindPendingMigrations(local, remote) - // Check error - assert.ErrorIs(t, err, ErrMissingLocal) - assert.ElementsMatch(t, remote, pending) - }) - - t.Run("throws error on missing remote version", func(t *testing.T) { - local := []string{ - "0_test.sql", - "1_test.sql", - } - remote := []string{"1"} - // Run test - pending, err := FindPendingMigrations(local, remote) - // Check error - assert.ErrorIs(t, err, ErrMissingRemote) - assert.ElementsMatch(t, local[:1], pending) - }) - - t.Run("throws error on out-of-order remote migrations", func(t *testing.T) { - local := []string{ - "20221201000000_test.sql", - "20221201000001_test.sql", - "20221201000002_test.sql", - "20221201000003_test.sql", - "20221201000004_test.sql", - } - remote := []string{ - "20221201000002", - "20221201000004", - } - // Run test - missing, err := FindPendingMigrations(local, remote) - // Check error - assert.ErrorIs(t, err, ErrMissingRemote) - assert.ElementsMatch(t, []string{local[0], local[1], local[3]}, missing) - }) - - t.Run("throws error on out-of-order local migrations", func(t *testing.T) { - local := []string{ - "20221201000000_test.sql", - "20221201000002_test.sql", - } - remote := []string{ - "20221201000000", - "20221201000001", - "20221201000002", - "20221201000003", - "20221201000004", - } - // Run test - missing, err := FindPendingMigrations(local, remote) - // Check error - assert.ErrorIs(t, err, ErrMissingLocal) - assert.ElementsMatch(t, []string{remote[1], remote[3], remote[4]}, missing) - }) -} - -var ( - //go:embed testdata - testMigrations embed.FS - //go:embed testdata/0_schema.sql - testSchema string -) - -func TestApplyMigrations(t *testing.T) { - pending := []string{"testdata/0_schema.sql"} - - t.Run("applies migrations and appends history", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - mockMigrationHistory(conn). - Query("RESET ALL"). - Reply("RESET"). - Query(testSchema). - Reply("CREATE SCHEMA"). - Query(INSERT_MIGRATION_VERSION, "0", "schema", []string{testSchema}). - Reply("INSERT 0 1") - // Run test - err := ApplyMigrations(context.Background(), pending, conn.MockClient(t), testMigrations) - // Check error - assert.NoError(t, err) - }) - - t.Run("throws error on history failure", func(t *testing.T) { - // Setup in-memory fs - fsys := fs.MapFS{} - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(SET_LOCK_TIMEOUT). - Query(CREATE_VERSION_SCHEMA). - Reply("CREATE SCHEMA"). - Query(CREATE_VERSION_TABLE). - ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for relation supabase_migrations"). - Query(ADD_STATEMENTS_COLUMN). - Query(ADD_NAME_COLUMN) - // Run test - err := ApplyMigrations(context.Background(), pending, conn.MockClient(t), fsys) - // Check error - assert.ErrorContains(t, err, "ERROR: permission denied for relation supabase_migrations (SQLSTATE 42501)") - }) - - t.Run("throws error on missing file", func(t *testing.T) { - // Setup in-memory fs - fsys := fs.MapFS{} - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - mockMigrationHistory(conn). - Query("RESET ALL"). - Reply("RESET") - // Run test - err := ApplyMigrations(context.Background(), pending, conn.MockClient(t), fsys) - // Check error - assert.ErrorIs(t, err, os.ErrNotExist) - }) - - t.Run("throws error on missing table", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - mockMigrationHistory(conn). - Query("RESET ALL"). - Reply("RESET"). - Query(testSchema). - ReplyError(pgerrcode.UndefinedTable, `relation "supabase_migrations.schema_migrations" does not exist`). - Query(INSERT_MIGRATION_VERSION, "0", "schema", []string{testSchema}). - Reply("INSERT 0 1") - // Run test - err := ApplyMigrations(context.Background(), pending, conn.MockClient(t), testMigrations) - // Check error - assert.ErrorContains(t, err, `ERROR: relation "supabase_migrations.schema_migrations" does not exist (SQLSTATE 42P01)`) - }) - - t.Run("throws error when RESET ALL fails", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - mockMigrationHistory(conn). - Query("RESET ALL"). - ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for RESET ALL") - // Run test - err := ApplyMigrations(context.Background(), pending, conn.MockClient(t), testMigrations) - // Check error - assert.ErrorContains(t, err, "failed to reset connection state") - assert.ErrorContains(t, err, "ERROR: permission denied for RESET ALL (SQLSTATE 42501)") - }) -} - -func mockMigrationHistory(conn *pgtest.MockConn) *pgtest.MockConn { - conn.Query(SET_LOCK_TIMEOUT). - Query(CREATE_VERSION_SCHEMA). - Reply("CREATE SCHEMA"). - Query(CREATE_VERSION_TABLE). - Reply("CREATE TABLE"). - Query(ADD_STATEMENTS_COLUMN). - Reply("ALTER TABLE"). - Query(ADD_NAME_COLUMN). - Reply("ALTER TABLE") - return conn -} diff --git a/apps/cli-go/pkg/migration/drop.go b/apps/cli-go/pkg/migration/drop.go deleted file mode 100644 index aa44407ca1..0000000000 --- a/apps/cli-go/pkg/migration/drop.go +++ /dev/null @@ -1,50 +0,0 @@ -package migration - -import ( - "context" - _ "embed" - - "github.com/go-errors/errors" - "github.com/jackc/pgx/v4" - "github.com/supabase/cli/pkg/pgxv5" -) - -var ( - //go:embed queries/drop.sql - DropObjects string - //go:embed queries/list.sql - ListSchemas string - - // Initialised by postgres image and owned by postgres role - ManagedSchemas = []string{ - `information\_schema`, - `pg\_%`, - `\_analytics`, - `\_realtime`, - `\_supavisor`, - "pgbouncer", - "pgmq", - "pgsodium", - "pgtle", - `supabase\_migrations`, - "vault", - } -) - -func DropUserSchemas(ctx context.Context, conn *pgx.Conn) error { - migration := MigrationFile{} - migration.Statements = append(migration.Statements, DropObjects) - return migration.ExecBatch(ctx, conn) -} - -func ListUserSchemas(ctx context.Context, conn *pgx.Conn, exclude ...string) ([]string, error) { - if len(exclude) == 0 { - exclude = ManagedSchemas - } - rows, err := conn.Query(ctx, ListSchemas, exclude) - if err != nil { - return nil, errors.Errorf("failed to list schemas: %w", err) - } - // TODO: show detail and hint from pgconn.PgError - return pgxv5.CollectStrings(rows) -} diff --git a/apps/cli-go/pkg/migration/drop_test.go b/apps/cli-go/pkg/migration/drop_test.go deleted file mode 100644 index 644fb69be0..0000000000 --- a/apps/cli-go/pkg/migration/drop_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package migration - -import ( - "context" - "testing" - - "github.com/jackc/pgerrcode" - "github.com/stretchr/testify/assert" - "github.com/supabase/cli/pkg/pgtest" -) - -func TestDropSchemas(t *testing.T) { - t.Run("resets remote database", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(DropObjects). - Reply("INSERT 0") - // Run test - err := DropUserSchemas(context.Background(), conn.MockClient(t)) - // Check error - assert.NoError(t, err) - }) - - t.Run("throws error on drop schema failure", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(DropObjects). - ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for relation supabase_migrations") - // Run test - err := DropUserSchemas(context.Background(), conn.MockClient(t)) - // Check error - assert.ErrorContains(t, err, "ERROR: permission denied for relation supabase_migrations (SQLSTATE 42501)") - }) -} diff --git a/apps/cli-go/pkg/migration/dump.go b/apps/cli-go/pkg/migration/dump.go deleted file mode 100644 index 0a1d6cdb3d..0000000000 --- a/apps/cli-go/pkg/migration/dump.go +++ /dev/null @@ -1,221 +0,0 @@ -package migration - -import ( - "context" - _ "embed" - "fmt" - "io" - "strings" - - "github.com/jackc/pgconn" -) - -var ( - //go:embed scripts/dump_schema.sh - dumpSchemaScript string - //go:embed scripts/dump_data.sh - dumpDataScript string - //go:embed scripts/dump_role.sh - dumpRoleScript string - - InternalSchemas = []string{ - "information_schema", - "pg_*", // Wildcard pattern follows pg_dump - // Initialised by supabase/postgres image and owned by postgres role - "_analytics", - "_realtime", - "_supavisor", - "auth", - "etl", - "extensions", - "pgbouncer", - "realtime", - "storage", - "supabase_functions", - "supabase_migrations", - // Owned by extensions - "cron", - "dbdev", - "graphql", - "graphql_public", - "net", - "pgmq", - "pgsodium", - "pgsodium_masks", - "pgtle", - "repack", - "tiger", - "tiger_data", - "timescaledb_*", - "_timescaledb_*", - "topology", - "vault", - } - // Data dump includes auth, storage, etc. for migrating to new project - excludedSchemas = []string{ - "information_schema", - "pg_*", // Wildcard pattern follows pg_dump - // Owned by extensions - // "cron", - "graphql", - "graphql_public", - // "net", - // "pgmq", - "pgsodium", - "pgsodium_masks", - "pgtle", - "repack", - "tiger", - "tiger_data", - "timescaledb_*", - "_timescaledb_*", - "topology", - "vault", - // Managed by Supabase - // "auth", - "etl", - "extensions", - "pgbouncer", - "realtime", - // "storage", - // "supabase_functions", - "supabase_migrations", - // TODO: Remove in a few version in favor of _supabase internal db - "_analytics", - "_realtime", - "_supavisor", - } - reservedRoles = []string{ - "anon", - "authenticated", - "authenticator", - "cli_login_.*", - "dashboard_user", - "pgbouncer", - "postgres", - "service_role", - "supabase_.*", - // Managed by extensions - "pgsodium_keyholder", - "pgsodium_keyiduser", - "pgsodium_keymaker", - "pgtle_admin", - } - allowedConfigs = []string{ - // Ref: https://github.com/supabase/postgres/blob/develop/ansible/files/postgresql_config/supautils.conf.j2#L10 - "pgaudit.*", - "pgrst.*", - "session_replication_role", - "statement_timeout", - "track_io_timing", - } -) - -type pgDumpOption struct { - schema []string - keepComments bool - excludeTable []string - columnInsert bool -} - -type DumpOptionFunc func(*pgDumpOption) - -func WithSchema(schema ...string) DumpOptionFunc { - return func(pdo *pgDumpOption) { - pdo.schema = schema - } -} - -func WithComments(keep bool) DumpOptionFunc { - return func(pdo *pgDumpOption) { - pdo.keepComments = keep - } -} - -func WithColumnInsert(use bool) DumpOptionFunc { - return func(pdo *pgDumpOption) { - pdo.columnInsert = use - } -} - -func WithoutTable(table ...string) DumpOptionFunc { - return func(pdo *pgDumpOption) { - pdo.excludeTable = table - } -} - -func toEnv(config pgconn.Config) []string { - return []string{ - "PGHOST=" + config.Host, - fmt.Sprintf("PGPORT=%d", config.Port), - "PGUSER=" + config.User, - "PGPASSWORD=" + config.Password, - "PGDATABASE=" + config.Database, - } -} - -type ExecFunc func(context.Context, string, []string, io.Writer) error - -func DumpSchema(ctx context.Context, config pgconn.Config, w io.Writer, exec ExecFunc, opts ...DumpOptionFunc) error { - var opt pgDumpOption - for _, apply := range opts { - apply(&opt) - } - env := toEnv(config) - if len(opt.schema) > 0 { - // Must append flag because empty string results in error - env = append(env, "EXTRA_FLAGS=--schema="+strings.Join(opt.schema, "|")) - } else { - env = append(env, "EXCLUDED_SCHEMAS="+strings.Join(InternalSchemas, "|")) - } - if !opt.keepComments { - env = append(env, "EXTRA_SED=/^--/d") - } - return exec(ctx, dumpSchemaScript, env, w) -} - -func DumpData(ctx context.Context, config pgconn.Config, w io.Writer, exec ExecFunc, opts ...DumpOptionFunc) error { - var opt pgDumpOption - for _, apply := range opts { - apply(&opt) - } - env := toEnv(config) - if len(opt.schema) > 0 { - env = append(env, "INCLUDED_SCHEMAS="+strings.Join(opt.schema, "|")) - } else { - env = append(env, "INCLUDED_SCHEMAS=*", "EXCLUDED_SCHEMAS="+strings.Join(excludedSchemas, "|")) - } - var extraFlags []string - if opt.columnInsert { - extraFlags = append(extraFlags, "--column-inserts", "--rows-per-insert 100000") - } - for _, table := range opt.excludeTable { - escaped := quoteUpperCase(table) - // Use separate flags to avoid error: too many dotted names - extraFlags = append(extraFlags, "--exclude-table "+escaped) - } - if len(extraFlags) > 0 { - env = append(env, "EXTRA_FLAGS="+strings.Join(extraFlags, " ")) - } - return exec(ctx, dumpDataScript, env, w) -} - -func quoteUpperCase(table string) string { - escaped := strings.ReplaceAll(table, ".", `"."`) - return fmt.Sprintf(`"%s"`, escaped) -} - -func DumpRole(ctx context.Context, config pgconn.Config, w io.Writer, exec ExecFunc, opts ...DumpOptionFunc) error { - var opt pgDumpOption - for _, apply := range opts { - apply(&opt) - } - env := append(toEnv(config), - "RESERVED_ROLES="+strings.Join(reservedRoles, "|"), - "ALLOWED_CONFIGS="+strings.Join(allowedConfigs, "|"), - ) - if !opt.keepComments { - env = append(env, "EXTRA_SED=/^--/d") - } - return exec(ctx, dumpRoleScript, env, w) -} diff --git a/apps/cli-go/pkg/migration/file.go b/apps/cli-go/pkg/migration/file.go deleted file mode 100644 index e74d322c78..0000000000 --- a/apps/cli-go/pkg/migration/file.go +++ /dev/null @@ -1,289 +0,0 @@ -package migration - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/hex" - "fmt" - "io" - "io/fs" - "path/filepath" - "regexp" - "strings" - - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" - "github.com/spf13/viper" - "github.com/supabase/cli/pkg/parser" -) - -type MigrationFile struct { - Version string - Name string - Statements []string -} - -var ( - migrateFilePattern = regexp.MustCompile(`^([0-9]+)_(.*)\.sql$`) - typeNamePattern = regexp.MustCompile(`type "([^"]+)" does not exist`) - createIndexPattern = regexp.MustCompile(`^CREATE\s+(UNIQUE\s+)?INDEX\s+CONCURRENTLY(\s|\z)`) - dropIndexPattern = regexp.MustCompile(`^DROP\s+INDEX\s+CONCURRENTLY(\s|\z)`) - reindexPattern = regexp.MustCompile(`^REINDEX(\s|\().*\sCONCURRENTLY(\s|\z)`) - vacuumPattern = regexp.MustCompile(`^VACUUM(\s|\(|\z)`) - alterSystemPattern = regexp.MustCompile(`^ALTER\s+SYSTEM(\s|\z)`) - clusterPattern = regexp.MustCompile(`^CLUSTER(\s|\z)`) -) - -func NewMigrationFromFile(path string, fsys fs.FS) (*MigrationFile, error) { - lines, err := parseFile(path, fsys) - if err != nil { - return nil, err - } - file := MigrationFile{Statements: lines} - // Parse version from file name - filename := filepath.Base(path) - matches := migrateFilePattern.FindStringSubmatch(filename) - if len(matches) > 2 { - file.Version = matches[1] - file.Name = matches[2] - } - return &file, nil -} - -func parseFile(path string, fsys fs.FS) ([]string, error) { - sql, err := fsys.Open(path) - if err != nil { - return nil, errors.Errorf("failed to open migration file: %w", err) - } - defer sql.Close() - // Unless explicitly specified, Use file length as max buffer size - if !viper.IsSet("SCANNER_BUFFER_SIZE") { - if fi, err := sql.Stat(); err == nil { - if size := int(fi.Size()); size > parser.MaxScannerCapacity { - parser.MaxScannerCapacity = size - } - } - } - return parser.SplitAndTrim(sql) -} - -func NewMigrationFromReader(sql io.Reader) (*MigrationFile, error) { - lines, err := parser.SplitAndTrim(sql) - if err != nil { - return nil, err - } - return &MigrationFile{Statements: lines}, nil -} - -func isPipelineIncompatible(sql string) bool { - upper := strings.ToUpper(trimLeadingSQLComments(sql)) - return createIndexPattern.MatchString(upper) || - dropIndexPattern.MatchString(upper) || - reindexPattern.MatchString(upper) || - vacuumPattern.MatchString(upper) || - alterSystemPattern.MatchString(upper) || - clusterPattern.MatchString(upper) -} - -func trimLeadingSQLComments(sql string) string { - trimmed := strings.TrimLeftFunc(sql, func(r rune) bool { - return r == '\ufeff' || r == ' ' || r == '\t' || r == '\n' || r == '\r' - }) - for { - switch { - case strings.HasPrefix(trimmed, "--"): - if idx := strings.IndexByte(trimmed, '\n'); idx >= 0 { - trimmed = strings.TrimLeft(trimmed[idx+1:], " \t\n\r") - continue - } - return "" - case strings.HasPrefix(trimmed, "/*"): - if idx := strings.Index(trimmed, "*/"); idx >= 0 { - trimmed = strings.TrimLeft(trimmed[idx+2:], " \t\n\r") - continue - } - return trimmed - default: - return strings.TrimSpace(trimmed) - } - } -} - -// ExecBatch is also reached from the shipped supabase-go sidecar via the -// remaining Go-delegated paths (e.g. remote `db push`/`db reset`'s -// apply.MigrateAndSeed) — local `db start`/`db reset` no longer delegate here -// at all: CLI-1954/CLI-1955 removed the hidden `db __db-bootstrap` seam -// (apps/cli-go/cmd/db.go) this comment used to describe, in favor of a fully -// native TypeScript container-bootstrap port. -func (m *MigrationFile) ExecBatch(ctx context.Context, conn *pgx.Conn) error { - batch := &pgconn.Batch{} - batchSize := 0 - executed := 0 - - formatError := func(err error, i int) error { - stat := INSERT_MIGRATION_VERSION - if i < len(m.Statements) { - stat = m.Statements[i] - } - var msg []string - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) { - stat = markError(stat, int(pgErr.Position)) - if len(pgErr.Detail) > 0 { - msg = append(msg, pgErr.Detail) - } - if typeName := extractTypeName(pgErr.Message); len(typeName) > 0 && pgErr.Code == "42704" && !IsSchemaQualified(typeName) { - msg = append(msg, "") - msg = append(msg, "Hint: This type may be defined in a schema that's not in your search_path.") - msg = append(msg, " Use schema-qualified type references to avoid this error:") - msg = append(msg, fmt.Sprintf(" CREATE TABLE example (col extensions.%s);", typeName)) - msg = append(msg, " Learn more: supabase migration new --help") - } - } - msg = append(msg, fmt.Sprintf("At statement: %d", i), stat) - return errors.Errorf("%w\n%s", err, strings.Join(msg, "\n")) - } - - flushBatch := func() error { - if batchSize == 0 { - return nil - } - if result, err := conn.PgConn().ExecBatch(ctx, batch).ReadAll(); err != nil { - return formatError(err, executed+len(result)) - } - executed += batchSize - batch = &pgconn.Batch{} - batchSize = 0 - return nil - } - - for _, line := range m.Statements { - if isPipelineIncompatible(line) { - if err := flushBatch(); err != nil { - return err - } - if _, err := conn.PgConn().Exec(ctx, line).ReadAll(); err != nil { - return formatError(err, executed) - } - executed++ - } else { - batch.ExecParams(line, nil, nil, nil, nil) - batchSize++ - } - } - - // Insert into migration history - if len(m.Version) > 0 { - if err := m.insertVersionSQL(conn, batch); err != nil { - return err - } - batchSize++ - } - - return flushBatch() -} - -func markError(stat string, pos int) string { - lines := strings.Split(stat, "\n") - for j, r := range lines { - if c := len(r); pos > c { - pos -= c + 1 - continue - } - // Show a caret below the error position - if pos > 0 { - caret := append(bytes.Repeat([]byte{' '}, pos-1), '^') - lines = append(lines[:j+1], string(caret)) - } - break - } - return strings.Join(lines, "\n") -} - -// extractTypeName extracts the type name from PostgreSQL error messages like: -// 'type "ltree" does not exist' -> "ltree" -func extractTypeName(errMsg string) string { - matches := typeNamePattern.FindStringSubmatch(errMsg) - if len(matches) > 1 { - return matches[1] - } - return "" -} - -// IsSchemaQualified checks if a type name already contains a schema qualifier (e.g., "extensions.ltree") -func IsSchemaQualified(typeName string) bool { - return strings.Contains(typeName, ".") -} - -func (m *MigrationFile) insertVersionSQL(conn *pgx.Conn, batch *pgconn.Batch) error { - value := pgtype.TextArray{} - if err := value.Set(m.Statements); err != nil { - return errors.Errorf("failed to set text array: %w", err) - } - ci := conn.ConnInfo() - var err error - var encoded []byte - var valueFormat int16 - if conn.Config().PreferSimpleProtocol { - encoded, err = value.EncodeText(ci, encoded) - valueFormat = pgtype.TextFormatCode - } else { - encoded, err = value.EncodeBinary(ci, encoded) - valueFormat = pgtype.BinaryFormatCode - } - if err != nil { - return errors.Errorf("failed to encode binary: %w", err) - } - batch.ExecParams( - INSERT_MIGRATION_VERSION, - [][]byte{[]byte(m.Version), []byte(m.Name), encoded}, - []uint32{pgtype.TextOID, pgtype.TextOID, pgtype.TextArrayOID}, - []int16{pgtype.TextFormatCode, pgtype.TextFormatCode, valueFormat}, - nil, - ) - return nil -} - -type SeedFile struct { - Path string - Hash string - Dirty bool `db:"-"` -} - -func NewSeedFile(path string, fsys fs.FS) (*SeedFile, error) { - sql, err := fsys.Open(path) - if err != nil { - return nil, errors.Errorf("failed to open seed file: %w", err) - } - defer sql.Close() - hash := sha256.New() - if _, err := io.Copy(hash, sql); err != nil { - return nil, errors.Errorf("failed to hash file: %w", err) - } - digest := hex.EncodeToString(hash.Sum(nil)) - return &SeedFile{Path: path, Hash: digest}, nil -} - -func (m *SeedFile) ExecBatchWithCache(ctx context.Context, conn *pgx.Conn, fsys fs.FS) error { - // Parse each file individually to reduce memory usage - lines, err := parseFile(m.Path, fsys) - if err != nil { - return err - } - // Data statements don't mutate schemas, safe to use statement cache - batch := pgx.Batch{} - if !m.Dirty { - for _, line := range lines { - batch.Queue(line) - } - } - batch.Queue(UPSERT_SEED_FILE, m.Path, m.Hash) - // No need to track version here because there are no schema changes - if err := conn.SendBatch(ctx, &batch).Close(); err != nil { - return errors.Errorf("failed to send batch: %w", err) - } - return nil -} diff --git a/apps/cli-go/pkg/migration/file_test.go b/apps/cli-go/pkg/migration/file_test.go deleted file mode 100644 index 83087ba0e6..0000000000 --- a/apps/cli-go/pkg/migration/file_test.go +++ /dev/null @@ -1,322 +0,0 @@ -package migration - -import ( - "bufio" - "context" - "strings" - "testing" - fs "testing/fstest" - - "github.com/jackc/pgerrcode" - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" - "github.com/supabase/cli/pkg/parser" - "github.com/supabase/cli/pkg/pgtest" -) - -func TestMigrationFile(t *testing.T) { - t.Run("new from file sets max token", func(t *testing.T) { - // Setup in-memory fs - path := "20220727064247_create_table.sql" - query := "BEGIN; " + strings.Repeat("a", parser.MaxScannerCapacity) - fsys := fs.MapFS{ - path: &fs.MapFile{Data: []byte(query)}, - } - // Run test - migration, err := NewMigrationFromFile(path, fsys) - // Check error - assert.NoError(t, err) - assert.Len(t, migration.Statements, 2) - assert.Equal(t, "20220727064247", migration.Version) - }) - - t.Run("new from reader errors on max token", func(t *testing.T) { - viper.Reset() - sql := "\tBEGIN; " + strings.Repeat("a", parser.MaxScannerCapacity) - // Run test - migration, err := NewMigrationFromReader(strings.NewReader(sql)) - // Check error - assert.ErrorIs(t, err, bufio.ErrTooLong) - assert.ErrorContains(t, err, "After statement 1: \tBEGIN;") - assert.Nil(t, migration) - }) - - t.Run("encodes statements in binary format", func(t *testing.T) { - migration := MigrationFile{ - Statements: []string{"create schema public"}, - Version: "0", - } - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.Statements[0]). - Reply("CREATE SCHEMA"). - Query(INSERT_MIGRATION_VERSION, "0", "", migration.Statements). - Reply("INSERT 0 1") - // Run test - err := migration.ExecBatch(context.Background(), conn.MockClient(t)) - // Check error - assert.NoError(t, err) - }) - - t.Run("executes pipeline incompatible statements outside batch", func(t *testing.T) { - migration := MigrationFile{ - Statements: []string{ - "create table public.widgets(id bigint primary key)", - "CREATE UNIQUE INDEX CONCURRENTLY widgets_id_idx ON public.widgets(id)", - "alter table public.widgets enable row level security", - }, - Version: "20260101000000", - Name: "create_widgets", - } - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.Statements[0]). - Reply("CREATE TABLE"). - SimpleQuery(migration.Statements[1]). - Reply("CREATE INDEX"). - Query(migration.Statements[2]). - Reply("ALTER TABLE"). - Query(INSERT_MIGRATION_VERSION, migration.Version, migration.Name, migration.Statements). - Reply("INSERT 0 1") - // Run test - err := migration.ExecBatch(context.Background(), conn.MockClient(t)) - // Check error - assert.NoError(t, err) - }) - - t.Run("records migration version when file has no statements", func(t *testing.T) { - migration := MigrationFile{ - Version: "20260101000000", - Name: "empty_migration", - } - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(INSERT_MIGRATION_VERSION, migration.Version, migration.Name, migration.Statements). - Reply("INSERT 0 1") - // Run test - err := migration.ExecBatch(context.Background(), conn.MockClient(t)) - // Check error - assert.NoError(t, err) - }) - - t.Run("reports pipeline incompatible statement errors with statement index", func(t *testing.T) { - migration := MigrationFile{ - Statements: []string{ - "create table public.widgets(id bigint primary key)", - "CREATE INDEX CONCURRENTLY widgets_id_idx ON public.widgets(id)", - "alter table public.widgets enable row level security", - }, - Version: "20260101000000", - Name: "create_widgets", - } - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.Statements[0]). - Reply("CREATE TABLE"). - SimpleQuery(migration.Statements[1]). - ReplyError("25001", "CREATE INDEX CONCURRENTLY cannot be executed within a pipeline") - // Run test - err := migration.ExecBatch(context.Background(), conn.MockClient(t)) - // Check error - assert.ErrorContains(t, err, "ERROR: CREATE INDEX CONCURRENTLY cannot be executed within a pipeline (SQLSTATE 25001)") - assert.ErrorContains(t, err, "At statement: 1\nCREATE INDEX CONCURRENTLY widgets_id_idx ON public.widgets(id)") - }) - - t.Run("throws error on insert failure", func(t *testing.T) { - migration := MigrationFile{ - Statements: []string{"create schema public"}, - Version: "0", - } - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.Statements[0]). - ReplyError(pgerrcode.DuplicateSchema, `schema "public" already exists`). - Query(INSERT_MIGRATION_VERSION, "0", "", migration.Statements). - Reply("INSERT 0 1") - // Run test - err := migration.ExecBatch(context.Background(), conn.MockClient(t)) - // Check error - assert.ErrorContains(t, err, "ERROR: schema \"public\" already exists (SQLSTATE 42P06)") - assert.ErrorContains(t, err, "At statement: 0\ncreate schema public") - }) - - t.Run("provides helpful hint for extension type errors", func(t *testing.T) { - migration := MigrationFile{ - Statements: []string{"CREATE TABLE test (path ltree NOT NULL)"}, - Version: "0", - } - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.Statements[0]). - ReplyError("42704", `type "ltree" does not exist`). - Query(INSERT_MIGRATION_VERSION, "0", "", migration.Statements). - Reply("INSERT 0 1") - // Run test - err := migration.ExecBatch(context.Background(), conn.MockClient(t)) - // Check error - assert.ErrorContains(t, err, `type "ltree" does not exist`) - assert.ErrorContains(t, err, "Hint: This type may be defined in a schema") - assert.ErrorContains(t, err, "extensions.ltree") - assert.ErrorContains(t, err, "supabase migration new --help") - assert.ErrorContains(t, err, "At statement: 0") - }) - - t.Run("skips hint for schema-qualified type errors", func(t *testing.T) { - migration := MigrationFile{ - Statements: []string{"CREATE TABLE test (path extensions.ltree NOT NULL)"}, - Version: "0", - } - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(migration.Statements[0]). - ReplyError("42704", `type "extensions.ltree" does not exist`). - Query(INSERT_MIGRATION_VERSION, "0", "", migration.Statements). - Reply("INSERT 0 1") - // Run test - err := migration.ExecBatch(context.Background(), conn.MockClient(t)) - // Check error - should NOT contain hint since type is already schema-qualified - assert.ErrorContains(t, err, `type "extensions.ltree" does not exist`) - assert.NotContains(t, err.Error(), "Hint: This type may be defined in a schema") - }) -} - -func TestExtractTypeName(t *testing.T) { - t.Run("extracts type name from standard error message", func(t *testing.T) { - result := extractTypeName(`type "ltree" does not exist`) - assert.Equal(t, "ltree", result) - }) - - t.Run("extracts schema-qualified type name", func(t *testing.T) { - result := extractTypeName(`type "extensions.ltree" does not exist`) - assert.Equal(t, "extensions.ltree", result) - }) - - t.Run("extracts type with underscores", func(t *testing.T) { - result := extractTypeName(`type "my_custom_type" does not exist`) - assert.Equal(t, "my_custom_type", result) - }) - - t.Run("returns empty string for non-matching message", func(t *testing.T) { - result := extractTypeName(`column "name" does not exist`) - assert.Equal(t, "", result) - }) - - t.Run("returns empty string for empty message", func(t *testing.T) { - result := extractTypeName("") - assert.Equal(t, "", result) - }) - - t.Run("handles type names with numbers", func(t *testing.T) { - result := extractTypeName(`type "type123" does not exist`) - assert.Equal(t, "type123", result) - }) -} - -func TestIsPipelineIncompatible(t *testing.T) { - cases := []struct { - name string - sql string - want bool - }{ - { - name: "create index concurrently", - sql: "CREATE INDEX CONCURRENTLY widgets_id_idx ON public.widgets(id)", - want: true, - }, - { - name: "create unique index concurrently", - sql: "CREATE UNIQUE INDEX CONCURRENTLY widgets_id_idx ON public.widgets(id)", - want: true, - }, - { - name: "create index concurrently after comments", - sql: "-- cannot run in a transaction\n/* generated */\nCREATE INDEX CONCURRENTLY widgets_id_idx ON public.widgets(id)", - want: true, - }, - { - name: "drop index concurrently", - sql: "DROP INDEX CONCURRENTLY public.widgets_id_idx", - want: true, - }, - { - name: "drop index concurrently if exists", - sql: "drop index concurrently if exists api.idx_rx_orders_clinic_id", - want: true, - }, - { - name: "reindex table concurrently", - sql: "REINDEX TABLE CONCURRENTLY public.widgets", - want: true, - }, - { - name: "reindex with options concurrently", - sql: "REINDEX (VERBOSE) INDEX CONCURRENTLY widgets_id_idx", - want: true, - }, - { - name: "vacuum bare", - sql: "VACUUM", - want: true, - }, - { - name: "vacuum with options", - sql: "VACUUM (ANALYZE) public.widgets", - want: true, - }, - { - name: "alter system", - sql: "ALTER SYSTEM SET log_statement = 'all'", - want: true, - }, - { - name: "cluster", - sql: "CLUSTER public.widgets USING widgets_id_idx", - want: true, - }, - { - name: "ordinary create index", - sql: "CREATE INDEX widgets_id_idx ON public.widgets(id)", - want: false, - }, - { - name: "ordinary drop index", - sql: "DROP INDEX IF EXISTS public.widgets_id_idx", - want: false, - }, - { - name: "concurrently in string literal", - sql: "SELECT 'CREATE INDEX CONCURRENTLY widgets_id_idx ON public.widgets(id)'", - want: false, - }, - { - name: "concurrently in leading comment only", - sql: "-- CREATE INDEX CONCURRENTLY widgets_id_idx ON public.widgets(id)\nSELECT 1", - want: false, - }, - { - name: "word prefix", - sql: "VACUUMING public.widgets", - want: false, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, isPipelineIncompatible(tt.sql)) - }) - } -} - -func TestIsSchemaQualified(t *testing.T) { - assert.True(t, IsSchemaQualified("extensions.ltree")) - assert.True(t, IsSchemaQualified("public.my_type")) - assert.False(t, IsSchemaQualified("ltree")) - assert.False(t, IsSchemaQualified("")) -} diff --git a/apps/cli-go/pkg/migration/history.go b/apps/cli-go/pkg/migration/history.go deleted file mode 100644 index 9f156faa4a..0000000000 --- a/apps/cli-go/pkg/migration/history.go +++ /dev/null @@ -1,73 +0,0 @@ -package migration - -import ( - "context" - - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" - "github.com/supabase/cli/pkg/pgxv5" -) - -const ( - SET_LOCK_TIMEOUT = "SET lock_timeout = '4s'" - CREATE_VERSION_SCHEMA = "CREATE SCHEMA IF NOT EXISTS supabase_migrations" - CREATE_VERSION_TABLE = "CREATE TABLE IF NOT EXISTS supabase_migrations.schema_migrations (version text NOT NULL PRIMARY KEY)" - ADD_STATEMENTS_COLUMN = "ALTER TABLE supabase_migrations.schema_migrations ADD COLUMN IF NOT EXISTS statements text[]" - ADD_NAME_COLUMN = "ALTER TABLE supabase_migrations.schema_migrations ADD COLUMN IF NOT EXISTS name text" - INSERT_MIGRATION_VERSION = "INSERT INTO supabase_migrations.schema_migrations(version, name, statements) VALUES($1, $2, $3)" - UPSERT_MIGRATION_VERSION = INSERT_MIGRATION_VERSION + " ON CONFLICT (version) DO UPDATE SET name = EXCLUDED.name, statements = EXCLUDED.statements" - DELETE_MIGRATION_VERSION = "DELETE FROM supabase_migrations.schema_migrations WHERE version = ANY($1)" - DELETE_MIGRATION_BEFORE = "DELETE FROM supabase_migrations.schema_migrations WHERE version <= $1" - TRUNCATE_VERSION_TABLE = "TRUNCATE supabase_migrations.schema_migrations" - SELECT_VERSION_TABLE = "SELECT version, coalesce(name, '') as name, statements FROM supabase_migrations.schema_migrations" - LIST_MIGRATION_VERSION = "SELECT version FROM supabase_migrations.schema_migrations ORDER BY version" - CREATE_SEED_TABLE = "CREATE TABLE IF NOT EXISTS supabase_migrations.seed_files (path text NOT NULL PRIMARY KEY, hash text NOT NULL)" - UPSERT_SEED_FILE = "INSERT INTO supabase_migrations.seed_files(path, hash) VALUES($1, $2) ON CONFLICT (path) DO UPDATE SET hash = EXCLUDED.hash" - SELECT_SEED_TABLE = "SELECT path, hash FROM supabase_migrations.seed_files" -) - -// TODO: support overriding `supabase_migrations.schema_migrations` with user defined . -func CreateMigrationTable(ctx context.Context, conn *pgx.Conn) error { - // This must be run without prepared statements because each statement in the batch depends on - // the previous schema change. The lock timeout will be reset when implicit transaction ends. - batch := pgconn.Batch{} - batch.ExecParams(SET_LOCK_TIMEOUT, nil, nil, nil, nil) - batch.ExecParams(CREATE_VERSION_SCHEMA, nil, nil, nil, nil) - batch.ExecParams(CREATE_VERSION_TABLE, nil, nil, nil, nil) - batch.ExecParams(ADD_STATEMENTS_COLUMN, nil, nil, nil, nil) - batch.ExecParams(ADD_NAME_COLUMN, nil, nil, nil, nil) - if _, err := conn.PgConn().ExecBatch(ctx, &batch).ReadAll(); err != nil { - return errors.Errorf("failed to create migration table: %w", err) - } - return nil -} - -func ReadMigrationTable(ctx context.Context, conn *pgx.Conn) ([]MigrationFile, error) { - rows, err := conn.Query(ctx, SELECT_VERSION_TABLE) - if err != nil { - return nil, errors.Errorf("failed to read migration table: %w", err) - } - return pgxv5.CollectRows[MigrationFile](rows) -} - -func CreateSeedTable(ctx context.Context, conn *pgx.Conn) error { - // This must be run without prepared statements because each statement in the batch depends on - // the previous schema change. The lock timeout will be reset when implicit transaction ends. - batch := pgconn.Batch{} - batch.ExecParams(SET_LOCK_TIMEOUT, nil, nil, nil, nil) - batch.ExecParams(CREATE_VERSION_SCHEMA, nil, nil, nil, nil) - batch.ExecParams(CREATE_SEED_TABLE, nil, nil, nil, nil) - if _, err := conn.PgConn().ExecBatch(ctx, &batch).ReadAll(); err != nil { - return errors.Errorf("failed to create seed table: %w", err) - } - return nil -} - -func ReadSeedTable(ctx context.Context, conn *pgx.Conn) ([]SeedFile, error) { - rows, err := conn.Query(ctx, SELECT_SEED_TABLE) - if err != nil { - return nil, errors.Errorf("failed to read seed table: %w", err) - } - return pgxv5.CollectRows[SeedFile](rows) -} diff --git a/apps/cli-go/pkg/migration/list.go b/apps/cli-go/pkg/migration/list.go deleted file mode 100644 index 8972bb2594..0000000000 --- a/apps/cli-go/pkg/migration/list.go +++ /dev/null @@ -1,77 +0,0 @@ -package migration - -import ( - "context" - "fmt" - "io/fs" - "os" - "path/filepath" - "regexp" - "strconv" - - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgerrcode" - "github.com/jackc/pgx/v4" - "github.com/supabase/cli/pkg/pgxv5" -) - -func ListRemoteMigrations(ctx context.Context, conn *pgx.Conn) ([]string, error) { - // We query the version string only for backwards compatibility - rows, _ := conn.Query(ctx, LIST_MIGRATION_VERSION) - versions, err := pgxv5.CollectStrings(rows) - if err != nil { - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) && pgErr.Code == pgerrcode.UndefinedTable { - // If migration history table is undefined, the remote project has no migrations - return nil, nil - } - } - return versions, err -} - -func ListLocalMigrations(migrationsDir string, fsys fs.FS, filter ...func(string) bool) ([]string, error) { - localMigrations, err := fs.ReadDir(fsys, migrationsDir) - if err != nil && !errors.Is(err, os.ErrNotExist) { - return nil, errors.Errorf("failed to read directory: %w", err) - } - var clean []string -OUTER: - for i, migration := range localMigrations { - if migration.IsDir() { - continue - } - filename := migration.Name() - if i == 0 && shouldSkip(filename) { - fmt.Fprintf(os.Stderr, "Skipping migration %s... (replace \"init\" with a different file name to apply this migration)\n", filename) - continue - } - matches := migrateFilePattern.FindStringSubmatch(filename) - if len(matches) == 0 { - fmt.Fprintf(os.Stderr, "Skipping migration %s... (file name must match pattern \"_name.sql\")\n", filename) - continue - } - path := filepath.Join(migrationsDir, filename) - for _, keep := range filter { - if version := matches[1]; !keep(version) { - continue OUTER - } - } - clean = append(clean, path) - } - return clean, nil -} - -var initSchemaPattern = regexp.MustCompile(`([0-9]{14})_init\.sql`) - -func shouldSkip(name string) bool { - // NOTE: To handle backward-compatibility. `_init.sql` as - // the first migration (prev versions of the CLI) is deprecated. - matches := initSchemaPattern.FindStringSubmatch(name) - if len(matches) == 2 { - if timestamp, err := strconv.ParseUint(matches[1], 10, 64); err == nil && timestamp < 20211209000000 { - return true - } - } - return false -} diff --git a/apps/cli-go/pkg/migration/list_test.go b/apps/cli-go/pkg/migration/list_test.go deleted file mode 100644 index 4654fa876a..0000000000 --- a/apps/cli-go/pkg/migration/list_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package migration - -import ( - "context" - "testing" - fs "testing/fstest" - - "github.com/jackc/pgerrcode" - "github.com/stretchr/testify/assert" - "github.com/supabase/cli/pkg/pgtest" -) - -func TestRemoteMigrations(t *testing.T) { - t.Run("loads migration versions", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(LIST_MIGRATION_VERSION). - Reply("SELECT 1", []any{"20220727064247"}) - // Run test - versions, err := ListRemoteMigrations(context.Background(), conn.MockClient(t)) - // Check error - assert.NoError(t, err) - assert.ElementsMatch(t, []string{"20220727064247"}, versions) - }) - - t.Run("loads empty migrations on missing table", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(LIST_MIGRATION_VERSION). - ReplyError(pgerrcode.UndefinedTable, "relation \"supabase_migrations.schema_migrations\" does not exist") - // Run test - versions, err := ListRemoteMigrations(context.Background(), conn.MockClient(t)) - // Check error - assert.NoError(t, err) - assert.Empty(t, versions) - }) - - t.Run("throws error on invalid row", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(LIST_MIGRATION_VERSION). - Reply("SELECT 1", []any{}) - // Run test - _, err := ListRemoteMigrations(context.Background(), conn.MockClient(t)) - // Check error - assert.ErrorContains(t, err, "number of field descriptions must equal number of destinations, got 0 and 1") - }) -} - -func TestLocalMigrations(t *testing.T) { - t.Run("loads migration versions", func(t *testing.T) { - // Setup in-memory fs - files := []string{ - "20220727064246_test.sql", - "20220727064248_test.sql", - } - fsys := fs.MapFS{} - for _, name := range files { - fsys[name] = &fs.MapFile{} - } - // Run test - versions, err := ListLocalMigrations(".", fsys) - // Check error - assert.NoError(t, err) - assert.ElementsMatch(t, files, versions) - }) - - t.Run("ignores outdated and invalid files", func(t *testing.T) { - // Setup in-memory fs - fsys := fs.MapFS{ - "20211208000000_init.sql": &fs.MapFile{}, - "20211208000001_invalid.ts": &fs.MapFile{}, - } - // Run test - versions, err := ListLocalMigrations(".", fsys) - // Check error - assert.NoError(t, err) - assert.Empty(t, versions) - }) - - t.Run("throws error on open failure", func(t *testing.T) { - // Setup in-memory fs - fsys := fs.MapFS{"migrations": &fs.MapFile{}} - // Run test - _, err := ListLocalMigrations("migrations", fsys) - // Check error - assert.ErrorContains(t, err, "failed to read directory:") - }) -} diff --git a/apps/cli-go/pkg/migration/queries/drop.sql b/apps/cli-go/pkg/migration/queries/drop.sql deleted file mode 100644 index 568e15eef0..0000000000 --- a/apps/cli-go/pkg/migration/queries/drop.sql +++ /dev/null @@ -1,136 +0,0 @@ -do $$ declare - rec record; -begin - -- schemas - for rec in - select pn.* - from pg_catalog.pg_namespace pn - left join pg_catalog.pg_depend pd on pd.objid = pn.oid and pd.classid = 'pg_catalog.pg_namespace'::regclass - where pd.deptype is null - and not pn.nspname like any(array['information\_schema', 'pg\_%', '\_analytics', '\_realtime', '\_supavisor', 'pgbouncer', 'pgmq', 'pgsodium', 'pgtle', 'supabase\_migrations', 'vault', 'extensions', 'public']) - and pn.nspowner::regrole::text != 'supabase_admin' - loop - -- If an extension uses a schema it doesn't create, dropping the schema will cascade to also - -- drop the extension. But if an extension creates its own schema, dropping the schema will - -- throw an error. Hence, we drop schemas first while excluding those created by extensions. - raise notice 'dropping schema: %', rec.nspname; - execute format('drop schema if exists %I cascade', rec.nspname); - end loop; - - -- extensions - for rec in - select * - from pg_extension p - where p.extname not in ('pg_graphql', 'pg_net', 'pg_stat_statements', 'pgcrypto', 'pgjwt', 'pgsodium', 'plpgsql', 'supabase_vault', 'uuid-ossp') - loop - raise notice 'dropping extension: %', rec.extname; - execute format('drop extension if exists %I cascade', rec.extname); - end loop; - - -- functions - for rec in - select * - from pg_proc p - where p.pronamespace::regnamespace::name = 'public' - loop - -- supports aggregate, function, and procedure - raise notice 'dropping function: %.%', rec.pronamespace::regnamespace::name, rec.proname; - execute format('drop routine if exists %I.%I(%s) cascade', rec.pronamespace::regnamespace::name, rec.proname, pg_catalog.pg_get_function_identity_arguments(rec.oid)); - end loop; - - -- views (necessary for views referencing objects in Supabase-managed schemas) - for rec in - select * - from pg_class c - where - c.relnamespace::regnamespace::name = 'public' - and c.relkind = 'v' - loop - raise notice 'dropping view: %.%', rec.relnamespace::regnamespace::name, rec.relname; - execute format('drop view if exists %I.%I cascade', rec.relnamespace::regnamespace::name, rec.relname); - end loop; - - -- materialized views (necessary for materialized views referencing objects in Supabase-managed schemas) - for rec in - select * - from pg_class c - where - c.relnamespace::regnamespace::name = 'public' - and c.relkind = 'm' - loop - raise notice 'dropping materialized view: %.%', rec.relnamespace::regnamespace::name, rec.relname; - execute format('drop materialized view if exists %I.%I cascade', rec.relnamespace::regnamespace::name, rec.relname); - end loop; - - -- tables (cascade to dependent objects) - for rec in - select * - from pg_class c - where - c.relnamespace::regnamespace::name = 'public' - and c.relkind not in ('c', 'S', 'v', 'm') - order by c.relkind desc - loop - -- supports all table like relations, except views, complex types, and sequences - raise notice 'dropping table: %.%', rec.relnamespace::regnamespace::name, rec.relname; - execute format('drop table if exists %I.%I cascade', rec.relnamespace::regnamespace::name, rec.relname); - end loop; - - -- truncate tables in auth, webhooks, and migrations schema - for rec in - select * - from pg_class c - where - (c.relnamespace::regnamespace::name = 'auth' and c.relname != 'schema_migrations' - or c.relnamespace::regnamespace::name = 'supabase_functions' and c.relname != 'migrations' - or c.relnamespace::regnamespace::name = 'supabase_migrations') - and c.relkind = 'r' - loop - raise notice 'truncating table: %.%', rec.relnamespace::regnamespace::name, rec.relname; - execute format('truncate %I.%I cascade', rec.relnamespace::regnamespace::name, rec.relname); - end loop; - - -- sequences - for rec in - select * - from pg_class c - where - c.relnamespace::regnamespace::name = 'public' - and c.relkind = 's' - loop - raise notice 'dropping sequence: %.%', rec.relnamespace::regnamespace::name, rec.relname; - execute format('drop sequence if exists %I.%I cascade', rec.relnamespace::regnamespace::name, rec.relname); - end loop; - - -- types - for rec in - select * - from pg_type t - where - t.typnamespace::regnamespace::name = 'public' - and typtype != 'b' - loop - raise notice 'dropping type: %.%', rec.typnamespace::regnamespace::name, rec.typname; - execute format('drop type if exists %I.%I cascade', rec.typnamespace::regnamespace::name, rec.typname); - end loop; - - -- policies - for rec in - select * - from pg_policies p - loop - raise notice 'dropping policy: %', rec.policyname; - execute format('drop policy if exists %I on %I.%I cascade', rec.policyname, rec.schemaname, rec.tablename); - end loop; - - -- publications - for rec in - select * - from pg_publication p - where - not p.pubname like any(array['supabase\_realtime%', 'realtime\_messages%']) - loop - raise notice 'dropping publication: %', rec.pubname; - execute format('drop publication if exists %I', rec.pubname); - end loop; -end $$; diff --git a/apps/cli-go/pkg/migration/queries/list.sql b/apps/cli-go/pkg/migration/queries/list.sql deleted file mode 100644 index 7ab6201174..0000000000 --- a/apps/cli-go/pkg/migration/queries/list.sql +++ /dev/null @@ -1,10 +0,0 @@ --- List user defined schemas, excluding --- Extension created schemas --- Supabase managed schemas -select pn.nspname -from pg_catalog.pg_namespace pn -left join pg_catalog.pg_depend pd on pd.objid = pn.oid and pd.classid = 'pg_catalog.pg_namespace'::regclass -where pd.deptype is null - and not pn.nspname like any($1) - and pn.nspowner::regrole::text != 'supabase_admin' -order by pn.nspname diff --git a/apps/cli-go/pkg/migration/scripts/dump_data.sh b/apps/cli-go/pkg/migration/scripts/dump_data.sh deleted file mode 100755 index fed47b54e1..0000000000 --- a/apps/cli-go/pkg/migration/scripts/dump_data.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -export PGHOST="$PGHOST" -export PGPORT="$PGPORT" -export PGUSER="$PGUSER" -export PGPASSWORD="$PGPASSWORD" -export PGDATABASE="$PGDATABASE" - -# Disable triggers so that data dump can be restored exactly as it is -echo "SET session_replication_role = replica; -" - -# Explanation of pg_dump flags: -# -# --exclude-schema omit data from internal schemas as they are maintained by platform -# --exclude-table omit data from migration history tables as they are managed by platform -# --column-inserts only column insert syntax is supported, ie. no copy from stdin -# --schema '*' include all other schemas by default -# -# Explanation of sed substitutions: -# -# - do not emit psql meta commands -# -# Never delete SQL comments because multiline records may begin with them. -pg_dump \ - --data-only \ - --quote-all-identifier \ - --role "postgres" \ - --exclude-schema "${EXCLUDED_SCHEMAS:-}" \ - --exclude-table "auth.schema_migrations" \ - --exclude-table "storage.migrations" \ - --exclude-table "supabase_functions.migrations" \ - --schema "$INCLUDED_SCHEMAS" \ - ${EXTRA_FLAGS:-} \ -| sed -E 's/^\\(un)?restrict .*$/-- &/' - -# Reset session config generated by pg_dump -echo "RESET ALL;" diff --git a/apps/cli-go/pkg/migration/scripts/dump_role.sh b/apps/cli-go/pkg/migration/scripts/dump_role.sh deleted file mode 100755 index 96fe6a3068..0000000000 --- a/apps/cli-go/pkg/migration/scripts/dump_role.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -export PGHOST="$PGHOST" -export PGPORT="$PGPORT" -export PGUSER="$PGUSER" -export PGPASSWORD="$PGPASSWORD" -export PGDATABASE="$PGDATABASE" - -# Explanation of pg_dumpall flags: -# -# --roles-only only include create, alter, and grant role statements -# -# Explanation of sed substitutions: -# -# - do not emit psql meta commands -# - do not create or alter reserved roles as they are blocked by supautils -# - explicitly allow altering safe attributes, ie. statement_timeout, pgrst.* -# - discard role attributes that require superuser, ie. nosuperuser, noreplication -# - do not alter membership grants by supabase_admin role -pg_dumpall \ - --roles-only \ - --role "postgres" \ - --quote-all-identifier \ - --no-role-passwords \ - --no-comments \ -| sed -E 's/^\\(un)?restrict .*$/-- &/' \ -| sed -E "s/^CREATE ROLE \"($RESERVED_ROLES)\"/-- &/" \ -| sed -E "s/^ALTER ROLE \"($RESERVED_ROLES)\"/-- &/" \ -| sed -E "s/ (NOSUPERUSER|NOREPLICATION)//g" \ -| sed -E "s/^-- (.* SET \"($ALLOWED_CONFIGS)\" .*)/\1/" \ -| sed -E "s/GRANT \".*\" TO \"($RESERVED_ROLES)\"/-- &/" \ -| sed -E "${EXTRA_SED:-}" \ -| uniq - -# Reset session config generated by pg_dump -echo "RESET ALL;" diff --git a/apps/cli-go/pkg/migration/scripts/dump_schema.sh b/apps/cli-go/pkg/migration/scripts/dump_schema.sh deleted file mode 100755 index c136cc086e..0000000000 --- a/apps/cli-go/pkg/migration/scripts/dump_schema.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -export PGHOST="$PGHOST" -export PGPORT="$PGPORT" -export PGUSER="$PGUSER" -export PGPASSWORD="$PGPASSWORD" -export PGDATABASE="$PGDATABASE" - -# Explanation of pg_dump flags: -# -# --schema-only omit data like migration history, pgsodium key, etc. -# --exclude-schema omit internal schemas as they are maintained by platform -# -# Explanation of sed substitutions: -# -# - do not emit psql meta commands -# - do not alter superuser role "supabase_admin" -# - do not alter foreign data wrappers owner -# - do not include ACL changes on internal schemas -# - do not include RLS policies on cron extension schema -# - do not include event triggers -# - do not create pgtle schema and extension comments -# - do not create publication "supabase_realtime" -# - do not set transaction_timeout which requires pg17 -pg_dump \ - --schema-only \ - --quote-all-identifier \ - --role "postgres" \ - --exclude-schema "${EXCLUDED_SCHEMAS:-}" \ - ${EXTRA_FLAGS:-} \ -| sed -E 's/^\\(un)?restrict .*$/-- &/' \ -| sed -E 's/^CREATE SCHEMA "/CREATE SCHEMA IF NOT EXISTS "/' \ -| sed -E 's/^CREATE TABLE "/CREATE TABLE IF NOT EXISTS "/' \ -| sed -E 's/^CREATE SEQUENCE "/CREATE SEQUENCE IF NOT EXISTS "/' \ -| sed -E 's/^CREATE VIEW "/CREATE OR REPLACE VIEW "/' \ -| sed -E 's/^CREATE FUNCTION "/CREATE OR REPLACE FUNCTION "/' \ -| sed -E 's/^CREATE TRIGGER "/CREATE OR REPLACE TRIGGER "/' \ -| sed -E 's/^CREATE PUBLICATION "supabase_realtime/-- &/' \ -| sed -E 's/^CREATE EVENT TRIGGER /-- &/' \ -| sed -E 's/^ WHEN TAG IN /-- &/' \ -| sed -E 's/^ EXECUTE FUNCTION /-- &/' \ -| sed -E 's/^ALTER EVENT TRIGGER /-- &/' \ -| sed -E 's/^ALTER PUBLICATION "supabase_realtime_/-- &/' \ -| sed -E 's/^ALTER FOREIGN DATA WRAPPER (.+) OWNER TO /-- &/' \ -| sed -E 's/^ALTER DEFAULT PRIVILEGES FOR ROLE "supabase_admin"/-- &/' \ -| sed -E 's/^GRANT ALL ON FOREIGN DATA WRAPPER (.+) TO "postgres" WITH GRANT OPTION/-- &/' \ -| sed -E "s/^GRANT (.+) ON (.+) \"(${EXCLUDED_SCHEMAS:-})\"/-- &/" \ -| sed -E "s/^REVOKE (.+) ON (.+) \"(${EXCLUDED_SCHEMAS:-})\"/-- &/" \ -| sed -E 's/^(CREATE EXTENSION IF NOT EXISTS "pg_tle").+/\1;/' \ -| sed -E 's/^(CREATE EXTENSION IF NOT EXISTS "pgsodium").+/\1;/' \ -| sed -E 's/^(CREATE EXTENSION IF NOT EXISTS "pgmq").+/\1;/' \ -| sed -E 's/^COMMENT ON EXTENSION (.+)/-- &/' \ -| sed -E 's/^CREATE POLICY "cron_job_/-- &/' \ -| sed -E 's/^ALTER TABLE "cron"/-- &/' \ -| sed -E 's/^SET transaction_timeout = 0;/-- &/' \ -| sed -E "${EXTRA_SED:-}" diff --git a/apps/cli-go/pkg/migration/seed.go b/apps/cli-go/pkg/migration/seed.go deleted file mode 100644 index 2e8fff71c8..0000000000 --- a/apps/cli-go/pkg/migration/seed.go +++ /dev/null @@ -1,100 +0,0 @@ -package migration - -import ( - "context" - "fmt" - "io/fs" - "os" - "path/filepath" - - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgerrcode" - "github.com/jackc/pgx/v4" - "github.com/supabase/cli/pkg/config" -) - -func getRemoteSeeds(ctx context.Context, conn *pgx.Conn) (map[string]string, error) { - remotes, err := ReadSeedTable(ctx, conn) - if err != nil { - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) && pgErr.Code == pgerrcode.UndefinedTable { - // If seed table is undefined, the remote project has no migrations - return nil, nil - } - return nil, err - } - applied := make(map[string]string, len(remotes)) - for _, seed := range remotes { - applied[seed.Path] = seed.Hash - } - return applied, nil -} - -func GetPendingSeeds(ctx context.Context, locals config.Glob, conn *pgx.Conn, fsys fs.FS) ([]SeedFile, error) { - locals, err := locals.SQLFiles(fsys) - if err != nil { - fmt.Fprintln(os.Stderr, "WARN:", err) - } - if len(locals) == 0 { - return nil, nil - } - applied, err := getRemoteSeeds(ctx, conn) - if err != nil { - return nil, err - } - var pending []SeedFile - for _, path := range locals { - seed, err := NewSeedFile(path, fsys) - if err != nil { - return nil, err - } - if hash, exists := applied[seed.Path]; exists { - // Skip seed files that already exist - if hash == seed.Hash { - continue - } - // Mark seed file as dirty - seed.Dirty = true - } - pending = append(pending, *seed) - } - return pending, nil -} - -func SeedData(ctx context.Context, pending []SeedFile, conn *pgx.Conn, fsys fs.FS) error { - if len(pending) > 0 { - if err := CreateSeedTable(ctx, conn); err != nil { - return err - } - } - for _, seed := range pending { - if seed.Dirty { - fmt.Fprintf(os.Stderr, "Updating seed hash to %s...\n", seed.Path) - } else { - fmt.Fprintf(os.Stderr, "Seeding data from %s...\n", seed.Path) - } - // Batch seed commands, safe to use statement cache - if err := seed.ExecBatchWithCache(ctx, conn, fsys); err != nil { - return err - } - } - return nil -} - -func SeedGlobals(ctx context.Context, pending []string, conn *pgx.Conn, fsys fs.FS) error { - for _, path := range pending { - filename := filepath.Base(path) - fmt.Fprintf(os.Stderr, "Seeding globals from %s...\n", filename) - globals, err := NewMigrationFromFile(path, fsys) - if err != nil { - return err - } - // Skip inserting to migration history - globals.Version = "" - if err := globals.ExecBatch(ctx, conn); err != nil { - return err - } - } - return nil -} diff --git a/apps/cli-go/pkg/migration/seed_test.go b/apps/cli-go/pkg/migration/seed_test.go deleted file mode 100644 index db4337b54c..0000000000 --- a/apps/cli-go/pkg/migration/seed_test.go +++ /dev/null @@ -1,176 +0,0 @@ -package migration - -import ( - "context" - _ "embed" - "os" - "testing" - fs "testing/fstest" - - "github.com/jackc/pgerrcode" - "github.com/jackc/pgx/v4" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/pkg/pgtest" -) - -//go:embed testdata/seed.sql -var testSeed string - -func TestPendingSeeds(t *testing.T) { - pending := []string{"testdata/seed.sql"} - - t.Run("finds new seeds", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(SELECT_SEED_TABLE). - Reply("SELECT 0") - // Run test - seeds, err := GetPendingSeeds(context.Background(), pending, conn.MockClient(t), testMigrations) - // Check error - assert.NoError(t, err) - require.Len(t, seeds, 1) - assert.Equal(t, seeds[0].Path, pending[0]) - assert.Equal(t, seeds[0].Hash, "61868484fc0ddca2a2022217629a9fd9a4cf1ca479432046290797d6d40ffcc3") - assert.False(t, seeds[0].Dirty) - }) - - t.Run("finds dirty seeds", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(SELECT_SEED_TABLE). - Reply("SELECT 1", SeedFile{Path: pending[0], Hash: "outdated"}) - // Run test - seeds, err := GetPendingSeeds(context.Background(), pending, conn.MockClient(t), testMigrations) - // Check error - assert.NoError(t, err) - require.Len(t, seeds, 1) - assert.Equal(t, seeds[0].Path, pending[0]) - assert.Equal(t, seeds[0].Hash, "61868484fc0ddca2a2022217629a9fd9a4cf1ca479432046290797d6d40ffcc3") - assert.True(t, seeds[0].Dirty) - }) - - t.Run("skips applied seed", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(SELECT_SEED_TABLE). - Reply("SELECT 1", SeedFile{Path: pending[0], Hash: "61868484fc0ddca2a2022217629a9fd9a4cf1ca479432046290797d6d40ffcc3"}) - // Run test - seeds, err := GetPendingSeeds(context.Background(), pending, conn.MockClient(t), testMigrations) - // Check error - assert.NoError(t, err) - require.Empty(t, seeds) - }) - - t.Run("ignores missing seed table", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(SELECT_SEED_TABLE). - ReplyError(pgerrcode.UndefinedTable, `relation "seed_files" does not exist`) - // Run test - _, err := GetPendingSeeds(context.Background(), pending, conn.MockClient(t), testMigrations) - // Check error - assert.NoError(t, err) - }) - - t.Run("ignores missing seed file", func(t *testing.T) { - // Setup in-memory fs - fsys := fs.MapFS{} - // Run test - _, err := GetPendingSeeds(context.Background(), pending, nil, fsys) - // Check error - assert.NoError(t, err) - }) -} - -func TestSeedData(t *testing.T) { - t.Run("seeds from file", func(t *testing.T) { - seed := SeedFile{ - Path: "testdata/seed.sql", - Hash: "61868484fc0ddca2a2022217629a9fd9a4cf1ca479432046290797d6d40ffcc3", - Dirty: true, - } - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - mockSeedHistory(conn). - Query(UPSERT_SEED_FILE, seed.Path, seed.Hash). - Reply("INSERT 0 1") - // Run test - err := SeedData(context.Background(), []SeedFile{seed}, conn.MockClient(t), testMigrations) - // Check error - assert.NoError(t, err) - }) - - t.Run("throws error on upsert failure", func(t *testing.T) { - seed := SeedFile{ - Path: "testdata/seed.sql", - Hash: "61868484fc0ddca2a2022217629a9fd9a4cf1ca479432046290797d6d40ffcc3", - } - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - mockSeedHistory(conn). - Query(testSeed+`;INSERT INTO supabase_migrations.seed_files(path, hash) VALUES( 'testdata/seed.sql' , '61868484fc0ddca2a2022217629a9fd9a4cf1ca479432046290797d6d40ffcc3' ) ON CONFLICT (path) DO UPDATE SET hash = EXCLUDED.hash`). - ReplyError(pgerrcode.NotNullViolation, `null value in column "age" of relation "employees"`) - // Run test - err := SeedData(context.Background(), []SeedFile{seed}, conn.MockClient(t, func(cc *pgx.ConnConfig) { - cc.PreferSimpleProtocol = true - }), testMigrations) - // Check error - assert.ErrorContains(t, err, `ERROR: null value in column "age" of relation "employees" (SQLSTATE 23502)`) - }) -} - -func mockSeedHistory(conn *pgtest.MockConn) *pgtest.MockConn { - conn.Query(SET_LOCK_TIMEOUT). - Query(CREATE_VERSION_SCHEMA). - Reply("CREATE SCHEMA"). - Query(CREATE_SEED_TABLE). - Reply("CREATE TABLE") - return conn -} - -//go:embed testdata/1_globals.sql -var testGlobals string - -func TestSeedGlobals(t *testing.T) { - pending := []string{"testdata/1_globals.sql"} - - t.Run("seeds from file", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(testGlobals). - Reply("CREATE ROLE") - // Run test - err := SeedGlobals(context.Background(), pending, conn.MockClient(t), testMigrations) - // Check error - assert.NoError(t, err) - }) - - t.Run("throws error on missing file", func(t *testing.T) { - // Setup in-memory fs - fsys := fs.MapFS{} - // Run test - err := SeedGlobals(context.Background(), pending, nil, fsys) - // Check error - assert.ErrorIs(t, err, os.ErrNotExist) - }) - - t.Run("throws error on create failure", func(t *testing.T) { - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(testGlobals). - ReplyError(pgerrcode.InvalidCatalogName, `database "postgres" does not exist`) - // Run test - err := SeedGlobals(context.Background(), pending, conn.MockClient(t), testMigrations) - // Check error - assert.ErrorContains(t, err, `ERROR: database "postgres" does not exist (SQLSTATE 3D000)`) - }) -} diff --git a/apps/cli-go/pkg/migration/testdata/0_schema.sql b/apps/cli-go/pkg/migration/testdata/0_schema.sql deleted file mode 100644 index ba3138abd8..0000000000 --- a/apps/cli-go/pkg/migration/testdata/0_schema.sql +++ /dev/null @@ -1 +0,0 @@ -create schema private \ No newline at end of file diff --git a/apps/cli-go/pkg/migration/testdata/1_globals.sql b/apps/cli-go/pkg/migration/testdata/1_globals.sql deleted file mode 100644 index 0e17ccd4d4..0000000000 --- a/apps/cli-go/pkg/migration/testdata/1_globals.sql +++ /dev/null @@ -1 +0,0 @@ -create role developer_role with login password 'developer' \ No newline at end of file diff --git a/apps/cli-go/pkg/migration/testdata/seed.sql b/apps/cli-go/pkg/migration/testdata/seed.sql deleted file mode 100644 index e48c726ce3..0000000000 --- a/apps/cli-go/pkg/migration/testdata/seed.sql +++ /dev/null @@ -1 +0,0 @@ -INSERT INTO employees(name) VALUES ('Alice') \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/state.go b/apps/cli-go/pkg/parser/state.go deleted file mode 100644 index 0a4bce6662..0000000000 --- a/apps/cli-go/pkg/parser/state.go +++ /dev/null @@ -1,296 +0,0 @@ -package parser - -import ( - "bytes" - "strings" - "unicode" - "unicode/utf8" -) - -const ( - // Omit BEGIN to allow arbitrary whitespaces between BEGIN and ATOMIC keywords. - // This can fail if ATOMIC is used as column name because it is not a reserved - // keyword: https://www.postgresql.org/docs/current/sql-keywords-appendix.html - BEGIN_ATOMIC = "ATOMIC" - END_ATOMIC = "END" -) - -type State interface { - // Return nil to emit token - Next(r rune, data []byte) State -} - -// Initial state: ready to parse next token -type ReadyState struct{} - -func (s *ReadyState) Next(r rune, data []byte) State { - switch r { - case '$': - // A $ after an identifier rune continues the identifier (pending$$foo$), not a - // dollar quote. A digit counts too (1$$), unlike PostgreSQL; valid SQL never has that. - offset := len(data) - utf8.RuneLen(r) - if hasIdentifierRuneBefore(data, offset) { - return s - } - return &TagState{offset: offset} - case '\'': - fallthrough - case '"': - return &QuoteState{delimiter: r} - case '-': - return &CommentState{} - case '/': - return &BlockState{} - case '\\': - return &EscapeState{} - case ';': - // Emit token - return nil - case '(': - return &ParenState{prev: s} - case 'c': - fallthrough - case 'C': - if isBeginAtomic(data) { - return &AtomicState{prev: s, statementStart: len(data)} - } - } - return s -} - -func isBeginAtomic(data []byte) bool { - if !endsWithKeyword(data, BEGIN_ATOMIC) { - return false - } - prefix := bytes.TrimRight(data[:len(data)-len(BEGIN_ATOMIC)], sqlWhitespace) - return endsWithKeyword(prefix, "BEGIN") -} - -// PostgreSQL's scan.l treats every byte at or above 0x80 as an identifier/dollar-tag -// character (ident_cont/dolq_cont), whatever its Unicode category. -func isIdentifierRune(r rune) bool { - return r >= utf8.RuneSelf || unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' || r == '$' -} - -func hasIdentifierRuneBefore(data []byte, offset int) bool { - if offset <= 0 { - return false - } - r, _ := utf8.DecodeLastRune(data[:offset]) - return isIdentifierRune(r) -} - -func endsWithKeyword(data []byte, keyword string) bool { - offset := len(data) - len(keyword) - if offset < 0 || !strings.EqualFold(string(data[offset:]), keyword) { - return false - } - return !hasIdentifierRuneBefore(data, offset) -} - -const sqlWhitespace = " \t\n\r\f\v" - -func isSqlWhitespace(b byte) bool { - return bytes.IndexByte([]byte(sqlWhitespace), b) >= 0 -} - -func isCommentsAndWhitespace(text []byte) bool { - for i := 0; i < len(text); { - switch { - case isSqlWhitespace(text[i]): - i++ - case bytes.HasPrefix(text[i:], []byte("--")): - newline := bytes.IndexByte(text[i+2:], '\n') - if newline == -1 { - return true - } - i += 2 + newline + 1 - case bytes.HasPrefix(text[i:], []byte("/*")): - // Match BlockState's sliding-window scan so both agree on overlapping delimiters. - depth := 1 - i += 2 - for i < len(text) && depth > 0 { - switch { - case bytes.HasPrefix(text[i-1:], []byte("/*")): - depth++ - case bytes.HasPrefix(text[i-1:], []byte("*/")): - depth-- - } - i++ - } - if depth > 0 { - return true - } - default: - return false - } - } - return true -} - -// Opened a line comment -type CommentState struct{} - -func (s *CommentState) Next(r rune, data []byte) State { - if r == '-' { - // No characters are escaped in comments, which is the same as dollar - return &DollarState{delimiter: []byte{'\n'}} - } - // Break out of comment state - state := &ReadyState{} - return state.Next(r, data) -} - -// Opened a block comment -type BlockState struct { - depth int -} - -func (s *BlockState) Next(r rune, data []byte) State { - const open = "/*" - const close = "*/" - window := data[len(data)-2:] - if bytes.Equal(window, []byte(open)) { - s.depth += 1 - return s - } - if s.depth == 0 { - // Break out of block state - state := &ReadyState{} - return state.Next(r, data) - } - if bytes.Equal(window, []byte(close)) { - s.depth -= 1 - if s.depth == 0 { - return &ReadyState{} - } - } - return s -} - -// Opened a single quote ' or double quote " -type QuoteState struct { - delimiter rune - escape bool -} - -func (s *QuoteState) Next(r rune, data []byte) State { - if s.escape { - // Preserve escaped quote '' - if r == s.delimiter { - s.escape = false - return s - } - // Break out of quote state - state := &ReadyState{} - return state.Next(r, data) - } - if r == s.delimiter { - s.escape = true - } - return s -} - -// Opened a dollar quote, no characters are ever esacped. -type DollarState struct { - delimiter []byte -} - -func (s *DollarState) Next(r rune, data []byte) State { - window := data[len(data)-len(s.delimiter):] - if bytes.Equal(window, s.delimiter) { - // Break out of dollar state - return &ReadyState{} - } - return s -} - -// Opened a tag, ie. $tag$ -type TagState struct { - offset int -} - -func (s *TagState) Next(r rune, data []byte) State { - if r == '$' { - // Make a copy since the data slice may be overwritten - tag := data[s.offset:] - dollar := DollarState{ - delimiter: make([]byte, len(tag)), - } - copy(dollar.delimiter, tag) - return &dollar - } - // Valid tag: https://www.postgresql.org/docs/current/sql-syntax-lexical.html - if isIdentifierRune(r) { - return s - } - // Break out of tag state - state := &ReadyState{} - return state.Next(r, data) -} - -// Opened a \ escape -type EscapeState struct{} - -func (s *EscapeState) Next(r rune, data []byte) State { - return &ReadyState{} -} - -// Opened a parenthesis group -type ParenState struct { - prev State -} - -func (s *ParenState) Next(r rune, data []byte) State { - curr := s.prev.Next(r, data) - if curr == nil { - s.prev = &ReadyState{} - return s - } - s.prev = curr - if _, ok := s.prev.(*ReadyState); !ok { - return s - } - if r == ')' { - return &ReadyState{} - } - return s -} - -// Opened BEGIN ATOMIC function body -type AtomicState struct { - prev State - pendingEnd bool - statementStart int - statementHasContent bool -} - -func (s *AtomicState) Next(r rune, data []byte) State { - pendingEnd := s.pendingEnd - s.pendingEnd = false - if pendingEnd && !isIdentifierRune(r) { - return (&ReadyState{}).Next(r, data) - } - // An END inside a nested quote/comment doesn't count. - curr := s.prev.Next(r, data) - if curr == nil { - s.prev = &ReadyState{} - s.statementStart = len(data) - s.statementHasContent = false - return s - } - s.prev = curr - if _, ok := s.prev.(*ReadyState); !ok { - return s - } - // PostgreSQL requires each inner statement to end with ';', so the closing END is - // always the first token of a statement; a later END is expression text. - if !s.statementHasContent && endsWithKeyword(data, END_ATOMIC) { - if isCommentsAndWhitespace(data[s.statementStart : len(data)-len(END_ATOMIC)]) { - s.pendingEnd = true - } else { - s.statementHasContent = true - } - } - return s -} diff --git a/apps/cli-go/pkg/parser/state_test.go b/apps/cli-go/pkg/parser/state_test.go deleted file mode 100644 index 921a33b852..0000000000 --- a/apps/cli-go/pkg/parser/state_test.go +++ /dev/null @@ -1,293 +0,0 @@ -package parser - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func checkSplit(t *testing.T, sql []string) { - input := strings.Join(sql, "") - output, err := Split(strings.NewReader(input)) - require.NoError(t, err) - assert.ElementsMatch(t, sql, output) -} - -func TestLineComment(t *testing.T) { - t.Run("after separator", func(t *testing.T) { - sql := []string{"END;", "-- comment"} - checkSplit(t, sql) - }) - - t.Run("before separator", func(t *testing.T) { - sql := []string{"SELECT --; 1"} - checkSplit(t, sql) - }) - - t.Run("not started", func(t *testing.T) { - sql := []string{"- ;", "END"} - checkSplit(t, sql) - }) - - t.Run("between lines", func(t *testing.T) { - sql := []string{"-- /* \n;", " */ END"} - checkSplit(t, sql) - }) -} - -func TestBlockComment(t *testing.T) { - t.Run("contains separator", func(t *testing.T) { - sql := []string{"SELECT /* ; */ 1;"} - checkSplit(t, sql) - }) - - t.Run("nested block", func(t *testing.T) { - sql := []string{"SELECT /*; /*;*/ ;*/ 1"} - checkSplit(t, sql) - }) - - t.Run("not started", func(t *testing.T) { - sql := []string{"/ * ;", " */ END"} - checkSplit(t, sql) - }) -} - -func TestSeparator(t *testing.T) { - t.Run("no spaces", func(t *testing.T) { - sql := []string{";", "END;", ";"} - checkSplit(t, sql) - }) - - t.Run("between spaces", func(t *testing.T) { - sql := []string{"BEGIN ;", " END"} - checkSplit(t, sql) - }) - - t.Run("backslash escaped", func(t *testing.T) { - sql := []string{"\\;;", "\\;"} - checkSplit(t, sql) - }) -} - -func TestDollarQuote(t *testing.T) { - t.Run("named tag", func(t *testing.T) { - sql := []string{"$tag$ any ; string$tag$"} - checkSplit(t, sql) - }) - - t.Run("non-ASCII named tag", func(t *testing.T) { - for _, tag := range []string{"a²", "a😀", "á"} { - t.Run(tag, func(t *testing.T) { - checkSplit(t, []string{"$" + tag + "$ any ; END; string$" + tag + "$;", " SELECT 2;"}) - }) - } - }) - - t.Run("anonymous tag", func(t *testing.T) { - sql := []string{"$$\"Dane's horse\"$$"} - checkSplit(t, sql) - }) - - t.Run("not started", func(t *testing.T) { - sql := []string{"SELECT \"$\";", " $$"} - checkSplit(t, sql) - }) -} - -func TestSingleQuote(t *testing.T) { - t.Run("escapes separator", func(t *testing.T) { - sql := []string{"SELECT ';' 1"} - checkSplit(t, sql) - }) - - t.Run("preserves single quote", func(t *testing.T) { - sql := []string{"SELECT ';'';' 1"} - checkSplit(t, sql) - }) - - t.Run("literal backslash", func(t *testing.T) { - sql := []string{"SELECT '\\';", " 1'"} - checkSplit(t, sql) - }) -} - -func TestDoubleQuote(t *testing.T) { - t.Run("escapes separator", func(t *testing.T) { - sql := []string{`CREATE POLICY "cats;dogs" on cats_dogs;`, " END"} - checkSplit(t, sql) - }) - - t.Run("preserves single quote", func(t *testing.T) { - sql := []string{`CREATE POLICY "cat's and dog's" on cats_dogs;`, " END"} - checkSplit(t, sql) - }) - - t.Run("preserves double quote", func(t *testing.T) { - sql := []string{`CREATE POLICY "pet""name" on pets;`, " END"} - checkSplit(t, sql) - }) -} - -func TestParentheses(t *testing.T) { - t.Run("preserves parentheses", func(t *testing.T) { - sql := []string{ - `CREATE RULE notify_me AS ON UPDATE TO mytable DO (NOTIFY mytable; SELECT "1");`, - `SELECT 1`, - } - checkSplit(t, sql) - }) - - t.Run("ignores literals", func(t *testing.T) { - sql := []string{`CREATE RULE notify_me AS ON UPDATE TO mytable DO (-- ) -SELECT ')'; --- ) -) -`} - checkSplit(t, sql) - }) -} - -func TestBeginAtomic(t *testing.T) { - t.Run("inline body", func(t *testing.T) { - sql := []string{`CREATE FUNCTION add(a integer, b integer) RETURNS integer -LANGUAGE SQL -IMMUTABLE STRICT PARALLEL SAFE -RETURNS NULL ON NULL INPUT -BEGIN ATOMIC - SELECT 'add'; - SELECT a + b; -END;`} - checkSplit(t, sql) - }) - - t.Run("case insenstive", func(t *testing.T) { - sql := []string{"begin atomic; select 'end'; end"} - checkSplit(t, sql) - }) - - t.Run("ignores literals", func(t *testing.T) { - sql := []string{`CREATE FUNCTION test() BEGIN -ATOMIC-- END; --- END; -END -;`} - checkSplit(t, sql) - }) - - t.Run("ignores atomic in identifiers", func(t *testing.T) { - names := []string{ - "fn_atomic", - "atomic_fn", - "my_atomic_thing", - "xatomicx", - "fn_ATomiC", - } - for _, name := range names { - t.Run(name, func(t *testing.T) { - sql := []string{ - `CREATE OR REPLACE FUNCTION ` + name + `() -RETURNS void LANGUAGE plpgsql AS $$ -BEGIN - NULL; -END; -$$;`, - ` -SELECT 1;`, - } - checkSplit(t, sql) - }) - } - }) - - t.Run("ignores end inside identifiers", func(t *testing.T) { - for _, name := range []string{"pending", "pending_change", "append", "legend", "𐐀end", "😀end", "́end", "²end", "pending$$foo$"} { - t.Run(name, func(t *testing.T) { - body := `CREATE FUNCTION f() RETURNS int LANGUAGE sql BEGIN ATOMIC SELECT 1 AS ` + name + `; END;` - checkSplit(t, []string{body, ` SELECT 2;`}) - }) - } - }) - - t.Run("ignores identifiers starting with end", func(t *testing.T) { - for _, name := range []string{"endpoint", "end_date", "ended_at", "end𐐀", "end😀", "end́", "end²", "end$$foo$"} { - t.Run(name, func(t *testing.T) { - body := `CREATE FUNCTION f() RETURNS int LANGUAGE sql BEGIN ATOMIC SELECT ` + name + `; SELECT 1; END;` - checkSplit(t, []string{body, ` SELECT 2;`}) - }) - } - }) - - t.Run("ignores end inside inner statements", func(t *testing.T) { - for _, expr := range []string{ - "CASE WHEN true THEN 1 ELSE 0 END", - "case when true then 1 end", - "CASE WHEN true THEN 1 END AS ended", - "CASE WHEN CASE WHEN true THEN true END THEN 1 END", - "(CASE WHEN (true) THEN 1 END)", - "coalesce(CASE WHEN length('a') > 0 THEN 1 END, 0)", - "CASE(1)WHEN 1 THEN 1 END", - "1 AS case", - "1 case", - "1 AS end", - "1 end", - "'end'", - } { - t.Run(expr, func(t *testing.T) { - body := `CREATE FUNCTION f() RETURNS int LANGUAGE sql BEGIN ATOMIC SELECT ` + expr + `; SELECT 1; END;` - checkSplit(t, []string{body, ` SELECT 2;`}) - }) - } - }) - - t.Run("closes at end preceded only by comments", func(t *testing.T) { - for _, comment := range []string{"-- note END\n", "/* note; */ ", "\n/* a /* b; */ */ -- c\n"} { - t.Run(comment, func(t *testing.T) { - body := `CREATE FUNCTION f() RETURNS int LANGUAGE sql BEGIN ATOMIC SELECT 1; ` + comment + `END;` - checkSplit(t, []string{body, ` SELECT 2;`}) - }) - } - }) - - t.Run("ignores non-ASCII begin atomic lookalikes", func(t *testing.T) { - // atomıc (dotless ı) case-folds to ATOMIC but is a plain identifier in SQL. - checkSplit(t, []string{"BEGIN atomıc;", " SELECT 'end';", " SELECT 2;"}) - }) - - t.Run("closes atomic body at end right after a positional parameter", func(t *testing.T) { - checkSplit(t, []string{"CREATE FUNCTION f(int) RETURNS int LANGUAGE sql BEGIN ATOMIC SELECT $1;END;", " SELECT 2;"}) - }) - - t.Run("requires sql whitespace between begin and atomic", func(t *testing.T) { - for _, gap := range []string{"\u00A0", "\uFEFF", "\u0085"} { - t.Run(gap, func(t *testing.T) { - checkSplit(t, []string{"BEGIN " + gap + " ATOMIC;", " SELECT 1;", " end;", " SELECT 2;"}) - }) - } - }) - - t.Run("closes nested atomic body inside parentheses", func(t *testing.T) { - checkSplit(t, []string{"DO (BEGIN ATOMIC SELECT 1; END; );", " SELECT 2;"}) - }) - - t.Run("ignores end after overlapping block comment", func(t *testing.T) { - body := `CREATE FUNCTION f() RETURNS int LANGUAGE sql BEGIN ATOMIC SELECT 1; /* a /*/ b */ SELECT 2 END; SELECT 3; END;` - checkSplit(t, []string{body, ` SELECT 4;`}) - }) - - t.Run("does not treat schema-qualified atomic function names as begin atomic", func(t *testing.T) { - sql := []string{`CREATE OR REPLACE FUNCTION public.atomic_example() -RETURNS INTEGER -LANGUAGE plpgsql -AS $$ -BEGIN - RETURN 1; -END; -$$;`, - ` -GRANT EXECUTE ON FUNCTION public.atomic_example() TO authenticated;`, - } - checkSplit(t, sql) - }) -} diff --git a/apps/cli-go/pkg/parser/testdata/all.sql b/apps/cli-go/pkg/parser/testdata/all.sql deleted file mode 100644 index c63948869c..0000000000 --- a/apps/cli-go/pkg/parser/testdata/all.sql +++ /dev/null @@ -1,140 +0,0 @@ -CREATE SCHEMA "blocks"; -CREATE TABLE "blocks"."block" -( - uuid UUID PRIMARY KEY NOT NULL, - settings_type CHARACTER VARYING NOT NULL, - settings JSONB NOT NULL, - title CHARACTER VARYING, - view_type CHARACTER VARYING, - enabled BOOL DEFAULT TRUE NOT NULL, - visibility_settings JSONB -); - -CREATE UNIQUE INDEX "block_uuid_uindex" - ON "blocks"."block" (uuid); - -CREATE INDEX "enabled_index" - ON "blocks"."block" (enabled); - -CREATE TABLE "blocks"."placement" -( - location_name CHARACTER VARYING NOT NULL, - index INT NOT NULL, - block_uuid UUID NOT NULL, - scope_path CHARACTER VARYING NOT NULL, - inherited BOOL DEFAULT FALSE NOT NULL, - excluded_scopes CHARACTER VARYING [], - - -- NOTE: Because we UPDATE the primary (location_name, index) key on the placement-table, - -- the constraint behavior on the primary key is set to DEFERRABLE and INITIALLY IMMEDIATE. - -- - -- For more inforation, refer to this bug-report: - -- - -- https://www.postgresql.org/message-id/flat/20170322123053.1421.55154%40wrigleys.postgresql.org - - CONSTRAINT placement_location_name_index_pk PRIMARY KEY (location_name, index) DEFERRABLE INITIALLY IMMEDIATE, - CONSTRAINT placement_block_uuid_fk FOREIGN KEY (block_uuid) REFERENCES "blocks"."block" (uuid) ON DELETE CASCADE -); - -CREATE INDEX "placement_location_name_index_index" - ON "blocks".placement (location_name, index); - -CREATE INDEX "placement_scope_path_index" - ON "blocks".placement (scope_path); - -CREATE INDEX "placement_index_index" - ON "blocks".placement (index); - -CREATE TABLE "blocks"."location_hash" -( - location_name CHARACTER VARYING PRIMARY KEY NOT NULL, - hash CHARACTER VARYING NOT NULL -); - -CREATE TABLE "blocks"."child_blocks" -( - parent_uuid UUID NOT NULL, - column_index INT NOT NULL, - child_uuids UUID [] NOT NULL, - CONSTRAINT child_blocks_parent_uuid_index_pk PRIMARY KEY (parent_uuid, column_index), - CONSTRAINT child_blocks_block_uuid_fk FOREIGN KEY (parent_uuid) REFERENCES "blocks"."block" (uuid) ON DELETE CASCADE - - -- TODO add a trigger to sanitize child_uuids when a block record is deleted -); - -CREATE INDEX "child_blocks_parent_uuid_index" - ON "blocks"."child_blocks" (parent_uuid); - -CREATE TABLE "blocks"."user_bucket" -( - user_uuid UUID PRIMARY KEY NOT NULL, - block_uuids UUID [] NOT NULL -); - -CREATE UNIQUE INDEX "user_bucket_user_uuid_uindex" - ON "blocks"."user_bucket" (user_uuid); - -CREATE VIEW "blocks"."block_count" AS - SELECT - b."uuid" AS "block_uuid", - ( - (SELECT COUNT(*) - FROM "blocks"."placement" p - WHERE p."block_uuid" = b."uuid") - + - (SELECT COALESCE(SUM((SELECT COUNT(*) - FROM unnest(c."child_uuids") cb - WHERE cb = b."uuid")), 0) - FROM "blocks"."child_blocks" c) - ) AS "ref_count", - (SELECT COUNT(*) - FROM "blocks"."user_bucket" k - WHERE "uuid" = ANY (k."block_uuids")) AS "user_count" - FROM "blocks"."block" b; - -CREATE OR REPLACE FUNCTION "blocks".delete_orphaned_blocks() - RETURNS TRIGGER -AS $$ -BEGIN - LOOP - -- Note that RETURN QUERY does not return from the function - it works - -- more like the yield-statement in PHP, in that records from the - -- DELETE..RETURNING statement are returned, and execution then - -- resumes from the following statement. - - DELETE FROM "blocks"."block" b - WHERE b.uuid IN ( - SELECT c.block_uuid - FROM "blocks"."block_count" c - WHERE c.ref_count = 0 AND c.user_count = 0 - ); - - -- The FOUND flag is set TRUE/FALSE after executing a query - so we - -- EXIT from the LOOP block when the DELETE..RETURNING statement does - -- not delete and return any records. - - EXIT WHEN NOT FOUND; - END LOOP; - - RETURN NULL; -END; -$$ -LANGUAGE plpgsql; - -CREATE TRIGGER delete_orphans -AFTER UPDATE OR DELETE - ON "blocks"."user_bucket" -FOR EACH STATEMENT -EXECUTE PROCEDURE "blocks".delete_orphaned_blocks(); - -CREATE TRIGGER delete_orphans -AFTER UPDATE OR DELETE - ON "blocks"."placement" -FOR EACH STATEMENT -EXECUTE PROCEDURE "blocks".delete_orphaned_blocks(); - -CREATE TRIGGER delete_orphans -AFTER UPDATE OR DELETE - ON "blocks"."child_blocks" -FOR EACH STATEMENT -EXECUTE PROCEDURE "blocks".delete_orphaned_blocks(); diff --git a/apps/cli-go/pkg/parser/testdata/split_00.sql b/apps/cli-go/pkg/parser/testdata/split_00.sql deleted file mode 100644 index 78a47bc16e..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_00.sql +++ /dev/null @@ -1 +0,0 @@ -CREATE SCHEMA "blocks"; \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_01.sql b/apps/cli-go/pkg/parser/testdata/split_01.sql deleted file mode 100644 index a877e55f0d..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_01.sql +++ /dev/null @@ -1,11 +0,0 @@ - -CREATE TABLE "blocks"."block" -( - uuid UUID PRIMARY KEY NOT NULL, - settings_type CHARACTER VARYING NOT NULL, - settings JSONB NOT NULL, - title CHARACTER VARYING, - view_type CHARACTER VARYING, - enabled BOOL DEFAULT TRUE NOT NULL, - visibility_settings JSONB -); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_02.sql b/apps/cli-go/pkg/parser/testdata/split_02.sql deleted file mode 100644 index a4eecb94c7..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_02.sql +++ /dev/null @@ -1,4 +0,0 @@ - - -CREATE UNIQUE INDEX "block_uuid_uindex" - ON "blocks"."block" (uuid); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_03.sql b/apps/cli-go/pkg/parser/testdata/split_03.sql deleted file mode 100644 index 20eec9067c..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_03.sql +++ /dev/null @@ -1,4 +0,0 @@ - - -CREATE INDEX "enabled_index" - ON "blocks"."block" (enabled); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_04.sql b/apps/cli-go/pkg/parser/testdata/split_04.sql deleted file mode 100644 index 493f7aac38..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_04.sql +++ /dev/null @@ -1,21 +0,0 @@ - - -CREATE TABLE "blocks"."placement" -( - location_name CHARACTER VARYING NOT NULL, - index INT NOT NULL, - block_uuid UUID NOT NULL, - scope_path CHARACTER VARYING NOT NULL, - inherited BOOL DEFAULT FALSE NOT NULL, - excluded_scopes CHARACTER VARYING [], - - -- NOTE: Because we UPDATE the primary (location_name, index) key on the placement-table, - -- the constraint behavior on the primary key is set to DEFERRABLE and INITIALLY IMMEDIATE. - -- - -- For more inforation, refer to this bug-report: - -- - -- https://www.postgresql.org/message-id/flat/20170322123053.1421.55154%40wrigleys.postgresql.org - - CONSTRAINT placement_location_name_index_pk PRIMARY KEY (location_name, index) DEFERRABLE INITIALLY IMMEDIATE, - CONSTRAINT placement_block_uuid_fk FOREIGN KEY (block_uuid) REFERENCES "blocks"."block" (uuid) ON DELETE CASCADE -); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_05.sql b/apps/cli-go/pkg/parser/testdata/split_05.sql deleted file mode 100644 index 003b969530..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_05.sql +++ /dev/null @@ -1,4 +0,0 @@ - - -CREATE INDEX "placement_location_name_index_index" - ON "blocks".placement (location_name, index); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_06.sql b/apps/cli-go/pkg/parser/testdata/split_06.sql deleted file mode 100644 index c9b41508fb..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_06.sql +++ /dev/null @@ -1,4 +0,0 @@ - - -CREATE INDEX "placement_scope_path_index" - ON "blocks".placement (scope_path); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_07.sql b/apps/cli-go/pkg/parser/testdata/split_07.sql deleted file mode 100644 index 824464ab6d..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_07.sql +++ /dev/null @@ -1,4 +0,0 @@ - - -CREATE INDEX "placement_index_index" - ON "blocks".placement (index); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_08.sql b/apps/cli-go/pkg/parser/testdata/split_08.sql deleted file mode 100644 index db30a5dd52..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_08.sql +++ /dev/null @@ -1,7 +0,0 @@ - - -CREATE TABLE "blocks"."location_hash" -( - location_name CHARACTER VARYING PRIMARY KEY NOT NULL, - hash CHARACTER VARYING NOT NULL -); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_09.sql b/apps/cli-go/pkg/parser/testdata/split_09.sql deleted file mode 100644 index fad90ece22..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_09.sql +++ /dev/null @@ -1,12 +0,0 @@ - - -CREATE TABLE "blocks"."child_blocks" -( - parent_uuid UUID NOT NULL, - column_index INT NOT NULL, - child_uuids UUID [] NOT NULL, - CONSTRAINT child_blocks_parent_uuid_index_pk PRIMARY KEY (parent_uuid, column_index), - CONSTRAINT child_blocks_block_uuid_fk FOREIGN KEY (parent_uuid) REFERENCES "blocks"."block" (uuid) ON DELETE CASCADE - - -- TODO add a trigger to sanitize child_uuids when a block record is deleted -); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_10.sql b/apps/cli-go/pkg/parser/testdata/split_10.sql deleted file mode 100644 index 4abf69d4a0..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_10.sql +++ /dev/null @@ -1,4 +0,0 @@ - - -CREATE INDEX "child_blocks_parent_uuid_index" - ON "blocks"."child_blocks" (parent_uuid); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_11.sql b/apps/cli-go/pkg/parser/testdata/split_11.sql deleted file mode 100644 index 91fa4a3250..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_11.sql +++ /dev/null @@ -1,7 +0,0 @@ - - -CREATE TABLE "blocks"."user_bucket" -( - user_uuid UUID PRIMARY KEY NOT NULL, - block_uuids UUID [] NOT NULL -); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_12.sql b/apps/cli-go/pkg/parser/testdata/split_12.sql deleted file mode 100644 index 835d649b37..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_12.sql +++ /dev/null @@ -1,4 +0,0 @@ - - -CREATE UNIQUE INDEX "user_bucket_user_uuid_uindex" - ON "blocks"."user_bucket" (user_uuid); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_13.sql b/apps/cli-go/pkg/parser/testdata/split_13.sql deleted file mode 100644 index c39a217906..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_13.sql +++ /dev/null @@ -1,19 +0,0 @@ - - -CREATE VIEW "blocks"."block_count" AS - SELECT - b."uuid" AS "block_uuid", - ( - (SELECT COUNT(*) - FROM "blocks"."placement" p - WHERE p."block_uuid" = b."uuid") - + - (SELECT COALESCE(SUM((SELECT COUNT(*) - FROM unnest(c."child_uuids") cb - WHERE cb = b."uuid")), 0) - FROM "blocks"."child_blocks" c) - ) AS "ref_count", - (SELECT COUNT(*) - FROM "blocks"."user_bucket" k - WHERE "uuid" = ANY (k."block_uuids")) AS "user_count" - FROM "blocks"."block" b; \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_14.sql b/apps/cli-go/pkg/parser/testdata/split_14.sql deleted file mode 100644 index 8e04845fba..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_14.sql +++ /dev/null @@ -1,30 +0,0 @@ - - -CREATE OR REPLACE FUNCTION "blocks".delete_orphaned_blocks() - RETURNS TRIGGER -AS $$ -BEGIN - LOOP - -- Note that RETURN QUERY does not return from the function - it works - -- more like the yield-statement in PHP, in that records from the - -- DELETE..RETURNING statement are returned, and execution then - -- resumes from the following statement. - - DELETE FROM "blocks"."block" b - WHERE b.uuid IN ( - SELECT c.block_uuid - FROM "blocks"."block_count" c - WHERE c.ref_count = 0 AND c.user_count = 0 - ); - - -- The FOUND flag is set TRUE/FALSE after executing a query - so we - -- EXIT from the LOOP block when the DELETE..RETURNING statement does - -- not delete and return any records. - - EXIT WHEN NOT FOUND; - END LOOP; - - RETURN NULL; -END; -$$ -LANGUAGE plpgsql; \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_15.sql b/apps/cli-go/pkg/parser/testdata/split_15.sql deleted file mode 100644 index 58920aae41..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_15.sql +++ /dev/null @@ -1,7 +0,0 @@ - - -CREATE TRIGGER delete_orphans -AFTER UPDATE OR DELETE - ON "blocks"."user_bucket" -FOR EACH STATEMENT -EXECUTE PROCEDURE "blocks".delete_orphaned_blocks(); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_16.sql b/apps/cli-go/pkg/parser/testdata/split_16.sql deleted file mode 100644 index 10cdd16a18..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_16.sql +++ /dev/null @@ -1,7 +0,0 @@ - - -CREATE TRIGGER delete_orphans -AFTER UPDATE OR DELETE - ON "blocks"."placement" -FOR EACH STATEMENT -EXECUTE PROCEDURE "blocks".delete_orphaned_blocks(); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/testdata/split_17.sql b/apps/cli-go/pkg/parser/testdata/split_17.sql deleted file mode 100644 index a8c67d265d..0000000000 --- a/apps/cli-go/pkg/parser/testdata/split_17.sql +++ /dev/null @@ -1,7 +0,0 @@ - - -CREATE TRIGGER delete_orphans -AFTER UPDATE OR DELETE - ON "blocks"."child_blocks" -FOR EACH STATEMENT -EXECUTE PROCEDURE "blocks".delete_orphaned_blocks(); \ No newline at end of file diff --git a/apps/cli-go/pkg/parser/token.go b/apps/cli-go/pkg/parser/token.go deleted file mode 100644 index db00843424..0000000000 --- a/apps/cli-go/pkg/parser/token.go +++ /dev/null @@ -1,119 +0,0 @@ -package parser - -import ( - "bufio" - "io" - "strings" - "unicode/utf8" - - "github.com/go-errors/errors" - "github.com/spf13/viper" - "github.com/supabase/cli/pkg/cast" -) - -// Equal to `startBufSize` from `bufio/scan.go` -const startBufSize = 4096 - -// MaxScannerCapacity defaults to 64 * 1024 which is not enough for certain lines -// containing e.g. geographical data. 256K ought to be enough for anybody... -var MaxScannerCapacity = 256 * 1024 - -// State transition table for tokenizer: -// -// Ready -> Ready (default) -// Ready -> Error (on invalid syntax) -// Ready -> Done (on ;, emit token) -// Ready -> Done (on EOF, emit token) -// -// Ready -> Comment (on --) -// Comment -> Comment (default) -// Comment -> Ready (on \n) -// -// Ready -> Block (on /*) -// Block -> Block (on /*, +-depth) -// Block -> Ready (on */, depth 0) -// -// Ready -> Quote (on ') -// Quote -> Quote (on '', default) -// Quote -> Ready (on ') -// -// Ready -> Dollar (on $tag$) -// Dollar -> Dollar (default) -// Dollar -> Ready (on $tag$) -// -// Ready -> Escape (on \) -// Escape -> Ready (on next) -type tokenizer struct { - state State - last int -} - -func (t *tokenizer) ScanToken(data []byte, atEOF bool) (advance int, token []byte, err error) { - // If we requested more data, resume from last position. - for width := 1; t.last < len(data); t.last += width { - r, width := utf8.DecodeRune(data[t.last:]) - end := t.last + width - t.state = t.state.Next(r, data[:end]) - // Emit token - if t.state == nil { - t.last = 0 - t.state = &ReadyState{} - return end, data[:end], nil - } - } - if !atEOF || len(data) == 0 { - // Request more data or end the stream - return 0, nil, nil - } - // We're at EOF. If we have a final, non-terminated token, return it. - return len(data), data, nil -} - -// Use bufio.Scanner to split a PostgreSQL string into multiple statements. -// -// The core problem is to figure out whether the current ; separator is inside -// an escaped string literal. PostgreSQL has multiple ways of opening a string -// literal, $$, ', --, /*, etc. We use a FSM to guarantee these states are -// entered exclusively. If not in one of the above escape states, the next ; -// token can be parsed as statement separator. -// -// Each statement is split as it is, without removing comments or white spaces. -func Split(sql io.Reader, transform ...func(string) string) (stats []string, err error) { - t := tokenizer{state: &ReadyState{}} - scanner := bufio.NewScanner(sql) - - // Increase scanner capacity to support very long lines containing e.g. geodata - buf := make([]byte, startBufSize) - maxbuf := cast.UintToInt(viper.GetSizeInBytes("SCANNER_BUFFER_SIZE")) - if maxbuf == 0 { - maxbuf = MaxScannerCapacity - } - scanner.Buffer(buf, maxbuf) - scanner.Split(t.ScanToken) - - var token string - for scanner.Scan() { - token = scanner.Text() - trim := token - for _, apply := range transform { - trim = apply(trim) - } - if len(trim) > 0 { - stats = append(stats, trim) - } - } - err = scanner.Err() - if err != nil { - err = errors.Errorf("%w\nAfter statement %d: %s", err, len(stats), token) - } - if errors.Is(err, bufio.ErrTooLong) { - err = errors.Errorf("%w\nTry setting SUPABASE_SCANNER_BUFFER_SIZE=5MB (current size is %dKB)", err, maxbuf>>10) - } - return stats, err -} - -func SplitAndTrim(sql io.Reader) (stats []string, err error) { - return Split(sql, func(token string) string { - return strings.TrimRight(token, ";") - }, strings.TrimSpace) -} diff --git a/apps/cli-go/pkg/parser/token_test.go b/apps/cli-go/pkg/parser/token_test.go deleted file mode 100644 index 566b11867b..0000000000 --- a/apps/cli-go/pkg/parser/token_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package parser - -import ( - "bufio" - _ "embed" - "io/fs" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestSplit(t *testing.T) { - const testdata = "testdata" - - var fixture []string - require.NoError(t, filepath.WalkDir(testdata, func(path string, f fs.DirEntry, err error) error { - if err != nil { - return err - } - if !strings.HasPrefix(f.Name(), "split_") { - return nil - } - contents, err := os.ReadFile(path) - if err != nil { - return err - } - fixture = append(fixture, string(contents)) - return nil - })) - require.Len(t, fixture, 18) - fixture = append(fixture, "\n") - - sql, err := os.Open(filepath.Join(testdata, "all.sql")) - require.NoError(t, err) - stats, err := Split(sql) - require.NoError(t, err) - - assert.ElementsMatch(t, fixture, stats) -} - -func TestSplitAndTrim(t *testing.T) { - sql := "\tBEGIN; " + strings.Repeat("a", MaxScannerCapacity) - stats, err := SplitAndTrim(strings.NewReader(sql)) - // Check error - assert.ErrorIs(t, err, bufio.ErrTooLong) - assert.ErrorContains(t, err, "After statement 1: \tBEGIN;") - assert.ElementsMatch(t, []string{"BEGIN"}, stats) -} diff --git a/apps/cli-go/pkg/pgtest/mock.go b/apps/cli-go/pkg/pgtest/mock.go deleted file mode 100644 index 999115b051..0000000000 --- a/apps/cli-go/pkg/pgtest/mock.go +++ /dev/null @@ -1,331 +0,0 @@ -package pgtest - -import ( - "context" - "fmt" - "net" - "reflect" - "testing" - "time" - - "github.com/jackc/pgmock" - "github.com/jackc/pgproto3/v2" - "github.com/jackc/pgtype" - "github.com/jackc/pgx/v4" - "github.com/supabase/cli/pkg/pgxv5" - "google.golang.org/grpc/test/bufconn" -) - -type MockConn struct { - client *pgx.Conn - - // Duplex server listener backed by in-memory buffer - server *bufconn.Listener - - // Mock server requests and responses - script pgmock.Script - - // Status parameters emitted by postgres on first connect - status map[string]string - - // Channel for reporting all server error - errChan chan error -} - -func (r *MockConn) getStartupMessage(config *pgx.ConnConfig) []pgmock.Step { - var steps []pgmock.Step - // Add auth message - params := map[string]string{"user": config.User} - if len(config.Database) > 0 { - params["database"] = config.Database - } - steps = append( - steps, - pgmock.ExpectMessage(&pgproto3.StartupMessage{ - ProtocolVersion: pgproto3.ProtocolVersionNumber, - Parameters: params, - }), - pgmock.SendMessage(&pgproto3.AuthenticationOk{}), - ) - // Add status message - r.status["session_authorization"] = config.User - for key, value := range r.status { - steps = append(steps, pgmock.SendMessage(&pgproto3.ParameterStatus{Name: key, Value: value})) - } - // Add ready message - steps = append( - steps, - pgmock.SendMessage(&pgproto3.BackendKeyData{ProcessID: 0, SecretKey: 0}), - pgmock.SendMessage(&pgproto3.ReadyForQuery{TxStatus: 'I'}), - ) - return steps -} - -// Configures pgx to use the mock dialer. -// -// The mock dialer provides a full duplex net.Conn backed by an in-memory buffer. -// It is implemented by grcp/test/bufconn package. -func (r *MockConn) Intercept(config *pgx.ConnConfig) { - // Override config for test - config.DialFunc = func(ctx context.Context, network, addr string) (net.Conn, error) { - return r.server.DialContext(ctx) - } - config.LookupFunc = func(ctx context.Context, host string) (addrs []string, err error) { - return []string{"127.0.0.1"}, nil - } - config.TLSConfig = nil - // Add startup message - r.script.Steps = append(r.getStartupMessage(config), r.script.Steps...) -} - -// Adds a simple query or prepared statement to the mock connection. -func (r *MockConn) Query(sql string, args ...any) *MockConn { - var oids []uint32 - var params [][]byte - for _, v := range args { - if value, oid := r.encodeValueArg(v); oid > 0 { - params = append(params, value) - oids = append(oids, oid) - } - } - r.script.Steps = append(r.script.Steps, ExpectQuery(sql, params, oids)) - return r -} - -// SimpleQuery adds a simple-protocol query to the mock connection. -func (r *MockConn) SimpleQuery(sql string) *MockConn { - r.script.Steps = append(r.script.Steps, ExpectSimpleQuery(sql)) - return r -} - -func (r *MockConn) encodeValueArg(v any) (value []byte, oid uint32) { - if v == nil { - return nil, pgtype.TextArrayOID - } - dt, ok := ci.DataTypeForValue(v) - if !ok { - r.errChan <- fmt.Errorf("no suitable type for arg: %v", v) - return nil, 0 - } - if err := dt.Value.Set(v); err != nil { - r.errChan <- fmt.Errorf("failed to set value: %w", err) - return nil, 0 - } - var err error - switch dt.OID { - case pgtype.TextOID: - value, err = (dt.Value).(pgtype.TextEncoder).EncodeText(ci, []byte{}) - default: - value, err = (dt.Value).(pgtype.BinaryEncoder).EncodeBinary(ci, []byte{}) - } - if err != nil { - r.errChan <- fmt.Errorf("failed to encode arg: %w", err) - return nil, 0 - } - return value, dt.OID -} - -func getDataTypeSize(v any) int16 { - t := reflect.TypeOf(v) - k := t.Kind() - if k < reflect.Int || k > reflect.Complex128 { - return -1 - } - return int16(t.Size()) -} - -func (r *MockConn) lastQuery() *extendedQueryStep { - return r.script.Steps[len(r.script.Steps)-1].(*extendedQueryStep) -} - -// Adds a server reply using binary or text protocol format. -// -// TODO: support prepared statements when using binary protocol -func (r *MockConn) Reply(tag string, rows ...any) *MockConn { - q := r.lastQuery() - // Add field description - if len(rows) > 0 { - var desc pgproto3.RowDescription - if arr, ok := rows[0].([]any); ok { - for i, v := range arr { - name := fmt.Sprintf("c_%02d", i) - if fd := toFieldDescription(v); fd != nil { - fd.Name = []byte(name) - desc.Fields = append(desc.Fields, *fd) - } else { - r.errChan <- fmt.Errorf("failed to describe field: %s", name) - } - } - } else if t := reflect.TypeOf(rows[0]); t.Kind() == reflect.Struct { - s := reflect.ValueOf(rows[0]) - for i := 0; i < s.NumField(); i++ { - name := pgxv5.GetColumnName(t.Field(i)) - if len(name) == 0 { - continue - } - v := s.Field(i).Interface() - if fd := toFieldDescription(v); fd != nil { - fd.Name = []byte(name) - desc.Fields = append(desc.Fields, *fd) - } else { - r.errChan <- fmt.Errorf("failed to describe field: %s", name) - } - } - } else { - r.errChan <- fmt.Errorf("reply type must be one of [array, struct], found: %s", t.Kind()) - } - q.reply.Steps = append(q.reply.Steps, pgmock.SendMessage(&desc)) - } else { - // No data is optional, but we add for completeness - q.reply.Steps = append(q.reply.Steps, pgmock.SendMessage(&pgproto3.NoData{})) - } - // Add row data - for _, data := range rows { - var dr pgproto3.DataRow - if arr, ok := data.([]any); ok { - for _, v := range arr { - if value, oid := r.encodeValueArg(v); oid > 0 { - dr.Values = append(dr.Values, value) - } - } - } else if t := reflect.TypeOf(data); t.Kind() == reflect.Struct { - s := reflect.ValueOf(rows[0]) - for i := 0; i < s.NumField(); i++ { - if name := pgxv5.GetColumnName(t.Field(i)); len(name) == 0 { - continue - } - v := s.Field(i).Interface() - if value, oid := r.encodeValueArg(v); oid > 0 { - dr.Values = append(dr.Values, value) - } - } - } else { - r.errChan <- fmt.Errorf("invalid reply value: %v", data) - } - q.reply.Steps = append(q.reply.Steps, pgmock.SendMessage(&dr)) - } - // Add completion message - var complete pgproto3.BackendMessage - if tag == "" { - complete = &pgproto3.EmptyQueryResponse{} - } else { - complete = &pgproto3.CommandComplete{CommandTag: []byte(tag)} - } - q.reply.Steps = append(q.reply.Steps, pgmock.SendMessage(complete)) - return r -} - -func toFieldDescription(v any) *pgproto3.FieldDescription { - if dt, ok := ci.DataTypeForValue(v); ok { - size := getDataTypeSize(v) - format := ci.ParamFormatCodeForOID(dt.OID) - return &pgproto3.FieldDescription{ - TableOID: 17131, - TableAttributeNumber: 1, - DataTypeOID: dt.OID, - DataTypeSize: size, - TypeModifier: -1, - Format: format, - } - } - return nil -} - -// Simulates an error reply from the server. -// -// TODO: simulate a notice reply -func (r *MockConn) ReplyError(code, message string) *MockConn { - q := r.lastQuery() - q.reply.Steps = append( - q.reply.Steps, - pgmock.SendMessage(&pgproto3.ErrorResponse{ - Severity: "ERROR", - SeverityUnlocalized: "ERROR", - Code: code, - Message: message, - }), - ) - return r -} - -func (r *MockConn) Close(t *testing.T) { - if r.client != nil { - if err := r.client.Close(context.Background()); err != nil { - t.Errorf("failed to close client: %v", err) - } - } - for err := range r.errChan { - t.Errorf("pgmock error:\n%v", err) - } - if err := r.server.Close(); err != nil { - t.Fatalf("failed to close server: %v", err) - } -} - -func (r *MockConn) MockClient(t *testing.T, opts ...func(*pgx.ConnConfig)) *pgx.Conn { - if r.client != nil { - return r.client - } - opts = append(opts, r.Intercept, func(cc *pgx.ConnConfig) { - cc.ConnectTimeout = time.Second * 2 - }) - var err error - if r.client, err = pgxv5.Connect(context.Background(), "", opts...); err != nil { - t.Errorf("failed to connect: %v", err) - } - return r.client -} - -func NewWithStatus(status map[string]string) *MockConn { - const bufSize = 1024 * 1024 - mock := MockConn{ - server: bufconn.Listen(bufSize), - status: status, - errChan: make(chan error, 10), - } - // Start server in background - const timeout = time.Millisecond * 450 - go func() { - defer close(mock.errChan) - // Block until we've opened a TCP connection - conn, err := mock.server.Accept() - if err != nil { - mock.errChan <- err - return - } - defer conn.Close() - // Prevent server from hanging the test - err = conn.SetDeadline(time.Now().Add(timeout)) - if err != nil { - mock.errChan <- err - return - } - // Always expect clients to terminate the request - mock.script.Steps = append(mock.script.Steps, ExpectTerminate()) - err = mock.script.Run(pgproto3.NewBackend(pgproto3.NewChunkReader(conn), conn)) - if err != nil { - mock.errChan <- err - return - } - }() - - return &mock -} - -func NewConn() *MockConn { - status := map[string]string{ - "application_name": "", - "client_encoding": "UTF8", - "DateStyle": "ISO, MDY", - "default_transaction_read_only": "off", - "in_hot_standby": "off", - "integer_datetimes": "on", - "IntervalStyle": "postgres", - "is_superuser": "on", - "server_encoding": "UTF8", - "server_version": "14.3 (Debian 14.3-1.pgdg110+1)", - "standard_conforming_strings": "on", - "TimeZone": "UTC", - } - return NewWithStatus(status) -} diff --git a/apps/cli-go/pkg/pgtest/step.go b/apps/cli-go/pkg/pgtest/step.go deleted file mode 100644 index a13d20d890..0000000000 --- a/apps/cli-go/pkg/pgtest/step.go +++ /dev/null @@ -1,141 +0,0 @@ -package pgtest - -import ( - "reflect" - - "github.com/go-errors/errors" - "github.com/jackc/pgmock" - "github.com/jackc/pgproto3/v2" - "github.com/jackc/pgtype" -) - -var ci = pgtype.NewConnInfo() - -type extendedQueryStep struct { - sql string - params [][]byte - oids []uint32 - simpleOnly bool - reply pgmock.Script -} - -func (e *extendedQueryStep) Step(backend *pgproto3.Backend) error { - msg, err := getFrontendMessage(backend) - if err != nil { - return err - } - - // Handle simple query - want := &pgproto3.Query{String: e.sql} - if m, ok := msg.(*pgproto3.Query); ok && reflect.DeepEqual(m, want) { - e.reply.Steps = append(e.reply.Steps, pgmock.SendMessage(&pgproto3.ReadyForQuery{TxStatus: 'I'})) - return e.reply.Run(backend) - } - if e.simpleOnly { - return errors.Errorf("expected => %#v\nactual => %#v", want, msg) - } - - // Handle prepared statements, name can be dynamic: lrupsc_5_0 - if m, ok := msg.(*pgproto3.Parse); ok { - want := &pgproto3.Parse{Name: m.Name, Query: e.sql, ParameterOIDs: m.ParameterOIDs} - if !reflect.DeepEqual(m, want) { - return errors.Errorf("expected => %#v\nactual => %#v", want, m) - } - // Anonymous ps falls through - if m.Name != "" { - script := pgmock.Script{Steps: []pgmock.Step{ - pgmock.ExpectMessage(&pgproto3.Describe{ObjectType: 'S', Name: m.Name}), - pgmock.ExpectMessage(&pgproto3.Sync{}), - pgmock.SendMessage(&pgproto3.ParseComplete{}), - pgmock.SendMessage(&pgproto3.ParameterDescription{ParameterOIDs: e.oids}), - // Postgres responds pgproto3.RowDescription but it's optional for pgx - pgmock.SendMessage(&pgproto3.ReadyForQuery{TxStatus: 'I'}), - }} - if err := script.Run(backend); err != nil { - return err - } - } - // Expect bind command next - msg, err = backend.Receive() - if err != nil { - return err - } - } - - if m, ok := msg.(*pgproto3.Bind); ok { - var codes []int16 - for _, oid := range e.oids { - codes = append(codes, ci.ParamFormatCodeForOID(oid)) - } - want := &pgproto3.Bind{ - ParameterFormatCodes: codes, - Parameters: e.params, - ResultFormatCodes: []int16{}, - DestinationPortal: m.DestinationPortal, - PreparedStatement: m.PreparedStatement, - } - if !reflect.DeepEqual(m, want) { - return errors.Errorf("expected => %#v\nactual => %#v", want, msg) - } - e.reply.Steps = append([]pgmock.Step{ - pgmock.ExpectMessage(&pgproto3.Describe{ObjectType: 'P'}), - pgmock.ExpectMessage(&pgproto3.Execute{}), - pgmock.SendMessage(&pgproto3.ParseComplete{}), - pgmock.SendMessage(&pgproto3.BindComplete{}), - }, e.reply.Steps...) - return e.reply.Run(backend) - } - - return errors.Errorf("expected => %#v\nactual => %#v", want, msg) -} - -// Expects a SQL query in any form: simple, prepared, or anonymous. -func ExpectQuery(sql string, params [][]byte, oids []uint32) pgmock.Step { - return &extendedQueryStep{sql: sql, params: params, oids: oids} -} - -// ExpectSimpleQuery expects SQL through the simple query protocol. -func ExpectSimpleQuery(sql string) pgmock.Step { - return &extendedQueryStep{sql: sql, simpleOnly: true} -} - -type terminateStep struct{} - -func (e *terminateStep) Step(backend *pgproto3.Backend) error { - msg, err := getFrontendMessage(backend) - if err != nil { - return err - } - - // Handle simple query - if _, ok := msg.(*pgproto3.Terminate); ok { - return nil - } - - return errors.Errorf("expected => %#v\nactual => %#v", &pgproto3.Terminate{}, msg) -} - -func ExpectTerminate() pgmock.Step { - return &terminateStep{} -} - -func getFrontendMessage(backend *pgproto3.Backend) (pgproto3.FrontendMessage, error) { - msg, err := backend.Receive() - if err != nil { - return nil, err - } - - // Sync signals end of batch statements - if _, ok := msg.(*pgproto3.Sync); ok { - reply := pgmock.SendMessage(&pgproto3.ReadyForQuery{TxStatus: 'I'}) - if err := reply.Step(backend); err != nil { - return nil, err - } - msg, err = backend.Receive() - if err != nil { - return nil, err - } - } - - return msg, nil -} diff --git a/apps/cli-go/pkg/pgxv5/connect.go b/apps/cli-go/pkg/pgxv5/connect.go deleted file mode 100644 index 4ae754f24e..0000000000 --- a/apps/cli-go/pkg/pgxv5/connect.go +++ /dev/null @@ -1,42 +0,0 @@ -package pgxv5 - -import ( - "context" - "fmt" - "os" - "strings" - - "github.com/go-errors/errors" - "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4" -) - -// Extends pgx.Connect with support for programmatically overriding parsed config -func Connect(ctx context.Context, connString string, options ...func(*pgx.ConnConfig)) (*pgx.Conn, error) { - // Parse connection url - config, err := pgx.ParseConfig(connString) - if err != nil { - return nil, errors.Errorf("failed to parse connection string: %w", err) - } - config.OnNotice = func(pc *pgconn.PgConn, n *pgconn.Notice) { - if !shouldIgnore(n.Message) { - fmt.Fprintf(os.Stderr, "%s (%s): %s\n", n.Severity, n.Code, n.Message) - } - } - // Apply config overrides - for _, op := range options { - op(config) - } - // Connect to database - conn, err := pgx.ConnectConfig(ctx, config) - if err != nil { - return nil, errors.Errorf("failed to connect to postgres: %w", err) - } - return conn, nil -} - -func shouldIgnore(msg string) bool { - return strings.Contains(msg, `schema "supabase_migrations" already exists`) || - strings.Contains(msg, `relation "schema_migrations" already exists`) || - strings.Contains(msg, `relation "seed_files" already exists`) -} diff --git a/apps/cli-go/pkg/pgxv5/rows.go b/apps/cli-go/pkg/pgxv5/rows.go deleted file mode 100644 index 96aa16f5ac..0000000000 --- a/apps/cli-go/pkg/pgxv5/rows.go +++ /dev/null @@ -1,132 +0,0 @@ -// Backported from pgx/v5: https://github.com/jackc/pgx/blob/v5.3.1/rows.go#L408 -package pgxv5 - -import ( - "reflect" - "strings" - - "github.com/go-errors/errors" - "github.com/jackc/pgproto3/v2" - "github.com/jackc/pgx/v4" -) - -func CollectStrings(rows pgx.Rows) ([]string, error) { - defer rows.Close() - result := []string{} - for rows.Next() { - var version string - if err := rows.Scan(&version); err != nil { - return nil, errors.Errorf("failed to scan rows: %w", err) - } - result = append(result, version) - } - if err := rows.Err(); err != nil { - return nil, errors.Errorf("failed to parse rows: %w", err) - } - return result, nil -} - -// CollectRows iterates through rows, calling fn for each row, and collecting the results into a slice of T. -func CollectRows[T any](rows pgx.Rows) ([]T, error) { - defer rows.Close() - - slice := []T{} - - for rows.Next() { - var value T - if err := ScanRowToStruct(rows, &value); err != nil { - return nil, err - } - slice = append(slice, value) - } - - if err := rows.Err(); err != nil { - return nil, errors.Errorf("failed to collect rows: %w", err) - } - - return slice, nil -} - -func ScanRowToStruct(rows pgx.Rows, dst any) error { - dstValue := reflect.ValueOf(dst) - if dstValue.Kind() != reflect.Ptr { - return errors.Errorf("dst not a pointer") - } - - dstElemValue := dstValue.Elem() - scanTargets, err := appendScanTargets(dstElemValue, nil, rows.FieldDescriptions()) - if err != nil { - return err - } - - for i, t := range scanTargets { - if t == nil { - return errors.Errorf("struct doesn't have corresponding row field %s", rows.FieldDescriptions()[i].Name) - } - } - - if err := rows.Scan(scanTargets...); err != nil { - return errors.Errorf("failed to scan targets: %w", err) - } - return nil -} - -const structTagKey = "db" - -func fieldPosByName(fldDescs []pgproto3.FieldDescription, field string) (i int) { - i = -1 - for i, desc := range fldDescs { - if strings.EqualFold(string(desc.Name), field) { - return i - } - } - return -} - -func appendScanTargets(dstElemValue reflect.Value, scanTargets []any, fldDescs []pgproto3.FieldDescription) ([]any, error) { - var err error - dstElemType := dstElemValue.Type() - - if scanTargets == nil { - scanTargets = make([]any, len(fldDescs)) - } - - for i := 0; i < dstElemType.NumField(); i++ { - sf := dstElemType.Field(i) - if sf.PkgPath != "" && !sf.Anonymous { - // Field is unexported, skip it. - continue - } - // Handle anoymous struct embedding, but do not try to handle embedded pointers. - if sf.Anonymous && sf.Type.Kind() == reflect.Struct { - scanTargets, err = appendScanTargets(dstElemValue.Field(i), scanTargets, fldDescs) - if err != nil { - return nil, err - } - } else { - colName := GetColumnName(sf) - if len(colName) == 0 { - // Field is ignored, skip it. - continue - } - fpos := fieldPosByName(fldDescs, colName) - if fpos == -1 || fpos >= len(scanTargets) { - return nil, errors.Errorf("cannot find field %s in returned row", colName) - } - scanTargets[fpos] = dstElemValue.Field(i).Addr().Interface() - } - } - - return scanTargets, err -} - -func GetColumnName(sf reflect.StructField) string { - dbTag, dbTagPresent := sf.Tag.Lookup(structTagKey) - if !dbTagPresent { - return sf.Name - } - if dbTag = strings.Split(dbTag, ",")[0]; dbTag != "-" { - return dbTag - } - return "" -} diff --git a/apps/cli-go/pkg/queue/queue.go b/apps/cli-go/pkg/queue/queue.go deleted file mode 100644 index e427e01c53..0000000000 --- a/apps/cli-go/pkg/queue/queue.go +++ /dev/null @@ -1,57 +0,0 @@ -package queue - -import ( - "sync" - - "github.com/go-errors/errors" -) - -// JobQueue implements a background job processor using a single channel and wait group. -// -// The channel is initialised with a maximum number of concurrent workers. Adding a new job -// consumes a worker from the channel. After finishing a job, the worker is added back to -// the channel. When all workers are consumed, adding new job will block. -// -// Example usage: -// -// jq := NewJobQueue(5) -// err := jq.Put(func() error { -// return nil -// }) -// errors.Join(err, jq.Collect()) -type JobQueue struct { - wg sync.WaitGroup - errCh chan error -} - -func NewJobQueue(maxConcurrency uint) *JobQueue { - q := &JobQueue{ - errCh: make(chan error, maxConcurrency), - } - for i := 0; i < cap(q.errCh); i++ { - q.errCh <- nil - } - return q -} - -// Put runs a job in the background, returning any error from the previous job. -func (q *JobQueue) Put(job func() error) error { - err := <-q.errCh - q.wg.Add(1) - go func() { - defer q.wg.Done() - q.errCh <- job() - }() - return err -} - -// Collect waits for all jobs to finish, returning any errors. -func (q *JobQueue) Collect() error { - q.wg.Wait() - var err []error - for i := 0; i < cap(q.errCh); i++ { - err = append(err, <-q.errCh) - q.errCh <- nil - } - return errors.Join(err...) -} diff --git a/apps/cli-go/pkg/storage/analytics.go b/apps/cli-go/pkg/storage/analytics.go deleted file mode 100644 index 12f2abe013..0000000000 --- a/apps/cli-go/pkg/storage/analytics.go +++ /dev/null @@ -1,68 +0,0 @@ -package storage - -import ( - "context" - "fmt" - "net/http" - "os" - - "github.com/supabase/cli/pkg/fetcher" -) - -type AnalyticsBucketResponse struct { - Id string `json:"id"` // "test" - Name string `json:"name"` // "test" - CreatedAt string `json:"created_at"` // "2023-10-13T17:48:58.491Z" - UpdatedAt string `json:"updated_at"` // "2023-10-13T17:48:58.491Z" -} - -type CreateAnalyticsBucketRequest struct { - BucketName string `json:"bucketName"` -} - -func (s *StorageAPI) UpsertAnalyticsBuckets(ctx context.Context, bucketConfig map[string]struct{}, filter ...func(string) bool) error { - resp, err := s.Send(ctx, http.MethodGet, "/storage/v1/iceberg/bucket", nil) - if err != nil { - return err - } - buckets, err := fetcher.ParseJSON[[]AnalyticsBucketResponse](resp.Body) - if err != nil { - return err - } - var toDelete []string - exists := make(map[string]struct{}, len(buckets)) - for _, b := range buckets { - exists[b.Name] = struct{}{} - if _, ok := bucketConfig[b.Name]; !ok { - toDelete = append(toDelete, b.Name) - } - } - for name := range bucketConfig { - if _, ok := exists[name]; ok { - fmt.Fprintln(os.Stderr, "Bucket already exists:", name) - continue - } - fmt.Fprintln(os.Stderr, "Creating analytics bucket:", name) - body := CreateAnalyticsBucketRequest{BucketName: name} - if resp, err := s.Send(ctx, http.MethodPost, "/storage/v1/iceberg/bucket", body); err != nil { - return err - } else if err := resp.Body.Close(); err != nil { - fmt.Fprintln(os.Stderr, err) - } - } -OUTER: - for _, name := range toDelete { - for _, keep := range filter { - if !keep(name) { - continue OUTER - } - } - fmt.Fprintln(os.Stderr, "Pruning analytics bucket:", name) - if resp, err := s.Send(ctx, http.MethodDelete, "/storage/v1/iceberg/bucket/"+name, nil); err != nil { - return err - } else if err := resp.Body.Close(); err != nil { - fmt.Fprintln(os.Stderr, err) - } - } - return nil -} diff --git a/apps/cli-go/pkg/storage/api.go b/apps/cli-go/pkg/storage/api.go deleted file mode 100644 index a2af3588e3..0000000000 --- a/apps/cli-go/pkg/storage/api.go +++ /dev/null @@ -1,11 +0,0 @@ -package storage - -import "github.com/supabase/cli/pkg/fetcher" - -type StorageAPI struct { - *fetcher.Fetcher -} - -const PAGE_LIMIT = 100 - -const DELETE_OBJECTS_LIMIT = 1000 diff --git a/apps/cli-go/pkg/storage/batch.go b/apps/cli-go/pkg/storage/batch.go deleted file mode 100644 index bb6dad0b5d..0000000000 --- a/apps/cli-go/pkg/storage/batch.go +++ /dev/null @@ -1,131 +0,0 @@ -package storage - -import ( - "context" - "fmt" - "io/fs" - "os" - "path" - "path/filepath" - - "github.com/go-errors/errors" - "github.com/supabase/cli/pkg/config" - "github.com/supabase/cli/pkg/queue" -) - -func (s *StorageAPI) UpsertBuckets(ctx context.Context, bucketConfig config.BucketConfig, filter ...func(string) bool) error { - buckets, err := s.ListBuckets(ctx) - if err != nil { - return err - } - exists := make(map[string]string, len(buckets)) - for _, b := range buckets { - exists[b.Name] = b.Id - } -OUTER: - for name, bucket := range bucketConfig { - // Update bucket properties if already exists - if bucketId, ok := exists[name]; ok { - for _, keep := range filter { - if !keep(bucketId) { - continue OUTER - } - } - fmt.Fprintln(os.Stderr, "Updating Storage bucket:", bucketId) - body := UpdateBucketRequest{ - Id: bucketId, - Public: bucket.Public, - FileSizeLimit: int64(bucket.FileSizeLimit), - AllowedMimeTypes: bucket.AllowedMimeTypes, - } - if _, err := s.UpdateBucket(ctx, body); err != nil { - return err - } - } else { - fmt.Fprintln(os.Stderr, "Creating Storage bucket:", name) - body := CreateBucketRequest{ - Name: name, - Public: bucket.Public, - FileSizeLimit: int64(bucket.FileSizeLimit), - AllowedMimeTypes: bucket.AllowedMimeTypes, - } - if _, err := s.CreateBucket(ctx, body); err != nil { - return err - } - } - } - return nil -} - -type UploadOptions struct { - MaxConcurrency uint - KeyPrefix string -} - -func isUploadableEntry(fsys fs.FS, filePath string, info fs.DirEntry) bool { - if info.Type().IsRegular() { - return true - } - if info.Type().IsDir() { - return false - } - if info.Type()&fs.ModeSymlink != 0 { - f, err := fsys.Open(filePath) - if err != nil { - fmt.Fprintln(os.Stderr, "Skipping non-regular file:", filePath) - return false - } - defer f.Close() - stat, err := f.Stat() - if err != nil || !stat.Mode().IsRegular() { - fmt.Fprintln(os.Stderr, "Skipping non-regular file:", filePath) - return false - } - return true - } - fmt.Fprintln(os.Stderr, "Skipping non-regular file:", filePath) - return false -} - -func (s *StorageAPI) UpsertObjects(ctx context.Context, bucketConfig config.BucketConfig, fsys fs.FS, opts ...func(*UploadOptions)) error { - uo := UploadOptions{MaxConcurrency: 5} - for _, apply := range opts { - apply(&uo) - } - jq := queue.NewJobQueue(uo.MaxConcurrency) - for name, bucket := range bucketConfig { - localPath := bucket.ObjectsPath - if len(localPath) == 0 { - continue - } - upload := func(filePath string, info fs.DirEntry, err error) error { - if err != nil { - return errors.New(err) - } - if !isUploadableEntry(fsys, filePath, info) { - return nil - } - dstPath := uo.KeyPrefix - relPath, err := filepath.Rel(localPath, filePath) - if err != nil { - return errors.Errorf("failed to resolve relative path: %w", err) - } else if relPath == "." { - // Copying single file - dstPath = path.Join(name, info.Name()) - } else { - dstPath = path.Join(name, filepath.ToSlash(relPath)) - } - fmt.Fprintln(os.Stderr, "Uploading:", filePath, "=>", dstPath) - job := func() error { - return s.UploadObject(ctx, dstPath, filePath, fsys, func(fo *FileOptions) { - fo.Overwrite = true - }) - } - return jq.Put(job) - } - if err := fs.WalkDir(fsys, localPath, upload); err != nil { - return err - } - } - return jq.Collect() -} diff --git a/apps/cli-go/pkg/storage/buckets.go b/apps/cli-go/pkg/storage/buckets.go deleted file mode 100644 index 1f4bf9cc83..0000000000 --- a/apps/cli-go/pkg/storage/buckets.go +++ /dev/null @@ -1,78 +0,0 @@ -package storage - -import ( - "context" - "net/http" - - "github.com/supabase/cli/pkg/fetcher" -) - -type BucketResponse struct { - Id string `json:"id"` // "test" - Name string `json:"name"` // "test" - Owner string `json:"owner"` // "" - Public bool `json:"public"` // true - FileSizeLimit *int `json:"file_size_limit"` // null - AllowedMimeTypes []string `json:"allowed_mime_types"` // null - CreatedAt string `json:"created_at"` // "2023-10-13T17:48:58.491Z" - UpdatedAt string `json:"updated_at"` // "2023-10-13T17:48:58.491Z" -} - -func (s *StorageAPI) ListBuckets(ctx context.Context) ([]BucketResponse, error) { - resp, err := s.Send(ctx, http.MethodGet, "/storage/v1/bucket", nil) - if err != nil { - return nil, err - } - return fetcher.ParseJSON[[]BucketResponse](resp.Body) -} - -type CreateBucketRequest struct { - Name string `json:"name"` // "string", - Id string `json:"id,omitempty"` // "string", - Public *bool `json:"public,omitempty"` // false, - FileSizeLimit int64 `json:"file_size_limit,omitempty"` // 0, - AllowedMimeTypes []string `json:"allowed_mime_types,omitempty"` // ["string"] -} - -type CreateBucketResponse struct { - Name string `json:"name"` -} - -func (s *StorageAPI) CreateBucket(ctx context.Context, body CreateBucketRequest) (CreateBucketResponse, error) { - resp, err := s.Send(ctx, http.MethodPost, "/storage/v1/bucket", body) - if err != nil { - return CreateBucketResponse{}, err - } - return fetcher.ParseJSON[CreateBucketResponse](resp.Body) -} - -type UpdateBucketRequest struct { - Id string `json:"-"` - Public *bool `json:"public,omitempty"` // false, - FileSizeLimit int64 `json:"file_size_limit,omitempty"` // 0, - AllowedMimeTypes []string `json:"allowed_mime_types,omitempty"` // ["string"] -} - -type UpdateBucketResponse struct { - Message string `json:"message"` -} - -func (s *StorageAPI) UpdateBucket(ctx context.Context, body UpdateBucketRequest) (UpdateBucketResponse, error) { - resp, err := s.Send(ctx, http.MethodPut, "/storage/v1/bucket/"+body.Id, body) - if err != nil { - return UpdateBucketResponse{}, err - } - return fetcher.ParseJSON[UpdateBucketResponse](resp.Body) -} - -type DeleteBucketResponse struct { - Message string `json:"message"` -} - -func (s *StorageAPI) DeleteBucket(ctx context.Context, bucketId string) (DeleteBucketResponse, error) { - resp, err := s.Send(ctx, http.MethodDelete, "/storage/v1/bucket/"+bucketId, nil) - if err != nil { - return DeleteBucketResponse{}, err - } - return fetcher.ParseJSON[DeleteBucketResponse](resp.Body) -} diff --git a/apps/cli-go/pkg/storage/objects.go b/apps/cli-go/pkg/storage/objects.go deleted file mode 100644 index f338a63b2a..0000000000 --- a/apps/cli-go/pkg/storage/objects.go +++ /dev/null @@ -1,219 +0,0 @@ -package storage - -import ( - "context" - "io" - "io/fs" - "mime" - "net/http" - "os" - "path" - "path/filepath" - "strings" - - "github.com/go-errors/errors" - "github.com/spf13/afero" - "github.com/supabase/cli/pkg/fetcher" -) - -type ListObjectsQuery struct { - Prefix string `json:"prefix"` - Search string `json:"search,omitempty"` - Limit int `json:"limit,omitempty"` - Offset int `json:"offset,omitempty"` -} - -type ObjectResponse struct { - Name string `json:"name"` // "abstract.pdf" - Id *string `json:"id"` // "9b7f9f48-17a6-4ca8-b14a-39b0205a63e9" - UpdatedAt *string `json:"updated_at"` // "2023-10-13T18:08:22.068Z" - CreatedAt *string `json:"created_at"` // "2023-10-13T18:08:22.068Z" - LastAccessedAt *string `json:"last_accessed_at"` // "2023-10-13T18:08:22.068Z" - Metadata *ObjectMetadata `json:"metadata"` // null -} - -type ObjectMetadata struct { - ETag string `json:"eTag"` // "\"887ea9be3c68e6f2fca7fd2d7c77d8fe\"" - Size int `json:"size"` // 82702 - Mimetype string `json:"mimetype"` // "application/pdf" - CacheControl string `json:"cacheControl"` // "max-age=3600" - LastModified string `json:"lastModified"` // "2023-10-13T18:08:22.000Z" - ContentLength int `json:"contentLength"` // 82702 - HttpStatusCode int `json:"httpStatusCode"` // 200 -} - -func (s *StorageAPI) ListObjects(ctx context.Context, bucket, prefix string, page int) ([]ObjectResponse, error) { - dir, name := path.Split(prefix) - query := ListObjectsQuery{ - Prefix: dir, - Search: name, - Limit: PAGE_LIMIT, - Offset: PAGE_LIMIT * page, - } - resp, err := s.Send(ctx, http.MethodPost, "/storage/v1/object/list/"+bucket, query) - if err != nil { - return nil, err - } - return fetcher.ParseJSON[[]ObjectResponse](resp.Body) -} - -type FileOptions struct { - CacheControl string - ContentType string - Overwrite bool -} - -func ParseFileOptions(f fs.File, opts ...func(*FileOptions)) (*FileOptions, error) { - // Customise file options - fo := &FileOptions{} - for _, apply := range opts { - apply(fo) - } - // Use default value of storage-js: https://github.com/supabase/storage-js/blob/main/src/packages/StorageFileApi.ts#L22 - if len(fo.CacheControl) == 0 { - fo.CacheControl = "max-age=3600" - } - // Decode mimetype - if len(fo.ContentType) == 0 { - header := io.LimitReader(f, 512) - buf, err := io.ReadAll(header) - if err != nil { - return nil, errors.Errorf("failed to read file: %w", err) - } - fo.ContentType = http.DetectContentType(buf) - if s, ok := f.(io.Seeker); !ok { - return nil, errors.Errorf("file is not seekable") - } else if _, err = s.Seek(0, io.SeekStart); err != nil { - return nil, errors.Errorf("failed to seek file: %w", err) - } - } - return fo, nil -} - -func (s *StorageAPI) UploadObject(ctx context.Context, remotePath, localPath string, fsys fs.FS, opts ...func(*FileOptions)) error { - f, err := fsys.Open(localPath) - if err != nil { - return errors.Errorf("failed to open file: %w", err) - } - defer f.Close() - fo, err := ParseFileOptions(f, opts...) - if err != nil { - return err - } - // For text/plain content types, we try to determine a more specific type - // based on the file extension, as the initial detection might be too generic - if strings.Contains(fo.ContentType, "text/plain") { - if extensionType := mime.TypeByExtension(filepath.Ext(localPath)); extensionType != "" { - fo.ContentType = extensionType - } - } - return s.UploadObjectStream(ctx, remotePath, f, *fo) -} - -func (s *StorageAPI) UploadObjectStream(ctx context.Context, remotePath string, localFile io.Reader, fo FileOptions) error { - headers := func(req *http.Request) { - if len(fo.ContentType) > 0 { - req.Header.Add("Content-Type", fo.ContentType) - } - if len(fo.CacheControl) > 0 { - req.Header.Add("Cache-Control", fo.CacheControl) - } - if fo.Overwrite { - req.Header.Add("x-upsert", "true") - } - } - // Prepare request - remotePath = strings.TrimPrefix(remotePath, "/") - resp, err := s.Send(ctx, http.MethodPost, "/storage/v1/object/"+remotePath, io.NopCloser(localFile), headers) - if err != nil { - return err - } - defer resp.Body.Close() - return nil -} - -func (s *StorageAPI) DownloadObject(ctx context.Context, remotePath, localPath string, fsys afero.Fs) error { - f, err := fsys.OpenFile(localPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) - if err != nil { - return errors.Errorf("failed to create file: %w", err) - } - defer f.Close() - return s.DownloadObjectStream(ctx, remotePath, f) -} - -func (s *StorageAPI) DownloadObjectStream(ctx context.Context, remotePath string, localFile io.Writer) error { - remotePath = strings.TrimPrefix(remotePath, "/") - resp, err := s.Send(ctx, http.MethodGet, "/storage/v1/object/"+remotePath, nil) - if err != nil { - return err - } - defer resp.Body.Close() - _, err = io.Copy(localFile, resp.Body) - return err -} - -type MoveObjectRequest struct { - BucketId string `json:"bucketId"` - SourceKey string `json:"sourceKey"` - DestinationKey string `json:"destinationKey"` -} - -type MoveObjectResponse = DeleteBucketResponse - -func (s *StorageAPI) MoveObject(ctx context.Context, bucketId, srcPath, dstPath string) (MoveObjectResponse, error) { - body := MoveObjectRequest{ - BucketId: bucketId, - SourceKey: srcPath, - DestinationKey: dstPath, - } - resp, err := s.Send(ctx, http.MethodPost, "/storage/v1/object/move", body) - if err != nil { - return MoveObjectResponse{}, err - } - return fetcher.ParseJSON[MoveObjectResponse](resp.Body) -} - -type CopyObjectRequest = MoveObjectRequest - -type CopyObjectResponse struct { - Key string `json:"key"` -} - -func (s *StorageAPI) CopyObject(ctx context.Context, bucketId, srcPath, dstPath string) (CopyObjectResponse, error) { - body := CopyObjectRequest{ - BucketId: bucketId, - SourceKey: srcPath, - DestinationKey: dstPath, - } - resp, err := s.Send(ctx, http.MethodPost, "/storage/v1/object/copy", body) - if err != nil { - return CopyObjectResponse{}, err - } - return fetcher.ParseJSON[CopyObjectResponse](resp.Body) -} - -type DeleteObjectsRequest struct { - Prefixes []string `json:"prefixes"` -} - -type DeleteObjectsResponse struct { - BucketId string `json:"bucket_id"` // "private" - Owner string `json:"owner"` // "" - OwnerId string `json:"owner_id"` // "" - Version string `json:"version"` // "cf5c5c53-ee73-4806-84e3-7d92c954b436" - Name string `json:"name"` // "abstract.pdf" - Id string `json:"id"` // "9b7f9f48-17a6-4ca8-b14a-39b0205a63e9" - UpdatedAt string `json:"updated_at"` // "2023-10-13T18:08:22.068Z" - CreatedAt string `json:"created_at"` // "2023-10-13T18:08:22.068Z" - LastAccessedAt string `json:"last_accessed_at"` // "2023-10-13T18:08:22.068Z" - Metadata ObjectMetadata `json:"metadata"` // null -} - -func (s *StorageAPI) DeleteObjects(ctx context.Context, bucket string, prefixes []string) ([]DeleteObjectsResponse, error) { - body := DeleteObjectsRequest{Prefixes: prefixes} - resp, err := s.Send(ctx, http.MethodDelete, "/storage/v1/object/"+bucket, body) - if err != nil { - return nil, err - } - return fetcher.ParseJSON[[]DeleteObjectsResponse](resp.Body) -} diff --git a/apps/cli-go/pkg/storage/objects_test.go b/apps/cli-go/pkg/storage/objects_test.go deleted file mode 100644 index 8983495097..0000000000 --- a/apps/cli-go/pkg/storage/objects_test.go +++ /dev/null @@ -1,178 +0,0 @@ -package storage - -import ( - "bytes" - "context" - "io" - "mime" - "net/http" - "os" - "path/filepath" - "testing" - fs "testing/fstest" - - "github.com/h2non/gock" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/supabase/cli/pkg/config" - "github.com/supabase/cli/pkg/fetcher" -) - -var mockApi = StorageAPI{Fetcher: fetcher.NewFetcher( - "http://127.0.0.1", -)} - -func TestParseFileOptionsContentTypeDetection(t *testing.T) { - tests := []struct { - name string - content []byte - filename string - opts []func(*FileOptions) - wantMimeType string - wantCacheCtrl string - }{ - { - name: "detects PNG image", - content: []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, // PNG header - filename: "test.image", - wantMimeType: "image/png", - wantCacheCtrl: "max-age=3600", - }, - { - name: "detects JavaScript file", - content: []byte("const hello = () => console.log('Hello, World!');"), - filename: "script.js", - wantMimeType: mime.TypeByExtension(".js"), - wantCacheCtrl: "max-age=3600", - }, - { - name: "detects CSS file", - content: []byte(".header { color: #333; font-size: 16px; }"), - filename: "styles.css", - wantMimeType: mime.TypeByExtension(".css"), - wantCacheCtrl: "max-age=3600", - }, - { - name: "detects SQL file", - content: []byte("SELECT * FROM users WHERE id = 1;"), - filename: "query.sql", - wantMimeType: mime.TypeByExtension(".sql"), - wantCacheCtrl: "max-age=3600", - }, - { - name: "use text/plain as fallback for unrecognized extensions", - content: []byte("const hello = () => console.log('Hello, World!');"), - filename: "main.nonexistent", - wantMimeType: "text/plain; charset=utf-8", - wantCacheCtrl: "max-age=3600", - }, - { - name: "respects custom content type", - content: []byte("const hello = () => console.log('Hello, World!');"), - filename: "custom.js", - wantMimeType: "application/custom", - wantCacheCtrl: "max-age=3600", - opts: []func(*FileOptions){func(fo *FileOptions) { fo.ContentType = "application/custom" }}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create a temporary file with test content - fsys := fs.MapFS{tt.filename: &fs.MapFile{Data: tt.content}} - // Setup mock api - defer gock.OffAll() - gock.New("http://127.0.0.1"). - Post("/storage/v1/object/"+tt.filename). - MatchHeader("Content-Type", tt.wantMimeType). - MatchHeader("Cache-Control", tt.wantCacheCtrl). - Reply(http.StatusOK) - // Parse options - err := mockApi.UploadObject(context.Background(), tt.filename, tt.filename, fsys, tt.opts...) - // Assert results - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - } -} - -func TestUpsertObjects(t *testing.T) { - t.Run("uploads regular files", func(t *testing.T) { - defer gock.OffAll() - gock.New("http://127.0.0.1"). - Post("/storage/v1/object/uploads/pizza.jpg"). - Reply(http.StatusOK) - fsys := fs.MapFS{ - "seeds/pizza.jpg": &fs.MapFile{Data: []byte("image")}, - } - err := mockApi.UpsertObjects(context.Background(), config.BucketConfig{ - "uploads": {ObjectsPath: "seeds"}, - }, fsys) - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("uploads symlinked files", func(t *testing.T) { - tmpDir := t.TempDir() - fixturesDir := filepath.Join(tmpDir, "fixtures") - seedsDir := filepath.Join(tmpDir, "seeds") - require.NoError(t, os.MkdirAll(fixturesDir, 0o755)) - require.NoError(t, os.MkdirAll(seedsDir, 0o755)) - require.NoError(t, os.WriteFile(filepath.Join(fixturesDir, "pizza.jpg"), []byte("image"), 0o600)) - require.NoError(t, os.Symlink(filepath.Join(fixturesDir, "pizza.jpg"), filepath.Join(seedsDir, "pizza.jpg"))) - - defer gock.OffAll() - gock.New("http://127.0.0.1"). - Post("/storage/v1/object/uploads/pizza.jpg"). - Reply(http.StatusOK) - err := mockApi.UpsertObjects(context.Background(), config.BucketConfig{ - "uploads": {ObjectsPath: "seeds"}, - }, os.DirFS(tmpDir)) - assert.NoError(t, err) - assert.Empty(t, gock.Pending()) - assert.Empty(t, gock.GetUnmatchedRequests()) - }) - - t.Run("skips broken symlinks with warning", func(t *testing.T) { - tmpDir := t.TempDir() - seedsDir := filepath.Join(tmpDir, "seeds") - require.NoError(t, os.MkdirAll(seedsDir, 0o755)) - require.NoError(t, os.Symlink(filepath.Join(tmpDir, "missing.jpg"), filepath.Join(seedsDir, "pizza.jpg"))) - - stderr := captureStderr(t, func() { - err := mockApi.UpsertObjects(context.Background(), config.BucketConfig{ - "uploads": {ObjectsPath: "seeds"}, - }, os.DirFS(tmpDir)) - assert.NoError(t, err) - }) - assert.Contains(t, stderr, "Skipping non-regular file:") - assert.Contains(t, stderr, "seeds/pizza.jpg") - }) -} - -func captureStderr(t *testing.T, fn func()) string { - t.Helper() - reader, writer, err := os.Pipe() - require.NoError(t, err) - original := os.Stderr - os.Stderr = writer - t.Cleanup(func() { - os.Stderr = original - _ = reader.Close() - _ = writer.Close() - }) - done := make(chan struct{}) - var buf bytes.Buffer - go func() { - _, _ = io.Copy(&buf, reader) - close(done) - }() - fn() - closeErr := writer.Close() - os.Stderr = original - <-done - require.NoError(t, closeErr) - return buf.String() -} diff --git a/apps/cli-go/pkg/storage/vector.go b/apps/cli-go/pkg/storage/vector.go deleted file mode 100644 index 7218f7b28b..0000000000 --- a/apps/cli-go/pkg/storage/vector.go +++ /dev/null @@ -1,81 +0,0 @@ -package storage - -import ( - "context" - "fmt" - "net/http" - "os" - - "github.com/supabase/cli/pkg/fetcher" -) - -type VectorBucket struct { - VectorBucketName string `json:"vectorBucketName"` - CreationTime uint64 `json:"creationTime"` -} - -type ListVectorBucketsResponse struct { - VectorBuckets []VectorBucket `json:"vectorBuckets"` -} - -type ListVectorBucketsRequest struct { - MaxResults uint64 `json:"maxResults,omitempty"` - NextToken string `json:"nextToken,omitempty"` - Prefix string `json:"prefix,omitempty"` -} - -type CreateVectorBucketRequest struct { - VectorBucketName string `json:"vectorBucketName"` -} - -type DeleteVectorBucketRequest struct { - VectorBucketName string `json:"vectorBucketName"` -} - -func (s *StorageAPI) UpsertVectorBuckets(ctx context.Context, bucketConfig map[string]struct{}, filter ...func(string) bool) error { - resp, err := s.Send(ctx, http.MethodPost, "/storage/v1/vector/ListVectorBuckets", ListVectorBucketsRequest{}) - if err != nil { - return err - } - result, err := fetcher.ParseJSON[ListVectorBucketsResponse](resp.Body) - if err != nil { - return err - } - var toDelete []string - exists := make(map[string]struct{}, len(result.VectorBuckets)) - for _, b := range result.VectorBuckets { - exists[b.VectorBucketName] = struct{}{} - if _, ok := bucketConfig[b.VectorBucketName]; !ok { - toDelete = append(toDelete, b.VectorBucketName) - } - } - for name := range bucketConfig { - if _, ok := exists[name]; ok { - fmt.Fprintln(os.Stderr, "Bucket already exists:", name) - continue - } - fmt.Fprintln(os.Stderr, "Creating vector bucket:", name) - body := CreateVectorBucketRequest{VectorBucketName: name} - if resp, err := s.Send(ctx, http.MethodPost, "/storage/v1/vector/CreateVectorBucket", body); err != nil { - return err - } else if err := resp.Body.Close(); err != nil { - fmt.Fprintln(os.Stderr, err) - } - } -OUTER: - for _, name := range toDelete { - for _, keep := range filter { - if !keep(name) { - continue OUTER - } - } - fmt.Fprintln(os.Stderr, "Pruning vector bucket:", name) - body := DeleteVectorBucketRequest{VectorBucketName: name} - if resp, err := s.Send(ctx, http.MethodPost, "/storage/v1/vector/DeleteVectorBucket", body); err != nil { - return err - } else if err := resp.Body.Close(); err != nil { - fmt.Fprintln(os.Stderr, err) - } - } - return nil -} diff --git a/apps/cli-go/pkg/vault/batch.go b/apps/cli-go/pkg/vault/batch.go deleted file mode 100644 index 8f44bb797e..0000000000 --- a/apps/cli-go/pkg/vault/batch.go +++ /dev/null @@ -1,60 +0,0 @@ -package vault - -import ( - "context" - "fmt" - "os" - - "github.com/go-errors/errors" - "github.com/jackc/pgx/v4" - "github.com/supabase/cli/pkg/config" - "github.com/supabase/cli/pkg/pgxv5" -) - -const ( - CREATE_VAULT_KV = "SELECT vault.create_secret($1, $2)" - READ_VAULT_KV = "SELECT id, name FROM vault.secrets WHERE name = ANY($1)" - UPDATE_VAULT_KV = "SELECT vault.update_secret($1, $2)" -) - -type VaultTable struct { - Id string - Name string -} - -func UpsertVaultSecrets(ctx context.Context, secrets map[string]config.Secret, conn *pgx.Conn) error { - var keys []string - toInsert := map[string]string{} - for k, v := range secrets { - if len(v.SHA256) > 0 { - keys = append(keys, k) - toInsert[k] = v.Value - } - } - if len(keys) == 0 { - return nil - } - fmt.Fprintln(os.Stderr, "Updating vault secrets...") - rows, err := conn.Query(ctx, READ_VAULT_KV, keys) - if err != nil { - return errors.Errorf("failed to read vault: %w", err) - } - toUpdate, err := pgxv5.CollectRows[VaultTable](rows) - if err != nil { - return err - } - batch := pgx.Batch{} - for _, r := range toUpdate { - secret := secrets[r.Name] - batch.Queue(UPDATE_VAULT_KV, r.Id, secret.Value) - delete(toInsert, r.Name) - } - // Remaining secrets should be created - for k, v := range toInsert { - batch.Queue(CREATE_VAULT_KV, v, k) - } - if err := conn.SendBatch(ctx, &batch).Close(); err != nil { - return errors.Errorf("failed to update vault: %w", err) - } - return nil -} diff --git a/apps/cli-go/tools/dump_initial_schema.sh b/apps/cli-go/tools/dump_initial_schema.sh deleted file mode 100755 index 1e07e1fcc2..0000000000 --- a/apps/cli-go/tools/dump_initial_schema.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# pg_dumpall -d "$DB_URL" -gf global.sql --no-role-passwords - -# Explanation of special flags: -# -# --column-inserts include migration history, but not pgsodium key -pg_dump \ - --column-inserts \ - --exclude-table-data 'pgsodium.key' \ - -d 'postgres://supabase_admin:postgres@127.0.0.1:54322/postgres' \ -| sed 's/^CREATE SCHEMA /CREATE SCHEMA IF NOT EXISTS /' \ -| sed 's/^CREATE TABLE /CREATE TABLE IF NOT EXISTS /' \ -| sed 's/^CREATE SEQUENCE /CREATE SEQUENCE IF NOT EXISTS /' \ -| sed 's/^CREATE VIEW /CREATE OR REPLACE VIEW /' \ -| sed 's/^CREATE FUNCTION /CREATE OR REPLACE FUNCTION /' \ -| sed 's/^GRANT ALL ON FUNCTION graphql_public./-- GRANT ALL ON FUNCTION graphql_public./' - -echo "DROP EXTENSION pg_graphql; CREATE EXTENSION pg_graphql WITH SCHEMA graphql;" - -# Reset session config generated by pg_dump -echo "RESET ALL;" diff --git a/apps/cli-go/tools/listdep/main.go b/apps/cli-go/tools/listdep/main.go deleted file mode 100644 index db72b7d429..0000000000 --- a/apps/cli-go/tools/listdep/main.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - "encoding/json" - "log" - "os" - "strings" - - "github.com/supabase/cli/pkg/config" -) - -func main() { - external := make([]string, 0) - for _, img := range config.Images.Services() { - if !strings.HasPrefix(img, "supabase/") || - strings.HasPrefix(img, "supabase/logflare") { - external = append(external, img) - } - } - - enc := json.NewEncoder(os.Stdout) - if err := enc.Encode(external); err != nil { - log.Fatal(err) - } -} diff --git a/apps/cli/AGENTS.md b/apps/cli/AGENTS.md index 99426185fb..3b2e6251df 100644 --- a/apps/cli/AGENTS.md +++ b/apps/cli/AGENTS.md @@ -100,15 +100,6 @@ compatibility decision in the PR. For the Compute transition, no local compatibi migrations are required. Compute error tags intentionally start new fingerprints; server-owned contracts remain unchanged. Update tests, generated schemas, and side-effect documentation. -## Go delegation - -- TypeScript owns CLI behavior. Consult `apps/cli-go` only for existing delegated - operations listed in [the delegation document](docs/go-cli-porting-status.md); do not expand delegation. -- Native replacements must preserve the public command, flags, output, side effects, - and exit behavior. Update the delegation document when removing a Go dependency. -- Emit command telemetry exactly once: bare proxies rely on Go telemetry; - instrumented TypeScript handlers suppress child telemetry. - ## Telemetry > An error’s tag is the PostHog `error_fingerprint` identity (`tag:`); preserve it when diff --git a/apps/cli/README.md b/apps/cli/README.md index 5baad3fd81..6799f46b86 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -14,18 +14,12 @@ This workspace contains: This workspace contains the stable, shipped `supabase` CLI. Earlier revisions carried two shells (a `legacy/` tree and an experimental `next/` tree); both are gone and `src/` is the single CLI tree. -For current migration/parity status, see: - -- [`docs/go-cli-porting-status.md`](./docs/go-cli-porting-status.md) — the residual Go delegation surface -- [`docs/go-cli-divergences.md`](./docs/go-cli-divergences.md) — TS-only flags and behavioral divergences from the old Go CLI - For the generated command/reference docs, see: -- [`docs/go-cli-reference.md`](./docs/go-cli-reference.md) - [`docs/supabase-home.md`](./docs/supabase-home.md) - [`../../packages/stack/docs/service-versioning.md`](../../packages/stack/docs/service-versioning.md) -The README is intentionally brief. Command details should live in the generated docs and the parity tracker above. +The README is intentionally brief. Command details should live in the generated docs above. ## Run From Source @@ -42,9 +36,7 @@ Examples: pnpm dev -- hello ``` -### CLI and the Go binary - -Phase 0 commands in the CLI proxy to the Go CLI binary. To run these commands from source you need `supabase` (the Go CLI) available on your PATH. +### Running from source For convenience, create a shell alias instead of using `pnpm dev` directly. For example in `.zshrc`: @@ -52,19 +44,6 @@ For convenience, create a shell alias instead of using `pnpm dev` directly. For alias supabase-dev="bun /absolute/path/to/dx-lab/apps/cli/src/main.ts" ``` -Then Phase 0 commands resolve the Go binary via PATH automatically: - -```sh -supabase-dev orgs list # proxied to supabase on PATH -supabase-dev login # native TypeScript -``` - -You can also point `SUPABASE_GO_BINARY` at a specific binary to skip the PATH lookup: - -```sh -export SUPABASE_GO_BINARY=/path/to/supabase -``` - ## Build There are two separate build paths depending on what you need. @@ -91,11 +70,10 @@ The shim resolves `SUPABASE_CLI_BINARY_OVERRIDE` (an absolute binary path) befor Used at release time to produce the compiled binaries that go into the platform-specific npm packages: ```sh -# CLI (TS SFE + Go binary for each platform) bun scripts/build.ts --version X.Y.Z ``` -For the CLI, this also cross-compiles the Go CLI binary from `apps/cli-go/` and places both binaries in `packages/cli-{platform}/bin/`. +This cross-compiles the CLI binary for each platform and places it in `packages/cli-{platform}/bin/`. See [`docs/binary-distribution.md`](./docs/binary-distribution.md) for a full explanation of the packaging model. @@ -165,11 +143,8 @@ Platform-specific packages live under: - `packages/cli-linux-*` - `packages/cli-windows-*` -Each platform package ships two binaries for the stable channel: +Each platform package ships a single compiled binary for the stable channel: - `bin/supabase` — the compiled TypeScript SFE (Bun single-file executable) -- `bin/supabase-go` — the compiled Go CLI binary, used by Phase 0 proxy commands - -The Go binary is compiled from `apps/cli-go/` at release time. Run `pnpm repos:install` after a fresh clone to make that source available. See [`docs/binary-distribution.md`](./docs/binary-distribution.md) for the full packaging model. diff --git a/apps/cli/docs/analytics.md b/apps/cli/docs/analytics.md index 225713f0fc..6f994749b4 100644 --- a/apps/cli/docs/analytics.md +++ b/apps/cli/docs/analytics.md @@ -49,6 +49,12 @@ installs the command span plus the per-invocation analytics context: That same context is then inherited by milestone events captured inside the command handler, which lets one CLI invocation share a single `command_run_id`. +The wrapper also rejects the removed global `--create-ticket` flag (with any value) before the +handler runs. The rejection happens inside the instrumented region, so it still emits +`cli_command_executed` with an `error_fingerprint` ending in `:removed_flag`. A command that fails +earlier, such as on a missing credential in its runtime layer, or that shows help, reports that +instead. + ## Event Model The primary analytics event is: @@ -79,9 +85,8 @@ and the KPI query semantics (strict recovery, repeat errors, internal/unknown bug rate) are documented there in `CliErrorActionabilityMetricDefinitions`. A `workflow` property is reserved in the catalog but not emitted yet. -Not every failure is classified: pure Go-proxy commands report through the Go -binary, which does not emit these fields, and events from CLI versions before -they existed never carry them. KPI queries therefore scope to +Not every failure is classified: events from CLI versions before these fields +existed never carry them. KPI queries therefore scope to `error_kind IS NOT NULL`, and the `classificationCoverage` metric definition reports the classified share of failures so the covered fraction is explicit rather than assumed. diff --git a/apps/cli/docs/binary-distribution.md b/apps/cli/docs/binary-distribution.md index dce49f57a9..cf111b3bb5 100644 --- a/apps/cli/docs/binary-distribution.md +++ b/apps/cli/docs/binary-distribution.md @@ -1,87 +1,52 @@ # Binary Distribution -This document explains how the Supabase CLI is packaged and distributed, covering the two-binary model used by the CLI. +This document explains how the Supabase CLI is packaged and distributed as a compiled binary per platform. ## Overview -The CLI is distributed as a set of platform-specific npm packages. Each platform package contains two binaries: +The CLI is distributed as a set of platform-specific npm packages. Each platform package contains one binary: ``` @supabase/cli-darwin-arm64/ └── bin/ - ├── supabase ← TypeScript CLI (Bun single-file executable) - └── supabase-go ← Go CLI binary (residual proxy commands only) + └── supabase ← TypeScript CLI (Bun single-file executable) ``` The base `supabase` package routes to the correct platform package via `src/shared/cli/bin.ts`, which resolves and `execFileSync`s the platform-specific `bin/supabase` binary. -## Why Two Binaries - -The CLI was built as a gradual TypeScript port of the Go CLI, moving each command through two phases: - -- **Phase 0** — The command is defined in the TS CLI tree but proxied to the Go binary at runtime via `GoProxy`. -- **Phase 1+** — The command is implemented natively in TypeScript. - -That port is complete (CLI-1970). `supabase-go` is the residual proxy target for a fixed, small command surface: `db diff` (for `--use-pg-schema`), the Go-deprecated `db branch`/`db remote changes` command families, `gen keys`, and `functions download` (for the hidden `--legacy-bundle` path). See "Go binary command surface" under Release Workflow below for the full list and why each command stays. Two lifecycles apply here and must not be conflated: the **public commands** in that surface are retained (dropping them was ruled a breaking change — CLI-1964), while their **Go implementations** are slated for eventual native TS replacement, after which `supabase-go` stops shipping. Until the proxied surface is empty, the TS binary (`supabase`) still needs `supabase-go` available on the same system for those invocations. Every other Go command from the original CLI has been deleted outright from `apps/cli-go/`, not merely excluded from the build — see the same section for how. - ## Package Layout ``` packages/ - cli-darwin-arm64/bin/ supabase + supabase-go - cli-darwin-x64/bin/ supabase + supabase-go - cli-linux-arm64/bin/ supabase + supabase-go - cli-linux-x64/bin/ supabase + supabase-go - cli-linux-arm64-musl/bin/ supabase (musl TS binary only) - cli-linux-x64-musl/bin/ supabase (musl TS binary only) - cli-windows-arm64/bin/ supabase.exe + supabase-go.exe - cli-windows-x64/bin/ supabase.exe + supabase-go.exe -``` - -The musl packages only carry the Bun TS binary (compiled for musl). The Go binary is statically linked (`CGO_ENABLED=0`), so the glibc Linux binary works on musl as well — it is installed alongside the musl TS binary by the Linux package managers (deb/rpm/apk) from the glibc build. - -## Runtime Resolution - -When a Phase 0 command runs, `go-proxy.layer.ts` resolves the Go binary in this order: - -1. **`SUPABASE_GO_BINARY` env var** — explicit override, takes priority. -2. **Co-located `supabase-go`** — looks next to `process.execPath`. Works in compiled SFE mode because the base shim uses `execFileSync`, making the TS SFE the main process with `process.execPath` pointing to itself. This is also how PATH-installed setups resolve: `supabase-go` ships next to the `supabase` shim. -3. **npm package resolution** — resolves `@supabase/cli-{platform}/bin/supabase-go`. Works when running from source with the platform packages installed. - -There is deliberately **no** `PATH` fallback (CLI-1488): the `supabase` shim itself is what's on `PATH`, so falling back to it would re-invoke the shim and fork-bomb. Resolution failure is a hard error with install guidance. - -## Source of the Go Binary - -The Go CLI source lives in `apps/cli-go/` and is managed via: - -```sh -pnpm repos:install + cli-darwin-arm64/bin/ supabase + cli-darwin-x64/bin/ supabase + cli-linux-arm64/bin/ supabase + cli-linux-x64/bin/ supabase + cli-linux-arm64-musl/bin/ supabase + cli-linux-x64-musl/bin/ supabase + cli-windows-arm64/bin/ supabase.exe + cli-windows-x64/bin/ supabase.exe ``` -This must be run after a fresh clone before building a release. - ## Development Workflow -No build step is required to run the CLI from source, but the Go binary must be resolvable — easiest via `SUPABASE_GO_BINARY` (below) or an installed `@supabase/cli-` package. +No build step is required to run the CLI from source. -1. Build the Go binary once: `cd apps/cli-go && go build -o supabase-go .` -2. Create a shell alias to run the CLI from source. For example in `.zshrc`: +1. Create a shell alias to run the CLI from source. For example in `.zshrc`: ```sh alias supabase-dev="bun /path/to/dx-lab/apps/cli/src/main.ts" ``` -3. Point `SUPABASE_GO_BINARY` at the built binary and run commands: +2. Run commands directly: ```sh - export SUPABASE_GO_BINARY=/path/to/apps/cli-go/supabase-go - supabase-dev db branch list # proxied to the Go binary - supabase-dev login # native TypeScript implementation + supabase-dev login ``` ## Release Workflow -The `scripts/build.ts` script compiles both binaries for all target platforms: +The `scripts/build.ts` script compiles the binary for all target platforms: ```sh bun scripts/build.ts --version X.Y.Z @@ -90,53 +55,21 @@ bun scripts/build.ts --version X.Y.Z This: 1. Compiles the TS CLI to a Bun SFE for each platform → `packages/cli-{platform}/bin/supabase` -2. Cross-compiles the Go CLI (`CGO_ENABLED=0`) for each platform → `packages/cli-{platform}/bin/supabase-go` -3. Signs the macOS binaries (both `supabase` and `supabase-go`) before archiving, so every channel ships the signed bytes — see [release-process.md § Code signing (macOS)](./release-process.md#code-signing-macos) and [ADR 0014](../../../docs/adr/0014-macos-code-signing-and-notarization.md) -4. Bundles both binaries into the platform archives (`.tar.gz` / `.zip`) -5. Includes both binaries in the Linux package manager packages (deb/rpm/apk) - -### Go binary command surface - -`supabase-go` does not ship the full old Go CLI — only the fixed subset the TypeScript CLI still proxies to via `GoProxy`: - -- `db diff` — kept for `--use-pg-schema`, which wraps the in-process `stripe/pg-schema-diff` Go library with no TS/container equivalent (CLI-1960) -- `db branch create`, `db branch delete`, `db branch list`, `db branch switch` -- `db remote changes` -- `gen keys` — the public command is kept (its planned removal, CLI-1964, was cancelled as a breaking change); the Go implementation stays only until a native TS replacement lands -- `functions download` — kept for the hidden `--legacy-bundle` path (CLI-1963) - -That list is exhaustive: `GoProxy` is the only code path in the TypeScript CLI that spawns `supabase-go`. The one historical exception — the hidden pg-delta seam (`db schema declarative __catalog` + `db start`), spawned directly by the native `db schema declarative generate|sync` commands to provision shadow databases and export pg-delta catalogs — was ported to native TypeScript as part of CLI-1970 (`pgdelta.seam.layer.ts` now composes `setupShadowDatabase`/`legacyExportCatalogPgDelta`/`startLocalDatabase` in-process), and those two Go commands were deleted with it. - -Everything else the original Go CLI implemented was deleted outright from `apps/cli-go/` (CLI-1970), not just excluded from the shipped binary. The reachable set was computed with a `go list -deps -test` fixpoint from the trimmed `main` package, and everything outside it was removed: the main module's first-party package count went from 138 to 38 (100 packages / ~29.5k LOC across 321 files deleted), including every other command's `cmd/*.go` file, `internal/{inspect,storage,sso,login,link,init,bootstrap}`, `internal/migration/{squash,up,fetch}`, the Go docs generator (`docs/`), `examples/`, and `tools/{jsonschema,shared}`. Counting stdlib and third-party dependencies too, the full dependency closure shrank from 1078 to 959 packages. `pkg/` (a separate, independently tagged/published Go module for external consumers) and `tools/listdep` (used by `cli-go-mirror.yml`) were left untouched. - -For any command or package deleted by CLI-1970, the parity/provenance reference is the last commit with the full source intact: `7b469f5b3` — the same convention CLI-1966 established for `internal/start` (see below). - -### Binary size - -Measured on the CLI-1970 branch with the real release build (`build.ts`, `go build -trimpath -ldflags "-s -w"`, `CGO_ENABLED=0`): - -| Platform | `supabase-go` size | -| ------------- | -----------------: | -| darwin-arm64 | 40.1 MB | -| darwin-x64 | 42.6 MB | -| linux-arm64 | 39.1 MB | -| linux-x64 | 41.8 MB | -| windows-arm64 | 39.5 MB | -| windows-x64 | 42.8 MB | - -(musl packages ship the glibc `supabase-go` binary — see Package Layout above.) - -Progression across both trims: the original two-binary baseline was 97–103 MB per platform; CLI-1966's `internal/start` deletion cut it to roughly 47–52 MB; CLI-1970 brings it to 39.1–42.8 MB. - -The remaining size is dominated by dependencies the retained commands still need: `stripe/pg-schema-diff` (`db diff --use-pg-schema`), the Docker client (shadow-database provisioning for diff), pgx, the Management API client, cobra/viper, and sentry/posthog. - -Release archive sizes (TS `supabase` binary + `supabase-go` together): `.tar.gz` 37.9–52.9 MB, `.zip` (Windows) 50.0–53.0 MB, `.deb` 52.7–53.4 MB, `.rpm` 52.3–53.2 MB, `.apk` 52.8–54.1 MB. +2. Signs the macOS binaries before archiving, so every channel ships the signed bytes — see [release-process.md § Code signing (macOS)](./release-process.md#code-signing-macos) and [ADR 0014](../../../docs/adr/0014-macos-code-signing-and-notarization.md) +3. Bundles the binary into the platform archives (`.tar.gz` / `.zip`) +4. Includes the binary in the Linux package manager packages (deb/rpm/apk) -### Historical note: CLI-1966 (`internal/start` deletion) +## History -`apps/cli-go/internal/start` (Go's `supabase start` implementation) was the first package deleted outright rather than excluded from the build. Native TS `start` talks to Docker directly and never proxied to Go for it, and a repo-wide grep confirmed no other TS→Go delegation seam called into it either. `internal/start` alone had previously accounted for roughly half the shipped Go binary's size via its exclusive dependency tree (docker-compose/v2, buildx, buildkit, k8s client-go, aws-sdk-go-v2, notary, secret-detector). The last commit with the source intact is `a253ccba2` (full hash `a253ccba25c21356ccd33044c4474aecb77d1ae4`). +The CLI was originally a two-binary distribution during its gradual port from a Go CLI to +TypeScript: a `supabase` Bun single-file executable alongside a `supabase-go` binary that the TS +CLI proxied to for not-yet-ported commands. The port completed (CLI-1970), the last residual +delegation surface was removed (CLI-2432), and the Go source tree (`apps/cli-go/`) was deleted in +the same effort — the CLI now ships as a single compiled binary per platform. See +[ADR 0030](../../../docs/adr/0030-go-cli-removal.md) for the full removal record. ## See Also - [ADR 0011](../../../docs/adr/0011-cli-release-and-distribution-strategy.md) — the release & distribution strategy decision (binary packaging choice, per-channel publish mechanisms, CI pipeline design, open blockers). +- [ADR 0030](../../../docs/adr/0030-go-cli-removal.md) — the Go CLI removal decision record. - [`release-process.md`](./release-process.md) — operational playbook for local, PoC, and production releases. diff --git a/apps/cli/docs/go-cli-divergences.md b/apps/cli/docs/go-cli-divergences.md deleted file mode 100644 index 1abb22e6bb..0000000000 --- a/apps/cli/docs/go-cli-divergences.md +++ /dev/null @@ -1,246 +0,0 @@ -# Go CLI Divergences - -**Frozen historical ledger** of deliberate TypeScript divergences from the old Go CLI -(pre-`7b469f5b3`) on the CLI: TS-only commands, flags, and behavior with no Go -counterpart. The TypeScript CLI is now the source of truth, so this ledger no longer accumulates -entries — new flags, commands, and behavioral changes are simply new CLI behavior, documented -through help text, tests, and each command's `SIDE_EFFECTS.md`. This document exists to answer -support and migration questions about why the TS CLI does something the old Go CLI didn't — it is -not a compatibility promise. - -## TS-only Commands - -These commands exist in the TS CLI today but have no direct top-level equivalent in the old Go CLI reference. - -| TS command | TS path | Notes | -| ---------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `dev` | `planned` | Reserved for a TS-native long-running local development workflow command that watches files and orchestrates subcommands. Track this as TS-only unless a direct Go equivalent emerges. | - -## Flag divergences from the Go reference - -- `db diff`, `db pull`, and `db schema declarative generate`/`sync` have a TS-only - `--strict-coverage` flag (no Go equivalent). It applies whenever the bundled - pg-delta engine runs (always for the declarative commands; for `db diff` and - migration-style `db pull` when pg-delta is selected via - `[experimental.pgdelta] enabled = true`, `--use-pg-delta`, or - `--diff-engine pg-delta`): coverage gaps that the engine reports — statements - it skipped or objects it could not represent — normally surface as warnings, - and `--strict-coverage` promotes them to hard failures. Selecting migra (the - `db diff` / migration-style `db pull` default, or explicitly via `--use-migra` - / `--diff-engine migra`) accepts the flag but has no effect, since migra does - not emit coverage diagnostics. Default behavior (omitted flag) matches Go. -- `db push` has a TS-only `--skip-vault` flag. It applies migrations without - resolving or updating `[db.vault]` secrets; default behavior still matches Go. -- Every command that resolves a linked project ref for its own database - connection has a TS-only `--project-ref` flag (no Go equivalent on any - user-facing command — only the `SUPABASE_PROJECT_ID` env var could override - the linked ref; the sole Go registration is a hidden, non-user-facing seam, - `db declarative __catalog --project-ref`, `cmd/pgdelta_catalog.go:44`). This - covers: `db push`, `db pull`, `db diff`, `db dump`, `db reset`, `db lint`, - `db advisors`, `db query`; `migration list`/`up`/`down`/`repair`/`fetch`/`squash`; - `seed buckets`; `storage ls`/`cp`/`mv`/`rm`; every `inspect db` subcommand and - `inspect report`; and `test db` (and its hidden `db test` alias, which shares - `test db`'s flag config verbatim). It feeds - `ProjectRefResolver.loadProjectRef`, keeping Go's precedence (flag > - `SUPABASE_PROJECT_ID` > `supabase/.temp/project-ref`) and taking effect only on - the linked path. It shares ONLY that ref-resolution precedence with - `SUPABASE_PROJECT_ID` — unlike the env var, it does not affect the local - container id or the pg-delta project id, and it does not imply `--linked`: - passing it alongside `--local`/`--db-url` (or, for `db diff`, in explicit mode - without `--from`/`--to linked`) is a hard error rather than a silently ignored - flag, since Go's env var going unused on a non-linked target has no TS-only - flag counterpart to accidentally discard. Default behavior (omitted flag) - matches Go exactly. -- `projects api-keys` has a TS-only `--reveal` flag (no Go equivalent). It sends - `reveal=true` so the Management API returns the full secret keys (`sb_secret_...`) in - full instead of redacting them, addressing issue #4775. Default behavior (omitted flag) - matches Go exactly. -- `projects create` has a TS-only `--high-availability` flag (no Go equivalent). It sets - `high_availability` in the create request body. Default behavior (omitted flag) matches - Go exactly. -- `projects create` has TS-only `--release-channel` and `--postgres-engine` flags (no Go - equivalent). They set `release_channel` / `postgres_engine` in the create request body — - fields the upstream Management API OpenAPI spec deliberately hides even though - `POST /v1/projects` accepts them (restored via `packages/api/scripts/openapi-overrides.json`, - CLI-2180). Both flags are hidden and gated behind `--experimental` (or `SUPABASE_EXPERIMENTAL`) - until PROD-548 exposes them officially; omitting them matches Go exactly. -- `db schema declarative generate` has a TS-only `--output-dir ` flag (no Go equivalent, - no short alias). It writes the generated declarative tree to the given directory for this - invocation only, without changing the configured `declarative_schema_path` — the staging step - of the legacy-tree upgrade recipe printed by the sync/generate compatibility gates. The name - deliberately avoids `--output`/`-o`, which the root command reserves for the global - machine-format flag; a leaf string flag would shadow it and turn `generate -o json` into a - write to a directory named `json`. Default behavior (omitted flag) matches Go. -- `link` has a TS-only `[ref-or-branch]` positional argument (no Go equivalent), and its - `--project-ref` flag additionally accepts a branch name. A value matching the 20-lowercase-letter - project ref shape is always treated as a ref; any other non-empty value is resolved to its - parent-project's branch project ref via the Management API before linking proceeds exactly as - today (CLI-2167). On the 404 (branch) link path, `link` also best-effort maintains - `linked-project.json`'s PARENT evidence (PR #6168 review, TS-only, no Go equivalent — Go never - writes this cache for a branch ref at all): a name/UUID-resolved branch link persists its known - parent ref (a ref-only record when no richer cache exists yet); a raw ref-shaped branch link - whose existing cache names a different project best-effort correlates the two via one extra - `listAllBranches` call and deletes the cache on EVERY unverified result — a verified mismatch, - a timeout, or any transport/status/decode failure (fail-safe: an unverifiable divergent cache is - untrustworthy). Both are best-effort and never affect `link`'s own outcome — see - `link/SIDE_EFFECTS.md`. - -## Behavioral divergences from the Go reference - -- `db schema declarative generate`/`sync` default declarative directory is `supabase/schemas`; - the old Go CLI reference (pre-`7b469f5b3`) used `supabase/database`. The move aligns the - default with the product-wide declarative-schemas convention. To keep the upgrade visible, - both commands print a TS-only warning when `declarative_schema_path` is unset, the new - default directory is empty, and the former `supabase/database` default still contains `.sql` - files or an export manifest — telling the user to set - `declarative_schema_path = "./database"` or move the tree. The warning never changes - behavior or exit codes; a non-interactive sync still fails with Go's - "no declarative schema found" message. Inside that directory the bundled pg-delta - engine writes one directory per schema at the root — `supabase/schemas/public/tables/x.sql` — - with cluster-level objects under a reserved `supabase/schemas/_cluster/`. - Structured export is TypeScript-only; the Go binary no longer ships a - pg-delta dump path. -- Local `pg_net` presence now converges with `[experimental.webhooks]` instead of being - installed unconditionally: `db-webhook.sql` no longer creates the extension at container - init, `supabase start`/`db start` install it (with grants reapplied via the - `issue_pg_net_access` event trigger) only when webhooks are enabled or migration history - contains a `create extension … pg_net`, and an existing-volume `start` with webhooks - disabled drops a `pg_net` that migration history does not own. Known accepted edge: `pg_net` - installed OUTSIDE migrations (for example via local Studio's SQL editor or extension toggle) - is invisible to the migration-ownership heuristic and is dropped on the next `start`; a - tracked dependency on `net.*` (PG14+ `BEGIN ATOMIC` functions) makes that non-`CASCADE` drop - — and therefore `start` — fail. Workaround in both cases: enable `[experimental.webhooks]` - or declare the extension in a migration. Documented rather than special-cased. -- `test db` (and its `db test` alias) exits `1` when `pg_prove` ran no tests (CLI-2194, #6206). - `pg_prove` prints `Result: NOTESTS` and still exits `0` for an empty run, and Go returns that - code verbatim (`internal/db/test/test.go` → `DockerRunOnceWithConfig`), so a typo'd path, an - empty tests directory, or a bind the daemon resolved against a different filesystem than the - CLI's (a sibling-container Docker socket) all reported a green build that ran zero tests. The - TAP stream on stdout is unchanged; the diagnostic goes to stderr like every other failure. -- SQL file runners on a stepped-down session re-assert `SET SESSION ROLE postgres` - immediately after each top-level role revert (`RESET ROLE`, the generic-`SET` spellings - such as `SET ROLE [TO|=] NONE|DEFAULT` and a case-sensitively quoted `'none'`, - `RESET SESSION AUTHORIZATION`, `SET SESSION AUTHORIZATION DEFAULT`, `DISCARD ALL`), - at the end of each file, and before the migration history insert and the `seed_files` - upsert — so the whole file (including a post-reset `granted by current_user` cleanup), - every CLI-owned ledger write, and every subsequent file run as `postgres`, matching a - password session for `current_user` and privilege checks (CLI-2205, #6236). - `session_user` remains the login role and `current_setting('role')` reads `postgres` - rather than `none`, so a file keying on `session_user` still diverges. A stepped-down - session is any remote connection authenticating as `cli_login_*` (the passwordless - linked path) or `supabase_admin`, which steps down with a session-level - `SET SESSION ROLE postgres`; a file's own `RESET ROLE` reverted it to the login role, - so the appended history insert failed with SQLSTATE 42501 and any later file ran as - the login role. The old Go CLI had the same defect; on a `supabase_admin` `--db-url`, - `RESET ROLE` consequently no longer re-escalates to superuser mid-file. - Statement-level re-assertion is used because a connection-time `role=postgres` default - cannot be guaranteed through the pooler. Injected restores never shift - `At statement: N` and are never recorded in the history row. Residual: a role revert - issued through dynamic SQL, or a spelling outside the list above (for example - `SET LOCAL ROLE NONE` — deliberately unmatched, a session-scoped restore would - override its transaction scope — or the `session_authorization` GUC spellings), is - invisible to the lexical check, so statements after it run as the login role until - the next restore point; the end-of-file restore still protects every CLI-owned write. - (`RESET ALL` needs no entry — `role` carries `GUC_NO_RESET_ALL`.) -- `functions serve` per-function env discovery (CLI-2184, #6179): without `--env-file`, each - `supabase/functions//.env` overrides matching values from the shared - `supabase/functions/.env` for that Function only; an explicit `--env-file` remains the - highest-priority source and disables both automatic reads. TS-only feature — the old Go - command read only the shared fallback. -- `functions deploy`/`functions serve` import-map resolution follows the import-maps spec - (implemented by Deno): a key matches exactly, or as a prefix only when it ends with `/`. The - old Go walker prefix-matched every key (`pkg/function/deno.go:150-155`, still in-tree), which - fabricated paths the runtime could never resolve — the ENOTDIR crash family fixed in - PR #6164 (CLI-2179). Intentional divergence: the spec behavior is the correct one. -- `config push`/`start` auth email `content_path` resolution: every relative - `[auth.email.template.*]` AND `[auth.email.notification.*]` path resolves from the discovered - project root, with notifications additionally falling back to the legacy `supabase/`-relative - location when the root-resolved file is missing. Go resolves notifications from `supabase/` only - (`(*baseConfig).resolve`'s own `// FIXME`-flagged asymmetry). Config validation, `config push` - content loading, and Kong's template mount builder all share one resolver - (`resolveNotificationContentPath`), so every consumer reads the SAME file — the drift the - asymmetry caused (validated against `/supabase/...`, mounted from `/...`) is gone. - The `init` scaffold ejects the root-relative form, which is incompatible with Go if uncommented - (#6159/#6160). -- `db remote changes --password

`: since CLI-1970, an explicit - `--password` beats the `SUPABASE_DB_PASSWORD` env var. Before the trim, Go's - package-wide "last `viper.BindPFlag("DB_PASSWORD", …)` wins" behavior bound - the key to `projects create --db-password` (lexically last `cmd/*.go` file), - so `db remote`'s own `--password` flag was never the bound instance and env - silently won over it — a latent bug. With `projects.go` deleted, the bind - lands on `db remote`'s persistent `--password` and flag-beats-env applies as - intended. Accepted (not restored) in the CLI-1970 parity audit. `db remote -commit` is now native `db pull`, so it uses pull's flag-then-env-then-dotenv - password order. `db pull` keeps that precedence unchanged. -- `branches {list,create,get,update,delete,pause,unpause,disable}` resolve their project ref - through a PARENT-scoped chain instead of plain `--project-ref` flag/env/file resolution: an - explicit `--project-ref` still wins outright, but the fallback is env `SUPABASE_PROJECT_ID` → - `supabase/.temp/linked-project.json`'s `ref` → `supabase/.temp/project-ref`, first ref-shaped - candidate wins. This is a direct consequence of the `link` branch-name divergence above: after - `supabase link `, `project-ref` holds the branch's own ref, and the Management API - returns 403 for a branch ref on every branches-management endpoint. No-op when linked to a real - (non-branch) project — the cache and the file hold the same ref — so this only changes behavior - in the previously-403ing branch-linked state (CLI-2167 follow-up, no Go equivalent). -- `branches list`'s pretty table (not `-o json|yaml|toml`, not `--output-format json|stream-json`) - marks the row matching the CURRENTLY linked ref with a ` (active)` NAME cell. TS-only - QoL, no Go equivalent (CLI-2167 follow-up). -- `status` prints the current linked project/branch as a "Linked Project:" block on stdout in - human text mode (Neon-style — `Org:`/`Project:`/`Branch:` lines, each omitted when unknown), - before any daemon/stack work begins, and folds the same linked state into its machine-readable - outputs — additive `linked_project: {...} | null` (with `org_slug`/`org_id`) in the TS - `--output-format json`/`stream-json` payload, and additive `linked_project_ref`/ - `linked_project_name`/`linked_org_slug`/`linked_org_id`/`linked_branch`/ - `linked_parent_project_ref` keys (absent entirely when not linked) appended after the existing - keys in `-o env|json|yaml|toml`. A confirmed branch-linked state (from `linked-project.json`) - keeps showing the parent/org fields even when the branch-name lookup itself degrades (no - token, offline, API error) — only the branch's own name is ever missing, so the user always - sees they're on a branch. The Management API client for that lookup is acquired lazily - (`CommandPlatformApiFactory`, not the eager `CommandPlatformApi`) so `status` stays fully - functional offline/token-less. Intent: let an agent driving `status` discover which - project/branch it's on without a separate `link`/`branches` call. Read-only, never affects - `status`'s exit code, and never alters any of its existing failure behavior — a Docker/daemon - connection failure still fails exactly as today, with the linked block already printed above it - in text mode (CLI-2167 follow-up, no Go equivalent). The same `linked_project` object is also - carried on the `--output-format json`/`stream-json` FAILURE envelope (top-level, next to - `_tag`/`error` or `type`/`error`/`timestamp`) — the agent-discovery use case matters most when - `status` fails to reach a stopped stack — via a new opt-in, shared mechanism - (`shared/output/machine-error-context.service.ts`'s `MachineErrorContext`, read by - `jsonOutputLayer`/`streamJsonOutputLayer`'s `fail`); `-o env|json|yaml|toml`'s failure output is - deliberately unchanged (still no payload, matching Go). See `status/SIDE_EFFECTS.md`. -- `projects list`'s `LINKED` marker (the `linked` boolean, rendered as the pretty table's `●` - bullet) now falls back to the PARENT chain when the linked ref matches no row exactly — the same - scenario as above, since a branch-linked ref never matches a real project row. **This changes - the `linked` field in the `-o json|yaml|toml` Go-struct payloads too**: in the branch-linked - state it was previously `false` on every row; it can now be `true` on the parent project's row. - The truthful fix IS the behavior change (CLI-2167 follow-up, no Go equivalent). -- `services` warns on a malformed linked project ref (matching Go's - `flags.LoadProjectRef` validation message) but, unlike Go, does not then use - that ref for the remote lookup. Go's `cmd/services.go` (deleted in CLI-1970; - last present at commit 7b469f5b3) treats the validation failure as - non-fatal and still calls `listRemoteImages` with the malformed - value; TS skips the remote lookup instead, since the ref is embedded - unescaped into the tenant gateway hostname and a malformed value could - redirect the service-role key to an attacker-controlled host. Intentional - TS-only hardening, not a parity bug — see - [`services/SIDE_EFFECTS.md`](../src/commands/services/SIDE_EFFECTS.md). -- `db pull` in-sync (`"No schema changes found"`) keeps Go's message and its non-zero - exit code, but replaces the generic "Try rerunning the command with --debug to - troubleshoot the error." stderr footer with an explanatory suggestion line - ("The remote database is already in sync with your local migrations — nothing to - pull."). An in-sync database is a finding, not a failure to troubleshoot, so the - debug hint sent users chasing a non-existent bug. Message text and exit code — the - parts scripts depend on — are unchanged. -- Edge Runtime's Docker container `--ulimit nofile` value (`functions serve` and `start`): Go - hardcodes `nofile=65536:65536`, raised from the daemon default to accommodate FD usage from - many concurrent Deno isolates (supabase/cli#5151). TS clamps that value to the host's own hard - nofile limit on Linux (`@supabase/stack`'s `edgeRuntimeNofileUlimit`, via - `process.report`'s `userLimits`), so a constrained sandbox (hard cap below 65536) can still start the - container instead of failing outright (CLI-2220). The CLI process's own limit is used as a - proxy for the daemon's — exact in the sandboxes this targets, where both share the cap; a - Linux client more constrained than its daemon (remote `DOCKER_HOST`, mounted socket) just - gets a smaller fd budget, never a failed start. When the clamp lowers the request, the CLI's - `functions serve`/`start` bring-up warns with the reduced limit. The `@supabase/stack` service - builder (`stack start`) applies the same clamp silently: its defs are built without - an output channel, and in managed mode inside the daemon process, so a user-visible warning - there needs a diagnostics channel on `BuildResult` first; the applied value stays visible via - `docker inspect`. diff --git a/apps/cli/docs/go-cli-porting-status.md b/apps/cli/docs/go-cli-porting-status.md deleted file mode 100644 index 60d7ab2215..0000000000 --- a/apps/cli/docs/go-cli-porting-status.md +++ /dev/null @@ -1,59 +0,0 @@ -# Go CLI Delegation - -> Still named `go-cli-porting-status.md` for historical reasons — the per-command porting tracker -> this file once held is gone now that the port is done; it documents only the residual Go -> delegation surface below. - -The Go→TypeScript port is complete (CLI-1970). The bundled `supabase-go` binary and the -`apps/cli-go/` tree contain **only** the delegation surface in the table below — Go source for -every other command was deleted outright once nothing in the TypeScript CLI could reach it, -directly or indirectly. For any other command's former Go source, the reference is the last commit -with it intact: `7b469f5b3` (CLI-1966's `internal/start` pin remains its own, separate commit, -`a253ccba2`). - -See [`binary-distribution.md`](./binary-distribution.md) for how these two binaries are packaged, -resolved at runtime, and sized. The TypeScript CLI is the source of truth for all CLI behavior; -`apps/cli-go/` is authoritative only for the proxied commands below, and the whole surface is -slated for cleanup and removal ([ADR 0016](../../../docs/adr/0016-port-completion-and-go-cli-authority-scope.md) -records the earlier transition policy). - -## The delegation surface - -| Command/path | TS proxy site | Go implementation (in-tree) | Why it stays | -| -------------------------------------------------- | ------------------------------------------------------------------------------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -| `db diff --use-pg-schema` | [`src/commands/db/diff/diff.handler.ts`](../src/commands/db/diff/diff.handler.ts) (delegate path) | `internal/db/diff/pgschema.go` | Wraps the Go-only `stripe/pg-schema-diff` library (CLI-1960); deprecated but sanctioned. | -| `db branch create\|delete\|list\|switch` | `src/commands/db/branch/*/` | `legacy/branch/*` | Go-deprecated wrapped commands kept indefinitely (CLI-1964 cancelled: dropping them was ruled a breaking change not worth shipping). | -| `db remote changes` | `src/commands/db/remote/changes/` | inline in `cmd/db.go` over `internal/db/diff` | Same CLI-1964 ruling. `db remote commit` is native `db pull`. | -| `gen keys` | `src/commands/gen/keys/` | `legacy/keys` | Same ruling; requires `--experimental`. | -| `functions download --legacy-bundle` (hidden flag) | `src/shared/functions/download.ts` `makeGoProxyLegacyBundleArgs` | `internal/functions/download` | Legacy Deno bundle extraction (CLI-1963). | - -## Mechanics - -All delegation goes through the shared `GoProxy` service -([`src/command-internal/go-proxy.service.ts`](../src/command-internal/go-proxy.service.ts), -[`go-proxy.layer.ts`](../src/command-internal/go-proxy.layer.ts)): - -- **Binary resolution order**: `SUPABASE_GO_BINARY` env var → binary co-located with the compiled - shim → the platform's `@supabase/cli-` npm package. There is deliberately **no** - `PATH` fallback (CLI-1488: the shim itself is what's on `PATH`, so falling back would re-invoke - it and fork-bomb) — resolution failure is a hard error with install guidance. PATH-installed - setups work via the co-location step: `supabase-go` sits next to the `supabase` shim. -- **Global-flag forwarding**: `cli/root.ts` translates the CLI's global flags - (`--output`, `--profile`, `--debug`, `--workdir`, `--experimental`, `--network-id`, `--yes`, - `--dns-resolver`, `--create-ticket`, `--agent`) into Go-style argv ahead of every proxied - invocation. -- **Child telemetry suppression**: a proxy handler that itself owns the parent - `cli_command_executed` event (wrapped in TS command instrumentation) suppresses the child's own - telemetry; a bare pass-through proxy leaves it enabled so the Go child stays the sole emitter. - -The spawn surface is guarded by -[`apps/cli-e2e/src/tests/go-binary-surface.e2e.test.ts`](../../cli-e2e/src/tests/go-binary-surface.e2e.test.ts) — -trimming the binary past this surface fails CI. - -The former pg-delta `__catalog`/`db start` seam (spawned directly by `db schema declarative -generate|sync`, outside `GoProxy`) was ported native in CLI-1970 and no longer exists. - -## See also - -TS-only flags and deliberate behavioral divergences from the old Go CLI live in -[`go-cli-divergences.md`](./go-cli-divergences.md). diff --git a/apps/cli/docs/go-cli-reference.md b/apps/cli/docs/go-cli-reference.md deleted file mode 100644 index e557eefadf..0000000000 --- a/apps/cli/docs/go-cli-reference.md +++ /dev/null @@ -1,2128 +0,0 @@ -# Old Go CLI Reference - -> Complete help output for the old Go-based `supabase` CLI. -> Use this document as the raw parity reference for the residual Go delegation surface in [`go-cli-porting-status.md`](./go-cli-porting-status.md) and the TS divergences tracked in [`go-cli-divergences.md`](./go-cli-divergences.md). - -## Global Flags - -These flags are available on all commands: - -``` -Flags: - --create-ticket create a support ticket for any CLI error - --debug output debug logs to stderr - --dns-resolver [ native | https ] lookup domain names using the specified resolver (default native) - --experimental enable experimental features - -h, --help help for supabase - --network-id string use the specified docker network instead of a generated one - -o, --output [ env | pretty | json | toml | yaml ] output format of status variables (default pretty) - --profile string use a specific profile for connecting to Supabase API (default "supabase") - --workdir string path to a Supabase project directory - --yes answer yes to all prompts -``` - -## Table of Contents - -- [Quick Start](#quick-start) - - [bootstrap](#bootstrap) -- [Local Development](#local-development) - - [init](#init) - - [link](#link) - - [unlink](#unlink) - - [login](#login) - - [logout](#logout) - - [start](#start) - - [stop](#stop) - - [status](#status) - - [services](#services) - - [db](#db) - - [gen](#gen) - - [inspect](#inspect) - - [migration](#migration) - - [seed](#seed) - - [test](#test) -- [Management APIs](#management-apis) - - [backups](#backups) - - [branches](#branches) - - [config](#config) - - [domains](#domains) - - [encryption](#encryption) - - [functions](#functions) - - [network-bans](#network-bans) - - [network-restrictions](#network-restrictions) - - [orgs](#orgs) - - [postgres-config](#postgres-config) - - [projects](#projects) - - [secrets](#secrets) - - [snippets](#snippets) - - [ssl-enforcement](#ssl-enforcement) - - [sso](#sso) - - [storage](#storage) - - [vanity-subdomains](#vanity-subdomains) -- [Additional Commands](#additional-commands) - - [completion](#completion) - - [help](#help-1) - ---- - -## Quick Start - -### bootstrap - -``` -Bootstrap a Supabase project from a starter template - -Usage: - supabase bootstrap [template] [flags] - -Flags: - -h, --help help for bootstrap - -p, --password string Password to your remote Postgres database. -``` - ---- - -## Local Development - -### init - -``` -Initialize a local project - -Usage: - supabase init [flags] - -Flags: - --force Overwrite existing supabase/config.toml. - -h, --help help for init - -i, --interactive Enables interactive mode to configure IDE settings. - --use-orioledb Use OrioleDB storage engine for Postgres. -``` - -### link - -``` -Link to a Supabase project - -Usage: - supabase link [flags] - -Flags: - -h, --help help for link - -p, --password string Password to your remote Postgres database. - --project-ref string Project ref of the Supabase project. - --skip-pooler Use direct connection instead of pooler. -``` - -### unlink - -``` -Unlink a Supabase project - -Usage: - supabase unlink [flags] - -Flags: - -h, --help help for unlink -``` - -### login - -``` -Authenticate using an access token - -Usage: - supabase login [flags] - -Flags: - -h, --help help for login - --name string Name that will be used to store token in your settings (default "built-in token name generator") - --no-browser Do not open browser automatically - --token string Use provided token instead of automatic login flow -``` - -### logout - -``` -Log out and delete access tokens locally - -Usage: - supabase logout [flags] - -Flags: - -h, --help help for logout -``` - -### start - -``` -Start containers for Supabase local development - -Usage: - supabase start [flags] - -Flags: - -x, --exclude strings Names of containers to not start. [gotrue,realtime,storage-api,imgproxy,kong,mailpit,postgrest,postgres-meta,studio,edge-runtime,logflare,vector,supavisor] - -h, --help help for start - --ignore-health-check Ignore unhealthy services and exit 0 - --sandbox Run in sandbox mode using native binaries (experimental) -``` - -### stop - -``` -Stop all local Supabase containers - -Usage: - supabase stop [flags] - -Flags: - --all Stop all local Supabase instances from all projects across the machine. - -h, --help help for stop - --no-backup Deletes all data volumes after stopping. - --project-id string Local project ID to stop. -``` - -### status - -``` -Show status of local Supabase containers - -Usage: - supabase status [flags] - -Examples: - supabase status -o env --override-name api.url=NEXT_PUBLIC_SUPABASE_URL - supabase status -o json - -Flags: - -h, --help help for status - --override-name strings Override specific variable names. -``` - -### services - -``` -Show versions of all Supabase services - -Usage: - supabase services [flags] - -Flags: - -h, --help help for services -``` - -### db - -``` -Manage Postgres databases - -Usage: - supabase db [command] - -Available Commands: - diff Diffs the local database for schema changes - dump Dumps data or schemas from the remote database - lint Checks local database for typing error - pull Pull schema from the remote database - push Push new migrations to the remote database - reset Resets the local database to current migrations - start Starts local Postgres database - -Flags: - -h, --help help for db -``` - -#### db diff - -``` -Diffs the local database for schema changes - -Usage: - supabase db diff [flags] - -Flags: - --db-url string Diffs against the database specified by the connection string (must be percent-encoded). - -f, --file string Saves schema diff to a new migration file. - -h, --help help for diff - --linked Diffs local migration files against the linked project. - --local Diffs local migration files against the local database. (default true) - -s, --schema strings Comma separated list of schema to include. - --use-migra Use migra to generate schema diff. (default true) - --use-pg-delta Use pg-delta to generate schema diff. - --use-pg-schema Use pg-schema-diff to generate schema diff. - --use-pgadmin Use pgAdmin to generate schema diff. -``` - -#### db dump - -``` -Dumps data or schemas from the remote database - -Usage: - supabase db dump [flags] - -Flags: - --data-only Dumps only data records. - --db-url string Dumps from the database specified by the connection string (must be percent-encoded). - --dry-run Prints the pg_dump script that would be executed. - -x, --exclude strings List of schema.tables to exclude from data-only dump. - -f, --file string File path to save the dumped contents. - -h, --help help for dump - --keep-comments Keeps commented lines from pg_dump output. - --linked Dumps from the linked project. (default true) - --local Dumps from the local database. - -p, --password string Password to your remote Postgres database. - --role-only Dumps only cluster roles. - -s, --schema strings Comma separated list of schema to include. - --use-copy Use copy statements in place of inserts. -``` - -#### db lint - -``` -Checks local database for typing error - -Usage: - supabase db lint [flags] - -Flags: - --db-url string Lints the database specified by the connection string (must be percent-encoded). - --fail-on [ none | warning | error ] Error level to exit with non-zero status. (default none) - -h, --help help for lint - --level [ warning | error ] Error level to emit. (default warning) - --linked Lints the linked project for schema errors. - --local Lints the local database for schema errors. (default true) - -s, --schema strings Comma separated list of schema to include. -``` - -#### db pull - -``` -Pull schema from the remote database - -Usage: - supabase db pull [migration name] [flags] - -Flags: - --db-url string Pulls from the database specified by the connection string (must be percent-encoded). - -h, --help help for pull - --linked Pulls from the linked project. (default true) - --local Pulls from the local database. - -p, --password string Password to your remote Postgres database. - -s, --schema strings Comma separated list of schema to include. -``` - -#### db push - -``` -Push new migrations to the remote database - -Usage: - supabase db push [flags] - -Flags: - --db-url string Pushes to the database specified by the connection string (must be percent-encoded). - --dry-run Print the migrations that would be applied, but don't actually apply them. - -h, --help help for push - --include-all Include all migrations not found on remote history table. - --include-roles Include custom roles from supabase/roles.sql. - --include-seed Include seed data from your config. - --linked Pushes to the linked project. (default true) - --local Pushes to the local database. - -p, --password string Password to your remote Postgres database. -``` - -#### db reset - -``` -Resets the local database to current migrations - -Usage: - supabase db reset [flags] - -Flags: - --db-url string Resets the database specified by the connection string (must be percent-encoded). - -h, --help help for reset - --last uint Reset up to the last n migration versions. - --linked Resets the linked project with local migrations. - --local Resets the local database with local migrations. (default true) - --no-seed Skip running the seed script after reset. - --version string Reset up to the specified version. -``` - -#### db start - -``` -Starts local Postgres database - -Usage: - supabase db start [flags] - -Flags: - --from-backup string Path to a logical backup file. - -h, --help help for start -``` - -### gen - -``` -Run code generation tools - -Usage: - supabase gen [command] - -Available Commands: - bearer-jwt Generate a Bearer Auth JWT for accessing Data API - signing-key Generate a JWT signing key - types Generate types from Postgres schema - -Flags: - -h, --help help for gen -``` - -#### gen bearer-jwt - -``` -Generate a Bearer Auth JWT for accessing Data API - -Usage: - supabase gen bearer-jwt [flags] - -Flags: - --exp time Expiry timestamp for this token. - -h, --help help for bearer-jwt - --payload string Custom claims in JSON format. (default "{}") - --role string Postgres role to use. - --sub string User ID to impersonate. (default "anonymous") - --valid-for duration Validity duration for this token. (default 30m0s) -``` - -#### gen signing-key - -``` -Securely generate a private JWT signing key for use in the CLI or to import in the dashboard. - -Supported algorithms: - ES256 - ECDSA with P-256 curve and SHA-256 (recommended) - RS256 - RSA with SHA-256 - -Usage: - supabase gen signing-key [flags] - -Flags: - --algorithm [ RS256 | ES256 ] Algorithm for signing key generation. (default ES256) - --append Append new key to existing keys file instead of overwriting. - -h, --help help for signing-key -``` - -#### gen types - -``` -Generate types from Postgres schema - -Usage: - supabase gen types [flags] - -Examples: - supabase gen types --local - supabase gen types --linked --lang=go - supabase gen types --project-id abc-def-123 --schema public --schema private - supabase gen types --db-url 'postgresql://...' --schema public --schema auth - -Flags: - --db-url string Generate types from a database url. - -h, --help help for types - --lang [ typescript | go | swift | python ] Output language of the generated types. (default typescript) - --linked Generate types from the linked project. - --local Generate types from the local dev database. - --postgrest-v9-compat Generate types compatible with PostgREST v9 and below. - --project-id string Generate types from a project ID. - --query-timeout duration Maximum timeout allowed for the database query. (default 15s) - -s, --schema strings Comma separated list of schema to include. - --swift-access-control [ internal | public ] Access control for Swift generated types. (default internal) -``` - -### inspect - -``` -Tools to inspect your Supabase project - -Usage: - supabase inspect [command] - -Available Commands: - db Tools to inspect your Supabase database - report Generate a CSV output for all inspect commands - -Flags: - --db-url string Inspect the database specified by the connection string (must be percent-encoded). - -h, --help help for inspect - --linked Inspect the linked project. (default true) - --local Inspect the local database. -``` - -#### inspect db - -``` -Tools to inspect your Supabase database - -Usage: - supabase inspect db [command] - -Available Commands: - bloat Estimates space allocated to a relation that is full of dead tuples - blocking Show queries that are holding locks and the queries that are waiting for them to be released - calls Show queries from pg_stat_statements ordered by total times called - db-stats Show stats such as cache hit rates, total sizes, and WAL size - index-stats Show combined index size, usage percent, scan counts, and unused status - locks Show queries which have taken out an exclusive lock on a relation - long-running-queries Show currently running queries running for longer than 5 minutes - outliers Show queries from pg_stat_statements ordered by total execution time - replication-slots Show information about replication slots on the database - role-stats Show information about roles on the database - table-stats Show combined table size, index size, and estimated row count - traffic-profile Show read/write activity ratio for tables based on block I/O operations - vacuum-stats Show statistics related to vacuum operations per table - -Flags: - -h, --help help for db -``` - -##### inspect db bloat - -``` -Estimates space allocated to a relation that is full of dead tuples - -Usage: - supabase inspect db bloat [flags] - -Flags: - -h, --help help for bloat -``` - -##### inspect db blocking - -``` -Show queries that are holding locks and the queries that are waiting for them to be released - -Usage: - supabase inspect db blocking [flags] - -Flags: - -h, --help help for blocking -``` - -##### inspect db calls - -``` -Show queries from pg_stat_statements ordered by total times called - -Usage: - supabase inspect db calls [flags] - -Flags: - -h, --help help for calls -``` - -##### inspect db db-stats - -``` -Show stats such as cache hit rates, total sizes, and WAL size - -Usage: - supabase inspect db db-stats [flags] - -Flags: - -h, --help help for db-stats -``` - -##### inspect db index-stats - -``` -Show combined index size, usage percent, scan counts, and unused status - -Usage: - supabase inspect db index-stats [flags] - -Flags: - -h, --help help for index-stats -``` - -##### inspect db locks - -``` -Show queries which have taken out an exclusive lock on a relation - -Usage: - supabase inspect db locks [flags] - -Flags: - -h, --help help for locks -``` - -##### inspect db long-running-queries - -``` -Show currently running queries running for longer than 5 minutes - -Usage: - supabase inspect db long-running-queries [flags] - -Flags: - -h, --help help for long-running-queries -``` - -##### inspect db outliers - -``` -Show queries from pg_stat_statements ordered by total execution time - -Usage: - supabase inspect db outliers [flags] - -Flags: - -h, --help help for outliers -``` - -##### inspect db replication-slots - -``` -Show information about replication slots on the database - -Usage: - supabase inspect db replication-slots [flags] - -Flags: - -h, --help help for replication-slots -``` - -##### inspect db role-stats - -``` -Show information about roles on the database - -Usage: - supabase inspect db role-stats [flags] - -Flags: - -h, --help help for role-stats -``` - -##### inspect db table-stats - -``` -Show combined table size, index size, and estimated row count - -Usage: - supabase inspect db table-stats [flags] - -Flags: - -h, --help help for table-stats -``` - -##### inspect db traffic-profile - -``` -Show read/write activity ratio for tables based on block I/O operations - -Usage: - supabase inspect db traffic-profile [flags] - -Flags: - -h, --help help for traffic-profile -``` - -##### inspect db vacuum-stats - -``` -Show statistics related to vacuum operations per table - -Usage: - supabase inspect db vacuum-stats [flags] - -Flags: - -h, --help help for vacuum-stats -``` - -#### inspect report - -``` -Generate a CSV output for all inspect commands - -Usage: - supabase inspect report [flags] - -Flags: - -h, --help help for report - --output-dir string Path to save CSV files in (default ".") -``` - -### migration - -``` -Manage database migration scripts - -Usage: - supabase migration [command] - -Aliases: - migration, migrations - -Available Commands: - down Resets applied migrations up to the last n versions - fetch Fetch migration files from history table - list List local and remote migrations - new Create an empty migration script - repair Repair the migration history table - squash Squash migrations to a single file - up Apply pending migrations to local database - -Flags: - -h, --help help for migration -``` - -#### migration down - -``` -Resets applied migrations up to the last n versions - -Usage: - supabase migration down [flags] - -Flags: - --db-url string Resets applied migrations on the database specified by the connection string (must be percent-encoded). - -h, --help help for down - --last uint Reset up to the last n migration versions. (default 1) - --linked Resets applied migrations on the linked project. - --local Resets applied migrations on the local database. (default true) -``` - -#### migration fetch - -``` -Fetch migration files from history table - -Usage: - supabase migration fetch [flags] - -Flags: - --db-url string Fetches migrations from the database specified by the connection string (must be percent-encoded). - -h, --help help for fetch - --linked Fetches migration history from the linked project. (default true) - --local Fetches migration history from the local database. -``` - -#### migration list - -``` -List local and remote migrations - -Usage: - supabase migration list [flags] - -Flags: - --db-url string Lists migrations of the database specified by the connection string (must be percent-encoded). - -h, --help help for list - --linked Lists migrations applied to the linked project. (default true) - --local Lists migrations applied to the local database. - -p, --password string Password to your remote Postgres database. -``` - -#### migration new - -``` -Create an empty migration script - -Usage: - supabase migration new [flags] - -Flags: - -h, --help help for new -``` - -#### migration repair - -``` -Repair the migration history table - -Usage: - supabase migration repair [version] ... [flags] - -Flags: - --db-url string Repairs migrations of the database specified by the connection string (must be percent-encoded). - -h, --help help for repair - --linked Repairs the migration history of the linked project. (default true) - --local Repairs the migration history of the local database. - -p, --password string Password to your remote Postgres database. - --status [ applied | reverted ] Version status to update. -``` - -#### migration squash - -``` -Squash migrations to a single file - -Usage: - supabase migration squash [flags] - -Flags: - --db-url string Squashes migrations of the database specified by the connection string (must be percent-encoded). - -h, --help help for squash - --linked Squashes the migration history of the linked project. - --local Squashes the migration history of the local database. (default true) - -p, --password string Password to your remote Postgres database. - --version string Squash up to the specified version. -``` - -#### migration up - -``` -Apply pending migrations to local database - -Usage: - supabase migration up [flags] - -Flags: - --db-url string Applies migrations to the database specified by the connection string (must be percent-encoded). - -h, --help help for up - --include-all Include all migrations not found on remote history table. - --linked Applies pending migrations to the linked project. - --local Applies pending migrations to the local database. (default true) -``` - -### seed - -``` -Seed a Supabase project from supabase/config.toml - -Usage: - supabase seed [command] - -Available Commands: - buckets Seed buckets declared in [storage.buckets] - -Flags: - -h, --help help for seed - --linked Seeds the linked project. - --local Seeds the local database. (default true) -``` - -#### seed buckets - -``` -Seed buckets declared in [storage.buckets] - -Usage: - supabase seed buckets [flags] - -Flags: - -h, --help help for buckets -``` - -### test - -``` -Run tests on local Supabase containers - -Usage: - supabase test [command] - -Available Commands: - db Tests local database with pgTAP - new Create a new test file - -Flags: - -h, --help help for test -``` - -#### test db - -``` -Tests local database with pgTAP - -Usage: - supabase test db [path] ... [flags] - -Flags: - --db-url string Tests the database specified by the connection string (must be percent-encoded). - -h, --help help for db - --linked Runs pgTAP tests on the linked project. - --local Runs pgTAP tests on the local database. (default true) -``` - -#### test new - -``` -Create a new test file - -Usage: - supabase test new [flags] - -Flags: - -h, --help help for new - -t, --template [ pgtap ] Template framework to generate. (default pgtap) -``` - ---- - -## Management APIs - -### backups - -``` -Manage Supabase physical backups - -Usage: - supabase backups [command] - -Available Commands: - list Lists available physical backups - restore Restore to a specific timestamp using PITR - -Flags: - -h, --help help for backups - --project-ref string Project ref of the Supabase project. -``` - -#### backups list - -``` -Lists available physical backups - -Usage: - supabase backups list [flags] - -Flags: - -h, --help help for list -``` - -#### backups restore - -``` -Restore to a specific timestamp using PITR - -Usage: - supabase backups restore [flags] - -Flags: - -h, --help help for restore - -t, --timestamp int The recovery time target in seconds since epoch. -``` - -### branches - -``` -Manage Supabase preview branches - -Usage: - supabase branches [command] - -Available Commands: - create Create a preview branch - delete Delete a preview branch - get Retrieve details of a preview branch - list List all preview branches - pause Pause a preview branch - unpause Unpause a preview branch - update Update a preview branch - -Flags: - -h, --help help for branches - --project-ref string Project ref of the Supabase project. -``` - -#### branches create - -``` -Create a preview branch for the linked project. - -Usage: - supabase branches create [name] [flags] - -Flags: - -h, --help help for create - --notify-url string URL to notify when branch is active healthy. - --persistent Whether to create a persistent branch. - --region string Select a region to deploy the branch database. - --size string Select a desired instance size for the branch database. - --with-data Whether to clone production data to the branch database. -``` - -#### branches delete - -``` -Delete a preview branch by its name or ID. - -Usage: - supabase branches delete [name] [flags] - -Flags: - -h, --help help for delete -``` - -#### branches get - -``` -Retrieve details of the specified preview branch. - -Usage: - supabase branches get [name] [flags] - -Flags: - -h, --help help for get -``` - -#### branches list - -``` -List all preview branches of the linked project. - -Usage: - supabase branches list [flags] - -Flags: - -h, --help help for list -``` - -#### branches pause - -``` -Pause a preview branch - -Usage: - supabase branches pause [name] [flags] - -Flags: - -h, --help help for pause -``` - -#### branches unpause - -``` -Unpause a preview branch - -Usage: - supabase branches unpause [name] [flags] - -Flags: - -h, --help help for unpause -``` - -#### branches update - -``` -Update a preview branch by its name or ID. - -Usage: - supabase branches update [name] [flags] - -Flags: - --git-branch string Change the associated git branch. - -h, --help help for update - --name string Rename the preview branch. - --notify-url string URL to notify when branch is active healthy. - --persistent Switch between ephemeral and persistent branch. - --status string Override the current branch status. -``` - -### config - -``` -Manage Supabase project configurations - -Usage: - supabase config [command] - -Available Commands: - push Pushes local config.toml to the linked project - -Flags: - -h, --help help for config - --project-ref string Project ref of the Supabase project. -``` - -#### config push - -``` -Pushes local config.toml to the linked project - -Usage: - supabase config push [flags] - -Flags: - -h, --help help for push -``` - -### domains - -``` -Manage custom domain names for Supabase projects. - -Use of custom domains and vanity subdomains is mutually exclusive. - -Usage: - supabase domains [command] - -Available Commands: - activate Activate the custom hostname for a project - create Create a custom hostname - delete Deletes the custom hostname config for your project - get Get the current custom hostname config - reverify Re-verify the custom hostname config for your project - -Flags: - -h, --help help for domains - --include-raw-output Include raw output (useful for debugging). - --project-ref string Project ref of the Supabase project. -``` - -#### domains activate - -``` -Activates the custom hostname configuration for a project. - -This reconfigures your Supabase project to respond to requests on your custom hostname. -After the custom hostname is activated, your project's auth services will no longer function on the Supabase-provisioned subdomain. - -Usage: - supabase domains activate [flags] - -Flags: - -h, --help help for activate -``` - -#### domains create - -``` -Create a custom hostname for your Supabase project. - -Expects your custom hostname to have a CNAME record to your Supabase project's subdomain. - -Usage: - supabase domains create [flags] - -Flags: - --custom-hostname string The custom hostname to use for your Supabase project. - -h, --help help for create -``` - -#### domains delete - -``` -Deletes the custom hostname config for your project - -Usage: - supabase domains delete [flags] - -Flags: - -h, --help help for delete -``` - -#### domains get - -``` -Retrieve the custom hostname config for your project, as stored in the Supabase platform. - -Usage: - supabase domains get [flags] - -Flags: - -h, --help help for get -``` - -#### domains reverify - -``` -Re-verify the custom hostname config for your project - -Usage: - supabase domains reverify [flags] - -Flags: - -h, --help help for reverify -``` - -### encryption - -``` -Manage encryption keys of Supabase projects - -Usage: - supabase encryption [command] - -Available Commands: - get-root-key Get the root encryption key of a Supabase project - update-root-key Update root encryption key of a Supabase project - -Flags: - -h, --help help for encryption - --project-ref string Project ref of the Supabase project. -``` - -#### encryption get-root-key - -``` -Get the root encryption key of a Supabase project - -Usage: - supabase encryption get-root-key [flags] - -Flags: - -h, --help help for get-root-key -``` - -#### encryption update-root-key - -``` -Update root encryption key of a Supabase project - -Usage: - supabase encryption update-root-key [flags] - -Flags: - -h, --help help for update-root-key -``` - -### functions - -``` -Manage Supabase Edge functions - -Usage: - supabase functions [command] - -Available Commands: - delete Delete a Function from Supabase - deploy Deploy a Function to Supabase - download Download a Function from Supabase - list List all Functions in Supabase - new Create a new Function locally - serve Serve all Functions locally - -Flags: - -h, --help help for functions -``` - -#### functions delete - -``` -Delete a Function from the linked Supabase project. This does NOT remove the Function locally. - -Usage: - supabase functions delete [flags] - -Flags: - -h, --help help for delete - --project-ref string Project ref of the Supabase project. -``` - -#### functions deploy - -``` -Deploy a Function to the linked Supabase project. - -Usage: - supabase functions deploy [Function name] [flags] - -Flags: - -h, --help help for deploy - --import-map string Path to import map file. - -j, --jobs uint Maximum number of parallel jobs. (default 1) - --no-verify-jwt Disable JWT verification for the Function. - --project-ref string Project ref of the Supabase project. - --prune Delete Functions that exist in Supabase project but not locally. - --use-api Bundle functions server-side without using Docker. -``` - -#### functions download - -``` -Download the source code for a Function from the linked Supabase project. If no function name is provided, downloads all functions. - -Usage: - supabase functions download [Function name] [flags] - -Flags: - -h, --help help for download - --project-ref string Project ref of the Supabase project. - --use-api Unbundle functions server-side without using Docker. -``` - -#### functions list - -``` -List all Functions in the linked Supabase project. - -Usage: - supabase functions list [flags] - -Flags: - -h, --help help for list - --project-ref string Project ref of the Supabase project. -``` - -#### functions new - -``` -Create a new Function locally - -Usage: - supabase functions new [flags] - -Flags: - -h, --help help for new -``` - -#### functions serve - -``` -Serve all Functions locally - -Usage: - supabase functions serve [flags] - -Flags: - --env-file string Path to an env file to be populated to the Function environment. - -h, --help help for serve - --import-map string Path to import map file. - --inspect Alias of --inspect-mode brk. - --inspect-main Allow inspecting the main worker. - --inspect-mode [ run | brk | wait ] Activate inspector capability for debugging. - --no-verify-jwt Disable JWT verification for the Function. -``` - -### network-bans - -``` -Network bans are IPs that get temporarily blocked if their traffic pattern looks abusive (e.g. multiple failed auth attempts). - -The subcommands help you view the current bans, and unblock IPs if desired. - -Usage: - supabase network-bans [command] - -Available Commands: - get Get the current network bans - remove Remove a network ban - -Flags: - -h, --help help for network-bans - --project-ref string Project ref of the Supabase project. -``` - -#### network-bans get - -``` -Get the current network bans - -Usage: - supabase network-bans get [flags] - -Flags: - -h, --help help for get -``` - -#### network-bans remove - -``` -Remove a network ban - -Usage: - supabase network-bans remove [flags] - -Flags: - --db-unban-ip strings IP to allow DB connections from. - -h, --help help for remove -``` - -### network-restrictions - -``` -Manage network restrictions - -Usage: - supabase network-restrictions [command] - -Available Commands: - get Get the current network restrictions - update Update network restrictions - -Flags: - -h, --help help for network-restrictions - --project-ref string Project ref of the Supabase project. -``` - -#### network-restrictions get - -``` -Get the current network restrictions - -Usage: - supabase network-restrictions get [flags] - -Flags: - -h, --help help for get -``` - -#### network-restrictions update - -``` -Update network restrictions - -Usage: - supabase network-restrictions update [flags] - -Flags: - --append Append to existing restrictions instead of replacing them. - --bypass-cidr-checks Bypass some of the CIDR validation checks. - --db-allow-cidr strings CIDR to allow DB connections from. - -h, --help help for update -``` - -### orgs - -``` -Manage Supabase organizations - -Usage: - supabase orgs [command] - -Available Commands: - create Create an organization - list List all organizations - -Flags: - -h, --help help for orgs -``` - -#### orgs create - -``` -Create an organization for the logged-in user. - -Usage: - supabase orgs create [flags] - -Flags: - -h, --help help for create -``` - -#### orgs list - -``` -List all organizations the logged-in user belongs. - -Usage: - supabase orgs list [flags] - -Flags: - -h, --help help for list -``` - -### postgres-config - -``` -Manage Postgres database config - -Usage: - supabase postgres-config [command] - -Available Commands: - delete Delete specific Postgres database config overrides - get Get the current Postgres database config overrides - update Update Postgres database config - -Flags: - -h, --help help for postgres-config - --project-ref string Project ref of the Supabase project. -``` - -#### postgres-config delete - -``` -Delete specific config overrides, reverting them to their default values. - -Usage: - supabase postgres-config delete [flags] - -Flags: - --config strings Config keys to delete (comma-separated) - -h, --help help for delete - --no-restart Do not restart the database after deleting config. -``` - -#### postgres-config get - -``` -Get the current Postgres database config overrides - -Usage: - supabase postgres-config get [flags] - -Flags: - -h, --help help for get -``` - -#### postgres-config update - -``` -Overriding the default Postgres config could result in unstable database behavior. -Custom configuration also overrides the optimizations generated based on the compute add-ons in use. - -Usage: - supabase postgres-config update [flags] - -Flags: - --config strings Config overrides specified as a 'key=value' pair - -h, --help help for update - --no-restart Do not restart the database after updating config. - --replace-existing-overrides If true, replaces all existing overrides with the ones provided. If false (default), merges existing overrides with the ones provided. -``` - -### projects - -``` -Manage Supabase projects - -Usage: - supabase projects [command] - -Available Commands: - api-keys List all API keys for a Supabase project - create Create a project on Supabase - delete Delete a Supabase project - list List all Supabase projects - -Flags: - -h, --help help for projects -``` - -#### projects api-keys - -``` -List all API keys for a Supabase project - -Usage: - supabase projects api-keys [flags] - -Flags: - -h, --help help for api-keys - --project-ref string Project ref of the Supabase project. -``` - -#### projects create - -``` -Create a project on Supabase - -Usage: - supabase projects create [project name] [flags] - -Examples: -supabase projects create my-project --org-id cool-green-pqdr0qc --db-password ******** --region us-east-1 - -Flags: - --db-password string Database password of the project. - -h, --help help for create - --org-id string Organization ID to create the project in. - --region string Select a region close to you for the best performance. - --size string Select a desired instance size for your project. -``` - -#### projects delete - -``` -Delete a Supabase project - -Usage: - supabase projects delete [ref] [flags] - -Flags: - -h, --help help for delete -``` - -#### projects list - -``` -List all Supabase projects the logged-in user can access. - -Usage: - supabase projects list [flags] - -Flags: - -h, --help help for list -``` - -### secrets - -``` -Manage Supabase secrets - -Usage: - supabase secrets [command] - -Available Commands: - list List all secrets on Supabase - set Set a secret(s) on Supabase - unset Unset a secret(s) on Supabase - -Flags: - -h, --help help for secrets - --project-ref string Project ref of the Supabase project. -``` - -#### secrets list - -``` -List all secrets in the linked project. - -Usage: - supabase secrets list [flags] - -Flags: - -h, --help help for list -``` - -#### secrets set - -``` -Set a secret(s) to the linked Supabase project. - -Usage: - supabase secrets set ... [flags] - -Flags: - --env-file string Read secrets from a .env file. - -h, --help help for set -``` - -#### secrets unset - -``` -Unset a secret(s) from the linked Supabase project. - -Usage: - supabase secrets unset [NAME] ... [flags] - -Flags: - -h, --help help for unset -``` - -### snippets - -``` -Manage Supabase SQL snippets - -Usage: - supabase snippets [command] - -Available Commands: - download Download contents of a SQL snippet - list List all SQL snippets - -Flags: - -h, --help help for snippets - --project-ref string Project ref of the Supabase project. -``` - -#### snippets download - -``` -Download contents of the specified SQL snippet. - -Usage: - supabase snippets download [flags] - -Flags: - -h, --help help for download -``` - -#### snippets list - -``` -List all SQL snippets of the linked project. - -Usage: - supabase snippets list [flags] - -Flags: - -h, --help help for list -``` - -### ssl-enforcement - -``` -Manage SSL enforcement configuration - -Usage: - supabase ssl-enforcement [command] - -Available Commands: - get Get the current SSL enforcement configuration - update Update SSL enforcement configuration - -Flags: - -h, --help help for ssl-enforcement - --project-ref string Project ref of the Supabase project. -``` - -#### ssl-enforcement get - -``` -Get the current SSL enforcement configuration - -Usage: - supabase ssl-enforcement get [flags] - -Flags: - -h, --help help for get -``` - -#### ssl-enforcement update - -``` -Update SSL enforcement configuration - -Usage: - supabase ssl-enforcement update [flags] - -Flags: - --disable-db-ssl-enforcement Whether the DB should disable SSL enforcement for all external connections. - --enable-db-ssl-enforcement Whether the DB should enable SSL enforcement for all external connections. - -h, --help help for update -``` - -### sso - -``` -Manage Single Sign-On (SSO) authentication for projects - -Usage: - supabase sso [command] - -Available Commands: - add Add a new SSO identity provider - info Returns the SAML SSO settings required for the identity provider - list List all SSO identity providers for a project - remove Remove an existing SSO identity provider - show Show information about an SSO identity provider - update Update information about an SSO identity provider - -Flags: - -h, --help help for sso - --project-ref string Project ref of the Supabase project. -``` - -#### sso add - -``` -Add and configure a new connection to a SSO identity provider to your Supabase project. - -Usage: - supabase sso add [flags] - -Examples: - supabase sso add --type saml --project-ref mwjylndxudmiehsxhmmz --metadata-url 'https://...' --domains example.com - -Flags: - --attribute-mapping-file string File containing a JSON mapping between SAML attributes to custom JWT claims. - --domains strings Comma separated list of email domains to associate with the added identity provider. - -h, --help help for add - --metadata-file string File containing a SAML 2.0 Metadata XML document describing the identity provider. - --metadata-url string URL pointing to a SAML 2.0 Metadata XML document describing the identity provider. - --name-id-format string URI reference representing the classification of string-based identifier information. - --skip-url-validation Whether local validation of the SAML 2.0 Metadata URL should not be performed. - -t, --type [ saml ] Type of identity provider (according to supported protocol). -``` - -#### sso info - -``` -Returns all of the important SSO information necessary for your project to be registered with a SAML 2.0 compatible identity provider. - -Usage: - supabase sso info [flags] - -Examples: - supabase sso info --project-ref mwjylndxudmiehsxhmmz - -Flags: - -h, --help help for info -``` - -#### sso list - -``` -List all connections to a SSO identity provider to your Supabase project. - -Usage: - supabase sso list [flags] - -Examples: - supabase sso list --project-ref mwjylndxudmiehsxhmmz - -Flags: - -h, --help help for list -``` - -#### sso remove - -``` -Remove a connection to an already added SSO identity provider. Removing the provider will prevent existing users from logging in. Please treat this command with care. - -Usage: - supabase sso remove [flags] - -Examples: - supabase sso remove b5ae62f9-ef1d-4f11-a02b-731c8bbb11e8 --project-ref mwjylndxudmiehsxhmmz - -Flags: - -h, --help help for remove -``` - -#### sso show - -``` -Provides the information about an established connection to an identity provider. You can use --metadata to obtain the raw SAML 2.0 Metadata XML document stored in your project's configuration. - -Usage: - supabase sso show [flags] - -Examples: - supabase sso show b5ae62f9-ef1d-4f11-a02b-731c8bbb11e8 --project-ref mwjylndxudmiehsxhmmz - -Flags: - -h, --help help for show - --metadata Show SAML 2.0 XML Metadata only -``` - -#### sso update - -``` -Update the configuration settings of a already added SSO identity provider. - -Usage: - supabase sso update [flags] - -Examples: - supabase sso update b5ae62f9-ef1d-4f11-a02b-731c8bbb11e8 --project-ref mwjylndxudmiehsxhmmz --add-domains example.com - -Flags: - --add-domains strings Add this comma separated list of email domains to the identity provider. - --attribute-mapping-file string File containing a JSON mapping between SAML attributes to custom JWT claims. - --domains strings Replace domains with this comma separated list of email domains. - -h, --help help for update - --metadata-file string File containing a SAML 2.0 Metadata XML document describing the identity provider. - --metadata-url string URL pointing to a SAML 2.0 Metadata XML document describing the identity provider. - --name-id-format string URI reference representing the classification of string-based identifier information. - --remove-domains strings Remove this comma separated list of email domains from the identity provider. - --skip-url-validation Whether local validation of the SAML 2.0 Metadata URL should not be performed. -``` - -### storage - -``` -Manage Supabase Storage objects - -Usage: - supabase storage [command] - -Available Commands: - cp Copy objects from src to dst path - ls List objects by path prefix - mv Move objects from src to dst path - rm Remove objects by file path - -Flags: - -h, --help help for storage - --linked Connects to Storage API of the linked project. (default true) - --local Connects to Storage API of the local database. -``` - -#### storage cp - -``` -Copy objects from src to dst path - -Usage: - supabase storage cp [flags] - -Examples: -cp readme.md ss:///bucket/readme.md -cp -r docs ss:///bucket/docs -cp -r ss:///bucket/docs . -Flags: - --cache-control string Custom Cache-Control header for HTTP upload. (default "max-age=3600") - --content-type string Custom Content-Type header for HTTP upload. (default "auto-detect") - -h, --help help for cp - -j, --jobs uint Maximum number of parallel jobs. (default 1) - -r, --recursive Recursively copy a directory. -``` - -#### storage ls - -``` -List objects by path prefix - -Usage: - supabase storage ls [path] [flags] - -Examples: -ls ss:///bucket/docs - -Flags: - -h, --help help for ls - -r, --recursive Recursively list a directory. -``` - -#### storage mv - -``` -Move objects from src to dst path - -Usage: - supabase storage mv [flags] - -Examples: -mv -r ss:///bucket/docs ss:///bucket/www/docs - -Flags: - -h, --help help for mv - -r, --recursive Recursively move a directory. -``` - -#### storage rm - -``` -Remove objects by file path - -Usage: - supabase storage rm ... [flags] - -Examples: -rm -r ss:///bucket/docs -rm ss:///bucket/docs/example.md ss:///bucket/readme.md -Flags: - -h, --help help for rm - -r, --recursive Recursively remove a directory. -``` - -### vanity-subdomains - -``` -Manage vanity subdomains for Supabase projects. - -Usage of vanity subdomains and custom domains is mutually exclusive. - -Usage: - supabase vanity-subdomains [command] - -Available Commands: - activate Activate a vanity subdomain - check-availability Checks if a desired subdomain is available for use - delete Deletes a project's vanity subdomain - get Get the current vanity subdomain - -Flags: - -h, --help help for vanity-subdomains - --project-ref string Project ref of the Supabase project. -``` - -#### vanity-subdomains activate - -``` -Activate a vanity subdomain for your Supabase project. - -This reconfigures your Supabase project to respond to requests on your vanity subdomain. -After the vanity subdomain is activated, your project's auth services will no longer function on the {project-ref}.{supabase-domain} hostname. - -Usage: - supabase vanity-subdomains activate [flags] - -Flags: - --desired-subdomain string The desired vanity subdomain to use for your Supabase project. - -h, --help help for activate -``` - -#### vanity-subdomains check-availability - -``` -Checks if a desired subdomain is available for use - -Usage: - supabase vanity-subdomains check-availability [flags] - -Flags: - --desired-subdomain string The desired vanity subdomain to use for your Supabase project. - -h, --help help for check-availability -``` - -#### vanity-subdomains delete - -``` -Deletes the vanity subdomain for a project, and reverts to using the project ref for routing. - -Usage: - supabase vanity-subdomains delete [flags] - -Flags: - -h, --help help for delete -``` - -#### vanity-subdomains get - -``` -Get the current vanity subdomain - -Usage: - supabase vanity-subdomains get [flags] - -Flags: - -h, --help help for get -``` - ---- - -## Additional Commands - -### completion - -``` -Generate the autocompletion script for supabase for the specified shell. -See each sub-command's help for details on how to use the generated script. - -Usage: - supabase completion [command] - -Available Commands: - bash Generate the autocompletion script for bash - fish Generate the autocompletion script for fish - powershell Generate the autocompletion script for powershell - zsh Generate the autocompletion script for zsh - -Flags: - -h, --help help for completion -``` - -#### completion bash - -``` -Generate the autocompletion script for the bash shell. - -This script depends on the 'bash-completion' package. -If it is not installed already, you can install it via your OS's package manager. - -To load completions in your current shell session: - - source <(supabase completion bash) - -To load completions for every new session, execute once: - -#### Linux: - - supabase completion bash > /etc/bash_completion.d/supabase - -#### macOS: - - supabase completion bash > $(brew --prefix)/etc/bash_completion.d/supabase - -You will need to start a new shell for this setup to take effect. - -Usage: - supabase completion bash - -Flags: - -h, --help help for bash - --no-descriptions disable completion descriptions -``` - -#### completion fish - -``` -Generate the autocompletion script for the fish shell. - -To load completions in your current shell session: - - supabase completion fish | source - -To load completions for every new session, execute once: - - supabase completion fish > ~/.config/fish/completions/supabase.fish - -You will need to start a new shell for this setup to take effect. - -Usage: - supabase completion fish [flags] - -Flags: - -h, --help help for fish - --no-descriptions disable completion descriptions -``` - -#### completion powershell - -``` -Generate the autocompletion script for powershell. - -To load completions in your current shell session: - - supabase completion powershell | Out-String | Invoke-Expression - -To load completions for every new session, add the output of the above command -to your powershell profile. - -Usage: - supabase completion powershell [flags] - -Flags: - -h, --help help for powershell - --no-descriptions disable completion descriptions -``` - -#### completion zsh - -``` -Generate the autocompletion script for the zsh shell. - -If shell completion is not already enabled in your environment you will need -to enable it. You can execute the following once: - - echo "autoload -U compinit; compinit" >> ~/.zshrc - -To load completions in your current shell session: - - source <(supabase completion zsh) - -To load completions for every new session, execute once: - -#### Linux: - - supabase completion zsh > "${fpath[1]}/_supabase" - -#### macOS: - - supabase completion zsh > $(brew --prefix)/share/zsh/site-functions/_supabase - -You will need to start a new shell for this setup to take effect. - -Usage: - supabase completion zsh [flags] - -Flags: - -h, --help help for zsh - --no-descriptions disable completion descriptions -``` - -### help - -``` -Help provides help for any command in the application. -Simply type supabase help [path to command] for full details. - -Usage: - supabase help [command] [flags] - -Flags: - -h, --help help for help -``` diff --git a/apps/cli/docs/release-process.md b/apps/cli/docs/release-process.md index f799d73657..6bbeb67f92 100644 --- a/apps/cli/docs/release-process.md +++ b/apps/cli/docs/release-process.md @@ -34,7 +34,6 @@ pnpm local-registry Publish the CLI into it from another terminal (current platform only, faster than a cross-platform build): ```sh -# CLI (Bun SFE + Go sidecar — requires Go on PATH and `pnpm repos:install`): pnpm cli-release ``` @@ -44,7 +43,7 @@ Test it: npx --registry http://localhost:4873 supabase@ --version ``` -`[tools/release/local-release.ts](../../../tools/release/local-release.ts)` does the heavy lifting: it builds the platform SFE (+ Go sidecar) and the umbrella `supabase` package, materialises them in a `tmp` dir (so no workspace `package.json` is modified), and publishes both to Verdaccio. The cleanup is automatic even on failure. +`[tools/release/local-release.ts](../../../tools/release/local-release.ts)` does the heavy lifting: it builds the platform SFE and the umbrella `supabase` package, materialises them in a `tmp` dir (so no workspace `package.json` is modified), and publishes both to Verdaccio. The cleanup is automatic even on failure. This is the right ring for: @@ -60,7 +59,7 @@ It is **not** a valid test for Homebrew or Scoop — those paths are covered in This is how you validate the Homebrew formula, Scoop manifest, and GitHub-Release-host resolution on real infrastructure without touching `supabase/`\* repos or risking a clash with an already-installed `supabase` CLI on the reviewer's machine. -Both updater scripts support a `--name ` flag that pushes the formula / manifest under a different name (e.g., `supabase-shim-poc`) — that is, a different filename and Ruby class / scoop manifest. The installed binary is always `supabase` (matching the Go CLI), so PoC reviewers should `brew uninstall supabase` / `scoop uninstall supabase` first if they already have the official CLI installed. +Both updater scripts support a `--name ` flag that pushes the formula / manifest under a different name (e.g., `supabase-shim-poc`) — that is, a different filename and Ruby class / scoop manifest. The installed binary is always `supabase`, so PoC reviewers should `brew uninstall supabase` / `scoop uninstall supabase` first if they already have the official CLI installed. ### One-time setup (per reviewer) @@ -87,7 +86,6 @@ The `--dry-run` flag on both updater scripts produces the `Formula/.rb` an ```sh # Build all eight platform archives + linux packages + checksums.txt. -# Ships the Go sidecar alongside the Bun SFE. bun apps/cli/scripts/build.ts --version 0.0.1 # Render the Homebrew formula against your PoC release host + tap. @@ -176,13 +174,13 @@ Validated on Windows x64 (`v0.0.1`, 2026-04-21): installed with no SmartScreen b ### What to validate -Beyond `--version` and `brew test`, exercise a Phase-0 proxied subcommand that requires the `supabase-go` sidecar: +Beyond `--version` and `brew test`, exercise the actual command tree rather than only a flag resolved at build time: ```sh -supabase completion bash +supabase --help ``` -This must spawn the colocated `supabase-go` and print the generated completion script — not return `NotFound: ChildProcess.spawn (supabase ...)`. (`supabase --version` is served by the Bun wrapper and never touches the sidecar, so it is not a sufficient check on its own.) If it fails, the Homebrew install step is wrong: check that `[apps/cli/scripts/update-homebrew.ts](../scripts/update-homebrew.ts)`'s install-lines block ran `bin.install "supabase-go" if File.exist?("supabase-go")`, and that the built archive actually contains `supabase-go` (it should, for any release build). +This must print the full command tree, not fail or truncate — a corrupted or partial binary can still resolve `--version` (a build-time `--define`) while failing on real command dispatch. ### Local-artifact testing (no GitHub Release upload) @@ -493,12 +491,12 @@ Keep two to three stable majors supported at the same time, roughly six to twelv `**build` (ubuntu-latest):\*\* 1. `[pnpm exec bun apps/cli/scripts/sync-versions.ts --version X.Y.Z](../scripts/sync-versions.ts)` — writes the release version into every `package.json` (umbrella + eight platform packages) and resolves the umbrella's `workspace:`\* `optionalDependencies` to `X.Y.Z`. -2. `[pnpm exec bun apps/cli/scripts/build.ts --version X.Y.Z](../scripts/build.ts)` — cross-compiles the Bun SFE for all eight targets (including windows-arm64), cross-compiles the Go sidecar, **ad-hoc signs the macOS binaries** (see [Code signing (macOS)](#code-signing-macos)), builds the six Linux packages via `nfpm`, produces the tar/zip archives, and writes `dist/checksums.txt`. +2. `[pnpm exec bun apps/cli/scripts/build.ts --version X.Y.Z](../scripts/build.ts)` — cross-compiles the Bun SFE for all eight targets (including windows-arm64), **ad-hoc signs the macOS binaries** (see [Code signing (macOS)](#code-signing-macos)), builds the six Linux packages via `nfpm`, produces the tar/zip archives, and writes `dist/checksums.txt`. 3. `actions/upload-artifact` preserves `packages/cli-*/bin/` and `dist/` for the downstream jobs. `**smoke-test` (matrix: `ubuntu-latest`, `macos-latest`, `macos-15-intel`, `windows-latest`):\*\* -Downloads the build artifact, makes the SFE executable (`chmod +x` on non-Windows), installs Scoop on Windows, and runs `pnpm run test:smoke -- --version X.Y.Z --tag .stable>` from `apps/cli`. On the macOS legs this also verifies each binary's signature (`codesign --verify --strict`, correct identifier, not linker-signed) and executes `supabase --version`, which is the real AMFI gate. Any failure blocks publishing. +Downloads the build artifact, makes the SFE executable (`chmod +x` on non-Windows), installs Scoop on Windows, and runs `pnpm run test:smoke -- --version X.Y.Z --tag .stable>` from `apps/cli`. On the macOS legs this also verifies the binary's signature (`codesign --verify --strict`, correct identifier, not linker-signed) and executes `supabase --version`, which is the real AMFI gate. Any failure blocks publishing. The matrix does not yet include `windows-11-arm` (gate 6) or an Alpine musl runner (also gate 6). Until those land, arm64 / musl regressions only surface in Ring 2 validation. @@ -527,13 +525,13 @@ Both updaters run automatically from `release-shared.yml`'s `publish-homebrew` a Once the channels are live, two reusable workflows run automatically (last in `release-shared.yml`, non-gating — by the time they run the artifacts are already published, so a failure surfaces as a red post-release signal rather than blocking distribution): - `[setup-cli-smoke-test.yml](../../../.github/workflows/setup-cli-smoke-test.yml)` (`setup-cli-smoke` job) — installs the released version through `supabase/setup-cli` (the GitHub Release download path) on Linux, macOS, Windows, and Alpine. -- `[verify-install-channels.yml](../../../.github/workflows/verify-install-channels.yml)` (`verify-install-channels` job) — runs a **real** `brew install` (macOS **and** Linux, so both the `on_macos` and `on_linux` stanzas of the formula are exercised), `scoop install`, and `curl|bash` install of the **published** install script (fetched from the release asset, not the repo checkout) against the just-published Homebrew tap, Scoop bucket, and GitHub Release. Each leg then asserts `supabase --version` matches and runs `supabase completion bash` (a Go-proxied command) so a package that omits or misplaces the `supabase-go` sidecar fails too. brew, scoop, and the install script each verify the published `sha256`/`hash` against the downloaded tarball, so this is the signal that would have caught CLI v2.107.0 (where the brew/scoop manifests shipped checksums that did not match the release tarballs and every `brew install` / `scoop install` failed). It only runs for `beta`/`stable`/`maintenance` (the channels that publish brew/scoop) and can be dispatched manually against any already-published version via the Actions tab. +- `[verify-install-channels.yml](../../../.github/workflows/verify-install-channels.yml)` (`verify-install-channels` job) — runs a **real** `brew install` (macOS **and** Linux, so both the `on_macos` and `on_linux` stanzas of the formula are exercised), `scoop install`, and `curl|bash` install of the **published** install script (fetched from the release asset, not the repo checkout) against the just-published Homebrew tap, Scoop bucket, and GitHub Release. Each leg then asserts `supabase --version` matches and runs `supabase --help` to exercise the actual command tree. brew, scoop, and the install script each verify the published `sha256`/`hash` against the downloaded tarball, so this is the signal that would have caught CLI v2.107.0 (where the brew/scoop manifests shipped checksums that did not match the release tarballs and every `brew install` / `scoop install` failed). It only runs for `beta`/`stable`/`maintenance` (the channels that publish brew/scoop) and can be dispatched manually against any already-published version via the Actions tab. ### Code signing (macOS) -The macOS binaries (`supabase` Bun SFE + `supabase-go` sidecar, `darwin-arm64` and `darwin-x64`) are signed inside `build.ts` between compilation and archiving, so the signed bytes flow into every channel that consumes `packages/cli-darwin-*/bin/` — npm platform packages, Homebrew, and the GitHub Release tarballs (which also feed the `install` script and `setup-cli`). Background: [ADR 0014](../../../docs/adr/0014-macos-code-signing-and-notarization.md). +The macOS binary (`supabase` Bun SFE, `darwin-arm64` and `darwin-x64`) is signed inside `build.ts` between compilation and archiving, so the signed bytes flow into every channel that consumes `packages/cli-darwin-*/bin/` — npm platform packages, Homebrew, and the GitHub Release tarballs (which also feed the `install` script and `setup-cli`). Background: [ADR 0014](../../../docs/adr/0014-macos-code-signing-and-notarization.md). -Why this exists: `bun build --compile` and the Go linker emit only a degenerate "linker-signed" ad-hoc signature (identifier `a.out`, no requirements blob). macOS 26+ AMFI rejects it and SIGKILLs the process at launch ([CLI-1621](https://linear.app/supabase/issue/CLI-1621) / [#5556](https://github.com/supabase/cli/issues/5556)). A full ad-hoc signature fixes it. +Why this exists: `bun build --compile` emits only a degenerate "linker-signed" ad-hoc signature (identifier `a.out`, no requirements blob). macOS 26+ AMFI rejects it and SIGKILLs the process at launch ([CLI-1621](https://linear.app/supabase/issue/CLI-1621) / [#5556](https://github.com/supabase/cli/issues/5556)). A full ad-hoc signature fixes it. - **Signing runs on the Linux build runner** via [`rcodesign`](https://github.com/indygreg/apple-platform-rs) (the apple-codesign project), which signs Mach-O binaries without a macOS host. No macOS signing job exists, and **no Apple credentials are required for the current ad-hoc signing** (Phase 1). The version + sha256 are pinned in the "Install rcodesign" step of [`build-cli-artifacts.yml`](../../../.github/workflows/build-cli-artifacts.yml). - **CI hard-fails if signing is unavailable**: the build job sets `SUPABASE_CLI_REQUIRE_SIGNING=1`, so a missing `rcodesign` fails the build rather than silently shipping unsigned binaries. Local builds without `rcodesign` degrade to a warning and skip signing. @@ -586,7 +584,7 @@ Rollback is straightforward because each channel is its own commit / release. Th - [ADR 0011](../../../docs/adr/0011-cli-release-and-distribution-strategy.md) — the decision record. Channel choices, signing rationale, open pre-cutover gates. - [ADR 0028](../../../docs/adr/0028-release-branches-and-maintenance-lines.md) — the `next` channel and `v.x` maintenance lines. -- `[apps/cli/docs/binary-distribution.md](./binary-distribution.md)` — why each platform package contains two binaries (`supabase` SFE + `supabase-go` sidecar) and how they're resolved at runtime. +- `[apps/cli/docs/binary-distribution.md](./binary-distribution.md)` — how each platform package's `supabase` binary is built and resolved at runtime. - `[tools/release/local-release.ts](../../../tools/release/local-release.ts)` — Ring 1 implementation. - `[apps/cli/scripts/build.ts](../scripts/build.ts)`, `[publish.ts](../scripts/publish.ts)`, `[sync-versions.ts](../scripts/sync-versions.ts)`, `[update-homebrew.ts](../scripts/update-homebrew.ts)`, `[update-scoop.ts](../scripts/update-scoop.ts)` — release script implementations. - `[.github/workflows/release.yml](../../../.github/workflows/release.yml)`, `[release-shared.yml](../../../.github/workflows/release-shared.yml)`, `[deploy.yml](../../../.github/workflows/deploy.yml)`, `[deploy-check.yml](../../../.github/workflows/deploy-check.yml)`, `[fast-forward.yml](../../../.github/workflows/fast-forward.yml)`, `[sync-branches.yml](../../../.github/workflows/sync-branches.yml)`, `[branch-policy.yml](../../../.github/workflows/branch-policy.yml)` — Ring 3 pipeline and branch promotion. diff --git a/apps/cli/package.json b/apps/cli/package.json index bffc8fd508..fcf389e7de 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -24,8 +24,7 @@ "access": "public" }, "scripts": { - "build": "pnpm build:go-sidecar && pnpm build:binary && pnpm build:shim", - "build:go-sidecar": "mkdir -p dist && cp ../cli-go/supabase-go dist/supabase-go", + "build": "pnpm build:binary && pnpm build:shim", "build:binary": "bun scripts/build-binary.ts", "build:shim": "bun build src/shared/cli/bin.ts --outfile dist/supabase.js --target node", "docs:spec": "bun scripts/generate-docs-spec.ts", diff --git a/apps/cli/scripts/build.ts b/apps/cli/scripts/build.ts index 52ee98efff..1ae21b6250 100644 --- a/apps/cli/scripts/build.ts +++ b/apps/cli/scripts/build.ts @@ -1,6 +1,6 @@ import { $ } from "bun"; import { createHash } from "node:crypto"; -import { copyFile, mkdir, readFile, rm, writeFile } from "node:fs/promises"; +import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; import path from "node:path"; import process from "node:process"; import { parseArgs } from "node:util"; @@ -87,7 +87,6 @@ const TARGETS = [ const entrypoint = path.join(root, "apps/cli/src/main.ts"); const distDir = path.join(root, "dist"); -const goSource = path.resolve(root, "apps/cli-go"); const buildDefines = { ...(await stackReleaseDefine()), SUPABASE_FUNCTIONS_SERVE_MAIN_TEMPLATE: JSON.stringify( @@ -100,17 +99,6 @@ const buildDefines = { "process.env.MSGPACKR_NATIVE_ACCELERATION_DISABLED": JSON.stringify("true"), }; -type BunTarget = (typeof TARGETS)[number]["bunTarget"]; - -const GO_TARGETS: Record = { - "bun-darwin-arm64": { goos: "darwin", goarch: "arm64" }, - "bun-darwin-x64": { goos: "darwin", goarch: "amd64" }, - "bun-linux-arm64": { goos: "linux", goarch: "arm64" }, - "bun-linux-x64-baseline": { goos: "linux", goarch: "amd64" }, - "bun-windows-x64-baseline": { goos: "windows", goarch: "amd64" }, - "bun-windows-arm64": { goos: "windows", goarch: "arm64" }, -}; - type SignMode = "adhoc" | "off"; async function runBunBuild(config: Bun.BuildConfig) { @@ -142,37 +130,6 @@ async function buildTarget(target: (typeof TARGETS)[number]) { console.log(`[${target.pkg}] Done.`); } -async function buildGoTarget(target: (typeof TARGETS)[number]) { - const binDir = path.join(root, "packages", target.pkg, "bin"); - await mkdir(binDir, { recursive: true }); - - const { goos, goarch } = GO_TARGETS[target.bunTarget]; - const outfile = path.join(binDir, `supabase-go${target.ext}`); - - console.log(`[${target.pkg}] Compiling Go CLI (${goos}/${goarch})...`); - const ldflagParts = ["-s", "-w", `-X github.com/supabase/cli/internal/utils.Version=${version}`]; - const { SENTRY_DSN, POSTHOG_API_KEY, POSTHOG_ENDPOINT } = process.env; - if (SENTRY_DSN) { - ldflagParts.push(`-X github.com/supabase/cli/internal/utils.SentryDsn=${SENTRY_DSN}`); - } - if (POSTHOG_API_KEY) { - ldflagParts.push(`-X github.com/supabase/cli/internal/utils.PostHogAPIKey=${POSTHOG_API_KEY}`); - } - if (POSTHOG_ENDPOINT) { - ldflagParts.push( - `-X github.com/supabase/cli/internal/utils.PostHogEndpoint=${POSTHOG_ENDPOINT}`, - ); - } - const goLdflags = ldflagParts.join(" "); - await $`go build -trimpath -ldflags=${goLdflags} -o ${outfile} .`.cwd(goSource).env({ - ...process.env, - GOOS: goos, - GOARCH: goarch, - CGO_ENABLED: "0", - }); - console.log(`[${target.pkg}] Go binary done.`); -} - /** * Decides how to sign macOS binaries. `rcodesign` signs Mach-O binaries from Linux, so signing * runs inline on this build runner; falls back to "off" with a warning unless @@ -214,11 +171,9 @@ async function signDarwinBinaries(mode: SignMode) { console.log(`[${target.pkg}] Ad-hoc signing ${binary} (${identifier})...`); // No key material, so rcodesign produces an ad-hoc signature, equivalent to - // `codesign --sign -`, replacing Bun/Go's linker-signed one. + // `codesign --sign -`, replacing Bun's linker-signed one. await $`rcodesign sign --binary-identifier ${identifier} ${binPath}`; - // Matches the identifier's whole value, so the SFE's `com.supabase.cli` can't satisfy - // the sidecar's `com.supabase.cli-go`, and confirms the signature is no longer linker-signed. const info = await $`rcodesign print-signature-info ${binPath}`.text(); const signedIdentifier = info.match(/^\s*identifier:\s*(\S+)\s*$/m)?.[1]; if (signedIdentifier !== identifier) { @@ -240,21 +195,18 @@ async function archiveTarget(target: (typeof TARGETS)[number]) { console.log(`[${target.pkg}] Creating archive ${target.archive}...`); if (target.archive.endsWith(".zip")) { - const files = [ - path.join(binDir, `supabase${target.ext}`), - path.join(binDir, `supabase-go${target.ext}`), - ]; + const files = [path.join(binDir, `supabase${target.ext}`)]; await $`zip -j ${archivePath} ${files}`; // setup-cli and other download clients always fetch a .tar.gz, even on Windows, so // publish one alongside the .zip. See #5257. const tarArchive = target.archive.replace(/\.zip$/, ".tar.gz"); const tarArchivePath = path.join(distDir, tarArchive); - const tarFiles = [`supabase${target.ext}`, `supabase-go${target.ext}`]; + const tarFiles = [`supabase${target.ext}`]; console.log(`[${target.pkg}] Creating archive ${tarArchive}...`); await $`tar -czf ${tarArchivePath} -C ${binDir} ${tarFiles}`; } else { - const files = [`supabase${target.ext}`, `supabase-go${target.ext}`]; + const files = [`supabase${target.ext}`]; await $`tar -czf ${archivePath} -C ${binDir} ${files}`; } } @@ -276,19 +228,6 @@ async function buildMuslBinaries() { }, }); - // The Go binary is fully static (CGO_ENABLED=0), so the glibc build works on musl too; - // copy it into the musl package so GoProxy finds supabase-go there. - const glibcTarget = TARGETS.find( - (candidate) => "nfpmArch" in candidate && candidate.nfpmArch === target.nfpmArch, - ); - if (!glibcTarget) { - throw new Error(`No glibc Linux target found for musl arch ${target.nfpmArch}`); - } - const src = path.join(root, "packages", glibcTarget.pkg, "bin", "supabase-go"); - const dst = path.join(binDir, "supabase-go"); - console.log(`[${target.pkg}] Copying Go binary from ${glibcTarget.pkg}...`); - await copyFile(src, dst); - console.log(`[${target.pkg}] Done.`); }), ); @@ -308,11 +247,8 @@ async function buildLinuxPackages(version: string) { const outPath = path.join(distDir, outFile); const binDir = fmt === "apk" ? muslBinDir : glibcBinDir; - // The Go binary is fully static, so apk (musl) still references supabase-go - // from the glibc dir where it was built. const contents: Array<{ src: string; dst: string }> = [ { src: path.join(binDir, "supabase"), dst: "/usr/bin/supabase" }, - { src: path.join(glibcBinDir, "supabase-go"), dst: "/usr/bin/supabase-go" }, ]; const nfpmConfig: Record = { @@ -383,9 +319,6 @@ console.log(`Building the CLI for ${TARGETS.length} targets...\n`); await Promise.all(TARGETS.map(buildTarget)); -console.log("\nCompiling Go CLI for all targets..."); -await Promise.all(TARGETS.map(buildGoTarget)); - // Must run before archiveTarget / buildLinuxPackages / generateChecksums so every // distribution channel ships the signed bytes. const signMode = resolveSignMode(); diff --git a/apps/cli/scripts/macos-signing.ts b/apps/cli/scripts/macos-signing.ts index 13b3037a63..b5d2656ae8 100644 --- a/apps/cli/scripts/macos-signing.ts +++ b/apps/cli/scripts/macos-signing.ts @@ -1,14 +1,12 @@ /** * macOS code-signing identifiers, shared by the signer (`build.ts`) and its verifier so both - * agree. `bun build --compile` and the Go linker emit an ad-hoc signature that macOS 26+ AMFI - * SIGKILLs at launch (GitHub #5556); re-signing with a full ad-hoc signature fixes it without - * Apple credentials. + * agree. `bun build --compile` emits an ad-hoc signature that macOS 26+ AMFI SIGKILLs at launch + * (GitHub #5556); re-signing with a full ad-hoc signature fixes it without Apple credentials. */ -export type MacBinaryName = "supabase" | "supabase-go"; +export type MacBinaryName = "supabase"; export const MACOS_IDENTIFIERS: Record = { supabase: "com.supabase.cli", - "supabase-go": "com.supabase.cli-go", }; /** @@ -16,10 +14,10 @@ export const MACOS_IDENTIFIERS: Record = { * macOS binary, so callers verifying an arbitrary path fail closed. */ export function macIdentifierFor(binary: string): string | undefined { - return binary === "supabase" || binary === "supabase-go" ? MACOS_IDENTIFIERS[binary] : undefined; + return binary === "supabase" ? MACOS_IDENTIFIERS[binary] : undefined; } -/** The macOS binaries shipped for the CLI: the Bun binary and its Go sidecar. */ +/** The macOS binaries shipped for the CLI. */ export function darwinBinaries(): MacBinaryName[] { - return ["supabase", "supabase-go"]; + return ["supabase"]; } diff --git a/apps/cli/scripts/render-service-dockerfile.ts b/apps/cli/scripts/render-service-dockerfile.ts index 6c6c114eb6..50a877c39e 100644 --- a/apps/cli/scripts/render-service-dockerfile.ts +++ b/apps/cli/scripts/render-service-dockerfile.ts @@ -1,11 +1,10 @@ // Rewrites the slim-capable `FROM ... AS ` lines of -// apps/cli/src/shared/services/Dockerfile (and its byte-identical Go copy, -// apps/cli-go/pkg/config/templates/Dockerfile) in place, from the stack catalog +// apps/cli/src/shared/services/Dockerfile in place, from the stack catalog // (packages/stack/src/Artifacts.ts): each such line's tag comes from that service's catalog pin // `upstreamImage`. Every other line — comments, kong, the Postgres 14 pin (no slim build), and // the one-shot job images — is hand- or Dependabot-managed and left byte-for-byte untouched. // -// bun apps/cli/scripts/render-service-dockerfile.ts # writes both files +// bun apps/cli/scripts/render-service-dockerfile.ts # writes the file // bun apps/cli/scripts/render-service-dockerfile.ts --check # fails on drift, writes nothing // // The `--check` mode is what CI runs (as a `render-service-dockerfile.unit.test.ts` assertion) to @@ -18,9 +17,6 @@ import { catalogPins, type ArtifactPin } from "@supabase/stack/internal/artifact export const TS_DOCKERFILE_PATH = fileURLToPath( new URL("../src/shared/services/Dockerfile", import.meta.url), ); -export const GO_DOCKERFILE_PATH = fileURLToPath( - new URL("../../cli-go/pkg/config/templates/Dockerfile", import.meta.url), -); type CatalogEntry = ReturnType[number]; @@ -156,32 +152,19 @@ async function main(argv: ReadonlyArray): Promise { const rendered = renderDockerfile(current); if (check) { - const go = await Bun.file(GO_DOCKERFILE_PATH).text(); - let failed = false; if (current !== rendered) { console.log( `::error ::${TS_DOCKERFILE_PATH} has drifted from packages/stack/src/Artifacts.ts. Run ` + "`bun apps/cli/scripts/render-service-dockerfile.ts` and commit the result.", ); - failed = true; - } - if (go !== rendered) { - console.log( - `::error ::${GO_DOCKERFILE_PATH} is not a byte copy of ${TS_DOCKERFILE_PATH}. Run ` + - "`bun apps/cli/scripts/render-service-dockerfile.ts` and commit the result.", - ); - failed = true; - } - if (failed) { process.exit(1); } - console.log("Both Dockerfiles match the catalog."); + console.log("The Dockerfile matches the catalog."); return; } await Bun.write(TS_DOCKERFILE_PATH, rendered); - await Bun.write(GO_DOCKERFILE_PATH, rendered); - console.log(`Regenerated ${TS_DOCKERFILE_PATH} and ${GO_DOCKERFILE_PATH} from the catalog.`); + console.log(`Regenerated ${TS_DOCKERFILE_PATH} from the catalog.`); } if (import.meta.main) { diff --git a/apps/cli/scripts/render-service-dockerfile.unit.test.ts b/apps/cli/scripts/render-service-dockerfile.unit.test.ts index 029a235af8..41e45bb9ed 100644 --- a/apps/cli/scripts/render-service-dockerfile.unit.test.ts +++ b/apps/cli/scripts/render-service-dockerfile.unit.test.ts @@ -1,11 +1,7 @@ import { readFileSync } from "node:fs"; import { describe, expect, test } from "vitest"; -import { - GO_DOCKERFILE_PATH, - TS_DOCKERFILE_PATH, - renderDockerfile, -} from "./render-service-dockerfile.ts"; +import { TS_DOCKERFILE_PATH, renderDockerfile } from "./render-service-dockerfile.ts"; // Both against the real, checked-in Dockerfile and the real catalog — no hardcoded version // literal, so a catalog bump never makes this drift-detection test itself go stale. @@ -20,13 +16,6 @@ describe("renderDockerfile against the real catalog and Dockerfile", () => { expect(renderDockerfile(currentTsDockerfile)).toBe(renderDockerfile(currentTsDockerfile)); }); - // The Go tree still `go:embed`s its own copy for a dependency that hasn't been removed yet; - // this is the single place that keeps the two copies in sync until apps/cli-go is deleted. - test("the Go tree's embedded Dockerfile is a byte copy of the TS-owned one", () => { - const goDockerfile = readFileSync(GO_DOCKERFILE_PATH, "utf8"); - expect(goDockerfile).toBe(currentTsDockerfile); - }); - test("detects drift when a generated line is hand-edited", () => { // A hand-edit to a generated tag (bumping gotrue without touching the catalog) must not // round-trip back to itself — this is the drift check's whole point. diff --git a/apps/cli/scripts/update-homebrew.ts b/apps/cli/scripts/update-homebrew.ts index 942cf61a0d..86e8d08a61 100644 --- a/apps/cli/scripts/update-homebrew.ts +++ b/apps/cli/scripts/update-homebrew.ts @@ -37,13 +37,7 @@ const distDir = path.join(root, "dist"); // "SupabaseAT2"), but the installed binary is always `supabase`. const className = homebrewClassName(name); -// The Go sidecar is looked up by exact filename next to the running binary, so it must install -// under its original name; `if File.exist?` keeps the formula working when a build ships only -// the CLI binary. -const installBlock = [ - ` bin.install "supabase"`, - ` bin.install "supabase-go" if File.exist?("supabase-go")`, -].join("\n"); +const installBlock = [` bin.install "supabase"`].join("\n"); const conflictsBlock = homebrewConflictsLine(name); const conflictsSection = conflictsBlock ? `${conflictsBlock}\n\n` : ""; diff --git a/apps/cli/src/SIDE_EFFECTS_TEMPLATE.md b/apps/cli/src/SIDE_EFFECTS_TEMPLATE.md index 4bc1f85932..cfb21c9fa4 100644 --- a/apps/cli/src/SIDE_EFFECTS_TEMPLATE.md +++ b/apps/cli/src/SIDE_EFFECTS_TEMPLATE.md @@ -77,12 +77,6 @@ ## Telemetry Events Fired | Event | When | Notable properties / groups | diff --git a/apps/cli/src/cli/root.ts b/apps/cli/src/cli/root.ts index a566110d36..7604fde4ff 100644 --- a/apps/cli/src/cli/root.ts +++ b/apps/cli/src/cli/root.ts @@ -54,7 +54,6 @@ import { whoamiCommand } from "../commands/whoami/whoami.command.ts"; import { CLI_VERSION, cliBuildChannel } from "../shared/cli/version.ts"; import { outputLayerFor } from "../shared/output/output.layer.ts"; import { quietProgressTextOutputLayer } from "../output/quiet-progress-text-output.layer.ts"; -import { makeGoProxyLayer } from "../command-internal/go-proxy.layer.ts"; import { AiTool } from "../shared/telemetry/ai-tool.service.ts"; import { aiToolLayer } from "../shared/telemetry/ai-tool.layer.ts"; import { CliArgs } from "../shared/cli/cli-args.service.ts"; @@ -65,16 +64,8 @@ import type { OutputFormat } from "../shared/output/types.ts"; import { GLOBAL_FLAGS, AgentFlag, - CreateTicketFlag, - DebugFlag, - DnsResolverFlag, - ExperimentalFlag, - NetworkIdFlag, OutputFlag, OutputFormatFlag, - ProfileFlag, - WorkdirFlag, - YesFlag, } from "../command-internal/global-flags.ts"; // The stack backend's `start`/`status`/`stop` are the same commands as `stack @@ -208,15 +199,7 @@ export const rootCommandForFeatures = (options: RootCommandFeatures = {}) => Layer.unwrap( Effect.gen(function* () { const explicitOutputFormat = yield* OutputFormatFlag; - const goOutput = yield* OutputFlag; - const profile = yield* ProfileFlag; - const debug = yield* DebugFlag; - const workdir = yield* WorkdirFlag; - const experimental = yield* ExperimentalFlag; - const networkId = yield* NetworkIdFlag; - const yes = yield* YesFlag; - const dnsResolver = yield* DnsResolverFlag; - const createTicket = yield* CreateTicketFlag; + const resourceOutput = yield* OutputFlag; const agent = yield* AgentFlag; const cliArgs = yield* CliArgs; @@ -226,34 +209,21 @@ export const rootCommandForFeatures = (options: RootCommandFeatures = {}) => const outputFormat = resolveAgentOutputFormat({ explicitOutputFormat, agentDefaultOutputFormat: agentDefaultOutputFormatFor(options, cliArgs.args), - goOutputFormat: goOutput, + goOutputFormat: resourceOutput, agentOverride: agent, detectedAgentName: aiTool.name, isBuiltInTextRequest: isBuiltInTextRequest(cliArgs.args), }); - const globalArgs: string[] = []; - if (Option.isSome(goOutput)) { - globalArgs.push("--output", goOutput.value); - } else if (outputFormat !== "text") { - globalArgs.push("--output", "json"); - } - if (profile !== "supabase") globalArgs.push("--profile", profile); - if (debug) globalArgs.push("--debug"); - if (Option.isSome(workdir)) globalArgs.push("--workdir", workdir.value); - if (experimental) globalArgs.push("--experimental"); - if (Option.isSome(networkId)) globalArgs.push("--network-id", networkId.value); - if (yes) globalArgs.push("--yes"); - if (dnsResolver !== "native") globalArgs.push("--dns-resolver", dnsResolver); - if (createTicket) globalArgs.push("--create-ticket"); - if (agent !== "auto") globalArgs.push("--agent", agent); - // Machine formats keep the text layer's error rendering but suppress the progress // spinner, which would otherwise corrupt the stdout payload. `-o pretty`/`-o table` // (db query's human default) and no `-o` keep the normal text/json layers. - const goFmt = Option.getOrUndefined(goOutput); - const isGoMachineFormat = goFmt !== undefined && goFmt !== "pretty" && goFmt !== "table"; - const outputLayer = isGoMachineFormat + const resourceFormat = Option.getOrUndefined(resourceOutput); + const isMachineResourceFormat = + resourceFormat !== undefined && + resourceFormat !== "pretty" && + resourceFormat !== "table"; + const outputLayer = isMachineResourceFormat ? quietProgressTextOutputLayer : outputLayerFor(outputFormat); @@ -262,7 +232,6 @@ export const rootCommandForFeatures = (options: RootCommandFeatures = {}) => ? Layer.empty : stackBackendLayer(options.stackBackend), outputLayer, - makeGoProxyLayer({ globalArgs, parentOwnsCapturedSuccessTail: true }), ); }), ), diff --git a/apps/cli/src/command-internal/db-pull-run.errors.ts b/apps/cli/src/command-internal/db-pull-run.errors.ts index 6717bf6520..0a61e61212 100644 --- a/apps/cli/src/command-internal/db-pull-run.errors.ts +++ b/apps/cli/src/command-internal/db-pull-run.errors.ts @@ -30,8 +30,7 @@ export class DbPullInSyncError extends Data.TaggedError("DbPullInSyncError")<{ /** * Explains the non-zero exit instead of letting `Output.fail` append the generic "Try * rerunning the command with --debug" footer: an in-sync database is a finding, not a failure - * to troubleshoot. See `docs/go-cli-divergences.md` for the established message/exit-code - * contract this preserves. + * to troubleshoot. */ readonly suggestion: string; }> { diff --git a/apps/cli/src/command-internal/db-pull-run.ts b/apps/cli/src/command-internal/db-pull-run.ts index caf4e0ced2..a583aa810e 100644 --- a/apps/cli/src/command-internal/db-pull-run.ts +++ b/apps/cli/src/command-internal/db-pull-run.ts @@ -94,8 +94,7 @@ const DEPRECATION_LINE = /** * Explains the in-sync non-zero exit instead of the generic "Try rerunning the command with - * --debug…" footer, which would read like a crash for what is really a finding. See - * `docs/go-cli-divergences.md` for the established message/exit-code contract. + * --debug…" footer, which would read like a crash for what is really a finding. */ const IN_SYNC_SUGGESTION = "The remote database is already in sync with your local migrations — nothing to pull."; diff --git a/apps/cli/src/command-internal/diff-engine.ts b/apps/cli/src/command-internal/diff-engine.ts index e0ebc1b3a5..b88a455853 100644 --- a/apps/cli/src/command-internal/diff-engine.ts +++ b/apps/cli/src/command-internal/diff-engine.ts @@ -18,18 +18,16 @@ export function shouldUsePgDelta(inputs: { } /** - * Reports whether `db diff` should run in pg-delta mode. An explicit `--use-migra`, - * `--use-pgadmin`, or `--use-pg-schema` is an authoritative rollback that clears pg-delta mode; - * `--use-migra` defaults to true, so only an explicit pass (`useMigraChanged`) counts as opting - * out. + * Reports whether `db diff` should run in pg-delta mode. An explicit `--use-migra` or + * `--use-pgadmin` is an authoritative rollback that clears pg-delta mode; `--use-migra` defaults + * to true, so only an explicit pass (`useMigraChanged`) counts as opting out. */ export function resolveDiffEngine(inputs: { readonly useMigraChanged: boolean; readonly usePgAdmin: boolean; - readonly usePgSchema: boolean; readonly pgDeltaDefault: boolean; }): boolean { - if (inputs.useMigraChanged || inputs.usePgAdmin || inputs.usePgSchema) { + if (inputs.useMigraChanged || inputs.usePgAdmin) { return false; } return inputs.pgDeltaDefault; diff --git a/apps/cli/src/command-internal/diff-engine.unit.test.ts b/apps/cli/src/command-internal/diff-engine.unit.test.ts index 7f1a7998b9..0c24c29cc4 100644 --- a/apps/cli/src/command-internal/diff-engine.unit.test.ts +++ b/apps/cli/src/command-internal/diff-engine.unit.test.ts @@ -29,7 +29,6 @@ describe("resolveDiffEngine", () => { const base = { useMigraChanged: false, usePgAdmin: false, - usePgSchema: false, pgDeltaDefault: true, }; @@ -45,10 +44,6 @@ describe("resolveDiffEngine", () => { it("--use-pgadmin clears pg-delta mode", () => { expect(resolveDiffEngine({ ...base, usePgAdmin: true })).toBe(false); }); - - it("--use-pg-schema clears pg-delta mode", () => { - expect(resolveDiffEngine({ ...base, usePgSchema: true })).toBe(false); - }); }); describe("resolvePullDiffEngine", () => { diff --git a/apps/cli/src/command-internal/global-flags.ts b/apps/cli/src/command-internal/global-flags.ts index 2348e9a7cf..df8b051ac9 100644 --- a/apps/cli/src/command-internal/global-flags.ts +++ b/apps/cli/src/command-internal/global-flags.ts @@ -99,10 +99,13 @@ export const DnsResolverFlag = GlobalFlag.setting("dns-resolver")({ ), }); +// Kept parsed (and hidden) only so passing it fails with the removal error from +// `withCommandTelemetry` instead of an unknown-flag parse error. export const CreateTicketFlag = GlobalFlag.setting("create-ticket")({ flag: Flag.boolean("create-ticket").pipe( - Flag.withDescription("create a support ticket for any CLI error"), + Flag.withDescription("Removed: report CLI problems with `supabase issue bug` instead."), Flag.withDefault(false), + Flag.withHidden, ), }); diff --git a/apps/cli/src/command-internal/go-child-exit.error.ts b/apps/cli/src/command-internal/go-child-exit.error.ts deleted file mode 100644 index 511f1a8d9f..0000000000 --- a/apps/cli/src/command-internal/go-child-exit.error.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Data, Runtime } from "effect"; - -import { - actionability, - type CliErrorActionabilityDeclaration, - ErrorActionabilityId, -} from "../shared/telemetry/error-actionability.ts"; - -/** - * A spawned `supabase-go` child process exited non-zero, or couldn't be spawned at all. - * - * Carries the exit code through `Runtime.errorExitCode` so it survives every `Effect.ensuring` - * finalizer up to `runCli`'s own exit call, instead of exiting the process directly and skipping - * them. `runCli` also special-cases this class to skip its own generic stderr line, since the - * child already wrote its own failure detail there. `exitCode` must be a real non-zero status; - * every construction site guards this before creating the error. - */ -export class GoChildExitError extends Data.TaggedError("GoChildExitError")<{ - readonly exitCode: number; - readonly message: string; -}> { - override readonly [Runtime.errorExitCode] = this.exitCode; - - get [ErrorActionabilityId](): CliErrorActionabilityDeclaration { - return actionability.unknown; - } -} diff --git a/apps/cli/src/command-internal/go-proxy-invocation.ts b/apps/cli/src/command-internal/go-proxy-invocation.ts deleted file mode 100644 index eb0abfe66f..0000000000 --- a/apps/cli/src/command-internal/go-proxy-invocation.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Context, Effect, Layer, Ref } from "effect"; - -interface GoProxyInvocationShape { - readonly markDelegated: Effect.Effect; - readonly wasDelegated: Effect.Effect; -} - -export class GoProxyInvocation extends Context.Service()( - "supabase/cli/GoProxyInvocation", -) {} - -export const goProxyInvocationLayer = Layer.effect( - GoProxyInvocation, - Effect.gen(function* () { - const delegated = yield* Ref.make(false); - return GoProxyInvocation.of({ - markDelegated: Ref.set(delegated, true), - wasDelegated: Ref.get(delegated), - }); - }), -); diff --git a/apps/cli/src/command-internal/go-proxy.layer.ts b/apps/cli/src/command-internal/go-proxy.layer.ts deleted file mode 100644 index 553d2c9acb..0000000000 --- a/apps/cli/src/command-internal/go-proxy.layer.ts +++ /dev/null @@ -1,305 +0,0 @@ -import { existsSync } from "node:fs"; -import { createRequire } from "node:module"; -import os from "node:os"; -import path from "node:path"; -import process from "node:process"; -import { Effect, Layer, Option, Stream } from "effect"; -import * as ChildProcess from "effect/unstable/process/ChildProcess"; -import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"; -import { CLI_VERSION } from "../shared/cli/version.ts"; -import { ProcessControl } from "../shared/runtime/process-control.service.ts"; -import { GoChildExitError } from "./go-child-exit.error.ts"; -import { GoProxyInvocation } from "./go-proxy-invocation.ts"; -import { GoProxy } from "./go-proxy.service.ts"; -import { withProcessSpan } from "../shared/telemetry/spans.ts"; - -const markDelegated = Effect.serviceOption(GoProxyInvocation).pipe( - Effect.flatMap((invocation) => - Option.isSome(invocation) ? invocation.value.markDelegated : Effect.void, - ), -); - -const PLATFORM_CANDIDATES: Partial>>>> = - { - darwin: { arm64: ["darwin-arm64"], x64: ["darwin-x64"] }, - linux: { - arm64: ["linux-arm64", "linux-arm64-musl"], - x64: ["linux-x64", "linux-x64-musl"], - }, - win32: { arm64: ["windows-arm64"], x64: ["windows-x64"] }, - }; - -const require = createRequire(import.meta.url); - -/** - * Outcome of looking up `supabase-go`. `notFound` carries every location checked, so the error can - * be specific about what was tried, and callers never silently fall back to `supabase` on PATH - * (which would resolve to this shim itself and fork-bomb). - */ -export type BinaryResolution = - | { readonly found: string } - | { readonly notFound: ReadonlyArray }; - -function resolveBinary(): BinaryResolution { - const tried: string[] = []; - - const envBin = process.env["SUPABASE_GO_BINARY"]; - if (envBin) return { found: envBin }; - tried.push("$SUPABASE_GO_BINARY (unset)"); - - const ext = process.platform === "win32" ? ".exe" : ""; - - // When running as a compiled standalone binary (exec'd by the base shim), process.execPath is - // this binary's own path; look for supabase-go co-located next to it. - const colocated = path.join(path.dirname(process.execPath), `supabase-go${ext}`); - if (existsSync(colocated)) return { found: colocated }; - tried.push(`${colocated} (not found alongside the shim)`); - - // When running from source, resolve via installed npm packages. - // Guard with existsSync — in dev the workspace stub packages exist but their bin/ is empty. - const candidates = PLATFORM_CANDIDATES[process.platform]?.[os.arch()] ?? []; - for (const suffix of candidates) { - try { - const pkgPath = path.dirname(require.resolve(`@supabase/cli-${suffix}/package.json`)); - const bin = path.join(pkgPath, "bin", `supabase-go${ext}`); - if (existsSync(bin)) return { found: bin }; - tried.push(`${bin} (npm package present, binary missing)`); - } catch { - tried.push(`@supabase/cli-${suffix} (npm package not installed)`); - } - } - - return { notFound: tried }; -} - -/** - * Builds a `curl | tar` install snippet for the host platform, using the version baked into this - * shim at build time. Returns null when there's no concrete release URL (dev build) or the host - * arch isn't one the release pipeline targets. - */ -function reinstallTarballSnippet(): ReadonlyArray | null { - if (CLI_VERSION === "0.0.0-dev") return null; - const archSuffix = process.arch === "x64" ? "amd64" : process.arch === "arm64" ? "arm64" : null; - if (archSuffix === null) return null; - // Node's `process.platform` is `win32`; GitHub release assets use the modern `windows` slug. - const osSlug = process.platform === "win32" ? "windows" : process.platform; - const asset = `supabase_${CLI_VERSION}_${osSlug}_${archSuffix}.tar.gz`; - return [ - ` mkdir -p "$HOME/.local/share/supabase"`, - ` curl -sL https://github.com/supabase/cli/releases/download/v${CLI_VERSION}/${asset} \\`, - ` | tar -xzf - -C "$HOME/.local/share/supabase"`, - ` export PATH="$HOME/.local/share/supabase:$PATH"`, - ]; -} - -export function formatGoBinaryNotFoundError(tried: ReadonlyArray): string { - const snippet = reinstallTarballSnippet(); - return [ - "Could not find the `supabase-go` binary.", - "", - "The Supabase CLI ships as two co-located binaries: `supabase` (this shim)", - "and `supabase-go` (the Go CLI that the shim forwards to). The shim looked", - "for `supabase-go` in:", - "", - ...tried.map((line) => ` • ${line}`), - "", - "To fix, do one of:", - " • Extract the release tarball into a directory and add the directory to", - " PATH (do not move `supabase` somewhere `supabase-go` doesn't follow).", - ...(snippet === null ? [] : [" For example, on this host:", "", ...snippet, ""]), - " • Install via npm: `npm i -g supabase`.", - " • Set SUPABASE_GO_BINARY to the absolute path of `supabase-go`.", - ].join("\n"); -} - -/** - * Creates a GoProxy layer. - * - * In production use `goProxyLayer` (no options). - * - * In tests pass `{ cwd, env }` to run the binary in an isolated directory - * with a controlled SUPABASE_HOME, so tests don't pollute the real home dir: - * - * makeGoProxyLayer({ - * cwd: projectDir, - * env: { SUPABASE_HOME: homeDir, SUPABASE_NO_KEYRING: "1", SUPABASE_TELEMETRY_DISABLED: "1" }, - * }) - * - * @public - */ -export function makeGoProxyLayer(opts?: { - cwd?: string; - /** - * Extra env for every spawned child. - */ - env?: Record; - globalArgs?: ReadonlyArray; - /** - * Let the parent emit its success tail after re-emitting captured stdout. - */ - parentOwnsCapturedSuccessTail?: boolean; - /** - * Override binary resolution. Primarily a test seam so specs don't have to - * mutate `process.env.SUPABASE_GO_BINARY` or stub the filesystem: - * - `string` — treat as the resolved Go binary path. - * - `{ notFound: [...] }` — simulate the not-found path; `.exec` will print - * the diagnostic and fail with a non-zero exit code. - * - * In production, leave unset and let `resolveBinary()` pick the right - * artifact for the host platform. - */ - binary?: string | BinaryResolution; -}): Layer.Layer { - return Layer.effect( - GoProxy, - Effect.gen(function* () { - const processControl = yield* ProcessControl; - const spawner = yield* ChildProcessSpawner; - const resolved: BinaryResolution = - typeof opts?.binary === "string" - ? { found: opts.binary } - : (opts?.binary ?? resolveBinary()); - const globalArgs = opts?.globalArgs ?? []; - - return GoProxy.of({ - exec: (args, execOpts) => - Effect.scoped( - Effect.gen(function* () { - if (!("found" in resolved)) { - yield* Effect.sync(() => { - process.stderr.write(`${formatGoBinaryNotFoundError(resolved.notFound)}\n`); - }); - return yield* Effect.fail( - new GoChildExitError({ - exitCode: 1, - message: "supabase-go binary not found", - }), - ); - } - const binary = resolved.found; - - // Hold terminal signals on the parent for the child's lifetime: the child spawner - // defaults to `detached: true` on non-Windows, which would put the child in its own - // process group and miss tty signals, so `detached: false` below lets Ctrl+C reach - // the Go binary directly. Without a listener, Bun/Node would also default-terminate - // the parent on SIGINT before the child's real exit code is known. - yield* processControl.holdSignals(["SIGINT", "SIGTERM", "SIGHUP"]); - // Only an instrumented caller that delegates the whole command suppresses child - // telemetry: the parent already emits `cli_command_executed` there. Pure proxy - // commands have no parent event, so the child must stay free to report. - const env = { - ...opts?.env, - ...execOpts?.env, - ...(execOpts?.suppressChildTelemetry === true - ? { SUPABASE_TELEMETRY_DISABLED: "1" } - : {}), - }; - const argv = [...globalArgs, ...args]; - const exitCode = yield* withProcessSpan( - "GoProxy.exec", - { executable: binary, argCount: argv.length }, - (traceEnv) => - spawner - .exitCode( - ChildProcess.make(binary, argv, { - cwd: execOpts?.cwd ?? opts?.cwd, - env: { ...env, ...traceEnv }, - extendEnv: true, - stdin: "inherit", - stdout: "inherit", - stderr: "inherit", - detached: false, - }), - ) - .pipe(Effect.orDie), - ); - if (exitCode !== 0) { - return yield* Effect.fail( - new GoChildExitError({ - exitCode, - message: `supabase-go exited with code ${exitCode} (see stderr for details)`, - }), - ); - } - yield* markDelegated; - }), - ), - execCapture: (args, execOpts) => - Effect.scoped( - Effect.gen(function* () { - if (!("found" in resolved)) { - yield* Effect.sync(() => { - process.stderr.write(`${formatGoBinaryNotFoundError(resolved.notFound)}\n`); - }); - return yield* Effect.fail( - new GoChildExitError({ - exitCode: 1, - message: "supabase-go binary not found", - }), - ); - } - const binary = resolved.found; - yield* processControl.holdSignals(["SIGINT", "SIGTERM", "SIGHUP"]); - // Same rule as `exec`: only an instrumented caller that owns the - // parent `cli_command_executed` event suppresses child telemetry. - const env = { - ...opts?.env, - ...execOpts?.env, - ...(execOpts?.suppressChildTelemetry === true - ? { SUPABASE_TELEMETRY_DISABLED: "1" } - : {}), - ...(opts?.parentOwnsCapturedSuccessTail === true - ? { SUPABASE_NO_UPDATE_NOTIFIER: "1" } - : {}), - }; - // Capture stdout while keeping stderr inherited, so progress still reaches the user - // while stdout is collected for wrapping. Callers pass stdin: "ignore" to give the - // child a non-TTY stdin so it can't block on a prompt before the wrapper emits its - // machine-output envelope. - const argv = [...globalArgs, ...args]; - const { captured, exitCode } = yield* withProcessSpan( - "GoProxy.execCapture", - { executable: binary, argCount: argv.length }, - (traceEnv) => - Effect.gen(function* () { - const handle = yield* spawner - .spawn( - ChildProcess.make(binary, argv, { - cwd: execOpts?.cwd ?? opts?.cwd, - env: { ...env, ...traceEnv }, - extendEnv: true, - stdin: execOpts?.stdin ?? "inherit", - stdout: "pipe", - stderr: "inherit", - detached: false, - }), - ) - .pipe(Effect.orDie); - // Drain stdout fully before awaiting exit so a full pipe buffer can't - // deadlock the child. - const captured = yield* Stream.mkString(Stream.decodeText(handle.stdout)).pipe( - Effect.orDie, - ); - const exitCode = yield* handle.exitCode.pipe(Effect.orDie); - return { captured, exitCode }; - }), - (result) => result.exitCode, - ); - if (exitCode !== 0) { - return yield* Effect.fail( - new GoChildExitError({ - exitCode, - message: `supabase-go exited with code ${exitCode} (see stderr for details)`, - }), - ); - } - if (opts?.parentOwnsCapturedSuccessTail !== true) { - yield* markDelegated; - } - return captured; - }), - ), - }); - }), - ); -} diff --git a/apps/cli/src/command-internal/go-proxy.layer.unit.test.ts b/apps/cli/src/command-internal/go-proxy.layer.unit.test.ts deleted file mode 100644 index 2ea151e9c1..0000000000 --- a/apps/cli/src/command-internal/go-proxy.layer.unit.test.ts +++ /dev/null @@ -1,571 +0,0 @@ -import { describe, expect, it, vi } from "@effect/vitest"; -import { Cause, Deferred, Effect, Exit, Fiber, Layer, Sink, Stream } from "effect"; -import { ChildProcessSpawner } from "effect/unstable/process"; -import { - type CliProcessSignal, - ProcessControl, -} from "../shared/runtime/process-control.service.ts"; -import { GoChildExitError } from "./go-child-exit.error.ts"; -import { GoProxyInvocation, goProxyInvocationLayer } from "./go-proxy-invocation.ts"; -import { GoProxy } from "./go-proxy.service.ts"; -import { formatGoBinaryNotFoundError, makeGoProxyLayer } from "./go-proxy.layer.ts"; - -/** - * Regression tests for SIGINT propagation: Ctrl+C on a proxied long-running command must reach - * the Go sidecar and not lose its exit code. `ChildProcess.make` must be called with - * `detached: false`, and `processControl.holdSignals` must be acquired before spawn (covering - * SIGINT/SIGTERM/SIGHUP) and released on every exit path. - */ - -type CapturedCommand = { - command: string; - args: readonly string[]; - options: { - detached?: boolean; - stdin?: unknown; - stdout?: unknown; - stderr?: unknown; - cwd?: string; - env?: Record; - extendEnv?: boolean; - }; -}; - -type ExitBehavior = - | { kind: "success"; code: number } - | { kind: "never" } - | { kind: "fail"; error: string }; - -type HoldEvent = - | { kind: "acquire"; id: number; signals: ReadonlyArray } - | { kind: "release"; id: number }; - -/** - * Records holdSignals(...) acquire/release transitions. Each acquire gets a monotonically - * increasing id so tests can pair an acquire with its release. - * - * `exit()` here only guards against a regression that reintroduces a direct - * `ProcessControl.exit()` call; it blocks on `Effect.never` since nothing in this file exercises - * it. - */ -function mockProcessControl() { - const holdEvents: HoldEvent[] = []; - const exitCalls: number[] = []; - let nextHoldId = 0; - - const exit = (code: number) => - Effect.sync(() => { - exitCalls.push(code); - }).pipe(Effect.flatMap(() => Effect.never)); - - return { - get holdEvents() { - return holdEvents; - }, - get exitCalls() { - return exitCalls; - }, - layer: Layer.succeed( - ProcessControl, - ProcessControl.of({ - awaitSignal: () => Effect.never, - awaitShutdown: Effect.never, - holdSignals: (signals) => - Effect.acquireRelease( - Effect.sync(() => { - const id = nextHoldId++; - holdEvents.push({ kind: "acquire", id, signals }); - return id; - }), - (id) => - Effect.sync(() => { - holdEvents.push({ kind: "release", id }); - }), - ).pipe(Effect.asVoid), - exit, - setExitCode: () => Effect.void, - getExitCode: Effect.succeed(undefined), - }), - ), - }; -} - -/** - * Builds a mock `ChildProcessSpawner` that records every spawned command and returns a - * controllable exit code. `spawnedBeforeExit` resolves as soon as the spawn is observed, to - * sequence a race-then-interrupt. - */ -function mockSpawner(exit: ExitBehavior, spawnedBeforeExit?: Deferred.Deferred) { - const spawned: CapturedCommand[] = []; - const layer = Layer.succeed( - ChildProcessSpawner.ChildProcessSpawner, - ChildProcessSpawner.make((command: any) => - Effect.sync(() => { - const cmd = command as CapturedCommand & { _tag: string }; - spawned.push({ - command: cmd.command, - args: cmd.args, - options: cmd.options, - }); - if (spawnedBeforeExit !== undefined) { - Deferred.doneUnsafe(spawnedBeforeExit, Effect.void); - } - const exitCode = - exit.kind === "success" - ? Effect.succeed(ChildProcessSpawner.ExitCode(exit.code)) - : exit.kind === "never" - ? Effect.never - : Effect.fail(new Error(exit.error) as any); - return ChildProcessSpawner.makeHandle({ - pid: ChildProcessSpawner.ProcessId(42_424), - exitCode, - isRunning: Effect.succeed(false), - kill: () => Effect.void, - unref: Effect.succeed(Effect.void), - stdin: Sink.drain as any, - stdout: Stream.empty, - stderr: Stream.empty, - all: Stream.empty, - getInputFd: () => Sink.drain as any, - getOutputFd: () => Stream.empty, - }); - }), - ), - ); - return { layer, spawned }; -} - -/** Injected directly via `makeGoProxyLayer({ binary })` so tests don't depend on workspace package state or `process.env`. */ -const TEST_BINARY = "/test/fake-supabase-go"; - -describe("formatGoBinaryNotFoundError", () => { - const TRIED = [ - "$SUPABASE_GO_BINARY (unset)", - "/usr/local/bin/supabase-go (not found alongside the shim)", - "@supabase/cli-linux-x64 (npm package not installed)", - ]; - - it("renders each tried location as a bullet and includes remediation hints", () => { - const message = formatGoBinaryNotFoundError(TRIED); - expect(message).toContain("Could not find the `supabase-go` binary"); - expect(message).toContain(" • $SUPABASE_GO_BINARY (unset)"); - expect(message).toContain(" • /usr/local/bin/supabase-go (not found alongside the shim)"); - expect(message).toContain(" • @supabase/cli-linux-x64 (npm package not installed)"); - expect(message).toContain("npm i -g supabase"); - expect(message).toContain("SUPABASE_GO_BINARY"); - }); - - it("omits the curl|tar snippet on dev builds (no CLI_VERSION baked in)", () => { - const message = formatGoBinaryNotFoundError(TRIED); - expect(message).not.toContain("curl -sL"); - expect(message).toContain("Extract the release tarball"); - }); -}); - -// Instantiates a fresh module with a stubbed CLI_VERSION to assert against a known release -// version + asset filename; nested in its own describe so the module mock doesn't bleed into -// other suites. -describe("formatGoBinaryNotFoundError - pinned snippet", () => { - const TRIED = ["$SUPABASE_GO_BINARY (unset)"]; - const PINNED_VERSION = "2.100.0"; - - async function withMockedHost( - opts: { platform: NodeJS.Platform; arch: NodeJS.Architecture }, - fn: (mod: typeof import("./go-proxy.layer.ts")) => void | Promise, - ): Promise { - vi.resetModules(); - vi.doMock("../shared/cli/version.ts", () => ({ CLI_VERSION: PINNED_VERSION })); - const originalPlatform = process.platform; - const originalArch = process.arch; - Object.defineProperty(process, "platform", { value: opts.platform, configurable: true }); - Object.defineProperty(process, "arch", { value: opts.arch, configurable: true }); - try { - const mod = await import("./go-proxy.layer.ts"); - await fn(mod); - } finally { - Object.defineProperty(process, "platform", { - value: originalPlatform, - configurable: true, - }); - Object.defineProperty(process, "arch", { value: originalArch, configurable: true }); - vi.doUnmock("../cli/version.ts"); - vi.resetModules(); - } - } - - it("renders a copy-pasteable install snippet for linux x64", async () => { - await withMockedHost({ platform: "linux", arch: "x64" }, (mod) => { - const message = mod.formatGoBinaryNotFoundError(TRIED); - expect(message).toContain( - `https://github.com/supabase/cli/releases/download/v${PINNED_VERSION}/supabase_${PINNED_VERSION}_linux_amd64.tar.gz`, - ); - expect(message).toContain(`mkdir -p "$HOME/.local/share/supabase"`); - expect(message).toContain(`export PATH="$HOME/.local/share/supabase:$PATH"`); - }); - }); - - it("maps Node's win32 platform to the release asset's `windows` slug", async () => { - await withMockedHost({ platform: "win32", arch: "x64" }, (mod) => { - const message = mod.formatGoBinaryNotFoundError(TRIED); - expect(message).toContain( - `https://github.com/supabase/cli/releases/download/v${PINNED_VERSION}/supabase_${PINNED_VERSION}_windows_amd64.tar.gz`, - ); - expect(message).not.toContain("win32"); - }); - }); - - it("maps darwin arm64 to the matching release asset", async () => { - await withMockedHost({ platform: "darwin", arch: "arm64" }, (mod) => { - expect(mod.formatGoBinaryNotFoundError(TRIED)).toContain( - `supabase_${PINNED_VERSION}_darwin_arm64.tar.gz`, - ); - }); - }); - - it("omits the snippet on unsupported architectures (no release asset)", async () => { - await withMockedHost({ platform: "linux", arch: "ia32" }, (mod) => { - expect(mod.formatGoBinaryNotFoundError(TRIED)).not.toContain("curl -sL"); - }); - }); -}); - -describe("makeGoProxyLayer", () => { - it.effect("records delegated execution for the parent success trailer", () => { - const spawner = mockSpawner({ kind: "success", code: 0 }); - const pc = mockProcessControl(); - const layer = Layer.mergeAll( - makeGoProxyLayer({ binary: TEST_BINARY }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ), - goProxyInvocationLayer, - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - const invocation = yield* GoProxyInvocation; - - expect(yield* invocation.wasDelegated).toBe(false); - yield* proxy.exec(["migration", "squash", "--local"]); - expect(yield* invocation.wasDelegated).toBe(true); - }).pipe(Effect.provide(layer)); - }); - - it.effect("leaves captured success tails to the parent when configured", () => { - const spawner = mockSpawner({ kind: "success", code: 0 }); - const pc = mockProcessControl(); - const layer = Layer.mergeAll( - makeGoProxyLayer({ - binary: TEST_BINARY, - env: { SUPABASE_NO_UPDATE_NOTIFIER: "0" }, - parentOwnsCapturedSuccessTail: true, - }).pipe(Layer.provide(Layer.mergeAll(spawner.layer, pc.layer))), - goProxyInvocationLayer, - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - const invocation = yield* GoProxyInvocation; - - yield* proxy.execCapture(["db", "diff"], { - env: { SUPABASE_NO_UPDATE_NOTIFIER: "0" }, - }); - - expect(spawner.spawned[0]?.options.env?.SUPABASE_NO_UPDATE_NOTIFIER).toBe("1"); - expect(yield* invocation.wasDelegated).toBe(false); - }).pipe(Effect.provide(layer)); - }); - - it.effect("passes detached:false and inherited stdio to the spawner", () => { - const spawner = mockSpawner({ kind: "success", code: 0 }); - const pc = mockProcessControl(); - const layer = makeGoProxyLayer({ binary: TEST_BINARY, globalArgs: ["--debug"] }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - yield* proxy.exec(["projects", "list"]); - - expect(spawner.spawned).toHaveLength(1); - const captured = spawner.spawned[0]!; - expect(captured.command).toBe(TEST_BINARY); - expect(captured.args).toEqual(["--debug", "projects", "list"]); - expect(captured.options.detached).toBe(false); - expect(captured.options.stdin).toBe("inherit"); - expect(captured.options.stdout).toBe("inherit"); - expect(captured.options.stderr).toBe("inherit"); - expect(captured.options.extendEnv).toBe(true); - }).pipe(Effect.provide(layer)); - }); - - it.effect("leaves child telemetry enabled for pure proxy commands", () => { - const spawner = mockSpawner({ kind: "success", code: 0 }); - const pc = mockProcessControl(); - const layer = makeGoProxyLayer({ binary: TEST_BINARY }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - yield* proxy.exec(["migration", "squash"]); - yield* proxy.execCapture(["gen", "keys"]); - - for (const captured of spawner.spawned) { - expect(captured.options.env ?? {}).not.toHaveProperty("SUPABASE_TELEMETRY_DISABLED"); - } - }).pipe(Effect.provide(layer)); - }); - - it.effect("suppresses child telemetry when the caller owns the parent event", () => { - const spawner = mockSpawner({ kind: "success", code: 0 }); - const pc = mockProcessControl(); - const layer = makeGoProxyLayer({ binary: TEST_BINARY }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - yield* proxy.exec(["db", "pull"], { suppressChildTelemetry: true }); - yield* proxy.execCapture(["db", "diff"], { - env: { CUSTOM: "kept" }, - suppressChildTelemetry: true, - }); - - for (const captured of spawner.spawned) { - expect(captured.options.env).toMatchObject({ SUPABASE_TELEMETRY_DISABLED: "1" }); - } - expect(spawner.spawned[1]?.options.env).toMatchObject({ CUSTOM: "kept" }); - }).pipe(Effect.provide(layer)); - }); - - it.effect("passes the layer's env to children, letting per-call env win", () => { - const spawner = mockSpawner({ kind: "success", code: 0 }); - const pc = mockProcessControl(); - const layer = makeGoProxyLayer({ - binary: TEST_BINARY, - env: { SUPABASE_NO_UPDATE_NOTIFIER: "1" }, - }).pipe(Layer.provide(Layer.mergeAll(spawner.layer, pc.layer))); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - yield* proxy.exec(["projects", "list"]); - yield* proxy.execCapture(["gen", "keys"]); - yield* proxy.exec(["projects", "list"], { env: { SUPABASE_NO_UPDATE_NOTIFIER: "0" } }); - - expect(spawner.spawned[0]?.options.env?.SUPABASE_NO_UPDATE_NOTIFIER).toBe("1"); - expect(spawner.spawned[1]?.options.env?.SUPABASE_NO_UPDATE_NOTIFIER).toBe("1"); - expect(spawner.spawned[2]?.options.env?.SUPABASE_NO_UPDATE_NOTIFIER).toBe("0"); - }).pipe(Effect.provide(layer)); - }); - - it.effect("propagates non-zero exit codes via GoChildExitError", () => { - const spawner = mockSpawner({ kind: "success", code: 7 }); - const pc = mockProcessControl(); - const layer = makeGoProxyLayer({ binary: TEST_BINARY }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - const exit = yield* proxy.exec(["some", "command"]).pipe(Effect.exit); - expect(Exit.isFailure(exit)).toBe(true); - if (Exit.isFailure(exit)) { - const error = Cause.squash(exit.cause); - expect(error).toBeInstanceOf(GoChildExitError); - expect((error as GoChildExitError).exitCode).toBe(7); - } - expect(pc.exitCalls).toEqual([]); - }).pipe(Effect.provide(layer)); - }); - - it.effect("lets an Effect.ensuring finalizer run after a non-zero exit (CLI-1879)", () => { - const spawner = mockSpawner({ kind: "success", code: 5 }); - const pc = mockProcessControl(); - const layer = makeGoProxyLayer({ binary: TEST_BINARY }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ); - let finalizerRan = false; - return Effect.gen(function* () { - const proxy = yield* GoProxy; - yield* proxy.exec(["some", "command"]).pipe( - Effect.ensuring( - Effect.sync(() => { - finalizerRan = true; - }), - ), - Effect.exit, - ); - expect(finalizerRan).toBe(true); - }).pipe(Effect.provide(layer)); - }); - - it.effect("does not call ProcessControl.exit when the Go binary exits zero", () => { - const spawner = mockSpawner({ kind: "success", code: 0 }); - const pc = mockProcessControl(); - const layer = makeGoProxyLayer({ binary: TEST_BINARY }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - yield* proxy.exec(["some", "command"]); - expect(pc.exitCalls).toEqual([]); - }).pipe(Effect.provide(layer)); - }); - - it.effect("calls holdSignals with SIGINT+SIGTERM+SIGHUP before spawning", () => { - const spawner = mockSpawner({ kind: "success", code: 0 }); - const pc = mockProcessControl(); - const layer = makeGoProxyLayer({ binary: TEST_BINARY }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - yield* proxy.exec([]); - - const acquires = pc.holdEvents.filter((e) => e.kind === "acquire"); - expect(acquires).toHaveLength(1); - expect(acquires[0]!.signals).toEqual(["SIGINT", "SIGTERM", "SIGHUP"]); - - expect(spawner.spawned).toHaveLength(1); - expect(pc.holdEvents[0]).toEqual(expect.objectContaining({ kind: "acquire" })); - }).pipe(Effect.provide(layer)); - }); - - it.effect("releases the holdSignals scope on successful exec", () => { - const spawner = mockSpawner({ kind: "success", code: 0 }); - const pc = mockProcessControl(); - const layer = makeGoProxyLayer({ binary: TEST_BINARY }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - yield* proxy.exec([]); - - expect(pc.holdEvents).toEqual([ - { kind: "acquire", id: 0, signals: ["SIGINT", "SIGTERM", "SIGHUP"] }, - { kind: "release", id: 0 }, - ]); - }).pipe(Effect.provide(layer)); - }); - - it.effect("releases the holdSignals scope when the spawner fails", () => { - const spawner = mockSpawner({ kind: "fail", error: "spawn failed" }); - const pc = mockProcessControl(); - const layer = makeGoProxyLayer({ binary: TEST_BINARY }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - // spawner failures are Effect.orDie'd, so we swallow the defect here. - yield* proxy.exec([]).pipe(Effect.exit); - - expect(pc.holdEvents).toContainEqual({ kind: "release", id: 0 }); - }).pipe(Effect.provide(layer)); - }); - - it.effect("releases the holdSignals scope when the fiber is interrupted", () => { - const spawned = Deferred.makeUnsafe(); - const spawner = mockSpawner({ kind: "never" }, spawned); - const pc = mockProcessControl(); - const layer = makeGoProxyLayer({ binary: TEST_BINARY }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - const fiber = yield* proxy.exec([]).pipe(Effect.forkChild({ startImmediately: true })); - yield* Deferred.await(spawned); - - expect(pc.holdEvents).toEqual([ - { kind: "acquire", id: 0, signals: ["SIGINT", "SIGTERM", "SIGHUP"] }, - ]); - - yield* Fiber.interrupt(fiber); - - expect(pc.holdEvents).toEqual([ - { kind: "acquire", id: 0, signals: ["SIGINT", "SIGTERM", "SIGHUP"] }, - { kind: "release", id: 0 }, - ]); - }).pipe(Effect.provide(layer)); - }); - - it.effect( - "prints a diagnostic and fails with exit code 1 when supabase-go cannot be resolved", - () => { - const spawner = mockSpawner({ kind: "success", code: 0 }); - const pc = mockProcessControl(); - const stderr = vi.spyOn(process.stderr, "write").mockImplementation(() => true); - const tried = [ - "$SUPABASE_GO_BINARY (unset)", - "/usr/local/bin/supabase-go (not found alongside the shim)", - ]; - const layer = makeGoProxyLayer({ binary: { notFound: tried } }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - const exit = yield* proxy.exec(["db", "start"]).pipe(Effect.exit); - - expect(spawner.spawned).toHaveLength(0); - expect(Exit.isFailure(exit)).toBe(true); - if (Exit.isFailure(exit)) { - const error = Cause.squash(exit.cause); - expect(error).toBeInstanceOf(GoChildExitError); - expect((error as GoChildExitError).exitCode).toBe(1); - } - expect(pc.exitCalls).toEqual([]); - expect(stderr).toHaveBeenCalledTimes(1); - const written = String(stderr.mock.calls[0]![0]); - expect(written).toContain("Could not find the `supabase-go` binary"); - expect(written).toContain("$SUPABASE_GO_BINARY (unset)"); - expect(written).toContain("/usr/local/bin/supabase-go"); - expect(written).toContain("SUPABASE_GO_BINARY"); - stderr.mockRestore(); - }).pipe(Effect.provide(layer)); - }, - ); - - it.effect( - "execCapture also prints a diagnostic and fails with exit code 1 when supabase-go cannot be resolved", - () => { - const spawner = mockSpawner({ kind: "success", code: 0 }); - const pc = mockProcessControl(); - const stderr = vi.spyOn(process.stderr, "write").mockImplementation(() => true); - const tried = ["$SUPABASE_GO_BINARY (unset)"]; - const layer = makeGoProxyLayer({ binary: { notFound: tried } }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - const exit = yield* proxy.execCapture(["db", "dump"]).pipe(Effect.exit); - - expect(spawner.spawned).toHaveLength(0); - expect(Exit.isFailure(exit)).toBe(true); - if (Exit.isFailure(exit)) { - const error = Cause.squash(exit.cause); - expect(error).toBeInstanceOf(GoChildExitError); - expect((error as GoChildExitError).exitCode).toBe(1); - } - expect(pc.exitCalls).toEqual([]); - expect(stderr).toHaveBeenCalledTimes(1); - stderr.mockRestore(); - }).pipe(Effect.provide(layer)); - }, - ); - - it.effect("opens and closes a fresh hold scope per sequential exec call", () => { - const spawner = mockSpawner({ kind: "success", code: 0 }); - const pc = mockProcessControl(); - const layer = makeGoProxyLayer({ binary: TEST_BINARY }).pipe( - Layer.provide(Layer.mergeAll(spawner.layer, pc.layer)), - ); - return Effect.gen(function* () { - const proxy = yield* GoProxy; - for (let i = 0; i < 3; i++) { - yield* proxy.exec([`call-${i}`]); - } - - expect(pc.holdEvents).toEqual([ - { kind: "acquire", id: 0, signals: ["SIGINT", "SIGTERM", "SIGHUP"] }, - { kind: "release", id: 0 }, - { kind: "acquire", id: 1, signals: ["SIGINT", "SIGTERM", "SIGHUP"] }, - { kind: "release", id: 1 }, - { kind: "acquire", id: 2, signals: ["SIGINT", "SIGTERM", "SIGHUP"] }, - { kind: "release", id: 2 }, - ]); - expect(spawner.spawned).toHaveLength(3); - }).pipe(Effect.provide(layer)); - }); -}); diff --git a/apps/cli/src/command-internal/go-proxy.service.ts b/apps/cli/src/command-internal/go-proxy.service.ts deleted file mode 100644 index 452fc2c51c..0000000000 --- a/apps/cli/src/command-internal/go-proxy.service.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { Effect } from "effect"; -import { Context } from "effect"; -import type { GoChildExitError } from "./go-child-exit.error.ts"; - -interface GoProxyShape { - /** - * Forwards args to the Go binary, inheriting stdio and propagating the exit code. Fails with - * `GoChildExitError` (carrying the exact exit code) on a non-zero exit or an unresolvable - * binary. - * - * `opts.suppressChildTelemetry` disables telemetry in the child; set it only when the caller's - * own command instrumentation already emits `cli_command_executed`, since a pure proxy - * handler's Go child is its only telemetry emitter. - */ - readonly exec: ( - args: ReadonlyArray, - opts?: { - readonly cwd?: string; - readonly env?: Record; - readonly suppressChildTelemetry?: boolean; - }, - ) => Effect.Effect; - - /** - * Like `exec`, but captures the child's stdout and returns it as a string instead of - * inheriting it; stderr stays inherited. Fails with `GoChildExitError` the same way `exec` - * does. - * - * `opts.stdin: "ignore"` gives the child a non-TTY stdin so a prompt (Go's `PromptYesNo`) - * takes its default instead of blocking — required when a machine-output caller delegates a - * command that would otherwise prompt before the JSON envelope is emitted. - */ - readonly execCapture: ( - args: ReadonlyArray, - opts?: { - readonly cwd?: string; - readonly env?: Record; - readonly stdin?: "inherit" | "ignore"; - readonly suppressChildTelemetry?: boolean; - }, - ) => Effect.Effect; -} - -export class GoProxy extends Context.Service()("supabase/cli/GoProxy") {} diff --git a/apps/cli/src/command-internal/removed-command.ts b/apps/cli/src/command-internal/removed-command.ts new file mode 100644 index 0000000000..898dfbbbd2 --- /dev/null +++ b/apps/cli/src/command-internal/removed-command.ts @@ -0,0 +1,68 @@ +import { Data, Effect, Option } from "effect"; +import { + actionability, + type CliErrorActionabilityDeclaration, + ErrorActionabilityId, +} from "../shared/telemetry/error-actionability.ts"; +import { + CommandRuntime, + getCommandRuntimeCommand, +} from "../shared/runtime/command-runtime.service.ts"; +import { TelemetryState } from "../telemetry/telemetry-state.service.ts"; + +/** + * A tombstoned command path or a rejected removed flag. The caller supplies its own + * replacement suggestion; there is no closed vocabulary for it since removal + * suggestions vary per surface. + */ +export class RemovedSurfaceError extends Data.TaggedError("RemovedSurfaceError")<{ + readonly message: string; + readonly suggestion: string; + readonly kind: "command" | "flag"; +}> { + get [ErrorActionabilityId](): CliErrorActionabilityDeclaration { + return { + ...actionability.removedSurface, + fingerprint_suffix: this.kind === "flag" ? "removed_flag" : "removed_command", + }; + } +} + +/** + * Fails with a `RemovedSurfaceError` naming the invoked command path. The caller wraps the + * returned effect in `withCommandTelemetry()` then `withJsonErrorHandling`, matching + * `commit.command.ts`'s composition order, and provides a `CommandRuntime` layer for its path. + * Flushes `TelemetryState` itself (`Effect.ensuring`), since a tombstoned command never reaches + * its own handler's finalizer wiring. + */ +export const removedCommand = (suggestion: string) => + Effect.gen(function* () { + const telemetryState = yield* TelemetryState; + return yield* Effect.gen(function* () { + const commandRuntime = yield* CommandRuntime; + const command = getCommandRuntimeCommand(commandRuntime); + return yield* Effect.fail( + new RemovedSurfaceError({ + message: `supabase ${command} was removed.`, + suggestion, + kind: "command", + }), + ); + }).pipe(Effect.ensuring(telemetryState.flush)); + }); + +/** + * Fails with a `RemovedSurfaceError` for a removed flag on an otherwise-native command. The host + * command already wraps its whole handler in `withCommandTelemetry`, so this is a bare effect. + * Flushes `TelemetryState` itself (`Effect.ensuring`) when the command provides one: call sites + * check this before their own handler reaches its own finalizer wiring further down. + */ +export const removedFlag = (flag: string, suggestion: string) => + Effect.gen(function* () { + const telemetryState = yield* Effect.serviceOption(TelemetryState); + return yield* Effect.fail( + new RemovedSurfaceError({ message: `${flag} was removed.`, suggestion, kind: "flag" }), + ).pipe( + Effect.ensuring(Option.isSome(telemetryState) ? telemetryState.value.flush : Effect.void), + ); + }); diff --git a/apps/cli/src/command-internal/removed-command.unit.test.ts b/apps/cli/src/command-internal/removed-command.unit.test.ts new file mode 100644 index 0000000000..a5b69eb24a --- /dev/null +++ b/apps/cli/src/command-internal/removed-command.unit.test.ts @@ -0,0 +1,101 @@ +import { BunCrypto } from "@effect/platform-bun"; +import { describe, expect, it } from "@effect/vitest"; +import { Effect, Exit, Layer } from "effect"; +import { classifyCliErrorActionability } from "../shared/telemetry/error-actionability.ts"; +import { commandRuntimeLayer } from "../shared/runtime/command-runtime.layer.ts"; +import { mockTelemetryStateTracked } from "../../tests/helpers/command-mocks.ts"; +import { removedCommand, removedFlag, RemovedSurfaceError } from "./removed-command.ts"; + +describe("RemovedSurfaceError actionability", () => { + it("classifies a removed command with the :removed_command fingerprint suffix", () => { + const classified = classifyCliErrorActionability( + new RemovedSurfaceError({ + message: "supabase db branch create was removed.", + suggestion: "Use `supabase branches --help` instead.", + kind: "command", + }), + ); + expect(classified).toMatchObject({ + error_kind: "user_actionable", + error_category: "invalid_input", + has_suggestion: true, + suggestion_type: "run_command", + }); + expect(classified.error_fingerprint).toBe("tag:RemovedSurfaceError:removed_command"); + }); + + it("classifies a removed flag with the :removed_flag fingerprint suffix", () => { + const classified = classifyCliErrorActionability( + new RemovedSurfaceError({ + message: "--use-pg-schema was removed.", + suggestion: "Use the default migra engine or --use-pg-delta.", + kind: "flag", + }), + ); + expect(classified).toMatchObject({ + error_kind: "user_actionable", + error_category: "invalid_input", + has_suggestion: true, + suggestion_type: "run_command", + }); + expect(classified.error_fingerprint).toBe("tag:RemovedSurfaceError:removed_flag"); + }); + + it("classifies a field-less probe instance without throwing", () => { + const probe = Object.create(RemovedSurfaceError.prototype) as RemovedSurfaceError; + expect(() => classifyCliErrorActionability(probe)).not.toThrow(); + const classified = classifyCliErrorActionability(probe); + expect(classified.error_fingerprint.endsWith(":removed_command")).toBe(true); + expect(classified).toMatchObject({ + error_kind: "user_actionable", + error_category: "invalid_input", + }); + }); +}); + +describe("TelemetryState flush", () => { + it.effect("flushes on a removedCommand failure", () => { + const telemetry = mockTelemetryStateTracked(); + return removedCommand("Use `supabase branches --help` instead.").pipe( + Effect.exit, + Effect.tap((exit) => + Effect.sync(() => { + expect(Exit.isFailure(exit)).toBe(true); + expect(telemetry.flushed).toBe(true); + expect(telemetry.flushCount).toBe(1); + }), + ), + Effect.provide( + commandRuntimeLayer(["db", "branch", "create"]).pipe(Layer.provide(BunCrypto.layer)), + ), + Effect.provide(telemetry.layer), + ); + }); + + it.effect("flushes on a removedFlag failure", () => { + const telemetry = mockTelemetryStateTracked(); + return removedFlag("--use-pg-schema", "Use the default migra engine or --use-pg-delta.").pipe( + Effect.exit, + Effect.tap((exit) => + Effect.sync(() => { + expect(Exit.isFailure(exit)).toBe(true); + expect(telemetry.flushed).toBe(true); + expect(telemetry.flushCount).toBe(1); + }), + ), + Effect.provide(telemetry.layer), + ); + }); + + it.effect("fails a removedFlag for a command that provides no TelemetryState", () => + removedFlag("--create-ticket", "Report CLI problems with `supabase issue bug`.").pipe( + Effect.flip, + Effect.tap((error) => + Effect.sync(() => { + expect(error).toBeInstanceOf(RemovedSurfaceError); + expect(error.message).toBe("--create-ticket was removed."); + }), + ), + ), + ); +}); diff --git a/apps/cli/src/command-internal/schema-flags.ts b/apps/cli/src/command-internal/schema-flags.ts index 9aa44377ce..a8599718d3 100644 --- a/apps/cli/src/command-internal/schema-flags.ts +++ b/apps/cli/src/command-internal/schema-flags.ts @@ -1,8 +1,7 @@ /** - * Normalizes a repeated `--schema` flag into a flat list, matching the CSV-per-occurrence - * parsing every `--schema`-accepting delegated Go subprocess expects (shared logic lives in + * Normalizes a repeated `--schema` flag into a flat list (shared logic lives in * `string-slice-flag.ts`). Also provides `schemaToCsvField`, the CSV re-encoder used when - * forwarding `--schema` back to that subprocess. + * printing a `--schema` value back into a suggested shell command. * * Shared by `gen types`, `db lint`, `db dump`, `db pull`, `db diff`, and `db schema * {generate,sync}`. @@ -13,9 +12,10 @@ export { StringSliceFlagParseError as SchemaFlagParseError }; export const parseSchemaFlags = parseStringSliceFlag; -// Whether a CSV field must be quoted (matches the `encoding/csv` writer a delegated Go -// subprocess re-parses): never quote the empty string; always quote `\.`; quote when the -// field contains `,`, `"`, `\r`, or `\n`; otherwise quote when the first rune is whitespace. +// Whether a CSV field must be quoted (matches Go's `encoding/csv` writer, so a printed +// suggestion round-trips through the same CSV parsing rules `--schema` values use): never quote +// the empty string; always quote `\.`; quote when the field contains `,`, `"`, `\r`, or `\n`; +// otherwise quote when the first rune is whitespace. function fieldNeedsQuotes(field: string): boolean { if (field === "") return false; if (field === "\\.") return true; @@ -26,9 +26,8 @@ function fieldNeedsQuotes(field: string): boolean { /** * Serializes a single parsed schema value back into one CSV field — the inverse of * `readAsCSVStrict` for one element. A schema parsed from `--schema '"tenant,one"'` is the - * single value `tenant,one`; forwarding it raw to a delegated Go subprocess would let pflag's - * `StringSlice` re-parse it as CSV and split it into two schemas, so this re-encodes it to - * keep it one field when rebuilding argv for `db diff`/`db pull`. + * single value `tenant,one`; printing it raw into a suggested `--schema` command would let the + * next CSV parse re-split it into two schemas, so this re-encodes it to keep it one field. */ export function schemaToCsvField(value: string): string { if (!fieldNeedsQuotes(value)) return value; diff --git a/apps/cli/src/command-internal/upgrade-notice.integration.test.ts b/apps/cli/src/command-internal/upgrade-notice.integration.test.ts index 75dbb060d4..70f4cad89b 100644 --- a/apps/cli/src/command-internal/upgrade-notice.integration.test.ts +++ b/apps/cli/src/command-internal/upgrade-notice.integration.test.ts @@ -54,7 +54,6 @@ describe("upgrade notice user-level cache", () => { ["projects", "list"], { cleanShowHelp: false, - delegatedToGo: false, workingDirectory: workdir, isValueTakingFlagToken: () => false, }, diff --git a/apps/cli/src/command-internal/upgrade-notice.ts b/apps/cli/src/command-internal/upgrade-notice.ts index 7c3d1509c4..528412d329 100644 --- a/apps/cli/src/command-internal/upgrade-notice.ts +++ b/apps/cli/src/command-internal/upgrade-notice.ts @@ -339,43 +339,40 @@ export const upgradeNoticeHook = ( args: ReadonlyArray, info: { readonly cleanShowHelp: boolean; - readonly delegatedToGo: boolean; readonly workingDirectory?: string; readonly isValueTakingFlagToken: (token: string) => boolean; }, fetchLatestTag: () => Promise = fetchLatestReleaseTag, ): Effect.Effect => - info.delegatedToGo - ? Effect.void - : Effect.gen(function* () { - const context = yield* Effect.context(); - const outcome = yield* Effect.promise(() => - runUpgradeNotice({ - env: process.env, - args, - cleanShowHelp: info.cleanShowHelp, - isValueTakingFlagToken: info.isValueTakingFlagToken, - cwd: process.cwd(), - resolvedCwd: info.workingDirectory, - currentVersion: CLI_VERSION, - supabaseHome: resolveSupabaseHome({ join }, process.env, homedir()), - now: Date.now, - // Runs under the check span's context so the fetch is its child span. - fetchLatestTag: () => - Effect.runPromiseWith(context)( - Effect.tryPromise({ try: fetchLatestTag, catch: (error) => error }).pipe( - Effect.withSpan("UpgradeNotice.fetch"), - ), - ), - writeStderr: (text) => { - process.stderr.write(text); - }, - }), - ); - if (outcome !== undefined) { - yield* Effect.annotateCurrentSpan({ - "cache.location": outcome.cache, - "cache.hit": outcome.cacheFresh, - }); - } - }).pipe(Effect.withSpan("UpgradeNotice.check"), Effect.ignoreCause); + Effect.gen(function* () { + const context = yield* Effect.context(); + const outcome = yield* Effect.promise(() => + runUpgradeNotice({ + env: process.env, + args, + cleanShowHelp: info.cleanShowHelp, + isValueTakingFlagToken: info.isValueTakingFlagToken, + cwd: process.cwd(), + resolvedCwd: info.workingDirectory, + currentVersion: CLI_VERSION, + supabaseHome: resolveSupabaseHome({ join }, process.env, homedir()), + now: Date.now, + // Runs under the check span's context so the fetch is its child span. + fetchLatestTag: () => + Effect.runPromiseWith(context)( + Effect.tryPromise({ try: fetchLatestTag, catch: (error) => error }).pipe( + Effect.withSpan("UpgradeNotice.fetch"), + ), + ), + writeStderr: (text) => { + process.stderr.write(text); + }, + }), + ); + if (outcome !== undefined) { + yield* Effect.annotateCurrentSpan({ + "cache.location": outcome.cache, + "cache.hit": outcome.cacheFresh, + }); + } + }).pipe(Effect.withSpan("UpgradeNotice.check"), Effect.ignoreCause); diff --git a/apps/cli/src/command-internal/upgrade-notice.unit.test.ts b/apps/cli/src/command-internal/upgrade-notice.unit.test.ts index 89458d139b..26d7067b1a 100644 --- a/apps/cli/src/command-internal/upgrade-notice.unit.test.ts +++ b/apps/cli/src/command-internal/upgrade-notice.unit.test.ts @@ -633,14 +633,8 @@ describe("runUpgradeNotice", () => { }); }); -/** - * `delegatedToGo` is asserted directly rather than via a real proxied - * command, so this doesn't depend on which commands still delegate to Go — - * that set only shrinks (`docs/go-cli-porting-status.md`), and a test pinned - * to one command would break the moment it's ported. - */ describe("upgradeNoticeHook", () => { - async function runHook(opts: { readonly delegatedToGo?: boolean; readonly cached?: boolean }) { + async function runHook(opts: { readonly cached?: boolean }) { const workdir = mkdtempSync(join(tmpdir(), "supabase-upgrade-notice-hook-")); mkdirSync(join(workdir, "supabase", ".temp"), { recursive: true }); writeFileSync(join(workdir, "supabase", "config.toml"), 'project_id = "demo"\n'); @@ -671,7 +665,6 @@ describe("upgradeNoticeHook", () => { ["db", "branch", "list"], { cleanShowHelp: false, - delegatedToGo: opts.delegatedToGo === true, workingDirectory: workdir, isValueTakingFlagToken: () => false, }, @@ -694,10 +687,6 @@ describe("upgradeNoticeHook", () => { ); }); - it("stays silent when the run delegated to Go, which printed its own notice", async () => { - expect((await runHook({ delegatedToGo: true })).stderr).toBe(""); - }); - it("traces a cache hit on the check span without a fetch span", async () => { const { span } = await runHook({}); expect(Object.fromEntries(span("UpgradeNotice.check")!.attributes)).toEqual({ diff --git a/apps/cli/src/commands/db/branch/SIDE_EFFECTS.md b/apps/cli/src/commands/db/branch/SIDE_EFFECTS.md new file mode 100644 index 0000000000..f896bb2616 --- /dev/null +++ b/apps/cli/src/commands/db/branch/SIDE_EFFECTS.md @@ -0,0 +1,69 @@ +# `supabase db branch ` + +Single shared side-effect document for all four `db branch` leaves. Local database +branches are no longer supported; each leaf is a tombstone that fails with a +removal error and a replacement suggestion instead of performing any work. + +## Files Read + +| Path | Format | When | +| ---- | ------ | ---- | +| — | — | — | + +## Files Written + +| Path | Format | When | +| ---- | ------ | ---- | +| — | — | — | + +## API Routes + +| Method | Path | Auth | Request body | Response (used fields) | +| ------ | ---- | ---- | ------------ | ---------------------- | +| — | — | — | — | — | + +## Environment Variables + +| Variable | Purpose | Required? | +| -------- | ------- | --------- | +| — | — | — | + +## Exit Codes + +| Code | Condition | +| ---- | ---------------------------------- | +| `1` | every invocation (removed command) | + +## Output + +### `--output-format text` + +Writes the removal message and the replacement suggestion to stderr, two lines, +regardless of `-o`/`--output`: + +``` +supabase db branch was removed. +Local database branches are no longer supported. For hosted preview branches, see `supabase branches --help`. +``` + +### `--output-format json` / `stream-json` + +Emits the JSON error envelope on stdout instead of the stderr lines above; the +envelope's `code` is `RemovedSurfaceError` and `suggestion` carries the same +replacement text. + +## Telemetry Events Fired + +One `cli_command_executed` event per invocation, with `exit_code: 1` and an +`error_fingerprint` ending in `:removed_command` (`RemovedSurfaceError`). + +## Notes + +- `create`/`delete`/`switch` accept an optional `` positional so a bare + invocation still reaches the tombstone (and emits telemetry) instead of failing + parse with a missing-argument error. +- No positional or flag value is read; every leaf fails identically regardless of + its arguments. +- `supabase branches --help` covers the hosted preview-branching product this + suggestion points to, which is a different product from local DB branches, not a + drop-in replacement. diff --git a/apps/cli/src/commands/db/branch/branch.command.ts b/apps/cli/src/commands/db/branch/branch.command.ts index d3363a0a22..a790aeffd2 100644 --- a/apps/cli/src/commands/db/branch/branch.command.ts +++ b/apps/cli/src/commands/db/branch/branch.command.ts @@ -1,12 +1,50 @@ -import { Command } from "effect/unstable/cli"; -import { dbBranchCreateCommand } from "./create/create.command.ts"; -import { dbBranchDeleteCommand } from "./delete/delete.command.ts"; -import { dbBranchListCommand } from "./list/list.command.ts"; -import { dbBranchSwitchCommand } from "./switch/switch.command.ts"; +import { Argument, Command } from "effect/unstable/cli"; +import { removedCommand } from "../../../command-internal/removed-command.ts"; +import { withJsonErrorHandling } from "../../../shared/output/json-error-handling.ts"; +import { commandRuntimeLayer } from "../../../shared/runtime/command-runtime.layer.ts"; +import { withCommandTelemetry } from "../../../telemetry/command-telemetry.ts"; +import { telemetryStateLayer } from "../../../telemetry/telemetry-state.layer.ts"; + +const REMOVED_SUGGESTION = + "Local database branches are no longer supported. For hosted preview branches, see `supabase branches --help`."; + +/** A tombstoned `db branch` leaf taking an optional `` positional. */ +function removedBranchLeaf(name: string, argDescription: string) { + return Command.make(name, { + branchName: Argument.string("branch name").pipe( + Argument.withDescription(argDescription), + Argument.optional, + ), + } as const).pipe( + Command.withDescription("Removed: local database branches are no longer supported."), + Command.withShortDescription("Removed: local database branches are no longer supported"), + Command.withHandler(() => + removedCommand(REMOVED_SUGGESTION).pipe(withCommandTelemetry(), withJsonErrorHandling), + ), + Command.provide(commandRuntimeLayer(["db", "branch", name])), + Command.provide(telemetryStateLayer), + ); +} + +const dbBranchCreateCommand = removedBranchLeaf("create", "Name for the new branch."); +const dbBranchDeleteCommand = removedBranchLeaf("delete", "Name of the branch to delete."); +const dbBranchSwitchCommand = removedBranchLeaf("switch", "Name of the branch to switch to."); + +const dbBranchListCommand = Command.make("list", {}).pipe( + Command.withDescription("Removed: local database branches are no longer supported."), + Command.withShortDescription("Removed: local database branches are no longer supported"), + Command.withHandler(() => + removedCommand(REMOVED_SUGGESTION).pipe(withCommandTelemetry(), withJsonErrorHandling), + ), + Command.provide(commandRuntimeLayer(["db", "branch", "list"])), + Command.provide(telemetryStateLayer), +); export const dbBranchCommand = Command.make("branch").pipe( - Command.withDescription("Manage local database branches."), - Command.withShortDescription("Manage local database branches"), + Command.withDescription( + "Removed: local database branches are no longer supported. See each subcommand for details.", + ), + Command.withShortDescription("Removed: local database branches are no longer supported"), Command.withSubcommands([ dbBranchCreateCommand, dbBranchDeleteCommand, diff --git a/apps/cli/src/commands/db/branch/branch.e2e.test.ts b/apps/cli/src/commands/db/branch/branch.e2e.test.ts new file mode 100644 index 0000000000..2b3e372295 --- /dev/null +++ b/apps/cli/src/commands/db/branch/branch.e2e.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "@effect/vitest"; +import { Effect } from "effect"; +import { runSupabaseEffect, withTempHome } from "../../../../tests/helpers/cli.ts"; + +const E2E_TIMEOUT_MS = 30_000; + +describe("supabase db branch (removed)", () => { + it.live( + "list exits 1 with the removal message and its replacement suggestion", + () => + withTempHome((home) => + Effect.gen(function* () { + const { exitCode, stderr } = yield* runSupabaseEffect(["db", "branch", "list"], { + home: home.dir, + env: { HOME: home.dir }, + }); + + expect(exitCode).toBe(1); + expect(stderr).toContain("supabase db branch list was removed."); + expect(stderr).toContain( + "Local database branches are no longer supported. For hosted preview branches, see `supabase branches --help`.", + ); + }), + ), + E2E_TIMEOUT_MS, + ); +}); diff --git a/apps/cli/src/commands/db/branch/create/SIDE_EFFECTS.md b/apps/cli/src/commands/db/branch/create/SIDE_EFFECTS.md deleted file mode 100644 index 4bc31b63e5..0000000000 --- a/apps/cli/src/commands/db/branch/create/SIDE_EFFECTS.md +++ /dev/null @@ -1,53 +0,0 @@ -# `supabase db branch create` - -## Files Read - -| Path | Format | When | -| -------------------------------- | ------ | ---------------------------------- | -| `/supabase/config.toml` | TOML | always, to resolve local DB config | - -## Files Written - -| Path | Format | When | -| --------------------------------------------------------- | --------- | ------ | -| `/supabase/.branches//` (directory) | directory | always | - -## API Routes - -| Method | Path | Auth | Request body | Response (used fields) | -| ------ | ---- | ---- | ------------ | ---------------------- | -| — | — | — | — | — | - -## Environment Variables - -| Variable | Purpose | Required? | -| -------- | ------- | --------- | -| — | — | — | - -## Exit Codes - -| Code | Condition | -| ---- | -------------------------- | -| `0` | success | -| `1` | branch already exists | -| `1` | local database not running | - -## Output - -### `--output-format text` - -Prints a confirmation message to stdout on success. - -### `--output-format json` - -Not applicable. - -### `--output-format stream-json` - -Not applicable. - -## Notes - -- Deprecated: use `branches create ` instead. -- Requires exactly one positional argument: the branch name. -- This is a local-only operation (forking remote databases is not supported). diff --git a/apps/cli/src/commands/db/branch/create/create.command.ts b/apps/cli/src/commands/db/branch/create/create.command.ts deleted file mode 100644 index bdcb982f2b..0000000000 --- a/apps/cli/src/commands/db/branch/create/create.command.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Argument, Command } from "effect/unstable/cli"; -import type * as CliCommand from "effect/unstable/cli/Command"; -import { dbBranchCreate } from "./create.handler.ts"; - -const config = { - branchName: Argument.string("branch name").pipe( - Argument.withDescription("Name for the new branch."), - ), -} as const; - -export type DbBranchCreateFlags = CliCommand.Command.Config.Infer; - -export const dbBranchCreateCommand = Command.make("create", config).pipe( - Command.withDescription("Create a branch."), - Command.withShortDescription("Create a branch"), - Command.withHandler((flags) => dbBranchCreate(flags)), -); diff --git a/apps/cli/src/commands/db/branch/create/create.handler.ts b/apps/cli/src/commands/db/branch/create/create.handler.ts deleted file mode 100644 index f1308809b4..0000000000 --- a/apps/cli/src/commands/db/branch/create/create.handler.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Effect } from "effect"; -import { GoProxy } from "../../../../command-internal/go-proxy.service.ts"; -import type { DbBranchCreateFlags } from "./create.command.ts"; - -export const dbBranchCreate = Effect.fn("db.branch.create")(function* (flags: DbBranchCreateFlags) { - const proxy = yield* GoProxy; - const args: string[] = ["db", "branch", "create", flags.branchName]; - yield* proxy.exec(args); -}); diff --git a/apps/cli/src/commands/db/branch/delete/SIDE_EFFECTS.md b/apps/cli/src/commands/db/branch/delete/SIDE_EFFECTS.md deleted file mode 100644 index af5a15e49d..0000000000 --- a/apps/cli/src/commands/db/branch/delete/SIDE_EFFECTS.md +++ /dev/null @@ -1,52 +0,0 @@ -# `supabase db branch delete` - -## Files Read - -| Path | Format | When | -| -------------------------------- | ------ | ---------------------------------- | -| `/supabase/config.toml` | TOML | always, to resolve local DB config | - -## Files Written - -| Path | Format | When | -| --------------------------------------------------------- | --------- | ------- | -| `/supabase/.branches//` (directory) | directory | removed | - -## API Routes - -| Method | Path | Auth | Request body | Response (used fields) | -| ------ | ---- | ---- | ------------ | ---------------------- | -| — | — | — | — | — | - -## Environment Variables - -| Variable | Purpose | Required? | -| -------- | ------- | --------- | -| — | — | — | - -## Exit Codes - -| Code | Condition | -| ---- | -------------------------- | -| `0` | success | -| `1` | branch not found | -| `1` | local database not running | - -## Output - -### `--output-format text` - -Prints a confirmation message to stdout on success. - -### `--output-format json` - -Not applicable. - -### `--output-format stream-json` - -Not applicable. - -## Notes - -- Deprecated: use `branches delete ` instead. -- Requires exactly one positional argument: the branch name. diff --git a/apps/cli/src/commands/db/branch/delete/delete.command.ts b/apps/cli/src/commands/db/branch/delete/delete.command.ts deleted file mode 100644 index cb35a6dbe7..0000000000 --- a/apps/cli/src/commands/db/branch/delete/delete.command.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Argument, Command } from "effect/unstable/cli"; -import type * as CliCommand from "effect/unstable/cli/Command"; -import { dbBranchDelete } from "./delete.handler.ts"; - -const config = { - branchName: Argument.string("branch name").pipe( - Argument.withDescription("Name of the branch to delete."), - ), -} as const; - -export type DbBranchDeleteFlags = CliCommand.Command.Config.Infer; - -export const dbBranchDeleteCommand = Command.make("delete", config).pipe( - Command.withDescription("Delete a branch."), - Command.withShortDescription("Delete a branch"), - Command.withHandler((flags) => dbBranchDelete(flags)), -); diff --git a/apps/cli/src/commands/db/branch/delete/delete.handler.ts b/apps/cli/src/commands/db/branch/delete/delete.handler.ts deleted file mode 100644 index dce5cc2c38..0000000000 --- a/apps/cli/src/commands/db/branch/delete/delete.handler.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Effect } from "effect"; -import { GoProxy } from "../../../../command-internal/go-proxy.service.ts"; -import type { DbBranchDeleteFlags } from "./delete.command.ts"; - -export const dbBranchDelete = Effect.fn("db.branch.delete")(function* (flags: DbBranchDeleteFlags) { - const proxy = yield* GoProxy; - const args: string[] = ["db", "branch", "delete", flags.branchName]; - yield* proxy.exec(args); -}); diff --git a/apps/cli/src/commands/db/branch/list/SIDE_EFFECTS.md b/apps/cli/src/commands/db/branch/list/SIDE_EFFECTS.md deleted file mode 100644 index b5e7aaad88..0000000000 --- a/apps/cli/src/commands/db/branch/list/SIDE_EFFECTS.md +++ /dev/null @@ -1,51 +0,0 @@ -# `supabase db branch list` - -## Files Read - -| Path | Format | When | -| -------------------------------- | ------ | ---------------------------------- | -| `/supabase/config.toml` | TOML | always, to resolve local DB config | - -## Files Written - -| Path | Format | When | -| ---- | ------ | ---- | -| — | — | — | - -## API Routes - -| Method | Path | Auth | Request body | Response (used fields) | -| ------ | ---- | ---- | ------------ | ---------------------- | -| — | — | — | — | — | - -## Environment Variables - -| Variable | Purpose | Required? | -| -------- | ------- | --------- | -| — | — | — | - -## Exit Codes - -| Code | Condition | -| ---- | -------------------------- | -| `0` | success | -| `1` | local database not running | - -## Output - -### `--output-format text` - -Prints a list of local branches to stdout. - -### `--output-format json` - -Not applicable. - -### `--output-format stream-json` - -Not applicable. - -## Notes - -- Deprecated: use `branches list` instead. -- This is a local-only operation listing branches in `/supabase/.branches/`. diff --git a/apps/cli/src/commands/db/branch/list/list.command.ts b/apps/cli/src/commands/db/branch/list/list.command.ts deleted file mode 100644 index e53aa60eca..0000000000 --- a/apps/cli/src/commands/db/branch/list/list.command.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Command } from "effect/unstable/cli"; -import type * as CliCommand from "effect/unstable/cli/Command"; -import { dbBranchList } from "./list.handler.ts"; - -const config = {} as const; - -export type DbBranchListFlags = CliCommand.Command.Config.Infer; - -export const dbBranchListCommand = Command.make("list", config).pipe( - Command.withDescription("List branches."), - Command.withShortDescription("List branches"), - Command.withHandler((flags) => dbBranchList(flags)), -); diff --git a/apps/cli/src/commands/db/branch/list/list.handler.ts b/apps/cli/src/commands/db/branch/list/list.handler.ts deleted file mode 100644 index c65ddace3c..0000000000 --- a/apps/cli/src/commands/db/branch/list/list.handler.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Effect } from "effect"; -import { GoProxy } from "../../../../command-internal/go-proxy.service.ts"; -import type { DbBranchListFlags } from "./list.command.ts"; - -export const dbBranchList = Effect.fn("db.branch.list")(function* (_flags: DbBranchListFlags) { - const proxy = yield* GoProxy; - yield* proxy.exec(["db", "branch", "list"]); -}); diff --git a/apps/cli/src/commands/db/branch/switch/SIDE_EFFECTS.md b/apps/cli/src/commands/db/branch/switch/SIDE_EFFECTS.md deleted file mode 100644 index 7325e7e84f..0000000000 --- a/apps/cli/src/commands/db/branch/switch/SIDE_EFFECTS.md +++ /dev/null @@ -1,52 +0,0 @@ -# `supabase db branch switch` - -## Files Read - -| Path | Format | When | -| -------------------------------- | ------ | ---------------------------------- | -| `/supabase/config.toml` | TOML | always, to resolve local DB config | - -## Files Written - -| Path | Format | When | -| ---------------------------------------------- | ---------- | ------ | -| `/supabase/.branches/_current_branch` | plain text | always | - -## API Routes - -| Method | Path | Auth | Request body | Response (used fields) | -| ------ | ---- | ---- | ------------ | ---------------------- | -| — | — | — | — | — | - -## Environment Variables - -| Variable | Purpose | Required? | -| -------- | ------- | --------- | -| — | — | — | - -## Exit Codes - -| Code | Condition | -| ---- | -------------------------- | -| `0` | success | -| `1` | branch not found | -| `1` | local database not running | - -## Output - -### `--output-format text` - -Prints a confirmation message to stdout on success. - -### `--output-format json` - -Not applicable. - -### `--output-format stream-json` - -Not applicable. - -## Notes - -- Deprecated: use `branches create ` instead. -- Requires exactly one positional argument: the branch name to switch to. diff --git a/apps/cli/src/commands/db/branch/switch/switch.command.ts b/apps/cli/src/commands/db/branch/switch/switch.command.ts deleted file mode 100644 index d948f7dcf3..0000000000 --- a/apps/cli/src/commands/db/branch/switch/switch.command.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Argument, Command } from "effect/unstable/cli"; -import type * as CliCommand from "effect/unstable/cli/Command"; -import { dbBranchSwitch } from "./switch.handler.ts"; - -const config = { - branchName: Argument.string("branch name").pipe( - Argument.withDescription("Name of the branch to switch to."), - ), -} as const; - -export type DbBranchSwitchFlags = CliCommand.Command.Config.Infer; - -export const dbBranchSwitchCommand = Command.make("switch", config).pipe( - Command.withDescription("Switch the active branch."), - Command.withShortDescription("Switch the active branch"), - Command.withHandler((flags) => dbBranchSwitch(flags)), -); diff --git a/apps/cli/src/commands/db/branch/switch/switch.handler.ts b/apps/cli/src/commands/db/branch/switch/switch.handler.ts deleted file mode 100644 index c631c538f8..0000000000 --- a/apps/cli/src/commands/db/branch/switch/switch.handler.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Effect } from "effect"; -import { GoProxy } from "../../../../command-internal/go-proxy.service.ts"; -import type { DbBranchSwitchFlags } from "./switch.command.ts"; - -export const dbBranchSwitch = Effect.fn("db.branch.switch")(function* (flags: DbBranchSwitchFlags) { - const proxy = yield* GoProxy; - const args: string[] = ["db", "branch", "switch", flags.branchName]; - yield* proxy.exec(args); -}); diff --git a/apps/cli/src/commands/db/diff/SIDE_EFFECTS.md b/apps/cli/src/commands/db/diff/SIDE_EFFECTS.md index 9556a0282f..e258425a89 100644 --- a/apps/cli/src/commands/db/diff/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/db/diff/SIDE_EFFECTS.md @@ -4,9 +4,9 @@ Native Effect port. Diffs the local project's expected schema (a throwaway shado database) against a target database (local / linked / `--db-url`), using one of three native engines: bundled in-process pg-delta, migra (edge-runtime), or pgAdmin (CLI-1968 — a native `docker run` of the differ container, no -edge-runtime involved). `--use-pg-schema` is the CLI's sole remaining Go -delegation on this command — a documented keep-in-Go exception (CLI-1960), not a -pending port. +edge-runtime involved). `--use-pg-schema` is removed: the flag stays parsed +(hidden) only so using it fails with an actionable removal error instead of an +unknown-flag parse error — see "`--use-pg-schema` is removed" below. When `[experimental].stack` is on, each shadow is a fresh database in an invocation-owned, unique temporary stack namespace. The command applies the catalog and project migrations as needed, @@ -140,7 +140,8 @@ migra/pg-delta-engine-specific). | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `0` | success; empty diff ("No schema changes found") | | `1` | `--from` without `--to`; engine-flag mutex; target mutex; unknown explicit target; connection/shadow/engine failure; file IO error; local db not running (`--use-pgadmin`); differ container non-zero exit; unparseable `--json-diff` output | -| `1` | `--project-ref` set with a resolved target other than linked; (in explicit mode) `--project-ref` with `--linked` unchanged and neither `--from` nor `--to` being `linked`; `--project-ref` combined with `--use-pg-schema` (see Notes) | +| `1` | `--project-ref` set with a resolved target other than linked; (in explicit mode) `--project-ref` with `--linked` unchanged and neither `--from` nor `--to` being `linked` | +| `1` | `--use-pg-schema` passed at all (any value, including `=false`) — removed, see Notes | ## Output @@ -201,9 +202,10 @@ transaction metadata. ## Notes / Delegation -- `--use-migra` (default), `--use-pgadmin`, `--use-pg-schema`, `--use-pg-delta` are a - mutually-exclusive engine group; `--db-url` / `--linked` / `--local` are a - mutually-exclusive target group (default `--local`). +- `--use-migra` (default), `--use-pgadmin`, `--use-pg-delta` are a mutually-exclusive engine + group; `--db-url` / `--linked` / `--local` are a mutually-exclusive target group (default + `--local`). `--use-pg-schema` is removed and rejects before this group is even checked (see + Notes below), so it is never a live member of the group. - **`--project-ref`** (TS-only, no Go equivalent on any user-facing `db` command) overrides ONLY the linked-ref resolution `ProjectRefResolver` performs (flag > `SUPABASE_PROJECT_ID` > `.temp/project-ref`) — unlike @@ -221,12 +223,8 @@ transaction metadata. (deliberately stricter than `SUPABASE_PROJECT_ID`, which Go's equivalent env var simply leaves unused on a non-linked target). `--use-pgadmin --linked` honors the flag like every other native engine (CLI-1968 — same target - resolve); `--use-pg-schema` rejects it up front, since the delegated Go child - never registered `--project-ref` and the flag would otherwise be silently - dropped. -- `--use-pg-schema` rebuilds the argv and exec's the bundled Go binary (its side - effects are Go's); the Go child's telemetry is disabled so the single - `cli_command_executed` event comes from this TS command. + resolve); `--use-pg-schema` is removed and rejects before any target + resolution happens (see Notes below), so this guard never runs for it. - Explicit `--from`/`--to` mode always uses pg-delta and writes the flattened review representation to `--output` (or stdout). It ignores `--file`; normal mode retains per-unit migration files for the CLI apply paths. @@ -306,39 +304,13 @@ reachable. Reaching both databases requires `--network-id host` **plus** a `[db] 5432` config override — a contrived setup no default user runs. Once both are reachable, this port reports the real diff. -### `--use-pg-schema` is deprecated (CLI-1960) — keep-in-Go exception - -`--use-pg-schema` wraps the in-process Go library `stripe/pg-schema-diff` -(`apps/cli-go/internal/db/diff/pgschema.go`). It is a keep-in-Go exception rather -than a pending port because: - -- it runs **in-process** inside the Go binary, with no container/binary boundary - to re-invoke from TS — unlike `--use-pgadmin` (now native, CLI-1968), which shelled - out to a container/binary path that could in principle be called from TS; -- no TS binding and no WASM build of the library exists, or is reasonably - buildable, within the M9 "Final Cleanup — Go Removal" milestone's scope; -- this specific exception (`db diff --use-pg-schema`) was pre-named when the M9 - milestone was scoped. - -The decision record is Linear issue CLI-1960 and the pull request that introduced -this deprecation notice; re-open only if a TS/WASM binding for -`stripe/pg-schema-diff` ships. It **is** the CLI's sole remaining Go delegation on -`db diff` now that `--use-pgadmin`'s delegation is gone (CLI-1968) — the sibling -`db __db-bootstrap` seam was already removed outright by CLI-1955, and the -`db __shadow` seam by CLI-1956. - -Given that, the flag is now deprecated rather than ported: - -- A TS-only stderr deprecation warning is printed immediately before delegating - (both text and machine `--output-format` modes — diagnostics stay stderr-only, - the CLI-1546 rule): `"--use-pg-schema" is deprecated. Use the pg-delta engine ([experimental.pgdelta] enabled = true / --use-pg-delta) or the default migra engine instead.` - The warning text intentionally does not promise a removal timeline. -- This is **additive** to (printed before) Go's own pre-existing "experimental" - warning (`cmd/db.go:121`, unchanged): `--use-pg-schema flag is experimental and may not include all entities, such as views and grants.` The delegated child - still prints its own warning; the TS wrapper does not suppress or replace it. -- `--help` for the flag now also carries a `Deprecated: …` suffix pointing at the - same migration path. -- Actual flag removal and any PostHog usage-telemetry gate for that removal are - explicitly out of scope for CLI-1960 — this is a documentation/deprecation-notice - change only, tracked as a follow-up decision outside this milestone, with no - owning issue yet. +### `--use-pg-schema` is removed + +The flag is removed: passing it (with any value, including `--use-pg-schema=false`) +fails with a removal error and a suggestion to use the default migra engine or +`--use-pg-delta` instead. It is checked before any other engine-conflict or +target resolution, so combining it with another engine flag (e.g. `--use-pgadmin`) +still hits the removal error first, not the mutex error. + +The flag definition itself stays in `diff.command.ts` (hidden from `--help`) only +so misuse produces this actionable error instead of an unknown-flag parse error. diff --git a/apps/cli/src/commands/db/diff/diff.command.ts b/apps/cli/src/commands/db/diff/diff.command.ts index 3fff3ac3d8..f8b0538ddd 100644 --- a/apps/cli/src/commands/db/diff/diff.command.ts +++ b/apps/cli/src/commands/db/diff/diff.command.ts @@ -19,15 +19,12 @@ const config = { Flag.withDescription("Use pgAdmin to generate schema diff."), Flag.optional, ), + // Kept parsed (and hidden) only so using it produces an actionable removal error instead of + // an unknown-flag parse error; see diff.handler.ts. usePgSchema: Flag.boolean("use-pg-schema").pipe( - // Deprecated in favor of the pg-delta engine (or the default migra engine): pg-schema-diff - // has no TS/container equivalent, so this stays proxied — see SIDE_EFFECTS.md. This - // description-only notice isn't enforced by the flag framework; see diff.handler.ts's - // runtime warning for the enforced half. - Flag.withDescription( - "Use pg-schema-diff to generate schema diff. Deprecated: use the pg-delta engine ([experimental.pgdelta] enabled = true / --use-pg-delta) or the default migra engine instead.", - ), + Flag.withDescription("Removed: use the default migra engine or --use-pg-delta instead."), Flag.optional, + Flag.withHidden, ), usePgDelta: Flag.boolean("use-pg-delta").pipe( Flag.withDescription("Use pg-delta to generate schema diff."), diff --git a/apps/cli/src/commands/db/diff/diff.errors.ts b/apps/cli/src/commands/db/diff/diff.errors.ts index 134c9e15ba..ba5e72e533 100644 --- a/apps/cli/src/commands/db/diff/diff.errors.ts +++ b/apps/cli/src/commands/db/diff/diff.errors.ts @@ -18,8 +18,9 @@ export class DbDiffTargetFlagsError extends Data.TaggedError("DbDiffTargetFlagsE } /** - * Conflicting diff-engine flags (`use-migra`/`use-pgadmin`/`use-pg-schema`/ - * `use-pg-delta`); message text is an established output contract. + * Conflicting diff-engine flags (`use-migra`/`use-pgadmin`/`use-pg-delta`); message text is + * an established output contract. `use-pg-schema` is rejected unconditionally before this + * check runs, so it can never appear in a real conflict. */ export class DbDiffEngineConflictError extends Data.TaggedError("DbDiffEngineConflictError")<{ readonly message: string; diff --git a/apps/cli/src/commands/db/diff/diff.handler.ts b/apps/cli/src/commands/db/diff/diff.handler.ts index 85a048de5a..e2304b8dba 100644 --- a/apps/cli/src/commands/db/diff/diff.handler.ts +++ b/apps/cli/src/commands/db/diff/diff.handler.ts @@ -6,7 +6,7 @@ import { DnsResolverFlag, NetworkIdFlag, } from "../../../command-internal/global-flags.ts"; -import { GoProxy } from "../../../command-internal/go-proxy.service.ts"; +import { removedFlag } from "../../../command-internal/removed-command.ts"; import { branchClause, detectGitBranch } from "../../../shared/git/git-branch.ts"; import { Output } from "../../../shared/output/output.service.ts"; import { RuntimeInfo } from "../../../shared/runtime/runtime-info.service.ts"; @@ -28,7 +28,6 @@ import { toolContainerUsesHostNetwork, } from "../../../command-internal/postgres-client.run.ts"; import { toPostgresURL } from "../../../command-internal/postgres-url.ts"; -import { schemaToCsvField } from "../../../command-internal/schema-flags.ts"; import { findDropStatements } from "../../../command-internal/sql-split.ts"; import { buildLocalDbContainerInputs } from "../../../command-internal/db-bootstrap/local-container-inputs.ts"; import { currentStackBackend } from "../../../command-internal/stack-backend.ts"; @@ -93,12 +92,6 @@ import { diffSchemaPgAdmin } from "./pgadmin-diff.ts"; const warnDiff = `WARNING: The diff tool is not foolproof, so you may need to manually rearrange and modify the generated migration. Run ${aqua("supabase db reset")} to verify that the new migration does not generate errors.`; -// `--use-pg-schema` delegates to the bundled Go binary's in-process `stripe/pg-schema-diff` -// library, which has no TS/container equivalent (see SIDE_EFFECTS.md); the flag is deprecated in -// favor of the pg-delta engine. This warning prints before the delegated child's own unchanged -// "experimental" warning. -const warnPgSchemaDeprecated = `${yellow("WARNING:")} "--use-pg-schema" is deprecated. Use the pg-delta engine ([experimental.pgdelta] enabled = true / --use-pg-delta) or the default migra engine instead.`; - const declarativeBaselineAdvisory = (declarativePath: string | null) => ({ code: "DeclarativeSchemaNotUsedAsDiffBaseline", severity: "info", @@ -113,35 +106,13 @@ const declarativeBaselineAdvisory = (declarativePath: string | null) => ({ const declarativeBaselineNote = (displayPath: string) => `Note: db diff -f uses supabase/migrations as its baseline. Declarative schema files in ${displayPath} are not part of that baseline. If migrations are empty or outdated, the generated migration may include existing declarative objects. -f names the migration; it does not filter objects.\n`; -/** - * Rebuilds the `db diff` argv for the `--use-pg-schema` delegate path — the CLI's sole remaining - * Go delegation on this command, since the in-process `stripe/pg-schema-diff` library has no - * TS/container equivalent. The explicit `--from`/`--to` and engine mutex are already handled - * before this runs, so it just forwards `--use-pg-schema` plus the target/schema/file flags. - */ -const rebuildPgSchemaDelegateArgs = (flags: DbDiffFlags): Array => { - const args = ["db", "diff", "--use-pg-schema"]; - const pushTarget = (name: string, value: Option.Option) => { - // The child binary treats an explicitly passed `--flag=false` as selecting that target, so - // forward every explicitly set flag, not just the true ones. - if (Option.isSome(value)) args.push(value.value ? `--${name}` : `--${name}=false`); - }; - if (Option.isSome(flags.dbUrl)) args.push("--db-url", flags.dbUrl.value); - pushTarget("linked", flags.linked); - pushTarget("local", flags.local); - if (Option.isSome(flags.file)) args.push("--file", flags.file.value); - if (Option.isSome(flags.output)) args.push("--output", flags.output.value); - // Re-encoded as a CSV field so the child's pflag CSV parser doesn't re-split a - // comma-containing schema (e.g. "tenant,one"). - for (const s of flags.schema) args.push("--schema", schemaToCsvField(s)); - return args; -}; - export const dbDiff = Effect.fn("db.diff")(function* (flags: DbDiffFlags) { + if (Option.isSome(flags.usePgSchema)) { + return yield* removedFlag("--use-pg-schema", "Use the default migra engine or --use-pg-delta."); + } const output = yield* Output; const resolver = yield* DbConfigResolver; const pgDelta = yield* PgDeltaEngine; - const proxy = yield* GoProxy; const cliSettings = yield* CommandSettings; const telemetryState = yield* TelemetryState; const linkedProjectCache = yield* LinkedProjectCache; @@ -157,17 +128,17 @@ export const dbDiff = Effect.fn("db.diff")(function* (flags: DbDiffFlags) { let linkedRefForCache: string | undefined; yield* Effect.gen(function* () { - // The engine flags (`use-migra use-pgadmin use-pg-schema use-pg-delta`) and the target flags + // The engine flags (`use-migra use-pgadmin use-pg-delta`) and the target flags // (`db-url linked local`) are each mutually exclusive groups; "set" means the flag was - // explicitly passed (`Option.isSome`). + // explicitly passed (`Option.isSome`). `use-pg-schema` is rejected unconditionally above, + // so it never reaches `engineSet`. const engineSet: Array = []; if (Option.isSome(flags.useMigra)) engineSet.push("use-migra"); if (Option.isSome(flags.usePgAdmin)) engineSet.push("use-pgadmin"); - if (Option.isSome(flags.usePgSchema)) engineSet.push("use-pg-schema"); if (Option.isSome(flags.usePgDelta)) engineSet.push("use-pg-delta"); if (engineSet.length > 1) { return yield* new DbDiffEngineConflictError({ - message: `if any flags in the group [use-migra use-pgadmin use-pg-schema use-pg-delta] are set none of the others can be; [${[...engineSet].sort().join(" ")}] were all set`, + message: `if any flags in the group [use-migra use-pgadmin use-pg-delta] are set none of the others can be; [${[...engineSet].sort().join(" ")}] were all set`, }); } const targetSet: Array = []; @@ -179,22 +150,15 @@ export const dbDiff = Effect.fn("db.diff")(function* (flags: DbDiffFlags) { message: `if any flags in the group [db-url linked local] are set none of the others can be; [${[...targetSet].sort().join(" ")}] were all set`, }); } - if ( - Option.isSome(flags.useMigra) || - Option.isSome(flags.usePgAdmin) || - Option.isSome(flags.usePgSchema) - ) { - const flag = Option.isSome(flags.useMigra) - ? "--use-migra" - : Option.isSome(flags.usePgAdmin) - ? "--use-pgadmin" - : "--use-pg-schema"; - yield* stackRejectNativeDockerDiffEngine(flag); + if (Option.isSome(flags.useMigra) || Option.isSome(flags.usePgAdmin)) { + yield* stackRejectNativeDockerDiffEngine( + Option.isSome(flags.useMigra) ? "--use-migra" : "--use-pgadmin", + ); } // Config is read lazily per path, not unconditionally up front: reading the base config // before the ref is known would validate fields a `[remotes.]` block overrides, which - // would fail a linked diff that should succeed. The delegate paths load config themselves. + // would fail a linked diff that should succeed. Explicit mode loads its own config below. // Explicit `--from`/`--to` mode: both required, always pg-delta. An empty value // (a shell var expanding to `""`) counts as unset — `--from "" --to ""` falls @@ -409,46 +373,7 @@ export const dbDiff = Effect.fn("db.diff")(function* (flags: DbDiffFlags) { return; } - // `--use-pg-schema` is an explicit engine selection that doesn't depend on config, so it - // short-circuits before the target resolve (disabling the child's telemetry so only this - // command's instrumentation fires). `--use-pgadmin` doesn't short-circuit: it needs the same - // config validation and target resolve as the other native engines, further down. const usePgAdmin = Option.getOrElse(flags.usePgAdmin, () => false); - const usePgSchema = Option.getOrElse(flags.usePgSchema, () => false); - // The pg-schema engine delegates to the bundled Go binary, whose `db diff` never registered - // `--project-ref`, so forwarding it would silently drop the flag and diff the workdir's own - // linked ref instead. Fail up front rather than risk the wrong project. - if (usePgSchema && Option.isSome(flags.projectRef)) { - return yield* new DbDiffTargetFlagsError({ - message: "--project-ref is not supported with --use-pg-schema", - }); - } - if (usePgSchema) { - // TS-only deprecation notice, printed before delegating (diagnostics stay stderr-only in - // every mode). The delegated Go `db diff --use-pg-schema` still prints its own - // experimental warning; this is additive, not a replacement, so don't drop it. - yield* output.raw(`${warnPgSchemaDeprecated}\n`, "stderr"); - const env = { SUPABASE_TELEMETRY_DISABLED: "1" }; - // In machine-output mode the child's stdout is captured and re-emitted as a structured - // envelope, so scripted callers get valid JSON instead of the raw SQL. The delegated - // child owns any `--file` write, so the written path isn't introspectable here (`file: - // null`). - if (output.format !== "text") { - const captured = yield* proxy.execCapture(rebuildPgSchemaDelegateArgs(flags), { - env, - suppressChildTelemetry: true, - }); - yield* output.success("Diff complete.", { - diff: captured, - file: null, - schemas: flags.schema, - engine: "pg-schema", - }); - return; - } - yield* proxy.exec(rebuildPgSchemaDelegateArgs(flags), { env, suppressChildTelemetry: true }); - return; - } // Native path: resolve the target, provision a live shadow source, then diff. const connType: DbConnType = Option.isSome(flags.dbUrl) @@ -540,7 +465,6 @@ export const dbDiff = Effect.fn("db.diff")(function* (flags: DbDiffFlags) { const useDelta = resolveDiffEngine({ useMigraChanged: Option.isSome(flags.useMigra), usePgAdmin, - usePgSchema, pgDeltaDefault, }); // pg-delta ignores schema_paths when building its migrations baseline. diff --git a/apps/cli/src/commands/db/diff/diff.integration.test.ts b/apps/cli/src/commands/db/diff/diff.integration.test.ts index 76832600d3..3ee27fd322 100644 --- a/apps/cli/src/commands/db/diff/diff.integration.test.ts +++ b/apps/cli/src/commands/db/diff/diff.integration.test.ts @@ -42,7 +42,6 @@ import { ExperimentalFlag, NetworkIdFlag, } from "../../../command-internal/global-flags.ts"; -import { GoProxy } from "../../../command-internal/go-proxy.service.ts"; import type { OutputFormat } from "../../../shared/output/types.ts"; import { ProjectRefNotLinkedError } from "../../../config/project-ref.errors.ts"; import { @@ -71,6 +70,7 @@ import { type PgDeltaExplicitDiffInput, type PgDeltaHazardReport, } from "../shared/pgdelta-engine.service.ts"; +import { RemovedSurfaceError } from "../../../command-internal/removed-command.ts"; import type { DbDiffFlags } from "./diff.command.ts"; import { dbDiff } from "./diff.handler.ts"; import { stackBackendLayer } from "../../../command-internal/stack-backend.ts"; @@ -88,7 +88,6 @@ interface SetupOpts { readonly diffSuffixes?: ReadonlyArray; readonly hazards?: PgDeltaHazardReport; readonly oom?: boolean; // edge-runtime OOMs; the bash fallback returns `diffSql` - readonly delegateStdout?: string; // stdout returned by a captured Go-delegate run // Message for a failing PGDELTA_DEBUG shadow-catalog export. readonly diffFailWith?: string; // Makes the shadow's PG15+ baseline job(s) exit non-zero; the shadow should @@ -383,18 +382,6 @@ function setup(workdir: string, opts: SetupOpts = {}) { promptProjectRef: () => Effect.succeed(opts.linkedRef ?? VALID_REF), }); - const proxyCalls: Array<{ args: ReadonlyArray; env?: Record }> = []; - const proxyCaptureCalls: Array<{ args: ReadonlyArray; env?: Record }> = - []; - const proxy = Layer.succeed(GoProxy, { - exec: (args, execOpts) => Effect.sync(() => void proxyCalls.push({ args, env: execOpts?.env })), - execCapture: (args, execOpts) => - Effect.sync(() => { - proxyCaptureCalls.push({ args, env: execOpts?.env }); - return opts.delegateStdout ?? ""; - }), - }); - const workdirFiles = Layer.effectDiscard( Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; @@ -426,7 +413,6 @@ function setup(workdir: string, opts: SetupOpts = {}) { alwaysReadyHttpClientLayer, resolver, projectRefResolver, - proxy, mockCommandSettings({ workdir, projectId: opts.projectId ?? Option.some("test") }), Layer.succeed(DnsResolverFlag, "native"), Layer.succeed( @@ -462,8 +448,6 @@ function setup(workdir: string, opts: SetupOpts = {}) { edgeCalls, spawnedBeforeEdgeRun, resolverCalls, - proxyCalls, - proxyCaptureCalls, dockerCalls, differCalls, differCaptureOpts, @@ -1058,51 +1042,61 @@ describe("db diff", () => { }, ); + it.effect("diffs with the native pgAdmin engine: shadow create/rm, one differ run", () => { + const s = setup(tmp.current, { pgadminStdout: [JSON.stringify([pgadminEntry()])] }); + return Effect.gen(function* () { + yield* dbDiff(flags({ usePgAdmin: Option.some(true) })); + expect(s.shadowSpawned.filter((c) => c.args[0] === "create")).toHaveLength(1); + expect(s.shadowSpawned.filter((c) => c.args[0] === "rm")).toHaveLength(1); + expect(s.differCalls).toHaveLength(1); + // Status lines go to stdout, not stderr. + expect(stdout(s.out)).toBe( + `Creating shadow database...\nDiffing local database with current migrations...\n${PGADMIN_DIFF_SQL}\n`, + ); + // Stderr carries the shared shadow-setup diagnostics but not pgAdmin's own status + // lines (those are on stdout) or the migra/pg-delta-only status lines. + const err = stderr(s.out); + expect(err).not.toContain("Creating shadow database..."); + expect(err).not.toContain("Diffing local database with current migrations..."); + expect(err).not.toContain("Diffing schemas"); + expect(err).not.toContain("Finished"); + }).pipe(Effect.provide(s.layer)); + }); + it.effect( - "diffs with the native pgAdmin engine: shadow create/rm, one differ run, no Go proxy call", + "rejects --use-pg-schema with a removal error before any engine-conflict or shadow work", () => { - const s = setup(tmp.current, { pgadminStdout: [JSON.stringify([pgadminEntry()])] }); + const s = setup(tmp.current); return Effect.gen(function* () { - yield* dbDiff(flags({ usePgAdmin: Option.some(true) })); - expect(s.proxyCalls).toEqual([]); - expect(s.proxyCaptureCalls).toEqual([]); - expect(s.shadowSpawned.filter((c) => c.args[0] === "create")).toHaveLength(1); - expect(s.shadowSpawned.filter((c) => c.args[0] === "rm")).toHaveLength(1); - expect(s.differCalls).toHaveLength(1); - // Status lines go to stdout, not stderr. - expect(stdout(s.out)).toBe( - `Creating shadow database...\nDiffing local database with current migrations...\n${PGADMIN_DIFF_SQL}\n`, - ); - // Stderr carries the shared shadow-setup diagnostics but not pgAdmin's own status - // lines (those are on stdout) or the migra/pg-delta-only status lines. - const err = stderr(s.out); - expect(err).not.toContain("Creating shadow database..."); - expect(err).not.toContain("Diffing local database with current migrations..."); - expect(err).not.toContain("Diffing schemas"); - expect(err).not.toContain("Finished"); + const error = yield* dbDiff(flags({ usePgSchema: Option.some(true) })).pipe(Effect.flip); + expect(error).toBeInstanceOf(RemovedSurfaceError); + expect(s.shadowSpawned).toEqual([]); + expect(s.resolverCalls).toEqual([]); }).pipe(Effect.provide(s.layer)); }, ); - it.effect("rejects --project-ref combined with --use-pg-schema before delegating", () => { - // The delegated Go binary doesn't support --project-ref, so this must fail before - // forwarding rather than silently dropping the flag. - const FLAG_REF = "flagflagflagflagflag"; + it.effect("rejects --use-pg-schema=false the same as an explicit true value", () => { const s = setup(tmp.current); return Effect.gen(function* () { - const exit = yield* Effect.exit( - dbDiff(flags({ usePgSchema: Option.some(true), projectRef: Option.some(FLAG_REF) })), - ); - expect(Exit.isFailure(exit)).toBe(true); - if (Exit.isFailure(exit)) { - const causeText = Cause.pretty(exit.cause); - expect(causeText).toContain("--project-ref is not supported with --use-pg-schema"); - } - expect(s.proxyCalls).toEqual([]); - expect(s.proxyCaptureCalls).toEqual([]); + const error = yield* dbDiff(flags({ usePgSchema: Option.some(false) })).pipe(Effect.flip); + expect(error).toBeInstanceOf(RemovedSurfaceError); }).pipe(Effect.provide(s.layer)); }); + it.effect( + "rejects --use-pg-schema combined with --use-pgadmin before the engine-conflict check", + () => { + const s = setup(tmp.current); + return Effect.gen(function* () { + const error = yield* dbDiff( + flags({ usePgSchema: Option.some(true), usePgAdmin: Option.some(true) }), + ).pipe(Effect.flip); + expect(error).toBeInstanceOf(RemovedSurfaceError); + }).pipe(Effect.provide(s.layer)); + }, + ); + it.effect("--use-pgadmin --linked honors --project-ref like the other native engines", () => { const FLAG_REF = "flagflagflagflagflag"; const s = setup(tmp.current, { @@ -1118,7 +1112,6 @@ describe("db diff", () => { projectRef: Option.some(FLAG_REF), }), ); - expect(s.proxyCalls).toEqual([]); expect(s.differCalls).toHaveLength(1); expect(s.cache.cached).toBe(true); expect(s.cache.cachedRef).toBe(FLAG_REF); @@ -1151,7 +1144,6 @@ describe("db diff", () => { return Effect.gen(function* () { yield* dbDiff(flags({ usePgAdmin: Option.some(true), linked: Option.some(true) })); expect(stderr(s.out)).toContain("Loading config override: [remotes.staging]"); - expect(s.proxyCalls).toEqual([]); expect(s.differCalls).toHaveLength(1); }).pipe(Effect.provide(s.layer)); }, @@ -1250,23 +1242,11 @@ describe("db diff", () => { }, ); - it.effect("re-quotes a comma-containing schema when delegating --use-pg-schema", () => { - // The parsed schema value `tenant,one` must be re-encoded as a quoted CSV field - // so the delegated Go child's pflag StringSlice doesn't split it into two schemas. - const s = setup(tmp.current); - return Effect.gen(function* () { - yield* dbDiff(flags({ usePgSchema: Option.some(true), schema: ["tenant,one"] })); - const args = s.proxyCalls[0]?.args ?? []; - const idx = args.indexOf("--schema"); - expect(args[idx + 1]).toBe('"tenant,one"'); - }).pipe(Effect.provide(s.layer)); - }); - it.effect( "forwards a comma-containing --schema value to the differ raw, with no CSV re-quoting (native path)", () => { - // Unlike the delegate above, the native differ argv isn't re-parsed by a pflag - // StringSlice, so the value reaches the container unchanged. + // The native differ argv is never re-parsed by a CSV-splitting flag parser, so the + // value reaches the container unchanged. const s = setup(tmp.current, { pgadminStdout: [JSON.stringify([pgadminEntry()])] }); return Effect.gen(function* () { yield* dbDiff(flags({ usePgAdmin: Option.some(true), schema: ["tenant,one"] })); @@ -1277,42 +1257,6 @@ describe("db diff", () => { }, ); - it.effect( - "delegates --use-pg-schema to the Go binary, printing a deprecation warning without duplicating Go's own warning", - () => { - const s = setup(tmp.current); - return Effect.gen(function* () { - yield* dbDiff(flags({ usePgSchema: Option.some(true) })); - // Asserts on a stable substring so wording tweaks don't require touching every test site. - expect(stderr(s.out)).toContain('"--use-pg-schema" is deprecated'); - expect(stderr(s.out)).not.toContain("--use-pg-schema flag is experimental"); - expect(s.proxyCalls[0]?.args).toEqual(["db", "diff", "--use-pg-schema"]); - // The child's own telemetry is disabled so the single `cli_command_executed` - // event comes from this TS command's instrumentation, not the delegated child. - expect(s.proxyCalls[0]?.env).toEqual({ SUPABASE_TELEMETRY_DISABLED: "1" }); - }).pipe(Effect.provide(s.layer)); - }, - ); - - it.effect("does not print the --use-pg-schema deprecation warning on other diff paths", () => { - const s = setup(tmp.current, { diffSql: "create table g ();\n" }); - return Effect.gen(function* () { - yield* dbDiff(flags()); - expect(stderr(s.out)).not.toContain('"--use-pg-schema" is deprecated'); - }).pipe(Effect.provide(s.layer)); - }); - - it.effect( - "does not print the --use-pg-schema deprecation warning on the native --use-pgadmin path", - () => { - const s = setup(tmp.current, { pgadminStdout: [JSON.stringify([pgadminEntry()])] }); - return Effect.gen(function* () { - yield* dbDiff(flags({ usePgAdmin: Option.some(true) })); - expect(stderr(s.out)).not.toContain('"--use-pg-schema" is deprecated'); - }).pipe(Effect.provide(s.layer)); - }, - ); - it.effect( "emits a json envelope for --use-pgadmin with status lines redirected to stderr (payload-only stdout)", () => { @@ -1326,8 +1270,6 @@ describe("db diff", () => { const err = stderr(s.out); expect(err).toContain("Creating shadow database..."); expect(err).toContain("Diffing local database with current migrations..."); - expect(s.proxyCalls).toEqual([]); - expect(s.proxyCaptureCalls).toEqual([]); const success = s.out.messages.find((m) => m.type === "success"); expect(success?.data).toMatchObject({ diff: PGADMIN_DIFF_SQL, @@ -1372,21 +1314,6 @@ describe("db diff", () => { }).pipe(Effect.provide(s.layer)); }); - it.effect("--use-pg-schema in json mode wraps the captured SQL in a structured envelope", () => { - const s = setup(tmp.current, { format: "json", delegateStdout: "create table e ();\n" }); - return Effect.gen(function* () { - yield* dbDiff(flags({ usePgSchema: Option.some(true) })); - expect(stdout(s.out)).toBe(""); - expect(s.proxyCaptureCalls).toHaveLength(1); - const success = s.out.messages.find((m) => m.type === "success"); - expect(success?.data).toMatchObject({ diff: "create table e ();\n", engine: "pg-schema" }); - // Diagnostics like the deprecation notice still reach stderr in machine mode. - expect(stderr(s.out)).toContain('"--use-pg-schema" is deprecated'); - // The child's own telemetry is disabled here too, same as the text-mode delegate. - expect(s.proxyCaptureCalls[0]?.env).toEqual({ SUPABASE_TELEMETRY_DISABLED: "1" }); - }).pipe(Effect.provide(s.layer)); - }); - it.effect("writes live-only SQL with --file even when declarative targets are configured", () => { const s = setup(tmp.current, { files: { @@ -1601,19 +1528,6 @@ describe("db diff", () => { }).pipe(Effect.provide(s.layer)); }); - it.effect( - "forwards an explicit --linked=false target flag to the delegated pg-schema child", - () => { - // Target flags are selectors keyed on the delegated child's flag.Changed; dropping - // `Some(false)` would default it to local instead of the linked target selected. - const s = setup(tmp.current); - return Effect.gen(function* () { - yield* dbDiff(flags({ usePgSchema: Option.some(true), linked: Option.some(false) })); - expect(s.proxyCalls[0]?.args).toEqual(["db", "diff", "--use-pg-schema", "--linked=false"]); - }).pipe(Effect.provide(s.layer)); - }, - ); - it.effect( "an empty --file value prints to stdout instead of writing a nameless migration", () => { @@ -2488,7 +2402,7 @@ describe("db diff", () => { flags({ usePgAdmin: Option.some(true), usePgDelta: Option.some(true) }), ).pipe(Effect.flip); expect((error as { message: string }).message).toBe( - "if any flags in the group [use-migra use-pgadmin use-pg-schema use-pg-delta] are set none of the others can be; [use-pg-delta use-pgadmin] were all set", + "if any flags in the group [use-migra use-pgadmin use-pg-delta] are set none of the others can be; [use-pg-delta use-pgadmin] were all set", ); }).pipe(Effect.provide(s.layer)); }, diff --git a/apps/cli/src/commands/db/pull/SIDE_EFFECTS.md b/apps/cli/src/commands/db/pull/SIDE_EFFECTS.md index e5577f534a..d6351a390a 100644 --- a/apps/cli/src/commands/db/pull/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/db/pull/SIDE_EFFECTS.md @@ -148,12 +148,10 @@ at all, so nothing is cached for it. | `1` | `--project-ref` set with a resolved target other than linked | > Note: unlike `db diff`, an empty diff (`No schema changes found`) is a **non-zero -> exit** for `db pull`. The message and exit code match Go, but the stderr footer -> does not: instead of Go's generic -> `Try rerunning the command with --debug to troubleshoot the error.`, `db pull` -> prints +> exit** for `db pull`. Instead of the generic +> `Try rerunning the command with --debug to troubleshoot the error.` stderr footer, +> `db pull` prints > `The remote database is already in sync with your local migrations — nothing to pull.` -> (deliberate divergence — see `docs/go-cli-divergences.md`). ## Output diff --git a/apps/cli/src/commands/db/pull/pull.integration.test.ts b/apps/cli/src/commands/db/pull/pull.integration.test.ts index e0129d4e8a..6b3e31dc65 100644 --- a/apps/cli/src/commands/db/pull/pull.integration.test.ts +++ b/apps/cli/src/commands/db/pull/pull.integration.test.ts @@ -42,7 +42,6 @@ import { YesFlag, } from "../../../command-internal/global-flags.ts"; import { CliArgs } from "../../../shared/cli/cli-args.service.ts"; -import { GoProxy } from "../../../command-internal/go-proxy.service.ts"; import type { OutputFormat } from "../../../shared/output/types.ts"; import { ProjectRefNotLinkedError } from "../../../config/project-ref.errors.ts"; import { @@ -111,7 +110,6 @@ interface SetupOpts { readonly edgeFailFirstWith?: string; // `resolvePoolerFallback` returns `Some(pooler conn)` when true, `None` otherwise. readonly poolerAvailable?: boolean; - readonly delegateStdout?: string; // stdout returned by a captured Go-delegate run // Initial-migra pull: the bytes the native pg_dump container streams to its sink, its // exit code/stderr, and (when set) an IPv6 stderr that fails the first dump attempt so // the pooler retry runs (the second attempt then streams `dumpStdout`). @@ -412,21 +410,6 @@ function setup(workdir: string, opts: SetupOpts = {}) { }, }); - const proxyCalls: Array<{ args: ReadonlyArray; env?: Record }> = []; - const proxyCaptureCalls: Array<{ - args: ReadonlyArray; - env?: Record; - stdin?: "inherit" | "ignore"; - }> = []; - const proxy = Layer.succeed(GoProxy, { - exec: (args, execOpts) => Effect.sync(() => void proxyCalls.push({ args, env: execOpts?.env })), - execCapture: (args, execOpts) => - Effect.sync(() => { - proxyCaptureCalls.push({ args, env: execOpts?.env, stdin: execOpts?.stdin }); - return opts.delegateStdout ?? ""; - }), - }); - // Mirrors the same ref `resolver`'s own mock embeds above, and gives an explicit // `--project-ref` flag top precedence over `opts.resolvedRef` (mirrors // `reset.integration.test.ts`'s identical mock). @@ -476,7 +459,6 @@ function setup(workdir: string, opts: SetupOpts = {}) { alwaysReadyHttpClientLayer, resolver, projectRefResolver, - proxy, mockCommandSettings({ workdir, projectId: opts.projectId ?? Option.some("test") }), mockTty({ stdinIsTty: opts.stdinIsTty ?? false, stdoutIsTty: false }), mockStdin( @@ -499,8 +481,6 @@ function setup(workdir: string, opts: SetupOpts = {}) { return { layer: baseLayer, out, - proxyCalls, - proxyCaptureCalls, historyUpserts, execLog, connectedDatabases, @@ -676,7 +656,6 @@ describe("db pull", () => { yield* dbPull(flags({ projectRef: Option.some(FLAG_REF) })); expect(s.engineCalls[0]?.operation).toBe("export"); expect(s.engineCalls[0]?.projectRef).toBe(FLAG_REF); - expect(s.proxyCalls).toEqual([]); }).pipe(Effect.provide(s.layer)); }); @@ -1161,8 +1140,6 @@ describe("db pull", () => { }); return Effect.gen(function* () { yield* dbPull(flags()); - expect(s.proxyCalls).toHaveLength(0); - expect(s.proxyCaptureCalls).toHaveLength(0); // pg_dump ran with the schema-dump env (internal-schema exclude + comment strip). expect(s.dumpCalls).toHaveLength(1); expect(s.dumpCalls[0]?.env["EXTRA_SED"]).toBe("/^--/d"); @@ -1205,8 +1182,6 @@ describe("db pull", () => { }); return Effect.gen(function* () { yield* dbPull(flags()); - expect(s.proxyCalls).toHaveLength(0); - expect(s.proxyCaptureCalls).toHaveLength(0); const success = s.out.messages.find((m) => m.type === "success"); // Machine mode never prompts, so history updates by default (true); `schemaWritten` // is the real native path (not null as when delegated). @@ -1394,7 +1369,7 @@ describe("db pull", () => { }); return Effect.gen(function* () { // The message and non-zero exit are the contract; the generic --debug footer is - // replaced with this explanation instead (docs/go-cli-divergences.md). + // replaced with this explanation instead. const error = yield* dbPull(flags()).pipe(Effect.flip); expect(error).toMatchObject({ _tag: "DbPullInSyncError", @@ -1756,7 +1731,6 @@ describe("db pull", () => { const s = setup(tmp.current, { edgeStdout: EXPORT_JSON }); return Effect.gen(function* () { yield* withEnvVar("SUPABASE_EXPERIMENTAL", "true", dbPull(flags())); - expect(s.proxyCalls).toHaveLength(0); expect(s.engineCalls[0]?.operation).toBe("export"); expect(streamText(s.out, "stderr")).toContain("Connecting to remote database..."); expect(streamText(s.out, "stderr")).toContain( @@ -1814,7 +1788,6 @@ describe("db pull", () => { return Effect.gen(function* () { yield* dbPull(flags({ declarative: Option.some(true), usePgDelta: Option.some(false) })); expect(s.engineCalls[0]?.operation).toBe("export"); - expect(s.proxyCalls).toHaveLength(0); expect(streamText(s.out, "stderr")).toContain( "The --experimental structured-dump mode for `db pull` is deprecated", ); @@ -1826,7 +1799,6 @@ describe("db pull", () => { return Effect.gen(function* () { yield* dbPull(flags({ diffEngine: Option.some("migra") })); expect(s.engineCalls[0]?.operation).toBe("export"); - expect(s.proxyCalls).toHaveLength(0); }).pipe(Effect.provide(s.layer)); }); @@ -1836,7 +1808,6 @@ describe("db pull", () => { const s = setup(tmp.current, { experimental: true, edgeStdout: EXPORT_JSON }); return Effect.gen(function* () { yield* dbPull(flags()); - expect(s.proxyCalls).toHaveLength(0); expect(s.engineCalls[0]?.operation).toBe("export"); expect(streamText(s.out, "stderr")).toContain("Connecting to remote database..."); expect(streamText(s.out, "stderr")).toContain( @@ -1856,7 +1827,6 @@ describe("db pull", () => { }); return Effect.gen(function* () { yield* dbPull(flags()); - expect(s.proxyCaptureCalls).toHaveLength(0); const success = s.out.messages.find((m) => m.type === "success"); expect(success?.data).toMatchObject({ declarative: true, @@ -1878,7 +1848,6 @@ describe("db pull", () => { expect(streamText(s.out, "stderr")).toContain( "Preparing declarative schema export using pg-delta...", ); - expect(s.proxyCalls).toHaveLength(0); expect(streamText(s.out, "stderr")).not.toContain("is deprecated"); }).pipe(Effect.provide(s.layer)); }, @@ -1917,7 +1886,6 @@ describe("db pull", () => { return Effect.gen(function* () { yield* dbPull(flags({ name: Option.some("--experimental=false") })); expect(s.engineCalls[0]?.operation).toBe("export"); - expect(s.proxyCalls).toHaveLength(0); }).pipe(Effect.provide(s.layer), (body) => withEnvVar("SUPABASE_EXPERIMENTAL", "true", body)); }, ); @@ -1935,7 +1903,6 @@ describe("db pull", () => { return Effect.gen(function* () { yield* dbPull(flags()); expect(s.engineCalls[0]?.operation).toBe("export"); - expect(s.proxyCalls).toHaveLength(0); }).pipe(Effect.provide(s.layer)); }, ); @@ -1953,7 +1920,6 @@ describe("db pull", () => { return Effect.gen(function* () { yield* dbPull(flags()); expect(s.engineCalls[0]?.operation).toBe("export"); - expect(s.proxyCalls).toHaveLength(0); }).pipe(Effect.provide(s.layer), (body) => withEnvVar("SUPABASE_EXPERIMENTAL", "true", body)); }, ); diff --git a/apps/cli/src/commands/db/remote/changes/SIDE_EFFECTS.md b/apps/cli/src/commands/db/remote/changes/SIDE_EFFECTS.md index 619062b43d..8c2f7df9e0 100644 --- a/apps/cli/src/commands/db/remote/changes/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/db/remote/changes/SIDE_EFFECTS.md @@ -1,10 +1,13 @@ # `supabase db remote changes` +Removed. The command is a tombstone: it fails with a removal error and a +replacement suggestion instead of connecting to any database. + ## Files Read -| Path | Format | When | -| -------------------------- | ---------- | ---------------------------------- | -| `~/.supabase/access-token` | plain text | when `SUPABASE_ACCESS_TOKEN` unset | +| Path | Format | When | +| ---- | ------ | ---- | +| — | — | — | ## Files Written @@ -20,34 +23,41 @@ ## Environment Variables -| Variable | Purpose | Required? | -| ----------------------- | --------------------------------------- | ------------------------------------------------------- | -| `SUPABASE_ACCESS_TOKEN` | auth token | no (falls back to keyring → `~/.supabase/access-token`) | -| `DB_PASSWORD` | password for direct database connection | no | +| Variable | Purpose | Required? | +| -------- | ------- | --------- | +| — | — | — | ## Exit Codes -| Code | Condition | -| ---- | --------------------------- | -| `0` | success | -| `1` | database connection failure | +| Code | Condition | +| ---- | ---------------------------------- | +| `1` | every invocation (removed command) | ## Output ### `--output-format text` -Prints the schema diff (changes on the remote database since the last migration) to stdout. +Writes the removal message and the replacement suggestion to stderr, two lines, +regardless of `-o`/`--output`: + +``` +supabase db remote changes was removed. +Use `supabase db diff --linked` instead. +``` -### `--output-format json` +### `--output-format json` / `stream-json` -Not applicable. +Emits the JSON error envelope on stdout instead of the stderr lines above; the +envelope's `code` is `RemovedSurfaceError` and `suggestion` carries the same +replacement text. -### `--output-format stream-json` +## Telemetry Events Fired -Not applicable. +One `cli_command_executed` event per invocation, with `exit_code: 1` and an +`error_fingerprint` ending in `:removed_command` (`RemovedSurfaceError`). ## Notes -- Deprecated: use `db diff --use-migra --linked` instead. -- `--schema` / `-s` restricts the diff to specific schemas. -- `--db-url` and `--linked` (default true) are mutually exclusive. +- No flag value is read; the command fails identically regardless of + `--schema`/`--db-url`/`--linked`/`--password`. +- The native sibling `db remote commit` is unaffected. diff --git a/apps/cli/src/commands/db/remote/changes/changes.command.ts b/apps/cli/src/commands/db/remote/changes/changes.command.ts index 2f30f047ec..3e242b117b 100644 --- a/apps/cli/src/commands/db/remote/changes/changes.command.ts +++ b/apps/cli/src/commands/db/remote/changes/changes.command.ts @@ -1,6 +1,9 @@ import { Command, Flag } from "effect/unstable/cli"; -import type * as CliCommand from "effect/unstable/cli/Command"; -import { dbRemoteChanges } from "./changes.handler.ts"; +import { removedCommand } from "../../../../command-internal/removed-command.ts"; +import { withJsonErrorHandling } from "../../../../shared/output/json-error-handling.ts"; +import { commandRuntimeLayer } from "../../../../shared/runtime/command-runtime.layer.ts"; +import { withCommandTelemetry } from "../../../../telemetry/command-telemetry.ts"; +import { telemetryStateLayer } from "../../../../telemetry/telemetry-state.layer.ts"; const config = { schema: Flag.string("schema").pipe( @@ -23,10 +26,15 @@ const config = { ), } as const; -export type DbRemoteChangesFlags = CliCommand.Command.Config.Infer; - export const dbRemoteChangesCommand = Command.make("changes", config).pipe( - Command.withDescription("Show changes on the remote database since last migration."), - Command.withShortDescription("Show changes on the remote database"), - Command.withHandler((flags) => dbRemoteChanges(flags)), + Command.withDescription("Removed: use `supabase db diff --linked` instead."), + Command.withShortDescription("Removed: use `db diff --linked` instead"), + Command.withHandler(() => + removedCommand("Use `supabase db diff --linked` instead.").pipe( + withCommandTelemetry(), + withJsonErrorHandling, + ), + ), + Command.provide(commandRuntimeLayer(["db", "remote", "changes"])), + Command.provide(telemetryStateLayer), ); diff --git a/apps/cli/src/commands/db/remote/changes/changes.handler.ts b/apps/cli/src/commands/db/remote/changes/changes.handler.ts deleted file mode 100644 index b7b6db5eb5..0000000000 --- a/apps/cli/src/commands/db/remote/changes/changes.handler.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Effect, Option } from "effect"; -import { GoProxy } from "../../../../command-internal/go-proxy.service.ts"; -import type { DbRemoteChangesFlags } from "./changes.command.ts"; - -export const dbRemoteChanges = Effect.fn("db.remote.changes")(function* ( - flags: DbRemoteChangesFlags, -) { - const proxy = yield* GoProxy; - const args: string[] = ["db", "remote", "changes"]; - for (const s of flags.schema) { - args.push("--schema", s); - } - if (Option.isSome(flags.dbUrl)) args.push("--db-url", flags.dbUrl.value); - if (flags.linked) args.push("--linked"); - if (Option.isSome(flags.password)) args.push("--password", flags.password.value); - yield* proxy.exec(args); -}); diff --git a/apps/cli/src/commands/db/schema/declarative/declarative.former-default.ts b/apps/cli/src/commands/db/schema/declarative/declarative.former-default.ts index c97ba22492..97fc0cc655 100644 --- a/apps/cli/src/commands/db/schema/declarative/declarative.former-default.ts +++ b/apps/cli/src/commands/db/schema/declarative/declarative.former-default.ts @@ -19,7 +19,7 @@ const formerDeclarativeDefaultWarning = (formerDirRel: string, defaultDirRel: st /** * Warns when a project still has a declarative tree at the former default `supabase/database` * while relying on the implicit default, which now resolves to `supabase/schemas` — otherwise an - * upgraded project silently stops reading its existing tree. See docs/go-cli-divergences.md. + * upgraded project silently stops reading its existing tree. * * Fires only when `declarative_schema_path` is unset, the new default directory has no entries, * and the former default contains `.sql` files or an export manifest; probe failures read as diff --git a/apps/cli/src/commands/db/schema/declarative/generate/SIDE_EFFECTS.md b/apps/cli/src/commands/db/schema/declarative/generate/SIDE_EFFECTS.md index 86ad6ef40a..63560b462a 100644 --- a/apps/cli/src/commands/db/schema/declarative/generate/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/db/schema/declarative/generate/SIDE_EFFECTS.md @@ -84,9 +84,8 @@ always go to stderr, in every `--output-format`. On success: - `--db-url` / `--linked` / `--local` are mutually exclusive; absent all three, smart mode prompts (existing-files overwrite → Local/Custom choice + reset offer). - `--output-dir

` selects a destination for this invocation without changing - config or activating it for later syncs (TS-only; deliberately not - `--output`/`-o`, which the legacy root reserves for the global machine-format - flag — see `docs/go-cli-divergences.md`). + config or activating it for later syncs (deliberately not `--output`/`-o`, which + the root command reserves for the global machine-format flag). - When `declarative_schema_path` is unset, the new `supabase/schemas` default is empty, and the former `supabase/database` default still contains `.sql` files or an export manifest, a WARNING on stderr explains the default move and how diff --git a/apps/cli/src/commands/db/schema/declarative/generate/generate.integration.test.ts b/apps/cli/src/commands/db/schema/declarative/generate/generate.integration.test.ts index 7d3cd0fef5..d75db03a06 100644 --- a/apps/cli/src/commands/db/schema/declarative/generate/generate.integration.test.ts +++ b/apps/cli/src/commands/db/schema/declarative/generate/generate.integration.test.ts @@ -48,7 +48,6 @@ import { NetworkIdFlag, YesFlag, } from "../../../../../command-internal/global-flags.ts"; -import { GoProxy } from "../../../../../command-internal/go-proxy.service.ts"; import { CommandPlatformApi } from "../../../../../auth/command-platform-api.service.ts"; import { CommandPlatformApiFactory } from "../../../../../auth/command-platform-api-factory.service.ts"; import { dockerRunLayer } from "../../../../../command-internal/docker-run.layer.ts"; @@ -301,11 +300,6 @@ function setup(workdir: string, opts: SetupOpts = {}) { }, resolvePoolerFallback: () => Effect.succeed(Option.none()), }); - const proxyCalls: ReadonlyArray[] = []; - const proxy = Layer.succeed(GoProxy, { - exec: (args) => Effect.sync(() => void proxyCalls.push(args)), - execCapture: () => Effect.succeed(""), - }); const runtimeInfo = mockRuntimeInfo({ platform: "linux" }); const processControl = mockProcessControl(); const experimentalFlag = Layer.succeed(ExperimentalFlag, opts.experimental ?? true); @@ -326,7 +320,6 @@ function setup(workdir: string, opts: SetupOpts = {}) { engine, mockLocalDockerEngineUnavailableLayer, resolver, - proxy, dbConn, mockCommandSettings({ workdir, projectId: opts.projectId ?? Option.some("test") }), mockTty({ stdinIsTty: opts.stdinIsTty ?? false, stdoutIsTty: false }), @@ -364,7 +357,6 @@ function setup(workdir: string, opts: SetupOpts = {}) { dbExec, engineExportCalls, resolverCalls, - proxyCalls, localPostgresImageChecks, get ensureStartedCalls() { return ensureStartedCalls; diff --git a/apps/cli/src/commands/db/start/SIDE_EFFECTS.md b/apps/cli/src/commands/db/start/SIDE_EFFECTS.md index 28c2e3c2cd..b4dd1a1512 100644 --- a/apps/cli/src/commands/db/start/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/db/start/SIDE_EFFECTS.md @@ -200,9 +200,9 @@ The resident owner process manages the database beyond the CLI invocation. Stack ## Notes - **`pg_net` converges with `[experimental.webhooks]` on every non-backup start** (shared - `startDatabase` behavior — see `supabase start`'s SIDE_EFFECTS.md note and - `docs/go-cli-divergences.md`): fresh volumes install it only when webhooks are enabled or - migration history owns it; existing volumes with webhooks disabled DROP a `pg_net` that + `startDatabase` behavior — see `supabase start`'s SIDE_EFFECTS.md note): fresh volumes + install it only when webhooks are enabled or migration history owns it; existing volumes + with webhooks disabled DROP a `pg_net` that migration history does not own. `pg_net` installed outside migrations (Studio SQL editor) is dropped on the next start — accepted, documented edge. - `--from-backup` restores the database from a logical backup file on start; the health diff --git a/apps/cli/src/commands/db/test/test.integration.test.ts b/apps/cli/src/commands/db/test/test.integration.test.ts index a960da47f7..54ccc1095f 100644 --- a/apps/cli/src/commands/db/test/test.integration.test.ts +++ b/apps/cli/src/commands/db/test/test.integration.test.ts @@ -52,7 +52,6 @@ import { DockerRun, type DockerRunOpts } from "../../../command-internal/docker- import { EdgeRuntimeScript } from "../../../command-internal/edge-runtime-script.service.ts"; import { PgDeltaSslProbe } from "../../../command-internal/pgdelta-ssl-probe.service.ts"; import { runTestDbCommand } from "../../../command-internal/test-db.command-handler.ts"; -import { GoProxy } from "../../../command-internal/go-proxy.service.ts"; import { dbCommand } from "../db.command.ts"; import { unusedStackServices } from "../../../../tests/helpers/unused-stack.ts"; @@ -231,8 +230,7 @@ describe("db test (alias) integration", () => { Stdio.layerTest({ args: Effect.succeed(args) }), Layer.succeed(CliArgs, { args }), // `dbCommand` is the whole `db` subtree, so its R includes every sibling subcommand's - // global-flag/Go-delegation requirements too, even though this test only dispatches - // `db test`. + // global-flag requirements too, even though this test only dispatches `db test`. Layer.succeed(AgentFlag, "auto"), Layer.succeed(CreateTicketFlag, false), Layer.succeed(DebugFlag, false), @@ -243,10 +241,6 @@ describe("db test (alias) integration", () => { Layer.succeed(ProfileFlag, "supabase"), Layer.succeed(WorkdirFlag, Option.none()), Layer.succeed(YesFlag, false), - Layer.succeed(GoProxy, { - exec: () => Effect.die("GoProxy not needed for `db test` dispatch"), - execCapture: () => Effect.die("GoProxy not needed for `db test` dispatch"), - }), Layer.succeed(EdgeRuntimeScript, { run: () => Effect.die("EdgeRuntimeScript not needed for `db test` dispatch"), }), diff --git a/apps/cli/src/commands/functions/download/SIDE_EFFECTS.md b/apps/cli/src/commands/functions/download/SIDE_EFFECTS.md index 94c7037995..8b01da46d1 100644 --- a/apps/cli/src/commands/functions/download/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/functions/download/SIDE_EFFECTS.md @@ -42,17 +42,9 @@ | `docker pull ` | Docker-unbundle path, cache miss on a candidate | pull with 2 retries (4s/8s backoff) before falling through to the next registry candidate | | `docker network inspect` / `network create` / `volume create` | Docker-unbundle path, when Docker is running | ensure the shared per-project network/named volume exist (same primitives as `functions deploy`'s Docker bundler); the Deno-cache volume is `supabase_edge_runtime_` (mounted at `/root/.cache/deno`) | | `docker run --rm ... --label com.supabase.cli.project= --label com.docker.compose.project= unbundle --eszip ... --output ...` | Docker-unbundle path | extract the downloaded eszip into `supabase/functions//...`; labeled so orphaned containers can be associated with the project | -| `supabase-go functions download ... --legacy-bundle` | `--legacy-bundle` only | preserve the hidden, deprecated pre-1.120.0 bundling fallback (native TS port tracked separately, CLI-1963) | - -The `--legacy-bundle` delegated call runs with `SUPABASE_TELEMETRY_DISABLED=1` -so the Go child's own `cli_command_executed` doesn't double-count on top of -this command's own telemetry (mirrors `db pull`/`db diff`'s delegated-call -pattern). In `--output-format json|stream-json`, the child's stdout is -captured and discarded instead of inherited (`GoProxy.execCapture`) — -the raw text never reaches the terminal, and this command emits the `Output` -envelope itself once the child exits successfully. The Docker-unbundle path's -own container stdout is routed the same way: to the real stdout in text mode, -to stderr in machine-output modes (CLI-1546). + +The Docker-unbundle path's own container stdout is routed to the real stdout +in text mode, to stderr in machine-output modes (CLI-1546). ## Environment Variables @@ -73,20 +65,21 @@ to stderr in machine-output modes (CLI-1546). ## Exit Codes -| Code | Condition | -| ---- | ---------------------------------------------------------------------- | -| `0` | success | -| `1` | API error (non-2xx response) | -| `1` | authentication error (no token found) | -| `1` | network / connection failure | -| `1` | invalid function slug or flag conflict | -| `1` | Docker-unbundle container exited non-zero (suggests `--legacy-bundle`) | +| Code | Condition | +| ---- | ----------------------------------------------------------------------------------------------------------------- | +| `0` | success | +| `1` | API error (non-2xx response) | +| `1` | authentication error (no token found) | +| `1` | network / connection failure | +| `1` | invalid function slug or flag conflict | +| `1` | Docker-unbundle container exited non-zero (suggests retrying with `--use-api`, or redeploying if that also fails) | +| `1` | `--legacy-bundle` passed at all (any value, including `=false`) — removed, see Notes | ## Telemetry Events Fired -| Event | When | Notable properties / groups | -| ---------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `cli_command_executed` | post-run, success or failure (via wrapper) | `exit_code`, `duration_ms`, `flags` (`project-ref` recorded verbatim, matching `functions list`/`delete`; `use-api`/`use-docker`/`legacy-bundle` also recorded verbatim since they are boolean flags; no flag on this command is currently redacted) | +| Event | When | Notable properties / groups | +| ---------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `cli_command_executed` | post-run, success or failure (via wrapper) | `exit_code`, `duration_ms`, `flags` (`project-ref` recorded verbatim, matching `functions list`/`delete`; `use-api`/`use-docker`/`legacy-bundle` also recorded verbatim since they are boolean flags; no flag on this command is currently redacted); a rejected `--legacy-bundle` fails with `error_fingerprint` ending in `:removed_flag` (`RemovedSurfaceError`) | ## Output @@ -95,19 +88,17 @@ to stderr in machine-output modes (CLI-1546). Prints progress and success messages as functions are downloaded. The Docker-unbundle path prints `Downloading function: ` (lowercase "function", unlike the `--use-api` path's "Downloading Function:") and does **not** print a final "Downloaded Function ... from project ..." line — that -line only appears on the `--use-api` and `--legacy-bundle` paths. +line only appears on the `--use-api` path. ### `--output-format json` Prints a structured success result with the downloaded function slugs and project ref. On the -`--legacy-bundle` proxy path, the Go child's stdout is captured/discarded (never inherited) so it -can't corrupt the envelope; the slug list is resolved independently for the payload. On the Docker-unbundle path, the `unbundle` container's own stdout is routed to stderr instead of stdout -for the same reason. +so it can't corrupt the envelope. ### `--output-format stream-json` -Same envelope as `json` above (including on the proxy and Docker-unbundle paths). +Same envelope as `json` above (including on the Docker-unbundle path). ## Notes @@ -120,11 +111,8 @@ Same envelope as `json` above (including on the proxy and Docker-unbundle paths) gap, not one introduced by this port. Slugs sourced from the Management API's function list (downloading-all) are validated against the same pattern as user-supplied slugs, on both paths, before any per-slug download runs (CLI-1891). -- `--legacy-bundle` is a hidden flag forwarded to the Go binary for backward compatibility — it - requires installing a real Deno binary on the host (`InstallOrUpgradeDeno`) and is a pre-1.120.0 - compatibility fallback; native TS port tracked separately (CLI-1963). `--use-docker` is a hidden - flag but now runs natively. -- `--use-docker`, `--use-api`, and `--legacy-bundle` are mutually exclusive. +- `--use-docker` is a hidden flag but runs natively. +- `--use-docker` and `--use-api` are mutually exclusive. - `--use-docker` defaults to `true`, so a bare `supabase functions download` runs the native Docker-unbundle downloader unless `--use-api` resolves to `true`, which forces the native server-side download path instead (the resolved flag value is what's checked, not presence — @@ -134,7 +122,35 @@ Same envelope as `json` above (including on the proxy and Docker-unbundle paths) installed or running. - The mutual-exclusivity check only counts flags the user explicitly passed on the command line, not `--use-docker`'s default value — so `--use-api` alone never trips the "mutually exclusive" - error. The `--legacy-bundle` Go proxy call itself only ever forwards `--legacy-bundle`, never - `--use-docker` alongside it, even though `--use-docker` defaults to `true`. + error. - Refreshes the linked-project telemetry cache and flushes telemetry state after resolving a project ref. + +### `--legacy-bundle` is removed + +The flag is removed: passing it (with any value, including `--legacy-bundle=false`) fails +with a removal error before credentials are resolved or any download, Docker, or API work +runs, so it needs no login or `SUPABASE_ACCESS_TOKEN`. `` is the requested +Function name (the literal `` when none was given). The suggestion reads: + +``` +Retry with `supabase functions download --use-api ` to unbundle server-side without Docker. If that also fails and the Function was deployed with a CLI older than 1.120.0, redeploy it with the current CLI. +``` + +It is checked before `--use-api`/`--use-docker` mutual-exclusivity validation, so combining +it with either still hits the removal error first. + +The flag definition and its rejection live in `download.command.ts` (hidden from `--help`) so +misuse produces this actionable error, with `error_fingerprint` ending in `:removed_flag` +(`RemovedSurfaceError`), instead of an unknown-flag parse error with no telemetry at all or a +login error. + +### Docker-extraction failure suggestion + +Any Docker-unbundle failure (network/volume creation, container create/start, log +streaming, or a non-zero container exit) prints a suggestion leading with the +Docker-avoiding retry: + +``` +Retry with supabase functions download --use-api to unbundle server-side. If that also fails and the Function was deployed with a CLI older than 1.120.0, redeploy it with the current CLI. +``` diff --git a/apps/cli/src/commands/functions/download/download.command.ts b/apps/cli/src/commands/functions/download/download.command.ts index 188f2f61b1..1e6f7e2aaf 100644 --- a/apps/cli/src/commands/functions/download/download.command.ts +++ b/apps/cli/src/commands/functions/download/download.command.ts @@ -1,9 +1,13 @@ +import { Effect, Layer, Option } from "effect"; import { Argument, Command, Flag } from "effect/unstable/cli"; import type * as CliCommand from "effect/unstable/cli/Command"; import { FUNCTIONS_PROJECT_REF_SAFE_FLAGS } from "../../../shared/functions/functions.shared.ts"; import { withJsonErrorHandling } from "../../../shared/output/json-error-handling.ts"; +import { commandRuntimeLayer } from "../../../shared/runtime/command-runtime.layer.ts"; import { managementApiRuntimeLayer } from "../../../command-internal/management-api-runtime.layer.ts"; +import { removedFlag } from "../../../command-internal/removed-command.ts"; import { withCommandTelemetry } from "../../../telemetry/command-telemetry.ts"; +import { telemetryStateLayer } from "../../../telemetry/telemetry-state.layer.ts"; import { functionsDownload } from "./download.handler.ts"; const config = { @@ -24,9 +28,11 @@ const config = { Flag.withDefault(true), Flag.withHidden, ), + // Kept parsed (and hidden) only so using it produces an actionable removal error instead of + // an unknown-flag parse error; see `functionsDownloadLegacyBundleHandler`. legacyBundle: Flag.boolean("legacy-bundle").pipe( - Flag.withDescription("Use legacy bundling."), - Flag.withDefault(false), + Flag.withDescription("Removed: use --use-api instead."), + Flag.optional, Flag.withHidden, ), } as const; @@ -41,6 +47,24 @@ export const functionsDownloadHandler = (flags: FunctionsDownloadFlags) => withJsonErrorHandling, ); +const COMMAND_PATH = ["functions", "download"]; +const managementApiLayer = managementApiRuntimeLayer(COMMAND_PATH); +const removedFlagLayer = Layer.mergeAll(commandRuntimeLayer(COMMAND_PATH), telemetryStateLayer); + +// Rejected on a credential-free runtime: `managementApiRuntimeLayer` resolves the access token +// eagerly, so building it first would fail with a login error before the removal message. +const functionsDownloadLegacyBundleHandler = (flags: FunctionsDownloadFlags) => { + const slug = Option.getOrElse(flags.functionName, () => ""); + return removedFlag( + "--legacy-bundle", + `Retry with \`supabase functions download --use-api ${slug}\` to unbundle server-side without Docker. If that also fails and the Function was deployed with a CLI older than 1.120.0, redeploy it with the current CLI.`, + ).pipe( + withCommandTelemetry({ flags, safeFlags: FUNCTIONS_PROJECT_REF_SAFE_FLAGS }), + withJsonErrorHandling, + Effect.provide(removedFlagLayer), + ); +}; + export const functionsDownloadCommand = Command.make("download", config).pipe( Command.withDescription( "Download the source code for a Function from the linked Supabase project. If no function name is provided, downloads all functions.", @@ -56,6 +80,9 @@ export const functionsDownloadCommand = Command.make("download", config).pipe( description: "Download all functions from a specific project", }, ]), - Command.withHandler(functionsDownloadHandler), - Command.provide(managementApiRuntimeLayer(["functions", "download"])), + Command.withHandler((flags) => + Option.isSome(flags.legacyBundle) + ? functionsDownloadLegacyBundleHandler(flags) + : functionsDownloadHandler(flags).pipe(Effect.provide(managementApiLayer)), + ), ); diff --git a/apps/cli/src/commands/functions/download/download.e2e.test.ts b/apps/cli/src/commands/functions/download/download.e2e.test.ts index caf99f9c69..2c7ed31ac0 100644 --- a/apps/cli/src/commands/functions/download/download.e2e.test.ts +++ b/apps/cli/src/commands/functions/download/download.e2e.test.ts @@ -14,8 +14,6 @@ const FAKE_REF = "a".repeat(20); describe("supabase functions download — argument validation", () => { const conflicts = [ { name: "--use-api + --use-docker", flags: ["--use-api", "--use-docker"] }, - { name: "--use-api + --legacy-bundle", flags: ["--use-api", "--legacy-bundle"] }, - { name: "--use-docker + --legacy-bundle", flags: ["--use-docker", "--legacy-bundle"] }, ] as const; for (const { name, flags } of conflicts) { @@ -42,8 +40,4 @@ describe("supabase functions download — argument validation", () => { // `--use-api` alone (without --use-docker) is covered in // download.integration.test.ts instead, since validating it here would // require a real network round-trip to the Management API. - - // `--legacy-bundle` alone is covered in download.integration.test.ts - // instead: it routes to the Go binary's downloader, which would trigger a - // real Deno download from GitHub on every e2e run. }); diff --git a/apps/cli/src/commands/functions/download/download.handler.ts b/apps/cli/src/commands/functions/download/download.handler.ts index 9bf4d9affa..ee0f4a160e 100644 --- a/apps/cli/src/commands/functions/download/download.handler.ts +++ b/apps/cli/src/commands/functions/download/download.handler.ts @@ -1,11 +1,7 @@ import { Effect, Option, Path, Stdio } from "effect"; -import { - downloadFunctions, - makeGoProxyLegacyBundleArgs, -} from "../../../shared/functions/download.ts"; +import { downloadFunctions } from "../../../shared/functions/download.ts"; import { resolveEdgeRuntimeVersionPin } from "../../../shared/functions/functions.shared.ts"; import { Output } from "../../../shared/output/output.service.ts"; -import { GoProxy } from "../../../command-internal/go-proxy.service.ts"; import { aqua, bold, yellow } from "../../../command-internal/colors.ts"; import { functionsGoConfigCompat } from "../../../command-internal/functions-go-config.ts"; import { CommandPlatformApi } from "../../../auth/command-platform-api.service.ts"; @@ -23,7 +19,6 @@ export const functionsDownload = Effect.fn("functions.download")(function* ( const resolver = yield* ProjectRefResolver; const linkedProjectCache = yield* LinkedProjectCache; const telemetryState = yield* TelemetryState; - const proxy = yield* GoProxy; const output = yield* Output; const stdio = yield* Stdio.Stdio; const path = yield* Path.Path; @@ -54,25 +49,8 @@ export const functionsDownload = Effect.fn("functions.download")(function* ( }), ), ), - // Suppresses the delegated binary's own `cli_command_executed` so a - // proxied invocation fires exactly one event. In machine-output mode its - // stdout is captured and discarded instead of inherited, since - // `downloadFunctions` emits the `Output` envelope itself. - proxyDownload: (proxyFlags, projectRef, captureOutput) => { - const args = makeGoProxyLegacyBundleArgs(proxyFlags.functionName, projectRef); - const env = { SUPABASE_TELEMETRY_DISABLED: "1" }; - return captureOutput - ? Effect.asVoid( - proxy.execCapture(args, { env, stdin: "ignore", suppressChildTelemetry: true }), - ) - : proxy.exec(args, { env, suppressChildTelemetry: true }); - }, }); - // `--legacy-bundle` emits its own final summary inside `downloadFunctions`. - if (flags.legacyBundle) { - return; - } yield* Effect.annotateCurrentSpan("function.count", result.slugs.length); if (result.empty) { diff --git a/apps/cli/src/commands/functions/download/download.integration.test.ts b/apps/cli/src/commands/functions/download/download.integration.test.ts index 0ce78d449b..7ed24275f1 100644 --- a/apps/cli/src/commands/functions/download/download.integration.test.ts +++ b/apps/cli/src/commands/functions/download/download.integration.test.ts @@ -36,7 +36,6 @@ import { } from "../../../../tests/helpers/command-mocks.ts"; import { mockOutput } from "../../../../tests/helpers/mocks.ts"; import { mockChildProcessSpawner } from "../../../../tests/helpers/child-process-spawner.ts"; -import { GoProxy } from "../../../command-internal/go-proxy.service.ts"; import { containerRuntimeNotFoundMessage } from "../../../command-internal/container-cli.ts"; import { downloadFunctions } from "../../../shared/functions/download.ts"; import { functionsGoConfigCompat } from "../../../command-internal/functions-go-config.ts"; @@ -154,7 +153,7 @@ const baseFlags: FunctionsDownloadFlags = { projectRef: Option.none(), useApi: false, useDocker: false, - legacyBundle: false, + legacyBundle: Option.none(), }; function multipartResponse( @@ -187,32 +186,6 @@ function multipartResponse( ); } -function mockProxy() { - const calls: Array> = []; - const envs: Array | undefined> = []; - const captureCalls: Array> = []; - const captureEnvs: Array | undefined> = []; - return { - calls, - envs, - captureCalls, - captureEnvs, - layer: Layer.succeed(GoProxy, { - exec: (args, opts) => - Effect.sync(() => { - calls.push([...args]); - envs.push(opts?.env); - }), - execCapture: (args, opts) => - Effect.sync(() => { - captureCalls.push([...args]); - captureEnvs.push(opts?.env); - return ""; - }), - }), - }; -} - describe("functions download", () => { it.live("downloads a function natively into the legacy workdir", () => { const out = mockOutput({ format: "text" }); @@ -222,7 +195,6 @@ describe("functions download", () => { ? Effect.succeed(multipartResponse(request)) : Effect.succeed(jsonResponse(request, 200, {})), }); - const proxy = mockProxy(); const linkedProjectCache = mockLinkedProjectCacheTracked(); const telemetry = mockTelemetryStateTracked(); const layer = Layer.mergeAll( @@ -233,7 +205,6 @@ describe("functions download", () => { linkedProjectCache: linkedProjectCache.layer, telemetry: telemetry.layer, }), - proxy.layer, Stdio.layerTest({ args: Effect.succeed([ "functions", @@ -250,7 +221,6 @@ describe("functions download", () => { const path = yield* Path.Path; yield* functionsDownload(baseFlags); - expect(proxy.calls).toEqual([]); expect( yield* fs.readFileString( path.join(tempRoot.current, "supabase", "functions", "hello-world", "index.ts"), @@ -280,7 +250,6 @@ describe("functions download", () => { api: mockCommandPlatformApi({ handler }), cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - mockProxy().layer, Stdio.layerTest({ args: Effect.succeed(args) }), ); } @@ -473,7 +442,6 @@ describe("functions download", () => { }), cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - mockProxy().layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -501,7 +469,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); // Non-empty stdout/stderr exercises both the text-mode stdout routing // and always-to-stderr branches in `downloadWithDockerUnbundle`. const child = mockDockerUnbundle({ @@ -514,7 +481,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -532,8 +498,6 @@ describe("functions download", () => { const path = yield* Path.Path; yield* functionsDownload({ ...baseFlags, useDocker: true }); - expect(proxy.calls).toEqual([]); - expect(proxy.captureCalls).toEqual([]); expect(api.requests.some((request) => request.url.endsWith("/hello-world/body"))).toBe( true, ); @@ -545,8 +509,8 @@ describe("functions download", () => { expect(out.stderrText).toContain("Downloading function: hello-world\n"); expect(out.stdoutText).toContain("unbundle: wrote index.ts\n"); expect(out.stderrText).toContain("unbundle: warning about deno.json\n"); - // Unlike the server-side and --legacy-bundle paths, the native Docker - // path never prints a "Downloaded Function ..." success line. + // Unlike the server-side path, the native Docker path never prints a + // "Downloaded Function ..." success line. expect(out.stderrText).not.toContain("Downloaded Function"); // No `--debug` — the temp eszip file is removed after the run. expect( @@ -568,14 +532,12 @@ describe("functions download", () => { ? Effect.succeed(multipartResponse(request)) : Effect.succeed(jsonResponse(request, 200, {})), }); - const proxy = mockProxy(); const layer = Layer.mergeAll( buildTestRuntime({ out, api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, Stdio.layerTest({ args: Effect.succeed([ "functions", @@ -595,7 +557,6 @@ describe("functions download", () => { // the explicit `--use-api`. yield* functionsDownload({ ...baseFlags, useApi: true, useDocker: true }); - expect(proxy.calls).toEqual([]); expect( yield* fs.readFileString( path.join(tempRoot.current, "supabase", "functions", "hello-world", "index.ts"), @@ -610,7 +571,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -618,7 +578,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -637,8 +596,6 @@ describe("functions download", () => { // `--use-api=false` leaves `--use-docker`'s own default (true) in effect. yield* functionsDownload({ ...baseFlags, useApi: false, useDocker: true }); - expect(proxy.calls).toEqual([]); - expect(proxy.captureCalls).toEqual([]); expect( child.spawned.some( (spawned) => spawned.command === "docker" && spawned.args[0] === "run", @@ -653,7 +610,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "json" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); // Non-empty container stdout exercises the machine-mode branch that // routes it to stderr, keeping stdout payload-only. const child = mockDockerUnbundle({ runStdout: ["unbundle: wrote index.ts"] }); @@ -663,7 +619,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -681,8 +636,6 @@ describe("functions download", () => { return Effect.gen(function* () { yield* functionsDownload({ ...baseFlags, useDocker: true }); - expect(proxy.calls).toEqual([]); - expect(proxy.captureCalls).toEqual([]); expect( child.spawned.some( (spawned) => spawned.command === "docker" && spawned.args[0] === "run", @@ -710,7 +663,6 @@ describe("functions download", () => { ) : Effect.succeed(jsonResponse(request, 200, {})), }); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -718,7 +670,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -739,8 +690,6 @@ describe("functions download", () => { useDocker: true, }); - expect(proxy.calls).toEqual([]); - expect(proxy.captureCalls).toEqual([]); expect( child.spawned.filter( (spawned) => spawned.command === "docker" && spawned.args[0] === "run", @@ -771,7 +720,6 @@ describe("functions download", () => { it.live("runs docker with the expected binds, network, and unbundle command", () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -779,7 +727,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -853,7 +800,6 @@ describe("functions download", () => { // `deploy.ts`'s `buildDockerBinds` applies the same carve-out. const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -861,7 +807,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -908,7 +853,6 @@ describe("functions download", () => { // risk a negotiated response instead of the raw eszip body. const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -916,7 +860,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -941,7 +884,6 @@ describe("functions download", () => { it.live("uses an explicit --network-id override instead of the derived network name", () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -949,7 +891,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -986,7 +927,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -994,7 +934,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1025,7 +964,6 @@ describe("functions download", () => { it.live("honors the final occurrence of a repeated --network-id flag", () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -1033,7 +971,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1065,7 +1002,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -1073,7 +1009,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1108,7 +1043,6 @@ describe("functions download", () => { // `--project-ref` rather than an ancestor's `project_id`. const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const nestedWorkdir = `${tempRoot.current}/nested`; const layer = Layer.mergeAll( @@ -1117,7 +1051,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: nestedWorkdir }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1159,7 +1092,6 @@ describe("functions download", () => { // both files resolves `project_id` from config.toml, not config.json. const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -1167,7 +1099,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1208,7 +1139,6 @@ describe("functions download", () => { // passed straight through to `docker run --network`. const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -1216,7 +1146,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1247,7 +1176,6 @@ describe("functions download", () => { // must not get a second `v`. const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -1255,7 +1183,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1290,7 +1217,6 @@ describe("functions download", () => { it.live("keeps the temporary eszip file when --debug is passed", () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -1298,7 +1224,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1331,7 +1256,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -1339,7 +1263,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1373,7 +1296,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); // Every docker command (including the `docker info` probe) fails, // modeling Docker not running. const child = mockChildProcessSpawner({ exitCode: 1 }); @@ -1383,7 +1305,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1417,10 +1338,9 @@ describe("functions download", () => { ); describe("docker unbundle container failures", () => { - it.live("fails with the legacy-bundle suggestion when the container exits non-zero", () => { + it.live("fails with the redeploy suggestion when the container exits non-zero", () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockDockerUnbundle({ runExitCode: 1, runStderr: ["boom"] }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -1428,7 +1348,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1448,7 +1367,7 @@ describe("functions download", () => { expect(error).toBeInstanceOf(Error); expect((error as Error).message).toBe("error running container: exit 1"); expect((error as Error & { suggestion?: string }).suggestion).toBe( - "\nIf your function is deployed using CLI < 1.120.0, trying running supabase functions download --legacy-bundle hello-world instead.", + "\nRetry with supabase functions download --use-api hello-world to unbundle server-side. If that also fails and the Function was deployed with a CLI older than 1.120.0, redeploy it with the current CLI.", ); }).pipe(Effect.provide(layer)); }); @@ -1458,7 +1377,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockDockerUnbundle({ runExitCode: 1, // Full-line, case-insensitive match required; a substring like @@ -1471,7 +1389,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1502,7 +1419,7 @@ describe("functions download", () => { expect((error as Error).message).toBe("error running container: exit 1"); expect((error as Error & { suggestion?: string }).suggestion).toBe( "Please use deno v2 in supabase/config.toml to download this Function:\n\n[edge_runtime]\ndeno_version = 2\n" + - "\nIf your function is deployed using CLI < 1.120.0, trying running supabase functions download --legacy-bundle hello-world instead.", + "\nRetry with supabase functions download --use-api hello-world to unbundle server-side. If that also fails and the Function was deployed with a CLI older than 1.120.0, redeploy it with the current CLI.", ); }).pipe(Effect.provide(layer)); }, @@ -1513,7 +1430,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockDockerUnbundle({ runExitCode: 1, runStderr: ["permission denied"] }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -1521,7 +1437,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1549,7 +1464,7 @@ describe("functions download", () => { ); expect((error as Error & { suggestion?: string }).suggestion).toBe( - "\nIf your function is deployed using CLI < 1.120.0, trying running supabase functions download --legacy-bundle hello-world instead.", + "\nRetry with supabase functions download --use-api hello-world to unbundle server-side. If that also fails and the Function was deployed with a CLI older than 1.120.0, redeploy it with the current CLI.", ); }).pipe(Effect.provide(layer)); }, @@ -1559,7 +1474,6 @@ describe("functions download", () => { it.live("fails when ensureDockerNetwork can't create a missing network", () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const spawnerOpts: { exitCode?: number; stderr?: string[]; @@ -1576,7 +1490,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1617,7 +1530,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockDockerRunSpawnFailure(); const layer = Layer.mergeAll( buildTestRuntime({ @@ -1625,7 +1537,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1652,7 +1563,7 @@ describe("functions download", () => { `failed to run the edge-runtime unbundle container: ${containerRuntimeNotFoundMessage}`, ); expect((error as Error & { suggestion?: string }).suggestion).toBe( - "\nIf your function is deployed using CLI < 1.120.0, trying running supabase functions download --legacy-bundle hello-world instead.", + "\nRetry with supabase functions download --use-api hello-world to unbundle server-side. If that also fails and the Function was deployed with a CLI older than 1.120.0, redeploy it with the current CLI.", ); expect(child.spawned.some((spawned) => spawned.args[0] === "run")).toBe(true); expect( @@ -1664,62 +1575,55 @@ describe("functions download", () => { }, ); - it.live( - "reports no functions found without delegating when the project is empty in machine mode", - () => { - const out = mockOutput({ format: "json" }); - const api = mockCommandPlatformApi({ - handler: (request) => - request.url.endsWith("/functions") - ? Effect.succeed(jsonResponse(request, 200, [])) - : Effect.succeed(jsonResponse(request, 200, {})), + it.live("reports no functions found when the project is empty in machine mode", () => { + const out = mockOutput({ format: "json" }); + const api = mockCommandPlatformApi({ + handler: (request) => + request.url.endsWith("/functions") + ? Effect.succeed(jsonResponse(request, 200, [])) + : Effect.succeed(jsonResponse(request, 200, {})), + }); + // Stand-in for the real `ChildProcessSpawner`: `useDocker: true` still + // probes `docker info` even with no functions to download, so this must + // not spawn a real `docker` process. + const child = mockChildProcessSpawner({ exitCode: 0 }); + const layer = Layer.mergeAll( + buildTestRuntime({ + out, + api, + cliSettings: mockCommandSettings({ workdir: tempRoot.current }), + }), + child.layer, + Stdio.layerTest({ + args: Effect.succeed([ + "functions", + "download", + "--project-ref", + "abcdefghijklmnopqrst", + "--output-format", + "json", + ]), + }), + ); + + return Effect.gen(function* () { + yield* functionsDownload({ + ...baseFlags, + functionName: Option.none(), + useDocker: true, }); - const proxy = mockProxy(); - // Stand-in for the real `ChildProcessSpawner`: `useDocker: true` still - // probes `docker info` even with no functions to download, so this must - // not spawn a real `docker` process. - const child = mockChildProcessSpawner({ exitCode: 0 }); - const layer = Layer.mergeAll( - buildTestRuntime({ - out, - api, - cliSettings: mockCommandSettings({ workdir: tempRoot.current }), - }), - proxy.layer, - child.layer, - Stdio.layerTest({ - args: Effect.succeed([ - "functions", - "download", - "--project-ref", - "abcdefghijklmnopqrst", - "--output-format", - "json", - ]), + + expect(out.messages).toContainEqual( + expect.objectContaining({ + type: "success", + message: "No functions found.", + data: { function_slugs: [], project_ref: "abcdefghijklmnopqrst" }, }), ); + }).pipe(Effect.provide(layer)); + }); - return Effect.gen(function* () { - yield* functionsDownload({ - ...baseFlags, - functionName: Option.none(), - useDocker: true, - }); - - expect(proxy.calls).toEqual([]); - expect(proxy.captureCalls).toEqual([]); - expect(out.messages).toContainEqual( - expect.objectContaining({ - type: "success", - message: "No functions found.", - data: { function_slugs: [], project_ref: "abcdefghijklmnopqrst" }, - }), - ); - }).pipe(Effect.provide(layer)); - }, - ); - - it.live("fails before delegating when the pre-flight function list fails in machine mode", () => { + it.live("fails when the pre-flight function list fails in machine mode", () => { const out = mockOutput({ format: "json" }); const api = mockCommandPlatformApi({ handler: (request) => @@ -1727,7 +1631,6 @@ describe("functions download", () => { ? Effect.succeed(jsonResponse(request, 500, { message: "unavailable" })) : Effect.succeed(jsonResponse(request, 200, {})), }); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -1735,7 +1638,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1757,8 +1659,6 @@ describe("functions download", () => { }).pipe(Effect.exit); expect(Exit.isFailure(exit)).toBe(true); - expect(proxy.calls).toEqual([]); - expect(proxy.captureCalls).toEqual([]); }).pipe(Effect.provide(layer)); }); @@ -1773,7 +1673,6 @@ describe("functions download", () => { ? Effect.succeed(jsonResponse(request, 200, [{}])) : Effect.succeed(jsonResponse(request, 200, {})), }); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -1781,7 +1680,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -1803,7 +1701,6 @@ describe("functions download", () => { }).pipe(Effect.exit); expect(Exit.isFailure(exit)).toBe(true); - expect(proxy.calls).toEqual([]); expect(out.messages).not.toContainEqual( expect.objectContaining({ type: "success", message: "No functions found." }), ); @@ -1829,14 +1726,12 @@ describe("functions download", () => { ? Effect.succeed(jsonResponse(request, 500, { message: "unavailable" })) : Effect.succeed(jsonResponse(request, 200, {})), }); - const proxy = mockProxy(); const layer = Layer.mergeAll( buildTestRuntime({ out, api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, Stdio.layerTest({ args: Effect.succeed(["functions", "download", "--project-ref", PROJECT_ID]), }), @@ -1866,60 +1761,15 @@ describe("functions download", () => { }, ); - it.live("forwards only --legacy-bundle to the Go proxy, not the --use-docker default too", () => { - const out = mockOutput({ format: "text" }); - const api = mockCommandPlatformApi(); - const proxy = mockProxy(); - const layer = Layer.mergeAll( - buildTestRuntime({ - out, - api, - cliSettings: mockCommandSettings({ workdir: tempRoot.current }), - }), - proxy.layer, - Stdio.layerTest({ - args: Effect.succeed([ - "functions", - "download", - "hello-world", - "--legacy-bundle", - "--project-ref", - "abcdefghijklmnopqrst", - ]), - }), - ); - - return Effect.gen(function* () { - // `useDocker: true` mirrors the flag's own default even though only - // `--legacy-bundle` was passed; forwarding both would make the Go - // binary's own MarkFlagsMutuallyExclusive reject the combination. - yield* functionsDownload({ ...baseFlags, useDocker: true, legacyBundle: true }); - - expect(proxy.calls).toEqual([ - [ - "functions", - "download", - "hello-world", - "--project-ref", - "abcdefghijklmnopqrst", - "--legacy-bundle", - ], - ]); - expect(proxy.envs).toEqual([{ SUPABASE_TELEMETRY_DISABLED: "1" }]); - }).pipe(Effect.provide(layer)); - }); - - it.live("rejects an invalid slug before ever reaching the Go proxy", () => { + it.live("rejects an invalid slug before any download work happens", () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const layer = Layer.mergeAll( buildTestRuntime({ out, api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, Stdio.layerTest({ args: Effect.succeed([ "functions", @@ -1932,8 +1782,6 @@ describe("functions download", () => { ); return Effect.gen(function* () { - // `useDocker: true` reflects the flag's own default; slug validation - // must run before the Go proxy sees this argv. const exit = yield* functionsDownload({ ...baseFlags, functionName: Option.some("../../etc"), @@ -1941,7 +1789,6 @@ describe("functions download", () => { }).pipe(Effect.exit); expect(Exit.isFailure(exit)).toBe(true); - expect(proxy.calls).toEqual([]); }).pipe(Effect.provide(layer)); }); @@ -1955,7 +1802,6 @@ describe("functions download", () => { ? Effect.succeed(multipartResponse(request)) : Effect.succeed(jsonResponse(request, 200, {})), }); - const proxy = mockProxy(); const analytics = mockContextualAnalytics(); const layer = Layer.mergeAll( buildTestRuntime({ @@ -1964,7 +1810,6 @@ describe("functions download", () => { cliSettings: mockCommandSettings({ workdir: tempRoot.current }), analytics, }), - proxy.layer, commandRuntimeLayer(["functions", "download"]).pipe(Layer.provide(BunCrypto.layer)), Stdio.layerTest({ args: Effect.succeed([ @@ -1992,14 +1837,12 @@ describe("functions download", () => { it.live("rejects the bundler mutex with cobra's exact error text", () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const layer = Layer.mergeAll( buildTestRuntime({ out, api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, Stdio.layerTest({ args: Effect.succeed(["functions", "download", "--use-api", "--use-docker"]), }), @@ -2017,9 +1860,8 @@ describe("functions download", () => { throw new Error(`unexpected error: ${String(error)}`); } expect(error.message).toBe( - "if any flags in the group [use-api use-docker legacy-bundle] are set none of the others can be; [use-api use-docker] were all set", + "if any flags in the group [use-api use-docker] are set none of the others can be; [use-api use-docker] were all set", ); - expect(proxy.calls).toEqual([]); }).pipe(Effect.provide(layer)); }); @@ -2029,7 +1871,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -2037,7 +1878,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -2076,7 +1916,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -2084,7 +1923,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -2125,7 +1963,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -2133,7 +1970,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -2165,7 +2001,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -2173,7 +2008,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -2205,7 +2039,6 @@ describe("functions download", () => { it.live("prefers an explicit --network-id flag over SUPABASE_NETWORK_ID", () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -2213,7 +2046,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -2249,7 +2081,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -2257,7 +2088,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -2332,14 +2162,12 @@ describe("functions download", () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const layer = Layer.mergeAll( buildTestRuntime({ out, api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -2373,7 +2201,6 @@ describe("functions download", () => { () => { const out = mockOutput({ format: "text" }); const api = mockCommandPlatformApi(); - const proxy = mockProxy(); const child = mockChildProcessSpawner({ exitCode: 0 }); const layer = Layer.mergeAll( buildTestRuntime({ @@ -2381,7 +2208,6 @@ describe("functions download", () => { api, cliSettings: mockCommandSettings({ workdir: tempRoot.current }), }), - proxy.layer, child.layer, Stdio.layerTest({ args: Effect.succeed([ @@ -2454,7 +2280,6 @@ describe("functions download", () => { goConfigCompat: functionsGoConfigCompat, edgeRuntimeVersion: "1.69.12", resolveProjectRef: () => Effect.succeed(PROJECT_ID), - proxyDownload: () => Effect.die("unexpected proxy invocation"), styleWarning: (text) => `${text}`, }, ); diff --git a/apps/cli/src/commands/functions/download/download.removed-flag.integration.test.ts b/apps/cli/src/commands/functions/download/download.removed-flag.integration.test.ts new file mode 100644 index 0000000000..38b0296357 --- /dev/null +++ b/apps/cli/src/commands/functions/download/download.removed-flag.integration.test.ts @@ -0,0 +1,136 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "@effect/vitest"; +import { Cause, Effect, Exit, Layer, Stdio } from "effect"; +import { CliOutput, Command } from "effect/unstable/cli"; + +import { AccessTokenRequiredError } from "../../../auth/errors.ts"; +import { GLOBAL_FLAGS } from "../../../command-internal/global-flags.ts"; +import { RemovedSurfaceError } from "../../../command-internal/removed-command.ts"; +import { textCliOutputFormatter } from "../../../shared/output/text-formatter.ts"; +import { + buildTestRuntime, + isolatedHomeLayer, + mockCommandPlatformApi, + mockCommandSettings, + useTempWorkdir, +} from "../../../../tests/helpers/command-mocks.ts"; +import { + mockContextualAnalytics, + mockOutput, + mockTelemetryRuntime, +} from "../../../../tests/helpers/mocks.ts"; +import { functionsCommand } from "../functions.command.ts"; + +const tempRoot = useTempWorkdir("supabase-functions-download-removed-flag-"); + +const testRoot = Command.make("supabase").pipe( + Command.withSubcommands([functionsCommand]), + Command.withGlobalFlags(GLOBAL_FLAGS), +); + +// The real `managementApiRuntimeLayer` resolves its token from the raw process env, the OS +// keyring, and `/.supabase/access-token`, so all three are pinned to an empty state. +function runDownload(args: ReadonlyArray, format: "text" | "json" = "text") { + const out = mockOutput({ format }); + const analytics = mockContextualAnalytics(); + const layer = Layer.mergeAll( + buildTestRuntime({ + out, + api: mockCommandPlatformApi(), + cliSettings: mockCommandSettings({ workdir: tempRoot.current }), + analytics, + runtimeInfo: isolatedHomeLayer(tempRoot.current, { + SUPABASE_HOME: tempRoot.current, + SUPABASE_NO_KEYRING: "1", + }), + }), + CliOutput.layer(textCliOutputFormatter()), + mockTelemetryRuntime({ configDir: `${tempRoot.current}/.supabase` }), + Stdio.layerTest({ args: Effect.succeed(args) }), + ); + return Command.runWith(testRoot, { version: "0.0.0-test" })(args).pipe( + Effect.exit, + Effect.provide(layer), + Effect.map((exit) => ({ exit, out, analytics })), + ); +} + +describe("functions download --legacy-bundle through command dispatch", () => { + let fetchSpy: ReturnType; + + beforeEach(() => { + fetchSpy = vi + .spyOn(globalThis, "fetch") + .mockRejectedValue(new Error("unexpected network request")); + }); + + afterEach(() => { + fetchSpy.mockRestore(); + }); + + it.live("needs credentials without the removed flag", () => + Effect.gen(function* () { + const { exit } = yield* runDownload(["functions", "download", "hello"]); + + expect(Exit.isFailure(exit)).toBe(true); + if (!Exit.isFailure(exit)) return; + expect(Cause.squash(exit.cause)).toBeInstanceOf(AccessTokenRequiredError); + }), + ); + + for (const flag of ["--legacy-bundle", "--legacy-bundle=false"]) { + it.live(`rejects ${flag} with the removal error and no credentials`, () => + Effect.gen(function* () { + const { exit, analytics } = yield* runDownload(["functions", "download", "hello", flag]); + + expect(Exit.isFailure(exit)).toBe(true); + if (!Exit.isFailure(exit)) return; + const error = Cause.squash(exit.cause); + expect(error).toBeInstanceOf(RemovedSurfaceError); + if (!(error instanceof RemovedSurfaceError)) return; + expect(error.kind).toBe("flag"); + expect(error.message).toBe("--legacy-bundle was removed."); + expect(error.suggestion).toBe( + "Retry with `supabase functions download --use-api hello` to unbundle server-side without Docker. If that also fails and the Function was deployed with a CLI older than 1.120.0, redeploy it with the current CLI.", + ); + expect(fetchSpy).not.toHaveBeenCalled(); + + const events = analytics.captured.filter((c) => c.event === "cli_command_executed"); + expect(events).toHaveLength(1); + expect(events[0]?.properties).toMatchObject({ + command: "functions download", + exit_code: 1, + error_fingerprint: "tag:RemovedSurfaceError:removed_flag", + }); + }), + ); + } + + it.live("keeps the placeholder when no function name was given", () => + Effect.gen(function* () { + const { exit } = yield* runDownload(["functions", "download", "--legacy-bundle"]); + + expect(Exit.isFailure(exit)).toBe(true); + if (!Exit.isFailure(exit)) return; + const error = Cause.squash(exit.cause); + expect(error).toBeInstanceOf(RemovedSurfaceError); + if (!(error instanceof RemovedSurfaceError)) return; + expect(error.suggestion).toContain("`supabase functions download --use-api `"); + }), + ); + + it.live("reports the removal through the JSON error envelope", () => + Effect.gen(function* () { + const { exit, out } = yield* runDownload( + ["functions", "download", "hello", "--legacy-bundle"], + "json", + ); + + expect(Exit.isSuccess(exit)).toBe(true); + expect(out.failures).toHaveLength(1); + expect(out.failures[0]).toMatchObject({ + code: "RemovedSurfaceError", + message: "--legacy-bundle was removed.", + }); + }), + ); +}); diff --git a/apps/cli/src/commands/gen/gen.command.ts b/apps/cli/src/commands/gen/gen.command.ts index 245640f2b5..1f96cc3588 100644 --- a/apps/cli/src/commands/gen/gen.command.ts +++ b/apps/cli/src/commands/gen/gen.command.ts @@ -11,6 +11,6 @@ export const genCommand = Command.make("gen").pipe( genTypesCommand, genSigningKeyCommand, genBearerJwtCommand, - genKeysCommand, + genKeysCommand.pipe(Command.unlisted), ]), ); diff --git a/apps/cli/src/commands/gen/keys/SIDE_EFFECTS.md b/apps/cli/src/commands/gen/keys/SIDE_EFFECTS.md index 6b2f9d460b..1494730ae1 100644 --- a/apps/cli/src/commands/gen/keys/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/gen/keys/SIDE_EFFECTS.md @@ -1,5 +1,8 @@ # `supabase gen keys` +Removed and unlisted. The command is a tombstone: it fails with a removal error +and a replacement suggestion instead of contacting the Management API. + ## Files Read | Path | Format | When | @@ -14,42 +17,47 @@ ## API Routes -| Method | Path | Auth | Request body | Response (used fields) | -| ------ | ----------------------------- | ------------ | ------------ | ---------------------- | -| `GET` | `/v1/projects/{ref}/api-keys` | Bearer token | none | `[{name, api_key}]` | +| Method | Path | Auth | Request body | Response (used fields) | +| ------ | ---- | ---- | ------------ | ---------------------- | +| — | — | — | — | — | ## Environment Variables -| Variable | Purpose | Required? | -| ----------------------- | --------------------------------------- | ------------------------------------------------------- | -| `SUPABASE_ACCESS_TOKEN` | auth token | no (falls back to keyring → `~/.supabase/access-token`) | -| `SUPABASE_PROFILE` | built-in profile name or YAML file path | no (falls back to `~/.supabase/profile` -> `supabase`) | +| Variable | Purpose | Required? | +| -------- | ------- | --------- | +| — | — | — | ## Exit Codes -| Code | Condition | -| ---- | ------------------------------------- | -| `0` | success — keys printed to stdout | -| `1` | authentication error (no token found) | -| `1` | API error (non-2xx response) | +| Code | Condition | +| ---- | ---------------------------------- | +| `1` | every invocation (removed command) | ## Output ### `--output-format text` -Prints key-value pairs in env format (default) or JSON. +Writes the removal message and the replacement suggestion to stderr, two lines, +regardless of `-o`/`--output`: + +``` +supabase gen keys was removed. +Use `supabase projects api-keys --project-ref ` to read a project's API keys. +``` -### `--output-format json` +### `--output-format json` / `stream-json` -Not applicable. +Emits the JSON error envelope on stdout instead of the stderr lines above; the +envelope's `code` is `RemovedSurfaceError` and `suggestion` carries the same +replacement text. -### `--output-format stream-json` +## Telemetry Events Fired -Not applicable. +One `cli_command_executed` event per invocation, with `exit_code: 1` and an +`error_fingerprint` ending in `:removed_command` (`RemovedSurfaceError`). ## Notes -- **Deprecated**: use `gen signing-key` instead. -- `--project-ref` flag specifies the project ref. -- `--override-name` overrides specific variable names in the output. -- Experimental command for generating preview branch keys. +- Unlisted (not shown in `supabase gen --help`), still reachable by exact name. +- No flag value is read; the command fails identically regardless of + `--project-ref`/`--override-name`. diff --git a/apps/cli/src/commands/gen/keys/keys.command.ts b/apps/cli/src/commands/gen/keys/keys.command.ts index 232f8f10e0..dedb6163df 100644 --- a/apps/cli/src/commands/gen/keys/keys.command.ts +++ b/apps/cli/src/commands/gen/keys/keys.command.ts @@ -1,6 +1,9 @@ import { Command, Flag } from "effect/unstable/cli"; -import type * as CliCommand from "effect/unstable/cli/Command"; -import { genKeys } from "./keys.handler.ts"; +import { removedCommand } from "../../../command-internal/removed-command.ts"; +import { withJsonErrorHandling } from "../../../shared/output/json-error-handling.ts"; +import { commandRuntimeLayer } from "../../../shared/runtime/command-runtime.layer.ts"; +import { withCommandTelemetry } from "../../../telemetry/command-telemetry.ts"; +import { telemetryStateLayer } from "../../../telemetry/telemetry-state.layer.ts"; const config = { projectRef: Flag.string("project-ref").pipe( @@ -13,12 +16,14 @@ const config = { ), } as const; -export type GenKeysFlags = CliCommand.Command.Config.Infer; - export const genKeysCommand = Command.make("keys", config).pipe( - Command.withDescription( - 'Generate keys for preview branch. Deprecated: use "gen signing-key" instead.', + Command.withDescription("Removed: use `supabase projects api-keys --project-ref ` instead."), + Command.withShortDescription("Removed: use `projects api-keys` instead"), + Command.withHandler(() => + removedCommand( + "Use `supabase projects api-keys --project-ref ` to read a project's API keys.", + ).pipe(withCommandTelemetry(), withJsonErrorHandling), ), - Command.withShortDescription("Generate keys for preview branch (experimental)"), - Command.withHandler((flags) => genKeys(flags)), + Command.provide(commandRuntimeLayer(["gen", "keys"])), + Command.provide(telemetryStateLayer), ); diff --git a/apps/cli/src/commands/gen/keys/keys.handler.ts b/apps/cli/src/commands/gen/keys/keys.handler.ts deleted file mode 100644 index 620768e490..0000000000 --- a/apps/cli/src/commands/gen/keys/keys.handler.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Effect, Option } from "effect"; -import { GoProxy } from "../../../command-internal/go-proxy.service.ts"; -import type { GenKeysFlags } from "./keys.command.ts"; - -export const genKeys = Effect.fn("gen.keys")(function* (flags: GenKeysFlags) { - const proxy = yield* GoProxy; - const args: string[] = ["gen", "keys"]; - if (Option.isSome(flags.projectRef)) args.push("--project-ref", flags.projectRef.value); - for (const name of flags.overrideName) { - args.push("--override-name", name); - } - yield* proxy.exec(args); -}); diff --git a/apps/cli/src/commands/issue/SIDE_EFFECTS.md b/apps/cli/src/commands/issue/SIDE_EFFECTS.md index 3c588804ad..489b02264b 100644 --- a/apps/cli/src/commands/issue/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/issue/SIDE_EFFECTS.md @@ -4,6 +4,8 @@ - Opens a GitHub issue form URL in the user's default browser, unless `--no-browser` is passed. - Writes the generated issue form URL to stdout. +- `--crash-report-id` only prefills the issue form's ticket-id field; the CLI no longer creates + crash reports or support tickets (the global `--create-ticket` flag is removed). ## No local project changes diff --git a/apps/cli/src/commands/issue/issue.command.ts b/apps/cli/src/commands/issue/issue.command.ts index 6b98fa91f4..64f51c09a6 100644 --- a/apps/cli/src/commands/issue/issue.command.ts +++ b/apps/cli/src/commands/issue/issue.command.ts @@ -27,7 +27,7 @@ const issueBugConfig = { reproduce: issueOptionalTextFlag("reproduce", "Steps to reproduce."), crashReportId: issueOptionalTextFlag( "crash-report-id", - "Crash report ID printed by --create-ticket.", + "Crash report or support ticket ID, if you have one.", ), dockerServices: issueOptionalTextFlag( "docker-services", diff --git a/apps/cli/src/commands/pull/pull.steps.ts b/apps/cli/src/commands/pull/pull.steps.ts index 842bb7d62a..3f9564239a 100644 --- a/apps/cli/src/commands/pull/pull.steps.ts +++ b/apps/cli/src/commands/pull/pull.steps.ts @@ -120,8 +120,8 @@ export const pullDbStep = Effect.fnUntraced(function* (context: PullStepContext) /** * `functions` step: downloads every Edge Function's source, matching the standalone `functions - * download` command's `--use-api`/`--use-docker` defaults. `legacyBundle: false` keeps - * `proxyDownload` unreachable, so `resolveProjectRef` just returns the already-resolved ref. + * download` command's `--use-api`/`--use-docker` defaults. `resolveProjectRef` just returns the + * already-resolved ref. */ export const pullFunctionsStep = Effect.fnUntraced(function* (context: PullStepContext) { const api = yield* CommandPlatformApi; @@ -139,7 +139,6 @@ export const pullFunctionsStep = Effect.fnUntraced(function* (context: PullStepC projectRef: Option.some(context.ref), useApi: false, useDocker: true, - legacyBundle: false, }, { api, @@ -151,12 +150,6 @@ export const pullFunctionsStep = Effect.fnUntraced(function* (context: PullStepC styleAqua: (text) => aqua(text), styleWarning: (text) => yellow(text), resolveProjectRef: () => Effect.succeed(context.ref), - proxyDownload: () => - Effect.die( - new Error( - "supabase pull: functions download unexpectedly delegated to the Go binary (legacyBundle is always false for pull)", - ), - ), }, ); return { kind: "downloaded", result } satisfies PullFunctionsStepOutcome; diff --git a/apps/cli/src/commands/start/SIDE_EFFECTS.md b/apps/cli/src/commands/start/SIDE_EFFECTS.md index fc600a2b31..c95688c094 100644 --- a/apps/cli/src/commands/start/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/start/SIDE_EFFECTS.md @@ -315,9 +315,9 @@ prose, not structured data. ## Notes -- **`pg_net` converges with `[experimental.webhooks]` on every non-backup start** (see - `docs/go-cli-divergences.md`): a fresh volume installs `pg_net` only when webhooks are - enabled or migration history contains a `create extension … pg_net`; an existing volume +- **`pg_net` converges with `[experimental.webhooks]` on every non-backup start**: a fresh + volume installs `pg_net` only when webhooks are enabled or migration history contains a + `create extension … pg_net`; an existing volume additionally DROPS a `pg_net` that migration history does not own when webhooks are disabled. Accepted, documented edge: `pg_net` installed outside migrations (local Studio SQL editor / extension toggle) is dropped on the next start, and a tracked dependency on diff --git a/apps/cli/src/docs/docs-spec.tables.ts b/apps/cli/src/docs/docs-spec.tables.ts index edd7bc02d4..f6ff69055a 100644 --- a/apps/cli/src/docs/docs-spec.tables.ts +++ b/apps/cli/src/docs/docs-spec.tables.ts @@ -101,7 +101,6 @@ export const DOCS_EXPERIMENTAL_OPTIONAL: ReadonlySet = new Set([ * suffix) but excluded from the published reference. */ export const DOCS_EXCLUDED: ReadonlySet = new Set([ - "supabase-gen-keys", "supabase-inspect-db-cache-hit", "supabase-inspect-db-index-sizes", "supabase-inspect-db-index-usage", diff --git a/apps/cli/src/shared/cli/hidden-flag.unit.test.ts b/apps/cli/src/shared/cli/hidden-flag.unit.test.ts index ab83e33047..6d808677a1 100644 --- a/apps/cli/src/shared/cli/hidden-flag.unit.test.ts +++ b/apps/cli/src/shared/cli/hidden-flag.unit.test.ts @@ -1,20 +1,23 @@ -import { Cause, Effect, Exit, FileSystem, Layer, Schema } from "effect"; +import { Cause, Effect, Exit, FileSystem, Layer, Option, Schema, Stdio } from "effect"; import { BunServices } from "@effect/platform-bun"; import { CliOutput, Command, type HelpDoc } from "effect/unstable/cli"; import { describe, expect, it } from "@effect/vitest"; import { branchesCommand } from "../../commands/branches/branches.command.ts"; import { dbCommand } from "../../commands/db/db.command.ts"; +import { dbDiffCommand } from "../../commands/db/diff/diff.command.ts"; import { functionsCommand } from "../../commands/functions/functions.command.ts"; import { functionsDeployCommand } from "../../commands/functions/deploy/deploy.command.ts"; import { functionsDownloadCommand } from "../../commands/functions/download/download.command.ts"; import { functionsServeCommand } from "../../commands/functions/serve/serve.command.ts"; +import { genCommand } from "../../commands/gen/gen.command.ts"; import { initCommand } from "../../commands/init/init.command.ts"; +import { issueCommand } from "../../commands/issue/issue.command.ts"; import { projectsCommand } from "../../commands/projects/projects.command.ts"; import { projectsCreateCommand } from "../../commands/projects/create/create.command.ts"; import { startCommand } from "../../commands/start/start.command.ts"; import { stopCommand } from "../../commands/stop/stop.command.ts"; import { GLOBAL_FLAGS } from "../../command-internal/global-flags.ts"; -import { GoProxy } from "../../command-internal/go-proxy.service.ts"; +import { RemovedSurfaceError } from "../../command-internal/removed-command.ts"; import { TestDbMutuallyExclusiveFlagsError } from "../../command-internal/test-db.errors.ts"; import { mockAnalytics, @@ -36,26 +39,8 @@ const buildHelpDoc = ( cmd: Command.Command, ): HelpDoc.HelpDoc => (cmd as unknown as CommandImpl).buildHelpDoc([]); -function mockGoProxy() { - const calls: Array> = []; - const layer = Layer.succeed(GoProxy, { - exec: (args) => - Effect.sync(() => { - calls.push([...args]); - }), - execCapture: () => Effect.succeed(""), - }); - - return { layer, calls }; -} - -const testRootLayer = ( - proxy: Layer.Layer, - formatter: CliOutput.Formatter, - args: ReadonlyArray, -) => +const testRootLayer = (formatter: CliOutput.Formatter, args: ReadonlyArray) => Layer.mergeAll( - proxy, CliOutput.layer(formatter), Layer.succeed(CliArgs, { args }), mockOutput({ format: "text" }).layer, @@ -73,9 +58,11 @@ const testRoot = Command.make("supabase").pipe( stopCommand, initCommand, functionsCommand, + genCommand, projectsCommand, branchesCommand, dbCommand, + issueCommand, ]), Command.withGlobalFlags(GLOBAL_FLAGS), ); @@ -152,6 +139,10 @@ describe("native hidden flags", () => { "size", "high-availability", ]); + + expect(buildHelpDoc(dbDiffCommand).flags.map((flag) => flag.name)).not.toContain( + "use-pg-schema", + ); }); it.effect("passes hidden flag values to handlers by exact name", () => { @@ -191,14 +182,7 @@ describe("native hidden flags", () => { "abcdefghijklmnopqrst", "--use-docker=false", ]); - yield* runParser([ - "functions", - "download", - "hello", - "--project-ref", - "abcdefghijklmnopqrst", - "--legacy-bundle", - ]); + yield* runParser(["functions", "download", "hello", "--legacy-bundle"]); yield* runParser(["functions", "deploy", "hello", "--use-docker=false"]); yield* runParser(["functions", "deploy", "hello", "--legacy-bundle"]); yield* runParser(["functions", "serve", "--all=false"]); @@ -206,7 +190,7 @@ describe("native hidden flags", () => { expect.objectContaining({ preview: true }), expect.objectContaining({ backup: false }), expect.objectContaining({ useDocker: false }), - expect.objectContaining({ legacyBundle: true }), + expect.objectContaining({ legacyBundle: Option.some(true) }), expect.objectContaining({ useDocker: false }), expect.objectContaining({ legacyBundle: true }), expect.objectContaining({ all: false }), @@ -216,11 +200,10 @@ describe("native hidden flags", () => { it.effect("does not leak hidden flag names through unknown-flag suggestions", () => Effect.gen(function* () { - const proxy = mockGoProxy(); const args = ["projects", "create", "demo", "--pla"]; const exit = yield* Command.runWith(testRoot, { version: "0.0.0-test" })(args).pipe( - Effect.provide(testRootLayer(proxy.layer, silentCliOutputFormatter, args)), + Effect.provide(testRootLayer(silentCliOutputFormatter, args)), Effect.exit, ); @@ -260,22 +243,30 @@ describe("hidden subcommands", () => { ]); }); - it.effect("still executes hidden subcommands by exact name", () => + it.effect("still executes hidden tombstoned subcommands by exact name", () => Effect.gen(function* () { - const proxy = mockGoProxy(); - const run = (args: ReadonlyArray) => - Command.runWith(testRoot, { version: "0.0.0-test" })(args).pipe( - Effect.provide(testRootLayer(proxy.layer, textCliOutputFormatter(), args)), - ); - - yield* run(["db", "branch", "list"]); - yield* run(["db", "remote", "changes"]); + const fs = yield* FileSystem.FileSystem; + const home = yield* fs.makeTempDirectoryScoped({ prefix: "supabase-hidden-flag-tombstone-" }); - expect(proxy.calls).toEqual([ + for (const args of [ ["db", "branch", "list"], ["db", "remote", "changes"], - ]); - }), + ["gen", "keys"], + ]) { + const exit = yield* Command.runWith(testRoot, { version: "0.0.0-test" })(args).pipe( + Effect.provide( + Layer.merge( + testRootLayer(textCliOutputFormatter(), args), + processEnvLayer({ SUPABASE_HOME: home }), + ), + ), + Effect.exit, + ); + expect(Exit.isFailure(exit)).toBe(true); + if (!Exit.isFailure(exit)) return; + expect(Cause.squash(exit.cause)).toBeInstanceOf(RemovedSurfaceError); + } + }).pipe(Effect.provide(BunServices.layer)), ); it.effect("still executes the native `db test` hidden alias by exact name (CLI-1962)", () => @@ -284,10 +275,9 @@ describe("hidden subcommands", () => { // DB/docker IO, so that typed failure (not success) is what proves dispatch reached it. const fs = yield* FileSystem.FileSystem; const home = yield* fs.makeTempDirectoryScoped({ prefix: "supabase-hidden-flag-" }); - const proxy = mockGoProxy(); const run = (args: ReadonlyArray) => Command.runWith(testRoot, { version: "0.0.0-test" })(args).pipe( - Effect.provide(testRootLayer(proxy.layer, textCliOutputFormatter(), args)), + Effect.provide(testRootLayer(textCliOutputFormatter(), args)), Effect.exit, ); @@ -306,3 +296,58 @@ describe("hidden subcommands", () => { }).pipe(Effect.provide(BunServices.layer)), ); }); + +describe("removed global flags", () => { + it.effect("hides --create-ticket from GLOBAL FLAGS help", () => + Effect.gen(function* () { + const args = ["issue", "bug", "--help"]; + let globalFlagNames: ReadonlyArray = []; + const capturingFormatter: CliOutput.Formatter = { + ...silentCliOutputFormatter, + formatHelpDoc: (doc) => { + globalFlagNames = doc.globalFlags?.map((flag) => flag.name) ?? []; + return ""; + }, + }; + + yield* Command.runWith(testRoot, { version: "0.0.0-test" })(args).pipe( + Effect.provide(testRootLayer(capturingFormatter, args)), + ); + + expect(globalFlagNames).toContain("debug"); + expect(globalFlagNames).not.toContain("create-ticket"); + }), + ); + + it.effect( + "fails a command passed --create-ticket with the removal error, not a parse error", + () => + Effect.gen(function* () { + const args = ["issue", "bug", "--no-browser", "--create-ticket"]; + const analytics = mockAnalytics(); + + const exit = yield* Command.runWith(testRoot, { version: "0.0.0-test" })(args).pipe( + Effect.provide( + Layer.mergeAll( + testRootLayer(textCliOutputFormatter(), args), + analytics.layer, + Stdio.layerTest({ args: Effect.succeed(args) }), + ), + ), + Effect.exit, + ); + + expect(Exit.isFailure(exit)).toBe(true); + if (!Exit.isFailure(exit)) return; + const error = Cause.squash(exit.cause); + expect(error).toBeInstanceOf(RemovedSurfaceError); + if (!(error instanceof RemovedSurfaceError)) return; + expect(error.kind).toBe("flag"); + expect(error.message).toBe("--create-ticket was removed."); + expect(analytics.captured.map((event) => event.event)).toEqual(["cli_command_executed"]); + expect(analytics.captured[0]?.properties).toMatchObject({ + error_fingerprint: "tag:RemovedSurfaceError:removed_flag", + }); + }), + ); +}); diff --git a/apps/cli/src/shared/cli/run.ts b/apps/cli/src/shared/cli/run.ts index ce9f626066..0e5a307728 100644 --- a/apps/cli/src/shared/cli/run.ts +++ b/apps/cli/src/shared/cli/run.ts @@ -29,11 +29,6 @@ import { outputLayerFor } from "../output/output.layer.ts"; import { normalizeCause } from "../output/normalize-error.ts"; import type { OutputFormat } from "../output/types.ts"; import { Output } from "../output/output.service.ts"; -import { GoChildExitError } from "../../command-internal/go-child-exit.error.ts"; -import { - GoProxyInvocation, - goProxyInvocationLayer, -} from "../../command-internal/go-proxy-invocation.ts"; import { cliSettingsLayer } from "../config/cli-settings.layer.ts"; import { cliConfigProviderLayer } from "../config/cli-config-provider.layer.ts"; import { cliProjectHomeLayer } from "../config/cli-project-home.layer.ts"; @@ -350,15 +345,10 @@ export function exitCodeForFailure(cause: Cause.Cause): number { /** * Whether `handledProgram` should render its generic `output.fail` stderr line for a failed run. - * False for a clean exit (`0`), an interrupt (`130`), and a `GoChildExitError` — a delegated Go - * child already wrote its own failure to the inherited stderr, so a second line here would be - * redundant. Checked by concrete type rather than Effect's shared `[Runtime.errorReported]` - * marker, since `CliError.ShowHelp` also sets that marker `false` for an unrelated reason and - * would otherwise suppress real error rendering too. + * False only for a clean exit (`0`) or an interrupt (`130`); every other exit code reports. */ -export function shouldReportFailure(cause: Cause.Cause, exitCode: number): boolean { - if (exitCode === 0 || exitCode === 130) return false; - return !(Cause.squash(cause) instanceof GoChildExitError); +export function shouldReportFailure(exitCode: number): boolean { + return exitCode !== 0 && exitCode !== 130; } /** @@ -554,7 +544,6 @@ export interface RunCliOptions { args: ReadonlyArray, info: { readonly cleanShowHelp: boolean; - readonly delegatedToGo: boolean; readonly workingDirectory?: string; /** Value-taking-token predicate for this argv (global + resolved leaf flags) — see `valueTakingFlagTokenPredicateForArgv`. */ readonly isValueTakingFlagToken: (token: string) => boolean; @@ -698,7 +687,6 @@ export const runCli = Effect.fnUntraced(function* < Layer.provideMerge(cliSettingsLayerFor(handledRuntimeLayer)), Layer.provideMerge(cliProjectContextLayerFor(handledRuntimeLayer)), Layer.provideMerge(ttyLayer), - Layer.provideMerge(goProxyInvocationLayer), Layer.provideMerge(successTrailerLayer), ); const handledProgramLayer = processControlLayer.pipe( @@ -710,7 +698,6 @@ export const runCli = Effect.fnUntraced(function* < const runToExitCode = (program: Effect.Effect) => Effect.gen(function* () { const processControl = yield* ProcessControl; - const goProxyInvocation = yield* GoProxyInvocation; const output = yield* Output; const successTrailer = yield* SuccessTrailer; const exit = yield* program.pipe(Effect.exit); @@ -725,11 +712,9 @@ export const runCli = Effect.fnUntraced(function* < Effect.andThen( Effect.gen(function* () { if (afterSuccessHook !== undefined) { - const delegatedToGo = yield* goProxyInvocation.wasDelegated; const workingDirectory = yield* successTrailer.workingDirectory; yield* afterSuccessHook(args, { cleanShowHelp, - delegatedToGo, workingDirectory, isValueTakingFlagToken: valueTakingFlagTokenPredicateForArgv( rootCommand, @@ -752,7 +737,7 @@ export const runCli = Effect.fnUntraced(function* < const exitCode = exitCodeForFailure(exit.cause); // See `shouldReportFailure` and `exitCodeForFailure` for the exit-code/reporting rules; a // literal `--help` never reaches this branch — it exits 0 via the success path below. - if (shouldReportFailure(exit.cause, exitCode)) { + if (shouldReportFailure(exitCode)) { yield* output.fail(normalizeCause(exit.cause, suggestionContext)); } yield* afterSuccess(exitCode, true); diff --git a/apps/cli/src/shared/cli/run.unit.test.ts b/apps/cli/src/shared/cli/run.unit.test.ts index 810fbc426c..5d5c748918 100644 --- a/apps/cli/src/shared/cli/run.unit.test.ts +++ b/apps/cli/src/shared/cli/run.unit.test.ts @@ -1,11 +1,10 @@ -import { Cause } from "effect"; +import { Cause, Data, Runtime } from "effect"; import { CliError, Command } from "effect/unstable/cli"; import { describe, expect, it } from "vitest"; import { branchesCommand } from "../../commands/branches/branches.command.ts"; import { migrationCommand } from "../../commands/migration/migration.command.ts"; import { ssoCommand } from "../../commands/sso/sso.command.ts"; -import { GoChildExitError } from "../../command-internal/go-child-exit.error.ts"; import { classifyParseErrorConsoleOutput, exitCodeForFailure, @@ -23,6 +22,14 @@ const testRoot = Command.make("supabase").pipe( Command.withSubcommands([branchesCommand, migrationCommand, ssoCommand]), ); +/** Local stand-in for a typed error opting into a custom process exit code. */ +class CustomExitCodeError extends Data.TaggedError("CustomExitCodeError")<{ + readonly exitCode: number; + readonly message: string; +}> { + override readonly [Runtime.errorExitCode] = this.exitCode; +} + describe("extractCommandPath", () => { it("returns positional command-path tokens", () => { expect(extractCommandPath(["functions", "serve"])).toEqual(["functions", "serve"]); @@ -133,9 +140,9 @@ describe("exitCodeForFailure", () => { expect(exitCodeForFailure(Cause.interrupt())).toBe(130); }); - it("exits with a GoChildExitError's exact exit code", () => { + it("exits with a typed error's exact custom exit code", () => { const cause = Cause.fail( - new GoChildExitError({ exitCode: 130, message: "supabase-go exited with code 130" }), + new CustomExitCodeError({ exitCode: 130, message: "exited with code 130" }), ); expect(exitCodeForFailure(cause)).toBe(130); }); @@ -143,32 +150,15 @@ describe("exitCodeForFailure", () => { describe("shouldReportFailure", () => { it("does not report a clean exit (0)", () => { - expect(shouldReportFailure(Cause.fail(new Error("unused")), 0)).toBe(false); + expect(shouldReportFailure(0)).toBe(false); }); it("does not report an interrupt (130)", () => { - expect(shouldReportFailure(Cause.interrupt(), 130)).toBe(false); - }); - - it("does not report a GoChildExitError", () => { - const cause = Cause.fail( - new GoChildExitError({ exitCode: 1, message: "supabase-go exited with code 1" }), - ); - expect(shouldReportFailure(cause, 1)).toBe(false); + expect(shouldReportFailure(130)).toBe(false); }); - it("reports a non-ShowHelp failure", () => { - expect(shouldReportFailure(Cause.fail(new Error("boom")), 1)).toBe(true); - }); - - it("still reports a ShowHelp failure carrying a genuine validation error (e.g. a missing required flag)", () => { - const cause = Cause.fail( - new CliError.ShowHelp({ - commandPath: ["sso", "add"], - errors: [new CliError.MissingOption({ option: "--type" })], - }), - ); - expect(shouldReportFailure(cause, 1)).toBe(true); + it("reports every other exit code", () => { + expect(shouldReportFailure(1)).toBe(true); }); }); diff --git a/apps/cli/src/shared/functions/deploy.ts b/apps/cli/src/shared/functions/deploy.ts index 74f845eed7..0961e0d1a0 100644 --- a/apps/cli/src/shared/functions/deploy.ts +++ b/apps/cli/src/shared/functions/deploy.ts @@ -40,7 +40,7 @@ import { } from "../cli/cobra-flag-groups.ts"; import { edgeRuntimeImage, - FUNCTIONS_BUNDLER_MUTEX_GROUP, + FUNCTIONS_DEPLOY_BUNDLER_MUTEX_GROUP, invalidFunctionSlugDetail, validateFunctionSlugMessage, } from "./functions.shared.ts"; @@ -735,7 +735,7 @@ function substituteImportMapValue( continue; } // Import-maps spec (implemented by Deno): a key matches exactly, or as a prefix only when it - // ends with "/" — see go-cli-divergences.md for why this differs from a naive prefix match. + // ends with "/", unlike a naive prefix match. if (prefix.endsWith("/")) { // Spec normalization: a `/`-suffixed key whose address lacks a trailing // `/` is an invalid mapping — dropped, not concatenated. @@ -2476,7 +2476,10 @@ export const deployFunctions = Effect.fn("functions.deploy")(function* < if (changedModes.length > 1) { return yield* new ConflictingFunctionDeployFlagsError({ - message: cobraMutuallyExclusiveErrorMessage(FUNCTIONS_BUNDLER_MUTEX_GROUP, changedModes), + message: cobraMutuallyExclusiveErrorMessage( + FUNCTIONS_DEPLOY_BUNDLER_MUTEX_GROUP, + changedModes, + ), }); } diff --git a/apps/cli/src/shared/functions/download.ts b/apps/cli/src/shared/functions/download.ts index d848afc205..f0f5d8bc9b 100644 --- a/apps/cli/src/shared/functions/download.ts +++ b/apps/cli/src/shared/functions/download.ts @@ -31,7 +31,7 @@ import { import { loadFunctionsCliConfig, type FunctionsGoConfigCompat } from "./functions-config.ts"; import { edgeRuntimeImage, - FUNCTIONS_BUNDLER_MUTEX_GROUP, + FUNCTIONS_DOWNLOAD_BUNDLER_MUTEX_GROUP, invalidFunctionSlugDetail, validateFunctionSlugMessage, } from "./functions.shared.ts"; @@ -57,7 +57,6 @@ export interface DownloadFunctionsOptions { readonly projectRef: Option.Option; readonly useApi: boolean; readonly useDocker: boolean; - readonly legacyBundle: boolean; } export interface DownloadFunctionsResult { @@ -83,9 +82,8 @@ interface DownloadDockerRuntimeDependencies extends DownloadRuntimeDependencies */ readonly styleEmphasis?: (text: string) => string; /** - * Optional shell-specific styling hook for the `--legacy-bundle` command - * suggested inside {@link suggestLegacyBundle} — same isolation rationale - * as {@link styleEmphasis}. + * Optional shell-specific styling hook for the command suggested inside + * {@link suggestUseApiRetry} — same isolation rationale as {@link styleEmphasis}. */ readonly styleAqua?: (text: string) => string; /** @@ -112,40 +110,11 @@ interface EdgeRuntimeImageDependencies { readonly edgeRuntimeVersion: string; } -export interface DownloadFunctionsDependencies< - ResolveError, - ResolveRequirements, - ProxyError, - ProxyRequirements, -> +export interface DownloadFunctionsDependencies extends DownloadDockerRuntimeDependencies, EdgeRuntimeImageDependencies { readonly resolveProjectRef: ( projectRef: Option.Option, ) => Effect.Effect; - /** - * `true` whenever `output.format !== "text"`: the child's raw stdout must - * not reach the terminal (it would corrupt the JSON/NDJSON envelope), so - * the dependency must capture/discard it instead of inheriting stdio. - */ - readonly proxyDownload: ( - flags: DownloadFunctionsOptions, - projectRef: string, - captureOutput: boolean, - ) => Effect.Effect; -} - -// `--legacy-bundle` is the only case `downloadFunctions()` still delegates -// to the Go binary; `functionName` is the one remaining input to forward. -export function makeGoProxyLegacyBundleArgs( - functionName: Option.Option, - projectRef: string, -): ReadonlyArray { - const args: string[] = ["functions", "download"]; - if (Option.isSome(functionName)) { - args.push(functionName.value); - } - args.push("--project-ref", projectRef, "--legacy-bundle"); - return args; } interface DownloadMetadata { @@ -224,16 +193,16 @@ function validateDownloadFlags( const changed = [ hasExplicitLongFlag(rawArgs, downloadCommandPath, "use-api") ? "use-api" : undefined, hasExplicitLongFlag(rawArgs, downloadCommandPath, "use-docker") ? "use-docker" : undefined, - hasExplicitLongFlag(rawArgs, downloadCommandPath, "legacy-bundle") - ? "legacy-bundle" - : undefined, ].filter((flag): flag is string => flag !== undefined); return changed.length <= 1 ? Effect.void : Effect.fail( new ConflictingFunctionDownloadFlagsError({ - message: cobraMutuallyExclusiveErrorMessage(FUNCTIONS_BUNDLER_MUTEX_GROUP, changed), + message: cobraMutuallyExclusiveErrorMessage( + FUNCTIONS_DOWNLOAD_BUNDLER_MUTEX_GROUP, + changed, + ), }), ); } @@ -853,14 +822,12 @@ const downloadEszipBody = Effect.fnUntraced(function* ( ); }); -function suggestLegacyBundle( +function suggestUseApiRetry( slug: string, styleAqua: (text: string) => string = (text) => text, ): string { - // Preserves the established "trying running" wording (not a typo) and - // leading newline; `styleAqua` wraps only the suggested command, not the - // whole sentence. - return `\nIf your function is deployed using CLI < 1.120.0, trying running ${styleAqua(`supabase functions download --legacy-bundle ${slug}`)} instead.`; + // Preserves the established leading newline. + return `\nRetry with ${styleAqua(`supabase functions download --use-api ${slug}`)} to unbundle server-side. If that also fails and the Function was deployed with a CLI older than 1.120.0, redeploy it with the current CLI.`; } function suggestDenoV2(styleEmphasis: (text: string) => string = (text) => text): string { @@ -870,21 +837,21 @@ function suggestDenoV2(styleEmphasis: (text: string) => string = (text) => text) } /** - * Attaches the legacy-bundle hint to any Docker-extraction failure — + * Attaches the `--use-api` retry hint to any Docker-extraction failure — * network/volume creation, container create/start, log streaming, or a * non-zero exit code alike. Only normalizes (never re-prefixes) whatever * {@link describeContainerCliFailure} reports, since * `ensureDockerNetwork`/`ensureDockerNamedVolume` prefix their own context. */ -function withLegacyBundleSuggestion(slug: string, styleAqua?: (text: string) => string) { +function withUseApiRetrySuggestion(slug: string, styleAqua?: (text: string) => string) { return (cause: unknown): Error => Object.assign(new Error(describeContainerCliFailure(cause)), { - suggestion: suggestLegacyBundle(slug, styleAqua), + suggestion: suggestUseApiRetry(slug, styleAqua), }); } /** - * Same as {@link withLegacyBundleSuggestion}, plus a `step` prefix: unlike + * Same as {@link withUseApiRetrySuggestion}, plus a `step` prefix: unlike * `ensureDockerNetwork`/`ensureDockerNamedVolume`'s self-describing errors, * `runChildProcess`'s own failure carries no context about which command * was running. @@ -892,7 +859,7 @@ function withLegacyBundleSuggestion(slug: string, styleAqua?: (text: string) => function withDockerStepFailure(step: string, slug: string, styleAqua?: (text: string) => string) { return (cause: unknown): Error => Object.assign(new Error(`${step}: ${describeContainerCliFailure(cause)}`), { - suggestion: suggestLegacyBundle(slug, styleAqua), + suggestion: suggestUseApiRetry(slug, styleAqua), }); } @@ -1026,10 +993,10 @@ const downloadWithDockerUnbundle = Effect.fn("functions.download.dockerUnbundle" // `image` is already pull-resolved once for the whole invocation — see // `downloadFunctions`'s own resolve site — not re-resolved per slug. yield* ensureDockerNetwork(networkMode, projectId).pipe( - Effect.mapError(withLegacyBundleSuggestion(slug, styleAqua)), + Effect.mapError(withUseApiRetrySuggestion(slug, styleAqua)), ); yield* ensureDockerNamedVolume(cacheVolume.name, projectId, projectEnvValues).pipe( - Effect.mapError(withLegacyBundleSuggestion(slug, styleAqua)), + Effect.mapError(withUseApiRetrySuggestion(slug, styleAqua)), ); // On Bitbucket, the named-volume bind is dropped entirely (not just its @@ -1075,7 +1042,7 @@ const downloadWithDockerUnbundle = Effect.fn("functions.download.dockerUnbundle" .split(/\r?\n/) .some((line) => line.trim().toLowerCase() === "invalid eszip v2"); const suggestion = - (invalidEszipV2 ? suggestDenoV2(styleEmphasis) : "") + suggestLegacyBundle(slug, styleAqua); + (invalidEszipV2 ? suggestDenoV2(styleEmphasis) : "") + suggestUseApiRetry(slug, styleAqua); return yield* Effect.fail( Object.assign(new Error(`error running container: exit ${result.exitCode}`), { suggestion, @@ -1183,16 +1150,9 @@ function attachDownloadWrittenSoFar( export const downloadFunctions = Effect.fn("functions.download")(function* < ResolveError, ResolveRequirements, - ProxyError, - ProxyRequirements, >( flags: DownloadFunctionsOptions, - dependencies: DownloadFunctionsDependencies< - ResolveError, - ResolveRequirements, - ProxyError, - ProxyRequirements - >, + dependencies: DownloadFunctionsDependencies, ) { const output = yield* Output; const path = yield* Path.Path; @@ -1203,52 +1163,6 @@ export const downloadFunctions = Effect.fn("functions.download")(function* < yield* validateSlug(flags.functionName.value); } - // `--legacy-bundle` still delegates to the Go binary: it requires - // installing/upgrading a Deno binary on the host and shelling out to an - // embedded Deno script, with no other precedent in this codebase. - // `--use-docker` (default `true`) runs natively below and falls through - // to the same server-side downloader when Docker isn't running. - if (flags.legacyBundle) { - const projectRef = yield* dependencies.resolveProjectRef(flags.projectRef); - - if (output.format === "text") { - yield* dependencies.proxyDownload(flags, projectRef, false); - // The slug list is never resolved in text mode here, so this result - // is not meaningful — callers never read it (the orchestrator never - // sets `legacyBundle: true`). - return { projectRef, slugs: [], empty: false }; - } - - // Resolved before delegating: this list is purely for the JSON - // payload (the delegated child's own stdout is captured/discarded, not - // inherited, since it never emits the `Output` envelope). Resolving it - // first means a transient listing failure is reported before any - // download side effect, rather than masking an already-successful - // delegated download with an unrelated listing failure after the fact. - const slugs = Option.isSome(flags.functionName) - ? [flags.functionName.value] - : yield* listRemoteFunctionSlugs(dependencies.api, projectRef); - - // Mirrors the native path's empty-project short-circuit below: an - // empty project has nothing to delegate, so this reports "No functions - // found." instead of invoking the Go child unnecessarily. - if (slugs.length === 0) { - yield* output.success("No functions found.", { - function_slugs: [], - project_ref: projectRef, - }); - return { projectRef, slugs: [], empty: true }; - } - - yield* dependencies.proxyDownload(flags, projectRef, true); - - yield* output.success("Downloaded Edge Function source.", { - function_slugs: slugs, - project_ref: projectRef, - }); - return { projectRef, slugs, empty: false }; - } - const projectRef = yield* dependencies.resolveProjectRef(flags.projectRef); // Resolved unconditionally here, before checking `useDocker` or whether @@ -1288,9 +1202,9 @@ export const downloadFunctions = Effect.fn("functions.download")(function* < yield* Effect.annotateCurrentSpan({ "function.count": slugs.length }); // Resolved once for the whole invocation, not once per slug — see - // `PulledEdgeRuntimeImage`'s own doc comment. The `--legacy-bundle` - // suggestion on a resolve failure uses the first slug as a - // representative example, since none is "the" one being processed yet. + // `PulledEdgeRuntimeImage`'s own doc comment. The redeploy suggestion on + // a resolve failure uses the first slug as a representative example, + // since none is "the" one being processed yet. const styleAqua = dependencies.styleAqua ?? ((text: string) => text); const pulledEdgeRuntimeImage: PulledEdgeRuntimeImage | undefined = edgeRuntimeImage === undefined @@ -1300,7 +1214,7 @@ export const downloadFunctions = Effect.fn("functions.download")(function* < image: yield* resolveFunctionsDockerImage( edgeRuntimeImage.rawImage, edgeRuntimeImage.projectEnvValues, - ).pipe(Effect.mapError(withLegacyBundleSuggestion(slugs[0] ?? "", styleAqua))), + ).pipe(Effect.mapError(withUseApiRetrySuggestion(slugs[0] ?? "", styleAqua))), }; const downloaded: string[] = []; diff --git a/apps/cli/src/shared/functions/functions.shared.ts b/apps/cli/src/shared/functions/functions.shared.ts index 1ef1612fee..a7d8b65247 100644 --- a/apps/cli/src/shared/functions/functions.shared.ts +++ b/apps/cli/src/shared/functions/functions.shared.ts @@ -14,7 +14,16 @@ export function validateFunctionSlugMessage(slug: string): string | undefined { export const FUNCTIONS_PROJECT_REF_SAFE_FLAGS = ["project-ref"] as const; // Order is rendered verbatim in the mutually-exclusive-flags error message. -export const FUNCTIONS_BUNDLER_MUTEX_GROUP = ["use-api", "use-docker", "legacy-bundle"] as const; +export const FUNCTIONS_DEPLOY_BUNDLER_MUTEX_GROUP = [ + "use-api", + "use-docker", + "legacy-bundle", +] as const; + +// Order is rendered verbatim in the mutually-exclusive-flags error message. `legacy-bundle` is +// a removed, tombstoned flag on `functions download` (see `removedFlag`), rejected +// unconditionally before this group is ever checked, so it is not a member here. +export const FUNCTIONS_DOWNLOAD_BUNDLER_MUTEX_GROUP = ["use-api", "use-docker"] as const; /** * Full deno-1 edge-runtime image tag, resolved from the embedded Dockerfile diff --git a/apps/cli/src/shared/output/json-error-handling.ts b/apps/cli/src/shared/output/json-error-handling.ts index e9f83f60e8..b9a62de5d6 100644 --- a/apps/cli/src/shared/output/json-error-handling.ts +++ b/apps/cli/src/shared/output/json-error-handling.ts @@ -14,7 +14,7 @@ export const withJsonErrorHandling = ( if (output.format === "text") return yield* Effect.fail(error); yield* output.fail(normalizeCliError(error)); // `Runtime.getErrorExitCode` defaults to 1 unless the error opts in via - // `[Runtime.errorExitCode]`, so a delegated child's real exit code still + // `[Runtime.errorExitCode]`, so a typed error's custom exit code still // reaches the user under json/stream-json, matching the text-mode path. yield* processControl.setExitCode(Runtime.getErrorExitCode(error)); }), diff --git a/apps/cli/src/shared/output/json-error-handling.unit.test.ts b/apps/cli/src/shared/output/json-error-handling.unit.test.ts index 5e26e0d017..270c08c778 100644 --- a/apps/cli/src/shared/output/json-error-handling.unit.test.ts +++ b/apps/cli/src/shared/output/json-error-handling.unit.test.ts @@ -1,7 +1,6 @@ import { describe, expect, it } from "@effect/vitest"; -import { Data, Effect, Exit, Layer, Option } from "effect"; +import { Data, Effect, Exit, Layer, Option, Runtime } from "effect"; import { mockProcessControl } from "../../../tests/helpers/mocks.ts"; -import { GoChildExitError } from "../../command-internal/go-child-exit.error.ts"; import { Output } from "./output.service.ts"; import { withJsonErrorHandling } from "./json-error-handling.ts"; @@ -15,6 +14,14 @@ class TaggedErrorMinimal extends Data.TaggedError("TaggedErrorMinimal")<{ readonly message: string; }> {} +/** Local stand-in for a typed error opting into a custom process exit code. */ +class CustomExitCodeError extends Data.TaggedError("CustomExitCodeError")<{ + readonly exitCode: number; + readonly message: string; +}> { + override readonly [Runtime.errorExitCode] = this.exitCode; +} + class PlainError { readonly message: string; constructor(message: string) { @@ -172,17 +179,17 @@ describe("withJsonErrorHandling", () => { }).pipe(Effect.provide(Layer.merge(out.layer, processControl.layer))); }); - it.effect("sets the exact exit code for a GoChildExitError, not a generic 1", () => { + it.effect("sets a typed error's exact custom exit code, not a generic 1", () => { const out = mockOutput("json"); const processControl = mockProcessControl(); return Effect.gen(function* () { - const error = new GoChildExitError({ + const error = new CustomExitCodeError({ exitCode: 130, - message: "supabase-go exited with code 130 (see stderr for details)", + message: "exited with code 130", }); yield* withJsonErrorHandling(Effect.fail(error)).pipe(Effect.provide(out.layer)); expect(out.failCalls).toHaveLength(1); - expect(out.failCalls[0]?.code).toBe("GoChildExitError"); + expect(out.failCalls[0]?.code).toBe("CustomExitCodeError"); expect(processControl.exitCode).toBe(130); }).pipe(Effect.provide(Layer.merge(out.layer, processControl.layer))); }); diff --git a/apps/cli/src/shared/output/normalize-error.ts b/apps/cli/src/shared/output/normalize-error.ts index 2695d593a2..8cb9a26f0b 100644 --- a/apps/cli/src/shared/output/normalize-error.ts +++ b/apps/cli/src/shared/output/normalize-error.ts @@ -137,7 +137,7 @@ export function normalizeCliError( const message = readString(error, "message") ?? readString(error, "detail") ?? code; const detail = readString(error, "detail"); // Raw read: some producers' suggestion text carries meaningful leading - // whitespace (e.g. `suggestLegacyBundle`'s leading `\n` for a blank + // whitespace (e.g. `suggestUseApiRetry`'s leading `\n` for a blank // separator line); `readString` would trim exactly that away. const suggestion = readRawString(error, "suggestion"); return { diff --git a/apps/cli/src/shared/telemetry/__fixtures__/error-tags.txt b/apps/cli/src/shared/telemetry/__fixtures__/error-tags.txt index fc5ef75610..73d3b6e9ec 100644 --- a/apps/cli/src/shared/telemetry/__fixtures__/error-tags.txt +++ b/apps/cli/src/shared/telemetry/__fixtures__/error-tags.txt @@ -295,7 +295,6 @@ GenTypesToolFailedError GenTypesToolNotInstalledError GenTypesUnexpectedStatusError GenTypesWorkdirError -GoChildExitError HealthCheckProbeError HealthCheckTimeoutError HostPostgresClientError @@ -449,6 +448,7 @@ PullParentRefInvalidError PullUncommittedChangesError PullWorkdirError RemoteJwksError +RemovedSurfaceError ResetLocalDbFailedError ResetLocalDbNotRunningError ResetReplicationSlotsError diff --git a/apps/cli/src/shared/telemetry/error-actionability.ts b/apps/cli/src/shared/telemetry/error-actionability.ts index 552e6bcc25..4569c813e8 100644 --- a/apps/cli/src/shared/telemetry/error-actionability.ts +++ b/apps/cli/src/shared/telemetry/error-actionability.ts @@ -116,6 +116,8 @@ const CLI_ERROR_FINGERPRINT_SUFFIXES = [ "port_conflict", "query", "registry_pull", + "removed_command", + "removed_flag", "replication_slots_active", "replication_slots_query", "request_encoding", @@ -381,6 +383,13 @@ export const actionability = { has_suggestion: true, suggestion_type: CliSuggestionType.UpdateConfig, }, + /** A removed command path or flag; the suggestion is free-form replacement guidance. */ + removedSurface: { + error_kind: CliErrorKind.UserActionable, + error_category: CliErrorCategory.InvalidInput, + has_suggestion: true, + suggestion_type: CliSuggestionType.RunCommand, + }, externalNetwork: { error_kind: CliErrorKind.ExternalService, error_category: CliErrorCategory.Network, diff --git a/apps/cli/src/telemetry/command-telemetry.ts b/apps/cli/src/telemetry/command-telemetry.ts index 58bb5c4230..c774a41105 100644 --- a/apps/cli/src/telemetry/command-telemetry.ts +++ b/apps/cli/src/telemetry/command-telemetry.ts @@ -6,7 +6,13 @@ import { getCommandRuntimeSpanName, } from "../shared/runtime/command-runtime.service.ts"; import { Output } from "../shared/output/output.service.ts"; -import { GLOBAL_FLAGS, OutputFlag, globalFlagValues } from "../command-internal/global-flags.ts"; +import { + CreateTicketFlag, + GLOBAL_FLAGS, + OutputFlag, + globalFlagValues, +} from "../command-internal/global-flags.ts"; +import { removedFlag, type RemovedSurfaceError } from "../command-internal/removed-command.ts"; import { ProcessControl } from "../shared/runtime/process-control.service.ts"; import { withAnalyticsContext } from "../shared/telemetry/analytics-context.ts"; import { Analytics } from "../shared/telemetry/analytics.service.ts"; @@ -214,6 +220,17 @@ function extractChangedFlagNames( return [...used].sort((left, right) => left.localeCompare(right)); } +/** + * Fails when argv carries the removed global `--create-ticket` (with any value). Runs inside the + * recorded region, so the rejection still emits `cli_command_executed`. + */ +const rejectRemovedGlobalFlags = Effect.gen(function* () { + const stdio = yield* Stdio.Stdio; + const args = yield* stdio.args; + if (!extractChangedFlagNames(args).includes(CreateTicketFlag.id)) return; + return yield* removedFlag("--create-ticket", "Report CLI problems with `supabase issue bug`."); +}); + function normalizeFlagValue(value: unknown): unknown { if (value === undefined) return undefined; if (!Option.isOption(value)) return value; @@ -476,7 +493,7 @@ export function withCommandTelemetry(): ( self: Effect.Effect, ) => Effect.Effect< A, - E | InvalidOutputFormatError, + E | InvalidOutputFormatError | RemovedSurfaceError, R | Analytics | CommandRuntime | Stdio.Stdio | Output | ProcessControl >; export function withCommandTelemetry>( @@ -485,7 +502,7 @@ export function withCommandTelemetry>( self: Effect.Effect, ) => Effect.Effect< A, - E | InvalidOutputFormatError, + E | InvalidOutputFormatError | RemovedSurfaceError, R | Analytics | CommandRuntime | Stdio.Stdio | Output | ProcessControl >; export function withCommandTelemetry>( @@ -499,5 +516,8 @@ export function withCommandTelemetry>( return (self: Effect.Effect) => // Validate the `-o` enum before instrumentation runs the handler, so a rejected flag fails // without emitting a `cli_command_executed` event. - Effect.andThen(validateOutputFormat(allowed), instrument(self)); + Effect.andThen( + validateOutputFormat(allowed), + instrument(Effect.andThen(rejectRemovedGlobalFlags, self)), + ); } diff --git a/apps/cli/src/telemetry/command-telemetry.unit.test.ts b/apps/cli/src/telemetry/command-telemetry.unit.test.ts index 1686ed56a7..777132e980 100644 --- a/apps/cli/src/telemetry/command-telemetry.unit.test.ts +++ b/apps/cli/src/telemetry/command-telemetry.unit.test.ts @@ -5,6 +5,7 @@ import { Flag } from "effect/unstable/cli"; import { commandRuntimeLayer } from "../shared/runtime/command-runtime.layer.ts"; import { AgentFlag, + CreateTicketFlag, DebugFlag, DnsResolverFlag, OutputFlag, @@ -26,6 +27,7 @@ import { ProcessControl } from "../shared/runtime/process-control.service.ts"; import { ConfigDiffLoadConfigError } from "../commands/config/diff/diff.errors.ts"; import { DbDumpRunError } from "../commands/db/dump/dump.errors.ts"; import { IdentityStitch } from "../command-internal/identity-stitch.ts"; +import { RemovedSurfaceError } from "../command-internal/removed-command.ts"; import { withCommandTelemetry } from "./command-telemetry.ts"; import { recordCommandTelemetry } from "./command-telemetry-attributes.ts"; import { stackBackendLayer } from "../command-internal/stack-backend.ts"; @@ -33,6 +35,7 @@ import { QUERY_OUTPUT_FORMATS, InvalidOutputFormatError, } from "../command-internal/go-output-flag.ts"; +import { mockTelemetryStateTracked } from "../../tests/helpers/command-mocks.ts"; import { mockContextualAnalytics, mockOutput, @@ -1604,4 +1607,68 @@ describe("withCommandTelemetry", () => { ); }, ); + + describe("removed --create-ticket global flag", () => { + const runWith = (args: ReadonlyArray, telemetryState = mockTelemetryStateTracked()) => { + const analytics = mockContextualAnalytics(); + let ran = false; + const run = Effect.sync(() => { + ran = true; + }).pipe( + withCommandTelemetry(), + Effect.provide(Layer.succeed(CreateTicketFlag, true)), + Effect.provide(analytics.layer), + Effect.provide(mockProcessControl().layer), + Effect.provide(mockOutput({ format: "text" }).layer), + Effect.provide(Stdio.layerTest({ args: Effect.succeed(args) })), + Effect.provide( + commandRuntimeLayer(["backups", "list"]).pipe(Layer.provide(BunCrypto.layer)), + ), + Effect.provide(telemetryState.layer), + Effect.exit, + ); + return { run, analytics, telemetryState, didRun: () => ran }; + }; + + for (const flag of ["--create-ticket", "--create-ticket=false"]) { + it.live(`fails with the removal error and records the event for ${flag}`, () => { + const { run, analytics, telemetryState, didRun } = runWith(["backups", "list", flag]); + + return Effect.gen(function* () { + const exit = yield* run; + + expect(Exit.isFailure(exit)).toBe(true); + if (!Exit.isFailure(exit)) return; + const error = Cause.squash(exit.cause); + expect(error).toBeInstanceOf(RemovedSurfaceError); + if (!(error instanceof RemovedSurfaceError)) return; + expect(error.kind).toBe("flag"); + expect(error.message).toBe("--create-ticket was removed."); + expect(error.suggestion).toBe("Report CLI problems with `supabase issue bug`."); + expect(didRun()).toBe(false); + expect(telemetryState.flushCount).toBe(1); + expect(analytics.captured).toHaveLength(1); + expect(analytics.captured[0]?.event).toBe("cli_command_executed"); + expect(analytics.captured[0]?.properties).toMatchObject({ + command: "backups list", + exit_code: 1, + error_fingerprint: "tag:RemovedSurfaceError:removed_flag", + flags: { "create-ticket": true }, + }); + }); + }); + } + + it.live("ignores --create-ticket after the -- operand terminator", () => { + const { run, analytics, didRun } = runWith(["backups", "list", "--", "--create-ticket"]); + + return Effect.gen(function* () { + const exit = yield* run; + + expect(Exit.isSuccess(exit)).toBe(true); + expect(didRun()).toBe(true); + expect(analytics.captured[0]?.properties.exit_code).toBe(0); + }); + }); + }); }); diff --git a/apps/cli/tests/helpers/macos-signature.ts b/apps/cli/tests/helpers/macos-signature.ts index 8663ee076f..572845a027 100644 --- a/apps/cli/tests/helpers/macos-signature.ts +++ b/apps/cli/tests/helpers/macos-signature.ts @@ -38,8 +38,7 @@ export async function verifyMacSignature(binPath: string): Promise` on its - // own line) so the SFE's `com.supabase.cli` can't satisfy the sidecar's - // `com.supabase.cli-go` by substring. + // own line) rather than substring-matching within the surrounding signature output. const actualId = info.match(/^Identifier=(.+)$/m)?.[1]?.trim(); if (actualId !== expectedId) { return { passed: false, detail: `expected Identifier=${expectedId}, got:\n${info}` }; diff --git a/apps/cli/tests/smoke-test-macos.ts b/apps/cli/tests/smoke-test-macos.ts index fed05328b0..c67e1595bb 100644 --- a/apps/cli/tests/smoke-test-macos.ts +++ b/apps/cli/tests/smoke-test-macos.ts @@ -1,5 +1,4 @@ import { $ } from "bun"; -import { existsSync } from "node:fs"; import { mkdir } from "node:fs/promises"; import path from "node:path"; import process from "node:process"; @@ -59,9 +58,6 @@ console.log("=".repeat(60)); const arch = process.arch; const binDir = path.join(root, "packages", `cli-darwin-${arch}`, "bin"); const binaries = ["supabase"]; - if (existsSync(path.join(binDir, "supabase-go"))) { - binaries.push("supabase-go"); - } for (const binary of binaries) { const name = `native-darwin-${arch}-signature-${binary}`; diff --git a/commitlint.config.js b/commitlint.config.js index e291f5072a..6187070558 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -2,7 +2,6 @@ const PROJECT_SCOPES = [ "api", "cli", "cli-e2e", - "cli-go", "cli-test-helpers", "config", "docs", diff --git a/docs/adr/0011-cli-release-and-distribution-strategy.md b/docs/adr/0011-cli-release-and-distribution-strategy.md index 1f04627fbd..0f5025f114 100644 --- a/docs/adr/0011-cli-release-and-distribution-strategy.md +++ b/docs/adr/0011-cli-release-and-distribution-strategy.md @@ -96,7 +96,7 @@ We intentionally avoid extra tags such as `next` or `canary` so install surfaces ### Why unsigned artifacts (matching the Go CLI) -The current Go CLI release — [`apps/cli-go/.goreleaser.yml`](../../apps/cli-go/.goreleaser.yml) and [`release.yml`](../../apps/cli-go/.github/workflows/release.yml) / [`release-beta.yml`](../../apps/cli-go/.github/workflows/release-beta.yml) — does **no signing of any kind**: +The current Go CLI release — `apps/cli-go/.goreleaser.yml` and `release.yml` / `release-beta.yml` — does **no signing of any kind**: - No `signs:` or `notarize:` block in `.goreleaser.yml` - No `codesign` / `notarytool submit` / macOS notarization in the release workflow @@ -187,7 +187,7 @@ Production tap: `supabase/homebrew-tap`. A single `supabase` formula replaces th #### Scoop -[`apps/cli/scripts/update-scoop.ts`](../../apps/cli/scripts/update-scoop.ts) clones the bucket repo, writes `supabase.json` (with an `autoupdate` block keyed off GitHub Releases), commits, and pushes. The manifest ships an `architecture` block with both `64bit` (x64) and `arm64` entries — matching the Go CLI's current GoReleaser output, which already targets `windows_arm64` in [`apps/cli-go/.goreleaser.yml`](../../apps/cli-go/.goreleaser.yml). Dropping arm64 would be a user-visible regression for anyone on a Windows-on-ARM device (Surface Pro X, recent Copilot+ PCs, Windows-11-ARM VMs on Apple Silicon). +[`apps/cli/scripts/update-scoop.ts`](../../apps/cli/scripts/update-scoop.ts) clones the bucket repo, writes `supabase.json` (with an `autoupdate` block keyed off GitHub Releases), commits, and pushes. The manifest ships an `architecture` block with both `64bit` (x64) and `arm64` entries — matching the Go CLI's current GoReleaser output, which already targets `windows_arm64` in `apps/cli-go/.goreleaser.yml`. Dropping arm64 would be a user-visible regression for anyone on a Windows-on-ARM device (Surface Pro X, recent Copilot+ PCs, Windows-11-ARM VMs on Apple Silicon). Windows-arm64 is live in the TS pipeline as of this branch — `packages/cli-windows-arm64/`, `bun-windows-arm64` in [`apps/cli/scripts/build.ts`](../../apps/cli/scripts/build.ts), and the `win32: { arm64, x64 }` map in [`apps/cli/src/shared/cli/bin.ts`](../../apps/cli/src/shared/cli/bin.ts) all shipped together. Bun supports this target natively ([Bun — Single-file executable § Supported targets](https://bun.com/docs/bundler/executables#supported-targets)), so no cross-compilation toolchain changes were required. See [`apps/cli/docs/release-process.md`](../../apps/cli/docs/release-process.md) for the full channel walkthrough. @@ -278,7 +278,7 @@ These split into **pre-cutover gates** (must complete before `latest` flips from ``` 4. **GitHub Release artifact host for PoC.** [`avallete/supabase-cli-release-poc`](https://github.com/avallete/supabase-cli-release-poc) hosts the `dist/supabase__*.{tar.gz,zip,deb,rpm,apk}` + `checksums.txt` the PoC formula/manifest URLs resolve against. At cutover this is replaced by `supabase/cli` (the current Go CLI Release host). The repo's git _tree_ is effectively empty; only its Releases matter. -5. **Wire updaters into `release-shared.yml`.** **Remaining largest item before cutover.** Today [`release-shared.yml`](../../.github/workflows/release-shared.yml) finishes at `gh release edit --draft=false`; no Homebrew/Scoop push happens from CI. Add a `post-publish` job that runs [`update-homebrew.ts`](../../apps/cli/scripts/update-homebrew.ts) and [`update-scoop.ts`](../../apps/cli/scripts/update-scoop.ts) behind the `!inputs.dry_run` gate, only when `npm_tag == 'latest'` (we do not push Homebrew/Scoop for alpha). Needs a GitHub App token (following the pattern in [`apps/cli-go/.github/workflows/release.yml`](../../apps/cli-go/.github/workflows/release.yml): `APP_ID` + `GH_APP_PRIVATE_KEY` secrets, scoped to `homebrew-tap` and `scoop-bucket` repos) so release tokens never hold broader access. Until this lands, the Ring 3 playbook in [`apps/cli/docs/release-process.md`](../../apps/cli/docs/release-process.md) documents the manual `bun apps/cli/scripts/update-{homebrew,scoop}.ts --version ` post-release step. +5. **Wire updaters into `release-shared.yml`.** **Remaining largest item before cutover.** Today [`release-shared.yml`](../../.github/workflows/release-shared.yml) finishes at `gh release edit --draft=false`; no Homebrew/Scoop push happens from CI. Add a `post-publish` job that runs [`update-homebrew.ts`](../../apps/cli/scripts/update-homebrew.ts) and [`update-scoop.ts`](../../apps/cli/scripts/update-scoop.ts) behind the `!inputs.dry_run` gate, only when `npm_tag == 'latest'` (we do not push Homebrew/Scoop for alpha). Needs a GitHub App token (following the pattern in `apps/cli-go/.github/workflows/release.yml`: `APP_ID` + `GH_APP_PRIVATE_KEY` secrets, scoped to `homebrew-tap` and `scoop-bucket` repos) so release tokens never hold broader access. Until this lands, the Ring 3 playbook in [`apps/cli/docs/release-process.md`](../../apps/cli/docs/release-process.md) documents the manual `bun apps/cli/scripts/update-{homebrew,scoop}.ts --version ` post-release step. 6. **Bun `--compile` musl validation + Windows arm64 smoke-test.** [`buildMuslBinaries`](../../apps/cli/scripts/build.ts) produces musl SFEs but the [smoke-test matrix](../../.github/workflows/release-shared.yml) only runs `ubuntu-latest` (glibc). Add an Alpine container job. Same matrix gap applies to Windows arm64 now that gate 1 is DONE: add a `windows-11-arm` runner (GitHub-hosted preview) so we catch arm64 regressions in CI rather than post-release. 7. **Local vs pipeline release-logic consolidation.** Tracked in sub-issue [CLI-1344](https://linear.app/supabase/issue/CLI-1344/investigate-if-we-can-consolidate-local-release-logic-and-pipeline). Intent: `bun apps/cli/scripts/{build,publish}.ts --dry-run` produces the identical output CI does; `sync-versions.ts` is the single versioning entry point. 8. **`findConfigInRoot` `ENOTDIR` handling.** Discovered while validating gate 2: [`packages/config/src/paths.ts`](../../packages/config/src/paths.ts)'s `fs.exists(supabase/config.json)` throws `BadResource` (`ENOTDIR`) instead of returning `false` when `cwd` contains a _file_ named `supabase` (e.g., the extracted SFE in a scratch dir during local install tests). The walk-upward resolver should treat `ENOTDIR` the same as `ENOENT`. Does not block the production brew path (`/opt/homebrew/bin/supabase-shim-poc` has no colliding `supabase` file in any parent), but it's a latent bug for any user whose `$HOME` or a parent contains a `supabase` file. Low-effort fix. @@ -295,7 +295,7 @@ These are **not** scheduled work. They activate only if the matching pre-cutover ### Note on PoC repo management -The three PoC repos (`avallete/homebrew-supabase-shim-poc`, `avallete/scoop-bucket`, `avallete/supabase-cli-release-poc`) are _*explicitly not added as `.repos/`* submodules__, even though `[.gitmodules](../../.gitmodules)` already tracks eight vendored reference repos (`effect`, `supabase-cli-go`, `process-compose`, etc.). Existing `.repos/`_ entries are read-only source-code references for `grep` / inspection; the PoC repos contain one generated artifact each (`Formula/supabase.rb`, `supabase.json`) or no interesting tree content at all. The `update-*.ts` scripts `gh repo clone` on demand into a tmpdir, so a submodule would change nothing about the release pipeline. Since these repos are PoC-phase only, vendoring now means a `.gitmodules` edit + `git rm` at cutover for zero operational benefit. +The three PoC repos (`avallete/homebrew-supabase-shim-poc`, `avallete/scoop-bucket`, `avallete/supabase-cli-release-poc`) are _*explicitly not added as `.repos/`* submodules__, even though `[.gitmodules](../../.gitmodules)` already tracks seven vendored reference repos (`effect`, `effect-patterns`, `effect-v3`, `lalph`, `cheffect`, `t3code`, `opencode`). Existing `.repos/`_ entries are read-only source-code references for `grep` / inspection; the PoC repos contain one generated artifact each (`Formula/supabase.rb`, `supabase.json`) or no interesting tree content at all. The `update-*.ts` scripts `gh repo clone` on demand into a tmpdir, so a submodule would change nothing about the release pipeline. Since these repos are PoC-phase only, vendoring now means a `.gitmodules` edit + `git rm` at cutover for zero operational benefit. ## Implementation progress @@ -326,6 +326,6 @@ This section tracks the work that has landed against the pre-cutover gates. Deta - [`apps/cli/docs/binary-distribution.md`](../../apps/cli/docs/binary-distribution.md) — runtime resolution details for the two-binary legacy model (TS SFE + Go binary). - [`.github/workflows/release-shared.yml`](../../.github/workflows/release-shared.yml), [`release.yml`](../../.github/workflows/release.yml) — the pipeline implementation (`release.yml` selects stable / beta / alpha channel per trigger). - [`apps/cli/scripts/build.ts`](../../apps/cli/scripts/build.ts), [`publish.ts`](../../apps/cli/scripts/publish.ts), [`sync-versions.ts`](../../apps/cli/scripts/sync-versions.ts), [`update-homebrew.ts`](../../apps/cli/scripts/update-homebrew.ts), [`update-scoop.ts`](../../apps/cli/scripts/update-scoop.ts) — release scripts. -- [`apps/cli-go/.goreleaser.yml`](../../apps/cli-go/.goreleaser.yml), [`release.yml`](../../apps/cli-go/.github/workflows/release.yml), [`release-beta.yml`](../../apps/cli-go/.github/workflows/release-beta.yml) — the Go CLI release config we mirror (no signing, `windows_arm64` target, GitHub-App-scoped publish tokens). +- `apps/cli-go/.goreleaser.yml`, `release.yml`, `release-beta.yml` — the Go CLI release config we mirror (no signing, `windows_arm64` target, GitHub-App-scoped publish tokens). - [CLI-1330](https://linear.app/supabase/issue/CLI-1330) — origin ticket. - [CLI-1344](https://linear.app/supabase/issue/CLI-1344/investigate-if-we-can-consolidate-local-release-logic-and-pipeline) — sub-issue: consolidate local and pipeline release logic. diff --git a/docs/adr/0016-legacy-port-completion-and-go-cli-authority-scope.md b/docs/adr/0016-legacy-port-completion-and-go-cli-authority-scope.md index 3067d974b6..ae595bff92 100644 --- a/docs/adr/0016-legacy-port-completion-and-go-cli-authority-scope.md +++ b/docs/adr/0016-legacy-port-completion-and-go-cli-authority-scope.md @@ -1,6 +1,6 @@ # 0016. Legacy Port Completion and Go CLI Authority Scope -**Status**: proposed +**Status**: accepted (2026-08-11), superseded by [0030](0030-go-cli-removal.md) **Date**: 2026-08-11 ## Problem Statement @@ -11,7 +11,7 @@ command began as a Phase 0 proxy to the Go binary, then moved to a native TypeSc touching `src/legacy/` was correct — nearly every change was either wrapping a new command or replacing its proxy, and the Go source was the only available spec for what the command should do. -That phase is essentially over. Per [`apps/cli/docs/go-cli-porting-status.md`](../../apps/cli/docs/go-cli-porting-status.md), +That phase is essentially over. Per `apps/cli/docs/go-cli-porting-status.md` (removed, CLI-2432), 95 of 103 legacy leaf commands (~92%) are natively ported; only 8 remain Phase 0 proxies. Most changes landing in `src/legacy/` today are ordinary engineering on already-ported commands — bug fixes, internal refactors, hoisting shared helpers, adding documented TS-only flags, telemetry and @@ -72,8 +72,8 @@ surface from a parity check it never needed. - Contributors now have to briefly classify a change (does it touch the parity surface?) rather than defaulting to "always check Go." Misclassification risk is partly, not fully, mitigated: - [`apps/cli/docs/go-cli-porting-status.md`](../../apps/cli/docs/go-cli-porting-status.md) stays the - source of truth for which commands are still `wrapped`, and CI's `testParity` / + `apps/cli/docs/go-cli-porting-status.md` (since removed, CLI-2432) stayed the + source of truth for which commands were still `wrapped`, and CI's `testParity` / `*.e2e.test.ts` suites catch output/behavior drift on the already-ported commands and code paths they cover — but that coverage is deliberately partial (e.g. `db pull --local` and `db lint --local` skip `testParity` today, see `apps/cli-e2e/src/tests/database-core.e2e.test.ts`), so a @@ -96,7 +96,7 @@ surface from a parity check it never needed. ## See Also - [`apps/cli/AGENTS.md`](../../apps/cli/AGENTS.md) -- [`apps/cli/docs/go-cli-porting-status.md`](../../apps/cli/docs/go-cli-porting-status.md) +- `apps/cli/docs/go-cli-porting-status.md` (since removed, CLI-2432) ## Addendum (2026-08-12): CLI-1970 outcome diff --git a/docs/adr/0030-go-cli-removal.md b/docs/adr/0030-go-cli-removal.md new file mode 100644 index 0000000000..45bcf3cbf8 --- /dev/null +++ b/docs/adr/0030-go-cli-removal.md @@ -0,0 +1,132 @@ +# 0030. Go CLI Removal + +**Status**: accepted +**Date**: 2026-09-14 + +## Problem Statement + +ADR 0016 described a residual Go delegation surface in `src/legacy/`: a handful of commands still +proxying to the Go binary (`GoProxy`) while the rest of the CLI had already been natively ported to +TypeScript. The remaining deprecated surfaces were removed rather than ported (CLI-2432); each now +exits 1 with replacement guidance: + +- `db remote changes` → `db diff --linked` +- `gen keys` → `projects api-keys` +- `db diff --use-pg-schema` → the default engine or `--use-pg-delta` +- `functions download --legacy-bundle` → `--use-api` +- `db branch create|delete|list|switch` has no local replacement (hosted preview branches via + `supabase branches` are a different feature) + +No command in the TypeScript CLI spawns `supabase-go` anymore. + +That leaves `apps/cli-go/` — roughly 300 tracked files across three Go modules (the root CLI +module, the `fsevents` helper module, and the separately-tagged `pkg` module) — serving nothing. +It still carries its own Go toolchain pin, CI (test/coverage/lint/CodeQL), Dependabot ecosystem, +release packaging (a second binary per platform, signed and archived alongside the TS binary), and +documentation, all to build and ship a binary nothing calls. + +## Decision + +Delete `apps/cli-go/` entirely. The CLI ships as a single compiled binary per platform +(`packages/cli-/bin/supabase`), built by `apps/cli/scripts/build.ts` with no Go +compilation step. + +The one file the Go tree owned that TypeScript still needed — the service-image Dockerfile +manifest parsed by `dockerfileServiceImages` — was already relocated into the TS tree +(`apps/cli/src/shared/services/Dockerfile`) by a prep commit on this branch before the tree was +deleted, so no runtime behavior depends on `apps/cli-go/` by the time it's removed. + +The tree's own `pkg` module (`github.com/supabase/cli/pkg`, a separately-tagged, externally +published Go module) already had a structurally broken publish path before this ADR, not merely +one this ADR chooses not to continue. At `pkg/v1.2.3` (2026-04-16, `ae7642d508`) the module lived +at repo-root `pkg/`, matching Go's subdirectory-module convention that a `pkg/vX.Y.Z` tag resolves +by finding `pkg/go.mod` at the repository root. `cad095718` ("chore(monorepo): move CLI sources +under apps/cli-go/", 2026-05-06) relocated it to `apps/cli-go/pkg/` while keeping the same module +path, so from that point any new `pkg/vX` tag would have been unresolvable — the module path no +longer matched where `go.mod` actually lived. `pkg/v1.2.3` is therefore the last resolvable +version, and was already roughly five months stale relative to `apps/cli-go/pkg`'s actual content +before this deletion. Deleting the tree makes that unresolvability permanent rather than causing +it. The durability guarantee for existing importers rests on two things: `refs/tags/pkg/v*` is +protected against deletion, creation, and force-push by a repository ruleset ("Protect pkg/v* +release tags", id 23327496), and on top of that the Go module proxy (`proxy.golang.org`, the +default `GOPROXY`) caches published module zips independently of this repository's current tree, +so an importer already pinned to a version keeps resolving it through the proxy even without +direct access to the tag. + +## Consequences + +### Positive + +- The repo ships zero Go: no toolchain pin, no CI surface, no packaging step, no documentation + drift between two implementations of the same CLI. +- Release archives, install scripts, and the Homebrew formula carry one binary instead of two, + removing an entire class of "sidecar present but stale/missing" failure mode. +- `cli-go-ci.yml`'s test/coverage/lint/codegen jobs, the per-PR Go module dependency cache in the + shared setup action, and Dependabot's `gomod` ecosystem entry are all gone — but these jobs were + path-filtered to `apps/cli-go/**` and so ran only on PRs that touched the Go tree already; most + ordinary PRs never paid this cost. CodeQL's shape changes rather than shrinking uniformly: its + `go` language matrix entry is gone entirely, including from the `merge_group` trigger it used to + run on, while the remaining `javascript-typescript` entry now runs on every `pull_request` push + — coverage it effectively never had before, since `merge_group` was CodeQL's only unfiltered + trigger — bounded by a new concurrency group so pushes to the same PR no longer queue up + uncancelled analysis runs. + +### Negative + +- No further `pkg` module releases. External importers pinned to existing tags are unaffected, but + anyone needing content newer than `pkg/v1.2.3` has no upgrade path within this module. Two + external importers are known from a code-search during planning: `supabase/terraform-provider-supabase` + (same org, pins `pkg v1.2.3`) and `turbot/steampipe-plugin-supabase` (third-party); both import + only `pkg/api`. Because `pkg/api` is pure `oapi-codegen` output, the concrete unblock for both is + to run `oapi-codegen` in the consuming repo against the Management API OpenAPI document — + the same one `packages/api` already consumes — and drop the dependency on + `github.com/supabase/cli/pkg` entirely. One caveat: the deleted `apps/cli-go/api/README.md` + documented the Go snapshot as generated from **staging** (`https://api.supabase.green/api/v1-yaml`), + while `packages/api` generates from **production** (`https://api.supabase.com`, see + `packages/api/scripts/download-openapi.ts`), so a consumer following this unblock gets a + different — production, not staging — surface than `pkg/api` had, which is worth knowing now + that the source doc explaining that distinction is gone. Beyond these two, this repository has + no visibility into other importers beyond the module proxy's own download counts, which aren't + queried here. +- Go-specific CI ends entirely: Coveralls coverage upload (Go-side) had no TypeScript-side + equivalent and simply stops with this PR — there is no CI-enforced coverage threshold for the + TypeScript CLI today. This is a known gap, not something this PR fixes; it is a candidate for a + follow-up if coverage enforcement is wanted. +- golangci-lint and the `go` CodeQL analysis both end, along with any lint/security coverage they + provided that had no TypeScript-side equivalent (e.g. Go-specific vulnerability classes CodeQL's + `javascript-typescript` matrix entry doesn't check for). +- The four `*.go-payload.ts` output-encoding specs (which drive `-o yaml`/`-o toml` byte-compatible + output) become frozen, hand-maintained contracts with no live Go source left to diff against for + drift. A prior commit on this branch already retargeted their drift test at the OpenAPI schema + instead of Go source, so this is a note about the ongoing nature of that contract, not new work. + `7b469f5b3` (2026-08-12, the CLI-1970 pin) is the last commit with the full pre-shrink Go source + tree intact and remains the closest available provenance reference for that contract's origin, + but it does not reflect `apps/cli-go/pkg/api`'s final state: that package kept regenerating via + the API-sync workflow for roughly another month after that pin, until this PR's tree deletion. + +## Alternatives Considered + +1. **Keep `pkg/` alone as a standalone Go module in this repo.** Rejected — this reintroduces the + entire Go toolchain, CI, and Dependabot surface this PR removes, to serve at most a couple of + external consumers of a single module. +2. **Extract `pkg/` to its own separate repository before deleting it here.** Not chosen for this + PR. Because the publish path was already structurally broken since the May 2026 monorepo move + (see Decision), this would be a restart from the last resolvable state (`pkg/v1.2.3`), not a + continuation of an active release cadence. The module shipped 14 packages (`api`, `cast`, + `config`, `diff`, `fetcher`, `function`, `migration`, `parser`, `pgtest`, `pgxv5`, `queue`, + `storage`, `vault`, plus the root module); "internal" audience is only evidenced for the two + known importers named above, both of which use only `pkg/api` — not for the module as a whole. + +## Related Decisions + +- Supersedes [ADR 0016](0016-legacy-port-completion-and-go-cli-authority-scope.md), whose + residual authority scope no longer applies now that `apps/cli-go/` is gone. +- [ADR 0011](0011-cli-release-and-distribution-strategy.md) and + [ADR 0014](0014-macos-code-signing-and-notarization.md) remain historically accurate records of + decisions made while the Go CLI still existed; they are not updated by this ADR. +- Linear: CLI-2432. + +## See Also + +- [`apps/cli/docs/binary-distribution.md`](../../apps/cli/docs/binary-distribution.md) +- [`apps/cli/docs/release-process.md`](../../apps/cli/docs/release-process.md) diff --git a/docs/adr/README.md b/docs/adr/README.md index 05b840861b..30b430fd30 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -57,7 +57,7 @@ When an ADR becomes outdated, mark it as `deprecated` or reference the supersedi | 0013 | [Live E2E Tests Bypass the Replay Server](0013-live-e2e-bypasses-replay-server.md) | accepted | | 0014 | [macOS Code Signing & Notarization](0014-macos-code-signing-and-notarization.md) | accepted | | 0015 | [Managed Stack Contract Fixtures](0015-managed-stack-contract-fixtures.md) | superseded | -| 0016 | [Legacy Port Completion and Go CLI Authority Scope](0016-legacy-port-completion-and-go-cli-authority-scope.md) | proposed | +| 0016 | [Legacy Port Completion and Go CLI Authority Scope](0016-legacy-port-completion-and-go-cli-authority-scope.md) | superseded | | 0017 | [Simplified Managed Stack Architecture](0017-simplified-managed-stack-architecture.md) | superseded | | 0018 | [Sparse Config Subtraction](0018-sparse-config-subtraction.md) | proposed | | 0019 | [Raw API-Response Passthrough on API-Sourced Config](0019-config-api-response-passthrough.md) | accepted | @@ -71,6 +71,7 @@ When an ADR becomes outdated, mark it as `deprecated` or reference the supersedi | 0027 | [CLI Tracing Conventions](0027-cli-tracing-conventions.md) | proposed | | 0028 | [Release Branches and Maintenance Lines](0028-release-branches-and-maintenance-lines.md) | accepted | | 0029 | [Native Postgres Privilege Step-Down](0029-native-postgres-privilege-step-down.md) | proposed | +| 0030 | [Go CLI Removal](0030-go-cli-removal.md) | accepted | ## Template diff --git a/docs/openapi-sync.md b/docs/openapi-sync.md index b55e756161..c5b34cce9c 100644 --- a/docs/openapi-sync.md +++ b/docs/openapi-sync.md @@ -2,6 +2,8 @@ > Extracted from [ADR 0005](adr/0005-openapi-driven-code-generation.md). This document covers the GitHub Actions workflows that keep checked-in OpenAPI types in sync with the live Management API. For the three-layer generation strategy and architectural decisions, see the ADR. +> This document describes an earlier iteration of the API sync pipeline; see [`.github/workflows/api-package-sync.yml`](../.github/workflows/api-package-sync.yml) for the current workflow. + Three GitHub Actions workflows keep the checked-in `v1.d.ts` in sync with the live Management API spec across the private API repo and the public CLI repo. ## 1. Sync workflow (CLI repo — `.github/workflows/openapi-sync.yml`) diff --git a/install b/install index 98f8105032..a184407f5e 100755 --- a/install +++ b/install @@ -181,6 +181,22 @@ verify_checksum() { fi } +# Older releases still ship and need the `supabase-go` companion; single-binary releases +# would otherwise leave a stale one behind. +sync_go_companion() { + local ext="$1" + local source="$2" + local transfer="$3" + local dest="${INSTALL_DIR}/${APP}-go${ext}" + + if [[ -f "$source" ]]; then + "$transfer" "$source" "$dest" + chmod 755 "$dest" + else + rm -f "$dest" + fi +} + install_from_binary() { local ext="$1" @@ -192,12 +208,13 @@ install_from_binary() { mkdir -p "$INSTALL_DIR" cp "$BINARY_PATH" "${INSTALL_DIR}/${APP}${ext}" chmod 755 "${INSTALL_DIR}/${APP}${ext}" + sync_go_companion "$ext" "$(dirname "$BINARY_PATH")/${APP}-go${ext}" cp } download_and_install() { local target="$1" local ext="$2" - local version filename base_url tmp_dir archive checksums source_dir companion_ext + local version filename base_url tmp_dir archive checksums source_dir need curl need tar @@ -250,11 +267,7 @@ download_and_install() { mv "${source_dir}/${APP}${ext}" "${INSTALL_DIR}/${APP}${ext}" chmod 755 "${INSTALL_DIR}/${APP}${ext}" - companion_ext="$ext" - if [[ -f "${source_dir}/${APP}-go${companion_ext}" ]]; then - mv "${source_dir}/${APP}-go${companion_ext}" "${INSTALL_DIR}/${APP}-go${companion_ext}" - chmod 755 "${INSTALL_DIR}/${APP}-go${companion_ext}" - fi + sync_go_companion "$ext" "${source_dir}/${APP}-go${ext}" mv } path_command_for_shell() { diff --git a/knip.jsonc b/knip.jsonc index c1edba7619..d4cfd0b2ab 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -2,12 +2,9 @@ "$schema": "./node_modules/knip/schema.json", // Required in the event that git submodules are present. Do not remove this, despite knip warnings. "ignore": [".repos/**"], - // `go` comes from mise. - "ignoreBinaries": ["go"], // Spawned through a node_modules/.bin path in tools/release/local-registry.ts and // bunx in apps/cli/tests/helpers/npm-registry.ts, which knip cannot trace. "ignoreDependencies": ["verdaccio"], - "ignoreWorkspaces": ["apps/cli-go"], "workspaces": { "apps/cli": { "entry": [ diff --git a/mise.lock b/mise.lock index 5980635e1a..717a0b27df 100644 --- a/mise.lock +++ b/mise.lock @@ -51,126 +51,6 @@ url = "https://github.com/oven-sh/bun/releases/download/bun-v1.4.2/bun-windows-x checksum = "sha256:78c221c2376f79731ccf4e4af0b3bb46d81fefa3296c5abee09ad8a1b21e68c6" url = "https://github.com/oven-sh/bun/releases/download/bun-v1.4.2/bun-windows-x64-baseline.zip" -[[tools.go]] -version = "1.26.5" -backend = "core:go" -specifiers = ["1.26.5"] - -[tools.go."platforms.linux-arm64"] -checksum = "sha256:fe4789e92b1f33358680864bbe8704289e7bb5fc207d80623c308935bd696d49" -url = "https://dl.google.com/go/go1.26.5.linux-arm64.tar.gz" - -[tools.go."platforms.linux-arm64-musl"] -checksum = "sha256:fe4789e92b1f33358680864bbe8704289e7bb5fc207d80623c308935bd696d49" -url = "https://dl.google.com/go/go1.26.5.linux-arm64.tar.gz" - -[tools.go."platforms.linux-x64"] -checksum = "sha256:5c2c3b16caefa1d968a94c1daca04a7ca301a496d9b086e17ad77bb81393f053" -url = "https://dl.google.com/go/go1.26.5.linux-amd64.tar.gz" - -[tools.go."platforms.linux-x64-baseline"] -checksum = "sha256:5c2c3b16caefa1d968a94c1daca04a7ca301a496d9b086e17ad77bb81393f053" -url = "https://dl.google.com/go/go1.26.5.linux-amd64.tar.gz" - -[tools.go."platforms.linux-x64-musl"] -checksum = "sha256:5c2c3b16caefa1d968a94c1daca04a7ca301a496d9b086e17ad77bb81393f053" -url = "https://dl.google.com/go/go1.26.5.linux-amd64.tar.gz" - -[tools.go."platforms.linux-x64-musl-baseline"] -checksum = "sha256:5c2c3b16caefa1d968a94c1daca04a7ca301a496d9b086e17ad77bb81393f053" -url = "https://dl.google.com/go/go1.26.5.linux-amd64.tar.gz" - -[tools.go."platforms.macos-arm64"] -checksum = "sha256:efb87ff28af9a188d0536ef5d42e63dd52ba8263cd7344a993cc48dd11dedb6a" -url = "https://dl.google.com/go/go1.26.5.darwin-arm64.tar.gz" - -[tools.go."platforms.macos-x64"] -checksum = "sha256:6231d8d3b8f5552ec6cbf6d685bdd5482e1e703214b120e89b3bf0d7bf1ef725" -url = "https://dl.google.com/go/go1.26.5.darwin-amd64.tar.gz" - -[tools.go."platforms.macos-x64-baseline"] -checksum = "sha256:6231d8d3b8f5552ec6cbf6d685bdd5482e1e703214b120e89b3bf0d7bf1ef725" -url = "https://dl.google.com/go/go1.26.5.darwin-amd64.tar.gz" - -[tools.go."platforms.windows-x64"] -checksum = "sha256:97e6b2a833b6d89f9ff17d25419ac0a7e3b482a044e9ab18cdef834bd834fd38" -url = "https://dl.google.com/go/go1.26.5.windows-amd64.zip" - -[tools.go."platforms.windows-x64-baseline"] -checksum = "sha256:97e6b2a833b6d89f9ff17d25419ac0a7e3b482a044e9ab18cdef834bd834fd38" -url = "https://dl.google.com/go/go1.26.5.windows-amd64.zip" - -[[tools.golangci-lint]] -version = "2.12.2" -backend = "aqua:golangci/golangci-lint" -specifiers = ["2.12.2"] - -[tools.golangci-lint."platforms.linux-arm64"] -checksum = "sha256:44cd40a8c76c86755375adfeea52cfd3533cb43d7bd647771e0ae065e166df3a" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-linux-arm64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/413470996" -provenance = "github-attestations" - -[tools.golangci-lint."platforms.linux-arm64-musl"] -checksum = "sha256:44cd40a8c76c86755375adfeea52cfd3533cb43d7bd647771e0ae065e166df3a" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-linux-arm64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/413470996" -provenance = "github-attestations" - -[tools.golangci-lint."platforms.linux-x64"] -checksum = "sha256:8df580d2670fed8fa984aac0507099af8df275e665215f5c7a2ae3943893a553" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-linux-amd64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/413471054" -provenance = "github-attestations" - -[tools.golangci-lint."platforms.linux-x64-baseline"] -checksum = "sha256:8df580d2670fed8fa984aac0507099af8df275e665215f5c7a2ae3943893a553" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-linux-amd64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/413471054" -provenance = "github-attestations" - -[tools.golangci-lint."platforms.linux-x64-musl"] -checksum = "sha256:8df580d2670fed8fa984aac0507099af8df275e665215f5c7a2ae3943893a553" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-linux-amd64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/413471054" -provenance = "github-attestations" - -[tools.golangci-lint."platforms.linux-x64-musl-baseline"] -checksum = "sha256:8df580d2670fed8fa984aac0507099af8df275e665215f5c7a2ae3943893a553" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-linux-amd64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/413471054" -provenance = "github-attestations" - -[tools.golangci-lint."platforms.macos-arm64"] -checksum = "sha256:a9c54498731b3128f79e090be6110f3e5fffccc617b08142ed244d4126c73f29" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-darwin-arm64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/413470950" -provenance = "github-attestations" - -[tools.golangci-lint."platforms.macos-x64"] -checksum = "sha256:f6f06d94b6241521c53d15450c5209b028270bf966f842afb11c030c79f5bc16" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-darwin-amd64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/413470980" -provenance = "github-attestations" - -[tools.golangci-lint."platforms.macos-x64-baseline"] -checksum = "sha256:f6f06d94b6241521c53d15450c5209b028270bf966f842afb11c030c79f5bc16" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-darwin-amd64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/413470980" -provenance = "github-attestations" - -[tools.golangci-lint."platforms.windows-x64"] -checksum = "sha256:bd42e3ebc8cb4ececb86941983baaf1dc221bbb04d838e94ce63b49cc91e02bb" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-windows-amd64.zip" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/413471017" -provenance = "github-attestations" - -[tools.golangci-lint."platforms.windows-x64-baseline"] -checksum = "sha256:bd42e3ebc8cb4ececb86941983baaf1dc221bbb04d838e94ce63b49cc91e02bb" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-windows-amd64.zip" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/413471017" -provenance = "github-attestations" - [[tools.node]] version = "24.21.0" backend = "core:node" diff --git a/mise.toml b/mise.toml index f76945f5a7..91ec4a0f74 100644 --- a/mise.toml +++ b/mise.toml @@ -1,9 +1,5 @@ min_version = '2026.9.0' -[tools] -go = "1.26.5" -golangci-lint = "2.12.2" - [settings] idiomatic_version_file_enable_tools = ["node", "bun", "pnpm"] lockfile = true diff --git a/package.json b/package.json index 8f86e271b2..74fd6ed21d 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "test:smoke": "pnpm exec turbo run supabase#test:smoke --", "test:github-scripts": "bun test ./.github/scripts", "dev:docs": "pnpm exec turbo run @supabase/docs#dev", - "test:unit": "pnpm exec turbo run test:unit:run --filter=!@supabase/cli-go --continue=always --", + "test:unit": "pnpm exec turbo run test:unit:run --continue=always --", "test:integration": "pnpm exec turbo run test:integration:run --continue=always --", "test:e2e": "pnpm exec turbo run supabase#build && pnpm exec turbo run test:e2e:run --only --concurrency=1 --continue=always --", "test:vitest": "bun --bun vitest run --project '!*e2e*' --project '!*live*'", diff --git a/packages/cli-darwin-arm64/package.json b/packages/cli-darwin-arm64/package.json index 9245b363b9..f2dd3b6d52 100644 --- a/packages/cli-darwin-arm64/package.json +++ b/packages/cli-darwin-arm64/package.json @@ -24,8 +24,7 @@ "publishConfig": { "access": "public", "executableFiles": [ - "bin/supabase", - "bin/supabase-go" + "bin/supabase" ] }, "preferUnplugged": true diff --git a/packages/cli-darwin-x64/package.json b/packages/cli-darwin-x64/package.json index f14a323770..4c89d3d696 100644 --- a/packages/cli-darwin-x64/package.json +++ b/packages/cli-darwin-x64/package.json @@ -24,8 +24,7 @@ "publishConfig": { "access": "public", "executableFiles": [ - "bin/supabase", - "bin/supabase-go" + "bin/supabase" ] }, "preferUnplugged": true diff --git a/packages/cli-linux-arm64-musl/package.json b/packages/cli-linux-arm64-musl/package.json index 0b95049d4c..03ed91112d 100644 --- a/packages/cli-linux-arm64-musl/package.json +++ b/packages/cli-linux-arm64-musl/package.json @@ -27,8 +27,7 @@ "publishConfig": { "access": "public", "executableFiles": [ - "bin/supabase", - "bin/supabase-go" + "bin/supabase" ] }, "preferUnplugged": true diff --git a/packages/cli-linux-arm64/package.json b/packages/cli-linux-arm64/package.json index cef64df964..a559fcc486 100644 --- a/packages/cli-linux-arm64/package.json +++ b/packages/cli-linux-arm64/package.json @@ -27,8 +27,7 @@ "publishConfig": { "access": "public", "executableFiles": [ - "bin/supabase", - "bin/supabase-go" + "bin/supabase" ] }, "preferUnplugged": true diff --git a/packages/cli-linux-x64-musl/package.json b/packages/cli-linux-x64-musl/package.json index 362f22edd1..8234394c7d 100644 --- a/packages/cli-linux-x64-musl/package.json +++ b/packages/cli-linux-x64-musl/package.json @@ -27,8 +27,7 @@ "publishConfig": { "access": "public", "executableFiles": [ - "bin/supabase", - "bin/supabase-go" + "bin/supabase" ] }, "preferUnplugged": true diff --git a/packages/cli-linux-x64/package.json b/packages/cli-linux-x64/package.json index aef98178c0..494867c9f2 100644 --- a/packages/cli-linux-x64/package.json +++ b/packages/cli-linux-x64/package.json @@ -27,8 +27,7 @@ "publishConfig": { "access": "public", "executableFiles": [ - "bin/supabase", - "bin/supabase-go" + "bin/supabase" ] }, "preferUnplugged": true diff --git a/packages/cli-test-helpers/src/harness.ts b/packages/cli-test-helpers/src/harness.ts index 7e4fa5c416..b51a6468f3 100644 --- a/packages/cli-test-helpers/src/harness.ts +++ b/packages/cli-test-helpers/src/harness.ts @@ -143,9 +143,7 @@ export async function exec( // The CLI resolves its API base URL through the profile system, not // SUPABASE_API_URL: `CommandSettings` accepts a built-in profile name or a YAML - // file path, and the still-proxied Go binary reads the same file via - // SUPABASE_PROFILE. Write a temp profile file pointing at the replay server so - // both paths reach it. + // file path. Write a temp profile file pointing at the replay server. const profilePath = join(tmpdir(), `cli-e2e-profile-${randomUUID()}.yaml`); const url = harness.options.apiUrl; writeFileSync( diff --git a/packages/cli-windows-arm64/package.json b/packages/cli-windows-arm64/package.json index b0a19b2e39..f74cf42ce5 100644 --- a/packages/cli-windows-arm64/package.json +++ b/packages/cli-windows-arm64/package.json @@ -24,8 +24,7 @@ "publishConfig": { "access": "public", "executableFiles": [ - "bin/supabase.exe", - "bin/supabase-go.exe" + "bin/supabase.exe" ] }, "preferUnplugged": true diff --git a/packages/cli-windows-x64/package.json b/packages/cli-windows-x64/package.json index f04d229711..02be42f797 100644 --- a/packages/cli-windows-x64/package.json +++ b/packages/cli-windows-x64/package.json @@ -24,8 +24,7 @@ "publishConfig": { "access": "public", "executableFiles": [ - "bin/supabase.exe", - "bin/supabase-go.exe" + "bin/supabase.exe" ] }, "preferUnplugged": true diff --git a/packages/config/src/project-config/api-attributes.ts b/packages/config/src/project-config/api-attributes.ts index 2a385e91cc..dca32a9ec1 100644 --- a/packages/config/src/project-config/api-attributes.ts +++ b/packages/config/src/project-config/api-attributes.ts @@ -12,6 +12,8 @@ import { Schema } from "effect"; const postgresSettingsAttributes = Schema.Struct({ effective_cache_size: Schema.optionalKey(Schema.String), logical_decoding_work_mem: Schema.optionalKey(Schema.String), + autovacuum_max_workers: Schema.optionalKey(Schema.Unknown), + autovacuum_work_mem: Schema.optionalKey(Schema.Unknown), log_autovacuum_min_duration: Schema.optionalKey(Schema.Unknown), log_checkpoints: Schema.optionalKey(Schema.Unknown), log_connections: Schema.optionalKey(Schema.Unknown), @@ -48,6 +50,8 @@ const postgresSettingsAttributes = Schema.Struct({ checkpoint_timeout: Schema.optionalKey(Schema.Unknown), hot_standby_feedback: Schema.optionalKey(Schema.Unknown), cron_log_statement: Schema.optionalKey(Schema.Unknown), + pg_net_batch_size: Schema.optionalKey(Schema.Unknown), + pg_stat_statements_max: Schema.optionalKey(Schema.Unknown), }); // `allowed_cidrs` is mapped (`filterCidrAddresses`, `./registry.ts`); the rest are unmapped. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e239809293..b2ff009992 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -450,8 +450,6 @@ importers: specifier: 'catalog:' version: 5.0.0(@types/node@26.4.1)(@vitest/coverage-v8@5.0.0)(vite@8.2.2(@types/node@26.4.1)(esbuild@0.28.2)(jiti@2.7.0)(yaml@2.9.0)) - apps/cli-go: {} - apps/docs: dependencies: fumadocs-core: diff --git a/tools/release/local-release.ts b/tools/release/local-release.ts index 370ba72188..750420023d 100644 --- a/tools/release/local-release.ts +++ b/tools/release/local-release.ts @@ -7,7 +7,6 @@ * pnpm cli-release [--version 0.0.0-local.1234567890] * * Requires `pnpm local-registry` to be running in another terminal. - * Requires Go in PATH to build the `supabase-go` sidecar. */ import { $ } from "bun"; @@ -39,8 +38,6 @@ type PlatformInfo = { bunTarget: string; platformPkg: string; ext: string; - goos: string; - goarch: string; }; const PLATFORM_MAP: Record = { @@ -48,43 +45,31 @@ const PLATFORM_MAP: Record = { bunTarget: "bun-darwin-arm64", platformPkg: "cli-darwin-arm64", ext: "", - goos: "darwin", - goarch: "arm64", }, "darwin-x64": { bunTarget: "bun-darwin-x64", platformPkg: "cli-darwin-x64", ext: "", - goos: "darwin", - goarch: "amd64", }, "linux-arm64": { bunTarget: "bun-linux-arm64", platformPkg: "cli-linux-arm64", ext: "", - goos: "linux", - goarch: "arm64", }, "linux-x64": { bunTarget: "bun-linux-x64-baseline", platformPkg: "cli-linux-x64", ext: "", - goos: "linux", - goarch: "amd64", }, "win32-x64": { bunTarget: "bun-windows-x64-baseline", platformPkg: "cli-windows-x64", ext: ".exe", - goos: "windows", - goarch: "amd64", }, "win32-arm64": { bunTarget: "bun-windows-arm64", platformPkg: "cli-windows-arm64", ext: ".exe", - goos: "windows", - goarch: "arm64", }, }; @@ -122,27 +107,6 @@ async function readToken(): Promise { } } -async function checkGo(): Promise { - try { - await $`go version`.quiet(); - } catch { - console.error("\nError: `go` not found in PATH."); - console.error("Install Go from https://go.dev/dl/ to build the CLI.\n"); - process.exit(1); - } -} - -async function checkGoSource(): Promise { - const goSource = path.join(root, "apps", "cli-go"); - const goMod = Bun.file(path.join(goSource, "go.mod")); - if (!(await goMod.exists())) { - console.error("\nError: Go CLI source not found at apps/cli-go"); - console.error("Run: pnpm repos:install\n"); - process.exit(1); - } - return goSource; -} - async function main() { const { values } = parseArgs({ options: { @@ -156,9 +120,6 @@ async function main() { const token = await readToken(); const platform = getPlatformInfo(); - await checkGo(); - const goSource = await checkGoSource(); - if (process.platform === "linux") { console.warn( "Note: local-release builds the glibc variant only (cli-linux-*). " + @@ -182,7 +143,7 @@ async function main() { const entrypoint = path.join(root, "apps", "cli", "src", "main.ts"); const bunBinary = path.join(tmpPlatformBinDir, `supabase${platform.ext}`); - console.log("[1/3] Compiling CLI binary..."); + console.log("[1/2] Compiling CLI binary..."); const buildResult = await Bun.build({ entrypoints: [entrypoint], compile: { target: platform.bunTarget, outfile: bunBinary }, @@ -196,27 +157,13 @@ async function main() { throw new Error("failed to compile the CLI binary"); } - { - const goBinary = path.join(tmpPlatformBinDir, `supabase-go${platform.ext}`); - console.log(`[2/3] Compiling Go CLI binary (${platform.goos}/${platform.goarch})...`); - // go build must run from the Go source directory: passing an absolute path as a positional - // arg makes Go resolve the module from CWD instead, which fails because the repo root has - // no go.mod. - await $`go build -trimpath -ldflags="-s -w" -o ${goBinary} .`.cwd(goSource).env({ - ...process.env, - GOOS: platform.goos, - GOARCH: platform.goarch, - CGO_ENABLED: "0", - }); - } - const tmpCliDir = path.join(tmpDir, "cli"); const tmpCliDistDir = path.join(tmpCliDir, "dist"); await mkdir(tmpCliDistDir, { recursive: true }); const shimSrc = path.join(root, "apps", "cli", "src", "shared", "cli", "bin.ts"); const shimOut = path.join(tmpCliDistDir, "supabase.js"); - console.log("[3/3] Building Node.js shim..."); + console.log("[2/2] Building Node.js shim..."); await $`bun build ${shimSrc} --outfile=${shimOut} --target=node`; const platformPkgJson = await Bun.file( diff --git a/tools/release/release-notes-prompt.md b/tools/release/release-notes-prompt.md index ba995dc2bf..5bcf2223cb 100644 --- a/tools/release/release-notes-prompt.md +++ b/tools/release/release-notes-prompt.md @@ -93,19 +93,10 @@ Earlier revisions carried an experimental `next/` (v3) shell and a `legacy/` wra both are gone and `apps/cli/src/` is the single CLI tree. Never mention `next/`, v3, or a "legacy shell" in release notes — describe every change as a change to the CLI. -### Go → TypeScript port - -Ongoing port: `apps/cli-go/` → `apps/cli/src/`. Parity PRs are **not** features/fixes. - -- If leaf commands were ported: **one line** under **TypeScript port progress** — list leaf commands only (`db diff`, not `db`); behavior matches Go CLI; cite PRs. Omit section if none. -- Port infra (services, tests, parity scripts) → tail count only. -- Port PR that **also** fixes a real bug or adds a non-Go flag → promote that part to Bug fixes / New features; still list the command under port progress. - ### Where user-visible changes usually live -- `apps/cli/src/commands/**` — behavior, output, flags, errors (beyond pure porting) +- `apps/cli/src/commands/**` — behavior, output, flags, errors - `apps/cli/src/shared/**` — telemetry, global flags, output inherited by every command -- `apps/cli-go/**` — while still the production binary - `packages/cli-*`, `apps/cli/scripts/` — install/packaging (homebrew, scoop, build) Everything else is usually internal. @@ -181,11 +172,6 @@ From the header line extract `VERSION`, `COMPARE_URL`, `DATE`. - . (#1234) -### TypeScript port progress - - -- **Now served by the TypeScript shell:** ``, ``. Behavior matches the Go CLI. (#1234) - --- Plus N internal improvements and dependency updates. diff --git a/turbo.json b/turbo.json index 70cb4b17c8..6bd0f3d939 100644 --- a/turbo.json +++ b/turbo.json @@ -47,15 +47,9 @@ "cache": false, "passThroughEnv": ["*"] }, - "@supabase/cli-go#build": { - "cache": true, - "inputs": ["$TURBO_DEFAULT$", "$TURBO_ROOT$/mise.lock"], - "outputs": ["supabase-go"], - "env": ["GO*", "CGO_*", "CC", "CXX"] - }, "supabase#build": { "cache": true, - "dependsOn": ["@supabase/config#build", "@supabase/cli-go#build"], + "dependsOn": ["@supabase/config#build"], "inputs": [ "$TURBO_DEFAULT$", "$TURBO_ROOT$/mise.lock", From b903db084e10e050aabaebc5cde6ab6b6aeb9198 Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Fri, 9 Oct 2026 08:48:32 +0000 Subject: [PATCH 002/129] refactor(cli)!: remove Go CLI naming and framing from code and docs (#7064) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary With the Go CLI removed on `next`, the codebase still described and named much of its behaviour in Go terms. This PR removes that, including user-facing error text that quoted Go library errors. Telemetry error identity (error `_tag`s and actionability fingerprints) is unchanged; the committed error-tag snapshot has no diff. **Config loading** - `@supabase/config`'s internal `goViperCompat` → `cliCompat` (still internal-only via `@supabase/config/internal`). - New `apps/cli/src/command-internal/cli-config-load.ts` wraps `loadCliConfig`, `resolveCliConfigValue`, `resolveCliConfigSubtree`, and the config-pull validation decode, always setting `cliCompat: true`. A code-structure guard keeps other `apps/cli` files from bypassing it. - Functions serve/deploy/download now require the CLI loader; the unused strict fallback and its constant `config.go_compat` span attribute are removed. Only `experimental compute` stays on the strict public loader. Whether to drop the lenient behaviour itself is a separate discussion. **Renames** - Go/pflag/cobra/viper-named files and identifiers are renamed for what they do, e.g. `go-json.ts` → `html-safe-json.ts`, `go-output.encoders.ts` → `output.encoders.ts`, `pflag-reconcile.ts` → `argv-flag-reconcile.ts`, `cobra-flag-groups.ts` → `flag-groups.ts`, `viper-env.ts` → `supabase-env.ts`, `*.go-payload.ts` → `*.response-shape.ts`, `FunctionsGoConfigCompat` → `FunctionsLocalConfigLoader`. - `ArgvWorkdirError`, `UrlParseError`, and `TomlEncodeError` keep their previous tag/fingerprint/`name` strings so error tracking is unaffected. - The `go-keyring-base64:` prefix on stored keyring tokens is unchanged (persisted data). **Docs, comments, test titles** - `SIDE_EFFECTS.md` files, CLI docs, `AGENTS.md`, comments, and test titles describe current behaviour instead of comparing against the Go CLI. ADRs are left as historical records. - Corrected two stale doc claims: `packages/stack` does not use the strict config default, and `test db` honours the global `--network-id`. ## Linked issue None; internal cleanup. - [x] I'm a Supabase maintainer. ## Checklist - [x] The PR title follows Conventional Commits. - [x] Tests added or updated for the change. - [x] `pnpm check:all` passes; relevant package tests pass for every touched workspace. 🤖 Generated with [Claude Code](https://claude.com/claude-code) BREAKING CHANGE: `COBRA_COMPLETION_DESCRIPTIONS` is no longer read; set `SUPABASE_COMPLETION_DESCRIPTIONS` to control shell completion descriptions. Error messages that quoted Go library errors are reworded: invalid boolean flag values (e.g. `sso add --skip-url-validation`) now say `expected a boolean` instead of `strconv.ParseBool: …`; invalid unsigned values for `storage cp --jobs` and `db reset --last` now say `expected an unsigned integer` (or `value out of range`) instead of `strconv.ParseUint: …`; oversized migration/seed statements report `scanner: token too long` instead of `bufio.Scanner: token too long`; `gen bearer-jwt` claim and JWK parse errors now read `invalid claims: expected a JSON object, got `, `invalid claims: number is out of range`, `failed to parse JWK: expected a JSON object, got `, and `invalid JWK field : expected , got ` instead of `json: cannot unmarshal … into Go value/struct field …`. Config type errors name values by shape: `SUPABASE_PROFILE` files report `expected a YAML mapping at the top level` and `array`/`object` type names, `[db]` glob entries report TOML type names (`local date`, `offset date-time`, `array`, `table`, …) instead of Go type names, and malformed `db lint`/`db advisors` payloads report `invalid : expected ` instead of naming Go types. `SUPABASE_PROFILE` errors are reworded (`unknown keys: …`, ` is required`, ` must be an http(s) URL`, `unsupported config file type …`, `no profile config file specified`) and multi-line profile errors no longer pad lines with trailing spaces. Config decode errors drop the `decoding failed due to the following error(s):` header and render entries as `: expected a string, got `, `: unknown keys: …`, `: must be one of …`. `failed to unmarshal …` messages (postgres-config, bootstrap templates, functions download) now read `failed to parse …`, `db lint`'s `failed to marshal json:` reads `failed to parse lint result:`, and TOML output errors drop the `toml:` prefix. --------- Co-authored-by: Claude Opus 5.5 --- AGENTS.md | 2 +- apps/cli-e2e/src/server/pg-mock.ts | 2 +- apps/cli-e2e/src/server/request-matcher.ts | 2 +- apps/cli-e2e/src/tests/auth.e2e.test.ts | 2 +- apps/cli/docs/binary-distribution.md | 10 - apps/cli/docs/profile.md | 10 +- apps/cli/docs/supabase/login.md | 2 +- apps/cli/src/SIDE_EFFECTS_TEMPLATE.md | 2 +- .../cli/src/auth/command-credentials.layer.ts | 43 +- .../command-credentials.layer.unit.test.ts | 109 ++-- .../command-platform-api.layer.unit.test.ts | 51 +- apps/cli/src/cli/complete.ts | 78 ++- apps/cli/src/cli/complete.unit.test.ts | 38 +- apps/cli/src/cli/root.ts | 2 +- apps/cli/src/command-internal/api-url.ts | 4 +- ...ag-reconcile.ts => argv-flag-reconcile.ts} | 95 ++- ...st.ts => argv-flag-reconcile.unit.test.ts} | 160 +++-- .../{go-quote.ts => byte-quote.ts} | 40 +- .../src/command-internal/cli-config-load.ts | 37 ++ .../src/command-internal/config-pull-run.ts | 20 +- .../src/command-internal/config-validate.ts | 29 +- .../config-validate.unit.test.ts | 44 +- .../src/command-internal/connect-errors.ts | 52 +- .../connect-errors.unit.test.ts | 10 +- .../await-storage-ready.unit.test.ts | 2 +- .../db-bootstrap/bootstrap-config.ts | 2 +- .../container-lifecycle.unit.test.ts | 19 +- .../db-bootstrap/db-setup.unit.test.ts | 168 +++--- .../docker-create-args.unit.test.ts | 4 +- .../db-bootstrap/local-container-inputs.ts | 4 +- .../db-bootstrap/postgres.service.ts | 2 +- .../postgres.service.unit.test.ts | 10 +- .../db-bootstrap/realtime-env.unit.test.ts | 2 +- .../recreate-local-database.unit.test.ts | 2 +- .../db-bootstrap/shadow-cache.ts | 8 +- .../db-bootstrap/shadow-database.ts | 4 +- .../db-bootstrap/start-local-database.ts | 16 +- .../db-config.integration.test.ts | 77 ++- .../src/command-internal/db-config.parse.ts | 2 +- .../db-config.parse.unit.test.ts | 58 +- .../command-internal/db-config.toml-read.ts | 88 ++- .../db-config.toml-read.unit.test.ts | 310 +++++----- .../command-internal/db-connection.service.ts | 2 +- .../db-connection.sql-pg.integration.test.ts | 62 +- .../db-connection.sql-pg.layer.ts | 2 +- .../db-connection.sql-pg.unit.test.ts | 26 +- .../command-internal/db-image.unit.test.ts | 2 +- .../db-target-flags.unit.test.ts | 4 +- .../command-internal/debug-logger.layer.ts | 2 +- .../debug-logger.layer.unit.test.ts | 2 +- .../command-internal/detect-content-type.ts | 2 +- .../detect-content-type.unit.test.ts | 4 +- .../command-internal/diff-engine.unit.test.ts | 6 +- .../command-internal/docker-ids.unit.test.ts | 14 +- .../docker-lifecycle.unit.test.ts | 23 +- .../docker-registry.unit.test.ts | 2 +- .../docker-run.args.unit.test.ts | 6 +- .../docker-suggest.unit.test.ts | 4 +- apps/cli/src/command-internal/dotenv.ts | 4 +- .../src/command-internal/dotenv.unit.test.ts | 12 +- .../{go-duration.ts => duration.ts} | 17 +- .../command-internal/duration.unit.test.ts | 146 +++++ .../edge-runtime-script.unit.test.ts | 2 +- .../experimental-gate.unit.test.ts | 74 ++- .../src/command-internal/fail-on.unit.test.ts | 2 +- .../{go-float.ts => format-float.ts} | 10 +- .../format-float.unit.test.ts | 27 + ...go-config.ts => functions-local-config.ts} | 4 +- .../src/command-internal/glob.unit.test.ts | 31 +- apps/cli/src/command-internal/global-flags.ts | 30 +- .../command-internal/go-duration.unit.test.ts | 146 ----- .../command-internal/go-float.unit.test.ts | 27 - .../src/command-internal/go-json.unit.test.ts | 102 ---- .../{go-json.ts => html-safe-json.ts} | 45 +- .../html-safe-json.unit.test.ts | 104 ++++ apps/cli/src/command-internal/linked-state.ts | 4 +- .../command-internal/local-config-values.ts | 57 +- .../local-config-values.unit.test.ts | 134 +++-- .../{go-jwt.ts => local-jwt.ts} | 40 +- ...wt.unit.test.ts => local-jwt.unit.test.ts} | 78 +-- .../command-internal/local-project-context.ts | 3 +- .../local-project-context.unit.test.ts | 25 +- .../command-internal/login-crypto.layer.ts | 4 +- .../migrate-and-seed.unit.test.ts | 61 +- .../src/command-internal/migration-apply.ts | 32 +- .../migration-apply.unit.test.ts | 300 +++++----- .../migration-history.unit.test.ts | 12 +- .../migration-timestamp.format.unit.test.ts | 14 +- .../{go-output-flag.ts => output-formats.ts} | 6 +- ...it.test.ts => output-formats.unit.test.ts} | 9 +- ...-output.encoders.ts => output.encoders.ts} | 42 +- ...t.test.ts => output.encoders.unit.test.ts} | 34 +- apps/cli/src/command-internal/parse-uint.ts | 2 +- .../command-internal/parse-uint.unit.test.ts | 8 +- apps/cli/src/command-internal/path-match.ts | 39 +- .../command-internal/path-match.unit.test.ts | 8 +- apps/cli/src/command-internal/pg-dump.run.ts | 7 +- .../cli/src/command-internal/pgdelta.paths.ts | 2 +- apps/cli/src/command-internal/postgres-url.ts | 6 +- .../postgres-url.unit.test.ts | 8 +- apps/cli/src/command-internal/profile-load.ts | 106 ++-- .../profile-load.unit.test.ts | 159 +++-- .../src/command-internal/profile.unit.test.ts | 6 +- .../command-internal/project-create-core.ts | 45 +- .../project-environment.unit.test.ts | 20 +- apps/cli/src/command-internal/schema-flags.ts | 4 +- .../schema-flags.unit.test.ts | 6 +- apps/cli/src/command-internal/seed-buckets.ts | 7 +- .../command-internal/seed-ops.unit.test.ts | 41 +- .../src/command-internal/seed.unit.test.ts | 53 +- apps/cli/src/command-internal/size-units.ts | 8 +- .../sql-files-glob.unit.test.ts | 470 +++++++-------- apps/cli/src/command-internal/sql-split.ts | 106 ++-- .../command-internal/sql-split.unit.test.ts | 2 +- apps/cli/src/command-internal/stack-config.ts | 13 +- .../command-internal/stack-shadow-cache.ts | 4 +- .../cli/src/command-internal/status-pretty.ts | 2 +- .../status-pretty.unit.test.ts | 6 +- .../cli/src/command-internal/status-values.ts | 4 +- .../storage-bucket-config.unit.test.ts | 6 +- .../storage-content-type.unit.test.ts | 2 +- .../command-internal/storage-credentials.ts | 4 +- .../src/command-internal/storage-gateway.ts | 4 +- .../storage-gateway.unit.test.ts | 2 +- apps/cli/src/command-internal/storage-url.ts | 45 +- .../command-internal/storage-url.unit.test.ts | 32 +- .../src/command-internal/string-slice-flag.ts | 17 +- .../string-slice-flag.unit.test.ts | 14 +- .../{rune-width.ts => string-width.ts} | 4 +- ...unit.test.ts => string-width.unit.test.ts} | 4 +- ....encoders.ts => struct-output.encoders.ts} | 342 ++++++----- ...ts => struct-output.encoders.unit.test.ts} | 561 +++++++++--------- ... struct-output.openapi-drift.unit.test.ts} | 112 ++-- .../{viper-env.ts => supabase-env.ts} | 18 +- .../supabase-env.unit.test.ts | 121 ++++ .../tenant-versions.unit.test.ts | 2 +- .../test-db.integration.test.ts | 8 +- .../timestamp.format.unit.test.ts | 2 +- .../upgrade-notice.unit.test.ts | 36 +- .../vault-decrypt.unit.test.ts | 2 +- .../command-internal/viper-env.unit.test.ts | 121 ---- .../src/commands/backups/list/SIDE_EFFECTS.md | 2 +- .../src/commands/backups/list/list.handler.ts | 66 +-- .../backups/list/list.integration.test.ts | 49 +- .../backups/restore/restore.handler.ts | 15 +- .../restore/restore.integration.test.ts | 14 +- .../src/commands/bootstrap/SIDE_EFFECTS.md | 18 +- .../commands/bootstrap/bootstrap.command.ts | 2 +- .../bootstrap/bootstrap.integration.test.ts | 2 +- .../bootstrap/bootstrap.pgconfig.unit.test.ts | 2 +- .../bootstrap.templates.integration.test.ts | 4 +- .../commands/bootstrap/bootstrap.templates.ts | 2 +- .../branches/branches.format.unit.test.ts | 2 +- .../commands/branches/branches.go-payload.ts | 44 -- .../branches/branches.response-shape.ts | 47 ++ .../commands/branches/create/SIDE_EFFECTS.md | 18 +- .../branches/create/create.handler.ts | 27 +- .../create/create.integration.test.ts | 14 +- .../commands/branches/delete/SIDE_EFFECTS.md | 2 +- .../delete/delete.integration.test.ts | 2 +- .../commands/branches/disable/SIDE_EFFECTS.md | 2 +- .../src/commands/branches/get/SIDE_EFFECTS.md | 4 +- .../src/commands/branches/get/get.handler.ts | 27 +- .../branches/get/get.integration.test.ts | 14 +- .../commands/branches/list/SIDE_EFFECTS.md | 24 +- .../commands/branches/list/list.handler.ts | 29 +- .../branches/list/list.integration.test.ts | 30 +- .../commands/branches/pause/SIDE_EFFECTS.md | 2 +- .../branches/pause/pause.integration.test.ts | 2 +- .../commands/branches/unpause/SIDE_EFFECTS.md | 2 +- .../commands/branches/update/SIDE_EFFECTS.md | 4 +- .../branches/update/update.handler.ts | 27 +- .../update/update.integration.test.ts | 4 +- .../src/commands/completion/SIDE_EFFECTS.md | 77 +-- .../completion/__fixtures__/bash.desc.txt | 5 +- .../completion/__fixtures__/bash.nodesc.txt | 5 +- .../completion/__fixtures__/fish.desc.txt | 1 - .../completion/__fixtures__/fish.nodesc.txt | 1 - .../__fixtures__/powershell.desc.txt | 4 +- .../__fixtures__/powershell.nodesc.txt | 4 +- .../completion/__fixtures__/zsh.desc.txt | 2 +- .../completion/__fixtures__/zsh.nodesc.txt | 2 +- .../completion/bash/bash.integration.test.ts | 33 +- .../commands/completion/completion-scripts.ts | 20 +- .../completion-scripts.unit.test.ts | 11 +- .../completion/fish/fish.integration.test.ts | 33 +- .../powershell/powershell.integration.test.ts | 33 +- .../completion/zsh/zsh.integration.test.ts | 33 +- apps/cli/src/commands/config/config.load.ts | 3 +- .../src/commands/config/diff/SIDE_EFFECTS.md | 5 +- .../src/commands/config/diff/diff.handler.ts | 6 +- .../config/diff/diff.integration.test.ts | 10 +- .../src/commands/config/pull/SIDE_EFFECTS.md | 5 +- .../src/commands/config/pull/pull.handler.ts | 6 +- .../config/pull/pull.integration.test.ts | 8 +- .../src/commands/db/advisors/SIDE_EFFECTS.md | 6 +- .../commands/db/advisors/advisors.errors.ts | 16 +- .../commands/db/advisors/advisors.format.ts | 24 +- .../db/advisors/advisors.format.unit.test.ts | 34 +- .../db/advisors/advisors.integration.test.ts | 37 +- .../commands/db/advisors/advisors.linked.ts | 6 +- apps/cli/src/commands/db/diff/SIDE_EFFECTS.md | 28 +- .../db/diff/diff.explicit.unit.test.ts | 2 +- .../commands/db/diff/diff.integration.test.ts | 29 +- apps/cli/src/commands/db/diff/pgadmin-diff.ts | 6 +- .../db/diff/pgadmin-diff.unit.test.ts | 18 +- apps/cli/src/commands/db/dump/SIDE_EFFECTS.md | 30 +- apps/cli/src/commands/db/dump/dump.handler.ts | 10 +- .../commands/db/dump/dump.integration.test.ts | 14 +- apps/cli/src/commands/db/lint/SIDE_EFFECTS.md | 6 +- apps/cli/src/commands/db/lint/lint.errors.ts | 12 +- apps/cli/src/commands/db/lint/lint.format.ts | 16 +- .../commands/db/lint/lint.format.unit.test.ts | 18 +- apps/cli/src/commands/db/lint/lint.handler.ts | 2 +- .../commands/db/lint/lint.integration.test.ts | 4 +- apps/cli/src/commands/db/pull/SIDE_EFFECTS.md | 22 +- apps/cli/src/commands/db/pull/pull.command.ts | 2 +- .../commands/db/pull/pull.integration.test.ts | 12 +- apps/cli/src/commands/db/push/SIDE_EFFECTS.md | 3 +- apps/cli/src/commands/db/push/push.errors.ts | 2 +- .../commands/db/push/push.integration.test.ts | 51 +- .../cli/src/commands/db/query/SIDE_EFFECTS.md | 13 +- .../src/commands/db/query/query.command.ts | 2 +- .../cli/src/commands/db/query/query.errors.ts | 2 +- .../cli/src/commands/db/query/query.format.ts | 56 +- .../db/query/query.format.unit.test.ts | 34 +- .../db/query/query.integration.test.ts | 63 +- .../commands/db/remote/commit/SIDE_EFFECTS.md | 11 +- .../db/remote/commit/commit.handler.ts | 2 +- .../cli/src/commands/db/reset/SIDE_EFFECTS.md | 42 +- .../src/commands/db/reset/reset.handler.ts | 2 +- .../db/reset/reset.integration.test.ts | 71 ++- .../declarative/declarative.gate.unit.test.ts | 2 +- .../generate/generate.integration.test.ts | 47 +- .../schema/declarative/sync/SIDE_EFFECTS.md | 2 +- .../declarative/sync/sync.integration.test.ts | 2 +- .../shared/pgdelta.seam.integration.test.ts | 5 +- .../db/shared/shadow-source.unit.test.ts | 34 +- .../cli/src/commands/db/start/SIDE_EFFECTS.md | 10 +- .../db/start/start.integration.test.ts | 251 ++++---- apps/cli/src/commands/db/test/SIDE_EFFECTS.md | 22 +- .../commands/db/test/test.integration.test.ts | 2 +- apps/cli/src/commands/domains/SIDE_EFFECTS.md | 20 +- .../activate/activate.integration.test.ts | 12 +- .../domains/create/create.integration.test.ts | 16 +- .../domains/delete/delete.integration.test.ts | 12 +- apps/cli/src/commands/domains/domains.emit.ts | 92 +-- .../domains/get/get.integration.test.ts | 61 +- .../reverify/reverify.integration.test.ts | 12 +- .../src/commands/encryption/SIDE_EFFECTS.md | 16 +- .../get-root-key/get-root-key.handler.ts | 2 +- .../compute.output.integration.test.ts | 6 +- .../experimental/compute/compute.output.ts | 24 +- .../compute/delete/delete.integration.test.ts | 6 +- .../compute/list/list.integration.test.ts | 14 +- .../compute/logs/logs.integration.test.ts | 10 +- .../compute/new/new.integration.test.ts | 8 +- .../compute/push/push.integration.test.ts | 4 +- .../compute/status/status.integration.test.ts | 4 +- .../src/commands/feedback/add/add.handler.ts | 12 +- .../feedback/add/add.integration.test.ts | 16 +- .../feedback/delete/delete.handler.ts | 12 +- .../delete/delete.integration.test.ts | 10 +- .../src/commands/feedback/feedback-output.ts | 4 +- .../delete/delete.integration.test.ts | 65 +- .../commands/functions/deploy/SIDE_EFFECTS.md | 38 +- .../functions/deploy/deploy.e2e.test.ts | 2 +- .../functions/deploy/deploy.handler.ts | 4 +- .../deploy/deploy.integration.test.ts | 166 +++--- .../functions/download/SIDE_EFFECTS.md | 26 +- .../functions/download/download.handler.ts | 4 +- .../download/download.integration.test.ts | 253 ++++---- .../commands/functions/list/SIDE_EFFECTS.md | 4 +- .../commands/functions/list/list.encoders.ts | 97 +-- .../functions/list/list.encoders.unit.test.ts | 36 +- .../commands/functions/list/list.handler.ts | 26 +- .../functions/list/list.integration.test.ts | 24 +- .../src/commands/functions/new/new.handler.ts | 3 +- .../functions/new/new.integration.test.ts | 2 +- .../commands/functions/serve/SIDE_EFFECTS.md | 2 +- .../commands/functions/serve/serve.handler.ts | 5 +- .../functions/serve/serve.integration.test.ts | 5 +- .../functions/serve/serve.stack.e2e.test.ts | 6 +- .../commands/gen/bearer-jwt/SIDE_EFFECTS.md | 9 +- .../gen/bearer-jwt/bearer-jwt.claims.ts | 58 +- .../bearer-jwt/bearer-jwt.claims.unit.test.ts | 48 +- .../gen/bearer-jwt/bearer-jwt.errors.ts | 6 +- .../gen/bearer-jwt/bearer-jwt.flags.ts | 9 +- .../bearer-jwt/bearer-jwt.flags.unit.test.ts | 24 +- .../gen/bearer-jwt/bearer-jwt.handler.ts | 2 +- .../bearer-jwt/bearer-jwt.integration.test.ts | 297 +++++----- .../gen/bearer-jwt/bearer-jwt.signing-key.ts | 12 +- .../commands/gen/gen.signing-keys-config.ts | 36 +- .../gen/signing-key/signing-key.handler.ts | 2 +- .../signing-key.integration.test.ts | 2 +- .../src/commands/gen/types/SIDE_EFFECTS.md | 16 +- .../src/commands/gen/types/types.command.ts | 4 +- .../src/commands/gen/types/types.handler.ts | 17 +- .../gen/types/types.integration.test.ts | 24 +- .../src/commands/gen/types/types.languages.ts | 2 +- .../src/commands/gen/types/types.unit.test.ts | 4 +- .../commands/init/init.integration.test.ts | 8 +- .../src/commands/inspect/db/SIDE_EFFECTS.md | 20 +- .../inspect/db/inspect-query.unit.test.ts | 4 +- .../inspect/db/inspect-schemas.unit.test.ts | 2 +- .../commands/inspect/report/SIDE_EFFECTS.md | 31 +- .../commands/inspect/report/report.config.ts | 4 +- .../inspect/report/report.config.unit.test.ts | 16 +- .../inspect/report/report.integration.test.ts | 2 +- .../report/report.queries.unit.test.ts | 2 +- apps/cli/src/commands/link/SIDE_EFFECTS.md | 113 ++-- apps/cli/src/commands/link/link.e2e.test.ts | 2 +- .../commands/link/link.integration.test.ts | 69 ++- apps/cli/src/commands/login/SIDE_EFFECTS.md | 2 +- apps/cli/src/commands/login/login.handler.ts | 2 +- apps/cli/src/commands/logout/SIDE_EFFECTS.md | 2 +- .../logout/logout.integration.test.ts | 2 +- .../commands/migration/down/SIDE_EFFECTS.md | 8 +- .../migration/down/down.integration.test.ts | 2 +- .../commands/migration/fetch/SIDE_EFFECTS.md | 7 +- .../commands/migration/fetch/fetch.errors.ts | 2 +- .../migration/fetch/fetch.integration.test.ts | 37 +- .../commands/migration/list/SIDE_EFFECTS.md | 8 +- .../migration/list/list.format.unit.test.ts | 4 +- .../migration/migration.integration.test.ts | 2 +- .../commands/migration/new/SIDE_EFFECTS.md | 2 +- .../migration/new/new.integration.test.ts | 77 ++- .../commands/migration/repair/SIDE_EFFECTS.md | 17 +- .../migration/repair/repair.errors.ts | 2 +- .../repair/repair.integration.test.ts | 29 +- .../commands/migration/squash/SIDE_EFFECTS.md | 26 +- .../migration/squash/squash.diff.unit.test.ts | 14 +- .../migration/squash/squash.e2e.test.ts | 2 +- .../migration/squash/squash.handler.ts | 6 +- .../squash/squash.integration.test.ts | 2 +- .../src/commands/migration/up/SIDE_EFFECTS.md | 5 +- .../commands/network-bans/get/get.command.ts | 2 +- .../commands/network-bans/get/get.handler.ts | 17 +- .../network-bans/get/get.integration.test.ts | 22 +- ...bans.experimental-gate.integration.test.ts | 6 +- .../network-bans/remove/remove.command.ts | 4 +- .../remove/remove.integration.test.ts | 14 +- .../network-restrictions/get/SIDE_EFFECTS.md | 2 +- .../network-restrictions/get/get.command.ts | 2 +- .../network-restrictions/get/get.handler.ts | 18 +- .../get/get.integration.test.ts | 32 +- .../network-restrictions.cidr.unit.test.ts | 6 +- ...ions.experimental-gate.integration.test.ts | 6 +- .../network-restrictions.format.ts | 6 +- .../update/update.command.ts | 4 +- .../update/update.handler.ts | 18 +- .../update/update.integration.test.ts | 64 +- .../notebooks/notebooks.integration.test.ts | 10 +- .../commands/notebooks/notebooks.output.ts | 22 +- .../notebooks/pull/pull.integration.test.ts | 2 +- .../notebooks/push/push.integration.test.ts | 2 +- .../src/commands/orgs/create/SIDE_EFFECTS.md | 9 +- .../commands/orgs/create/create.handler.ts | 31 +- .../orgs/create/create.integration.test.ts | 18 +- .../src/commands/orgs/list/SIDE_EFFECTS.md | 8 +- .../src/commands/orgs/list/list.handler.ts | 31 +- .../orgs/list/list.integration.test.ts | 22 +- apps/cli/src/commands/orgs/orgs.command.ts | 2 +- apps/cli/src/commands/orgs/orgs.go-payload.ts | 23 - .../src/commands/orgs/orgs.response-shape.ts | 23 + .../postgres-config/delete/SIDE_EFFECTS.md | 4 +- .../postgres-config/delete/delete.command.ts | 4 +- .../delete/delete.command.unit.test.ts | 8 +- .../postgres-config/delete/delete.handler.ts | 4 +- .../postgres-config/get/SIDE_EFFECTS.md | 2 +- .../postgres-config/get/get.command.ts | 2 +- ...nfig.experimental-gate.integration.test.ts | 6 +- .../postgres-config.integration.test.ts | 48 +- .../postgres-config/postgres-config.shared.ts | 40 +- .../postgres-config.shared.unit.test.ts | 4 +- .../postgres-config/update/SIDE_EFFECTS.md | 4 +- .../postgres-config/update/update.command.ts | 4 +- .../update/update.command.unit.test.ts | 10 +- .../postgres-config/update/update.handler.ts | 4 +- .../cli/src/commands/projects/SIDE_EFFECTS.md | 12 +- .../projects/api-keys/SIDE_EFFECTS.md | 4 +- .../projects/api-keys/api-keys.command.ts | 2 +- .../projects/api-keys/api-keys.handler.ts | 62 +- .../api-keys/api-keys.integration.test.ts | 20 +- .../commands/projects/create/SIDE_EFFECTS.md | 29 +- .../create/create.integration.test.ts | 18 +- .../commands/projects/delete/SIDE_EFFECTS.md | 2 +- .../delete/delete.integration.test.ts | 4 +- .../commands/projects/list/SIDE_EFFECTS.md | 16 +- .../commands/projects/list/list.handler.ts | 74 +-- .../projects/list/list.integration.test.ts | 16 +- apps/cli/src/commands/pull/SIDE_EFFECTS.md | 4 +- apps/cli/src/commands/pull/pull.handler.ts | 6 +- .../commands/pull/pull.integration.test.ts | 6 +- apps/cli/src/commands/pull/pull.steps.ts | 4 +- .../src/commands/secrets/list/SIDE_EFFECTS.md | 2 +- .../src/commands/secrets/list/list.handler.ts | 48 +- .../secrets/list/list.integration.test.ts | 20 +- .../secrets/secrets.format.unit.test.ts | 2 +- .../src/commands/secrets/set/set.handler.ts | 11 +- .../secrets/set/set.integration.test.ts | 116 ++-- .../secrets/unset/unset.integration.test.ts | 12 +- .../src/commands/seed/buckets/SIDE_EFFECTS.md | 7 +- .../commands/seed/buckets/buckets.e2e.test.ts | 2 +- .../seed/buckets/buckets.flags.unit.test.ts | 2 +- .../seed/buckets/buckets.integration.test.ts | 169 +++--- apps/cli/src/commands/seed/seed.command.ts | 2 +- .../cli/src/commands/services/SIDE_EFFECTS.md | 14 +- .../src/commands/services/services.handler.ts | 48 +- .../services/services.integration.test.ts | 36 +- .../snippets/download/SIDE_EFFECTS.md | 2 +- .../snippets/download/download.handler.ts | 8 +- .../download/download.integration.test.ts | 12 +- .../download/download.uuid.unit.test.ts | 4 +- .../commands/snippets/list/SIDE_EFFECTS.md | 4 +- .../commands/snippets/list/list.handler.ts | 82 +-- .../snippets/list/list.integration.test.ts | 36 +- .../commands/snippets/snippets.e2e.test.ts | 2 +- .../snippets/snippets.format.unit.test.ts | 2 +- .../ssl-enforcement/get/get.command.ts | 2 +- .../ssl-enforcement/get/get.handler.ts | 29 +- .../get/get.integration.test.ts | 22 +- .../ssl-enforcement/ssl-enforcement.errors.ts | 2 +- ...ment.experimental-gate.integration.test.ts | 2 +- .../ssl-enforcement.go-payload.ts | 11 - .../ssl-enforcement.response-shape.ts | 15 + .../ssl-enforcement/update/update.command.ts | 2 +- .../ssl-enforcement/update/update.handler.ts | 27 +- .../update/update.integration.test.ts | 22 +- apps/cli/src/commands/sso/add/SIDE_EFFECTS.md | 48 +- apps/cli/src/commands/sso/add/add.command.ts | 2 +- .../commands/sso/add/add.command.unit.test.ts | 8 +- apps/cli/src/commands/sso/add/add.handler.ts | 101 ++-- .../commands/sso/add/add.integration.test.ts | 111 ++-- .../cli/src/commands/sso/info/SIDE_EFFECTS.md | 4 +- .../cli/src/commands/sso/info/info.handler.ts | 18 +- .../sso/info/info.integration.test.ts | 20 +- .../cli/src/commands/sso/list/SIDE_EFFECTS.md | 4 +- .../cli/src/commands/sso/list/list.handler.ts | 29 +- .../sso/list/list.integration.test.ts | 36 +- .../src/commands/sso/remove/SIDE_EFFECTS.md | 4 +- .../src/commands/sso/remove/remove.handler.ts | 27 +- .../sso/remove/remove.integration.test.ts | 20 +- .../cli/src/commands/sso/show/SIDE_EFFECTS.md | 4 +- .../cli/src/commands/sso/show/show.handler.ts | 29 +- .../sso/show/show.integration.test.ts | 28 +- apps/cli/src/commands/sso/sso.e2e.test.ts | 8 +- apps/cli/src/commands/sso/sso.errors.ts | 14 +- apps/cli/src/commands/sso/sso.format.ts | 4 +- .../src/commands/sso/sso.format.unit.test.ts | 10 +- apps/cli/src/commands/sso/sso.go-payload.ts | 68 --- .../src/commands/sso/sso.response-shape.ts | 68 +++ apps/cli/src/commands/sso/sso.saml.ts | 4 +- ...sso.string-slice-flags.integration.test.ts | 6 +- .../src/commands/sso/update/SIDE_EFFECTS.md | 48 +- .../src/commands/sso/update/update.command.ts | 2 +- .../sso/update/update.command.unit.test.ts | 12 +- .../src/commands/sso/update/update.handler.ts | 99 ++-- .../sso/update/update.integration.test.ts | 248 ++++---- apps/cli/src/commands/start/SIDE_EFFECTS.md | 71 +-- .../start/lib/env-or-default.unit.test.ts | 2 +- .../src/commands/start/lib/template-render.ts | 14 +- .../start/lib/template-render.unit.test.ts | 26 +- .../edge-runtime.service.integration.test.ts | 6 +- .../commands/start/services/gotrue.service.ts | 24 +- .../services/gotrue.service.unit.test.ts | 8 +- .../services/imgproxy.service.unit.test.ts | 2 +- .../start/services/kong.service.unit.test.ts | 80 ++- .../services/logflare.service.unit.test.ts | 90 ++- .../services/mailpit.service.unit.test.ts | 10 +- .../services/postgrest.service.unit.test.ts | 4 +- .../services/storage.service.unit.test.ts | 6 +- .../services/studio.service.unit.test.ts | 66 +-- .../services/supavisor.service.unit.test.ts | 14 +- .../services/vector.service.unit.test.ts | 14 +- apps/cli/src/commands/start/start.command.ts | 2 +- .../commands/start/start.command.unit.test.ts | 8 +- apps/cli/src/commands/start/start.exclude.ts | 9 +- .../commands/start/start.exclude.unit.test.ts | 8 +- .../commands/start/start.format.unit.test.ts | 12 +- apps/cli/src/commands/start/start.handler.ts | 24 +- .../commands/start/start.integration.test.ts | 508 ++++++++-------- ...art.string-slice-flags.integration.test.ts | 12 +- .../start/templates/custom_nginx.template.ts | 2 +- apps/cli/src/commands/status/SIDE_EFFECTS.md | 74 ++- .../cli/src/commands/status/status.command.ts | 4 +- .../status/status.command.unit.test.ts | 12 +- .../cli/src/commands/status/status.handler.ts | 35 +- .../status/status.integration.test.ts | 164 +++-- ...tus.string-slice-flags.integration.test.ts | 6 +- apps/cli/src/commands/stop/SIDE_EFFECTS.md | 25 +- .../commands/stop/stop.integration.test.ts | 12 +- .../src/commands/storage/cp/SIDE_EFFECTS.md | 7 +- .../storage/cp/cp.command.integration.test.ts | 36 +- .../cli/src/commands/storage/cp/cp.command.ts | 4 +- .../cli/src/commands/storage/cp/cp.handler.ts | 8 +- .../storage/cp/cp.integration.test.ts | 4 +- .../src/commands/storage/ls/SIDE_EFFECTS.md | 2 +- .../storage/ls/ls.integration.test.ts | 8 +- .../src/commands/storage/mv/SIDE_EFFECTS.md | 2 +- .../storage/mv/mv.integration.test.ts | 6 +- .../src/commands/storage/rm/SIDE_EFFECTS.md | 2 +- .../storage/rm/rm.integration.test.ts | 67 ++- .../src/commands/storage/storage.errors.ts | 9 +- ...rage.experimental-gate.integration.test.ts | 2 +- .../storage/storage.flags.unit.test.ts | 2 +- .../cli/src/commands/storage/storage.frame.ts | 15 +- .../src/commands/storage/storage.iterate.ts | 4 +- .../telemetry/telemetry.integration.test.ts | 6 +- apps/cli/src/commands/test/db/SIDE_EFFECTS.md | 31 +- .../cli/src/commands/test/new/SIDE_EFFECTS.md | 6 +- .../commands/test/new/new.integration.test.ts | 2 +- apps/cli/src/commands/unlink/SIDE_EFFECTS.md | 7 +- .../activate/activate.command.ts | 2 +- .../activate/activate.handler.ts | 30 +- .../check-availability.command.ts | 2 +- .../check-availability.handler.ts | 30 +- .../delete/delete.command.ts | 2 +- .../delete/delete.handler.ts | 7 +- .../vanity-subdomains/get/get.command.ts | 2 +- .../vanity-subdomains/get/get.handler.ts | 38 +- ...ains.experimental-gate.integration.test.ts | 2 +- .../vanity-subdomains.integration.test.ts | 42 +- .../cli/src/commands/whoami/whoami.handler.ts | 6 +- .../whoami/whoami.integration.test.ts | 16 +- apps/cli/src/config/command-settings.layer.ts | 4 +- .../command-settings.layer.unit.test.ts | 76 ++- apps/cli/src/config/project-ref.errors.ts | 2 +- .../src/config/project-ref.layer.unit.test.ts | 6 +- apps/cli/src/config/project-ref.service.ts | 2 +- apps/cli/src/docs/docs-spec.ts | 2 +- apps/cli/src/docs/docs-spec.unit.test.ts | 2 +- .../cli/src/output/glamour-table.unit.test.ts | 4 +- ...et-progress-text-output.layer.unit.test.ts | 2 +- apps/cli/src/shared/auth/jwks.ts | 2 +- apps/cli/src/shared/auth/jwks.unit.test.ts | 4 +- apps/cli/src/shared/auth/keyring-token.ts | 6 +- apps/cli/src/shared/cli/agent-output.ts | 20 +- .../src/shared/cli/agent-output.unit.test.ts | 2 +- .../shared/cli/code-structure.unit.test.ts | 53 ++ .../{cobra-flag-groups.ts => flag-groups.ts} | 73 ++- ....unit.test.ts => flag-groups.unit.test.ts} | 144 ++--- .../src/shared/cli/invalid-value-message.ts | 8 +- apps/cli/src/shared/cli/run.e2e.test.ts | 2 +- apps/cli/src/shared/cli/run.ts | 26 +- .../shared/cli/subcommand-flag-suggestions.ts | 2 +- .../subcommand-flag-suggestions.unit.test.ts | 8 +- apps/cli/src/shared/functions/deploy.ts | 44 +- apps/cli/src/shared/functions/download.ts | 33 +- .../src/shared/functions/functions-config.ts | 52 +- .../src/shared/functions/functions-docker.ts | 2 +- apps/cli/src/shared/functions/serve.ts | 110 ++-- .../project-init.modes.integration.test.ts | 6 +- .../init/project-init.templates.unit.test.ts | 52 +- .../{go-templates => templates}/config.toml | 0 .../{go-templates => templates}/gitignore | 0 .../{go-templates => templates}/idea-deno.xml | 0 .../vscode-extensions.json.golden | 0 .../vscode-settings.json.golden | 0 .../output/normalize-error.unit.test.ts | 30 +- .../shared/output/output.layer.unit.test.ts | 2 +- .../telemetry/analytics.layer.unit.test.ts | 2 +- apps/cli/src/telemetry/command-telemetry.ts | 26 +- .../telemetry/command-telemetry.unit.test.ts | 159 +++-- .../linked-project-cache.integration.test.ts | 141 +++-- .../src/telemetry/telemetry-state.layer.ts | 24 +- .../telemetry-state.layer.unit.test.ts | 63 +- apps/cli/tests/helpers/command-mocks.ts | 12 +- apps/cli/tests/helpers/compute.ts | 4 +- apps/cli/tests/helpers/notebooks.ts | 6 +- apps/cli/tests/helpers/storage.ts | 6 +- ...ull-write-strategy-and-scope-resolution.md | 2 +- docs/cli/dev-alpha-command-structure.md | 4 +- packages/config/AGENTS.md | 10 +- packages/config/README.md | 19 +- packages/config/docs/cli-config-loading.md | 12 +- packages/config/src/config-document.ts | 10 +- packages/config/src/effect.ts | 4 +- packages/config/src/internal.ts | 2 +- packages/config/src/io.ts | 22 +- packages/config/src/io.unit.test.ts | 82 ++- packages/config/src/lib/env.ts | 43 +- packages/config/src/lib/resolve.ts | 34 +- .../project-config.unit.test.ts | 36 +- .../src/project-config/registry-auth.ts | 48 +- packages/config/src/project.ts | 10 +- packages/config/src/project.unit.test.ts | 14 +- packages/config/src/storage.unit.test.ts | 4 +- .../preparation/artifacts.integration.test.ts | 4 +- 589 files changed, 7843 insertions(+), 8379 deletions(-) rename apps/cli/src/command-internal/{pflag-reconcile.ts => argv-flag-reconcile.ts} (79%) rename apps/cli/src/command-internal/{pflag-reconcile.unit.test.ts => argv-flag-reconcile.unit.test.ts} (62%) rename apps/cli/src/command-internal/{go-quote.ts => byte-quote.ts} (54%) create mode 100644 apps/cli/src/command-internal/cli-config-load.ts rename apps/cli/src/command-internal/{go-duration.ts => duration.ts} (91%) create mode 100644 apps/cli/src/command-internal/duration.unit.test.ts rename apps/cli/src/command-internal/{go-float.ts => format-float.ts} (61%) create mode 100644 apps/cli/src/command-internal/format-float.unit.test.ts rename apps/cli/src/command-internal/{functions-go-config.ts => functions-local-config.ts} (94%) delete mode 100644 apps/cli/src/command-internal/go-duration.unit.test.ts delete mode 100644 apps/cli/src/command-internal/go-float.unit.test.ts delete mode 100644 apps/cli/src/command-internal/go-json.unit.test.ts rename apps/cli/src/command-internal/{go-json.ts => html-safe-json.ts} (65%) create mode 100644 apps/cli/src/command-internal/html-safe-json.unit.test.ts rename apps/cli/src/command-internal/{go-jwt.ts => local-jwt.ts} (83%) rename apps/cli/src/command-internal/{go-jwt.unit.test.ts => local-jwt.unit.test.ts} (76%) rename apps/cli/src/command-internal/{go-output-flag.ts => output-formats.ts} (89%) rename apps/cli/src/command-internal/{go-output-flag.unit.test.ts => output-formats.unit.test.ts} (84%) rename apps/cli/src/command-internal/{go-output.encoders.ts => output.encoders.ts} (79%) rename apps/cli/src/command-internal/{go-output.encoders.unit.test.ts => output.encoders.unit.test.ts} (81%) rename apps/cli/src/command-internal/{rune-width.ts => string-width.ts} (98%) rename apps/cli/src/command-internal/{rune-width.unit.test.ts => string-width.unit.test.ts} (88%) rename apps/cli/src/command-internal/{go-struct-output.encoders.ts => struct-output.encoders.ts} (74%) rename apps/cli/src/command-internal/{go-struct-output.encoders.unit.test.ts => struct-output.encoders.unit.test.ts} (50%) rename apps/cli/src/command-internal/{go-struct-output.openapi-drift.unit.test.ts => struct-output.openapi-drift.unit.test.ts} (77%) rename apps/cli/src/command-internal/{viper-env.ts => supabase-env.ts} (74%) create mode 100644 apps/cli/src/command-internal/supabase-env.unit.test.ts delete mode 100644 apps/cli/src/command-internal/viper-env.unit.test.ts delete mode 100644 apps/cli/src/commands/branches/branches.go-payload.ts create mode 100644 apps/cli/src/commands/branches/branches.response-shape.ts delete mode 100644 apps/cli/src/commands/orgs/orgs.go-payload.ts create mode 100644 apps/cli/src/commands/orgs/orgs.response-shape.ts delete mode 100644 apps/cli/src/commands/ssl-enforcement/ssl-enforcement.go-payload.ts create mode 100644 apps/cli/src/commands/ssl-enforcement/ssl-enforcement.response-shape.ts delete mode 100644 apps/cli/src/commands/sso/sso.go-payload.ts create mode 100644 apps/cli/src/commands/sso/sso.response-shape.ts rename apps/cli/src/shared/cli/{cobra-flag-groups.ts => flag-groups.ts} (82%) rename apps/cli/src/shared/cli/{cobra-flag-groups.unit.test.ts => flag-groups.unit.test.ts} (83%) rename apps/cli/src/shared/init/testdata/{go-templates => templates}/config.toml (100%) rename apps/cli/src/shared/init/testdata/{go-templates => templates}/gitignore (100%) rename apps/cli/src/shared/init/testdata/{go-templates => templates}/idea-deno.xml (100%) rename apps/cli/src/shared/init/testdata/{go-templates => templates}/vscode-extensions.json.golden (100%) rename apps/cli/src/shared/init/testdata/{go-templates => templates}/vscode-settings.json.golden (100%) diff --git a/AGENTS.md b/AGENTS.md index eb5e0c2e11..dd495e39ac 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -111,7 +111,7 @@ page, or upstream issue. If the rationale needs more than three lines, move it t ### Never write - Code narration, provenance/history, evidence trails, ticket IDs as provenance, or meta-commentary on code shape. A ticket ID belongs only in a `TODO(CLI-1234):` or when no ADR exists and the ticket is the only home for a decision. -- Restatements of docs, section banners, or Go-parity framing. Link to maintained docs instead. +- Restatements of docs, or section banners. Link to maintained docs instead. - ALL-CAPS emphasis or words such as “deliberately”, “crucially”, and “exactly”. Tests should carry intent in their names; comments only explain non-obvious fixture setup. Keep diff --git a/apps/cli-e2e/src/server/pg-mock.ts b/apps/cli-e2e/src/server/pg-mock.ts index b419eed1b1..f187f3cfed 100644 --- a/apps/cli-e2e/src/server/pg-mock.ts +++ b/apps/cli-e2e/src/server/pg-mock.ts @@ -367,7 +367,7 @@ function processMessages(socket: Bun.Socket, getState: () => PgMockS socket.data.buf = buf.slice(totalLen); if (msgType === 0x51) { - // 'Q' — simple query (used by pgconn.CopyTo and simple-protocol callers) + // 'Q' — simple query (used by COPY and simple-protocol callers) const query = payload.slice(0, payload.length - 1).toString("utf8"); const isProbe = /^\s*select\s+1\s*;?\s*$/i.test(query); const isCopy = /^\s*COPY\s+/i.test(query); diff --git a/apps/cli-e2e/src/server/request-matcher.ts b/apps/cli-e2e/src/server/request-matcher.ts index b77e02e0fd..7f75ca9fd5 100644 --- a/apps/cli-e2e/src/server/request-matcher.ts +++ b/apps/cli-e2e/src/server/request-matcher.ts @@ -86,7 +86,7 @@ export function resetCounters(counters: SequenceCounters): void { /** Sorts array bodies canonically so comparison is order-insensitive where * element order isn't semantically meaningful (e.g. bulk-create secrets) — a - * proxied command building a request body from a Go map can produce a + * proxied command building a request body from a map can produce a * different key order per platform. Nested object fields are still compared * exactly; only the top-level array order is normalized. */ export function sortBody(body: unknown): unknown { diff --git a/apps/cli-e2e/src/tests/auth.e2e.test.ts b/apps/cli-e2e/src/tests/auth.e2e.test.ts index d2ea7ae761..6172a265e4 100644 --- a/apps/cli-e2e/src/tests/auth.e2e.test.ts +++ b/apps/cli-e2e/src/tests/auth.e2e.test.ts @@ -5,5 +5,5 @@ import { describe, it } from "vitest"; // keychain dialog that blocks the subprocess indefinitely. describe("auth", () => { - it.todo("login and logout tests blocked on Go CLI keyring opt-out (see file comment)"); + it.todo("login and logout tests blocked on keyring opt-out (see file comment)"); }); diff --git a/apps/cli/docs/binary-distribution.md b/apps/cli/docs/binary-distribution.md index cf111b3bb5..1c9705a51d 100644 --- a/apps/cli/docs/binary-distribution.md +++ b/apps/cli/docs/binary-distribution.md @@ -59,17 +59,7 @@ This: 3. Bundles the binary into the platform archives (`.tar.gz` / `.zip`) 4. Includes the binary in the Linux package manager packages (deb/rpm/apk) -## History - -The CLI was originally a two-binary distribution during its gradual port from a Go CLI to -TypeScript: a `supabase` Bun single-file executable alongside a `supabase-go` binary that the TS -CLI proxied to for not-yet-ported commands. The port completed (CLI-1970), the last residual -delegation surface was removed (CLI-2432), and the Go source tree (`apps/cli-go/`) was deleted in -the same effort — the CLI now ships as a single compiled binary per platform. See -[ADR 0030](../../../docs/adr/0030-go-cli-removal.md) for the full removal record. - ## See Also - [ADR 0011](../../../docs/adr/0011-cli-release-and-distribution-strategy.md) — the release & distribution strategy decision (binary packaging choice, per-channel publish mechanisms, CI pipeline design, open blockers). -- [ADR 0030](../../../docs/adr/0030-go-cli-removal.md) — the Go CLI removal decision record. - [`release-process.md`](./release-process.md) — operational playbook for local, PoC, and production releases. diff --git a/apps/cli/docs/profile.md b/apps/cli/docs/profile.md index 59949999a6..b5b6065a67 100644 --- a/apps/cli/docs/profile.md +++ b/apps/cli/docs/profile.md @@ -4,8 +4,8 @@ This document captures the intended design for advanced non-default backend rout CLI. The file is intentionally named `profile.md` to preserve continuity with the earlier discussion -and the old Go CLI concept. The design in this document does **not** recommend carrying the old -`profile` abstraction forward. The preferred term in the new CLI is **platform**. +and the previous `profile` concept. The design in this document does **not** recommend carrying the +previous `profile` abstraction forward. The preferred term in the new CLI is **platform**. ## Overview @@ -21,9 +21,9 @@ A much smaller group of users needs to point the CLI somewhere else: The goal is to support those cases cleanly without turning non-default backend routing into a first-class everyday feature. -## Why Not Go-Style Profiles +## Why Not Profiles -The old Go CLI `profile` abstraction became too broad. +The previous `profile` abstraction became too broad. It started as a way to switch API endpoints, but it ended up bundling multiple unrelated concerns: @@ -195,7 +195,7 @@ This design does not propose: - a built-in first-party staging alias in the public UX - customer-specific platform definitions baked into the CLI - plugin or product behavior encoded directly inside platform config -- backward compatibility with Go CLI `profile` names or storage behavior +- backward compatibility with previous `profile` names or storage behavior ## Open Follow-up Work diff --git a/apps/cli/docs/supabase/login.md b/apps/cli/docs/supabase/login.md index 0387dbd3f6..5dbc7df448 100644 --- a/apps/cli/docs/supabase/login.md +++ b/apps/cli/docs/supabase/login.md @@ -2,7 +2,7 @@ Connect the Supabase CLI to your Supabase account by logging in with your [personal access token](https://supabase.com/dashboard/account/tokens). -Your access token is stored securely in [native credentials storage](https://github.com/zalando/go-keyring#dependencies). If native credentials storage is unavailable, it will be written to a plain text file at `/access-token`. +Your access token is stored securely in native credentials storage (the operating system keyring). If native credentials storage is unavailable, it will be written to a plain text file at `/access-token`. > If this behavior is not desired, such as in a CI environment, you may skip login by specifying the `SUPABASE_ACCESS_TOKEN` environment variable in other commands. diff --git a/apps/cli/src/SIDE_EFFECTS_TEMPLATE.md b/apps/cli/src/SIDE_EFFECTS_TEMPLATE.md index cfb21c9fa4..b2b4bb1a51 100644 --- a/apps/cli/src/SIDE_EFFECTS_TEMPLATE.md +++ b/apps/cli/src/SIDE_EFFECTS_TEMPLATE.md @@ -120,5 +120,5 @@ diff --git a/apps/cli/src/auth/command-credentials.layer.ts b/apps/cli/src/auth/command-credentials.layer.ts index 8701566a7c..7edba3e7b7 100644 --- a/apps/cli/src/auth/command-credentials.layer.ts +++ b/apps/cli/src/auth/command-credentials.layer.ts @@ -52,9 +52,9 @@ const tryKeyringRead = ( if (value && value.length > 0) return Option.some(normalizeKeyringToken(value)); if (platform === "win32") { - const goWindowsValue = readGoWindowsTarget(module, account); - if (goWindowsValue && goWindowsValue.length > 0) { - return Option.some(normalizeKeyringToken(goWindowsValue)); + const windowsValue = readWindowsTarget(module, account); + if (windowsValue && windowsValue.length > 0) { + return Option.some(normalizeKeyringToken(windowsValue)); } } @@ -72,7 +72,7 @@ const tryKeyringWrite = ( Effect.try({ try: () => { if (platform === "win32") { - return writeGoWindowsTarget(module, account, token); + return writeWindowsTarget(module, account, token); } const entry = new module.Entry(KEYRING_SERVICE, account); @@ -99,7 +99,7 @@ const tryKeyringDelete = ( } if (platform === "win32" && probeWindowsTarget(module, account) !== "absent") { - deleted = deleteGoWindowsTarget(module, account) || deleted; + deleted = deleteWindowsTarget(module, account) || deleted; } return deleted; @@ -115,15 +115,15 @@ function readEntryPassword(entry: KeyringEntry): string | null { } } -function goWindowsCredentialTarget(account: string): string { +function windowsCredentialTarget(account: string): string { return `${KEYRING_SERVICE}:${account}`; } -function readGoWindowsTarget(module: KeyringModule, account: string): string | null { +function readWindowsTarget(module: KeyringModule, account: string): string | null { try { - const credentials = module.findCredentials(KEYRING_SERVICE, goWindowsCredentialTarget(account)); + const credentials = module.findCredentials(KEYRING_SERVICE, windowsCredentialTarget(account)); const credential = credentials.find((item) => item.account === account); - return credential ? normalizeGoWindowsPassword(credential.password) : null; + return credential ? normalizeWindowsPassword(credential.password) : null; } catch { return null; } @@ -135,7 +135,7 @@ type WindowsTargetProbe = "present" | "absent" | "unknown"; function probeWindowsTarget(module: KeyringModule, account: string): WindowsTargetProbe { try { - const credentials = module.findCredentials(KEYRING_SERVICE, goWindowsCredentialTarget(account)); + const credentials = module.findCredentials(KEYRING_SERVICE, windowsCredentialTarget(account)); // An empty password is an orphaned placeholder, not a real credential. const credential = credentials.find( (item) => item.account === account && item.password.length > 0, @@ -157,7 +157,7 @@ const deleteProbedWindowsTarget = ( Effect.gen(function* () { const result = yield* Effect.try(() => module.Entry.withTarget( - goWindowsCredentialTarget(account), + windowsCredentialTarget(account), KEYRING_SERVICE, account, ).deleteCredential(), @@ -167,11 +167,11 @@ const deleteProbedWindowsTarget = ( return yield* Effect.fail(onFailure(cause)); }); -function normalizeGoWindowsPassword(value: string): string { +function normalizeWindowsPassword(value: string): string { const direct = normalizeKeyringToken(value); if (ACCESS_TOKEN_PATTERN.test(direct)) return direct; - // Go writes Windows CredentialBlob values as raw UTF-8 bytes, which the keyring search API + // Legacy Windows CredentialBlob values are raw UTF-8 bytes, which the keyring search API // can surface packed into UTF-16 code units; unpack each back into the original byte sequence. const bytes: number[] = []; for (let index = 0; index < value.length; index += 1) { @@ -183,10 +183,10 @@ function normalizeGoWindowsPassword(value: string): string { return Buffer.from(bytes).toString("utf8"); } -function writeGoWindowsTarget(module: KeyringModule, account: string, token: string): boolean { +function writeWindowsTarget(module: KeyringModule, account: string, token: string): boolean { try { const entry = module.Entry.withTarget( - goWindowsCredentialTarget(account), + windowsCredentialTarget(account), KEYRING_SERVICE, account, ); @@ -197,10 +197,10 @@ function writeGoWindowsTarget(module: KeyringModule, account: string, token: str } } -function deleteGoWindowsTarget(module: KeyringModule, account: string): boolean { +function deleteWindowsTarget(module: KeyringModule, account: string): boolean { try { const entry = module.Entry.withTarget( - goWindowsCredentialTarget(account), + windowsCredentialTarget(account), KEYRING_SERVICE, account, ); @@ -298,12 +298,9 @@ const deleteAllKeyringEntries = ( Effect.sync(() => { if (platform === "win32") { try { - const entries = module.findCredentials( - KEYRING_SERVICE, - `${goWindowsCredentialTarget("")}*`, - ); + const entries = module.findCredentials(KEYRING_SERVICE, `${windowsCredentialTarget("")}*`); for (const { account } of entries) { - deleteGoWindowsTarget(module, account); + deleteWindowsTarget(module, account); } } catch { // best-effort @@ -379,7 +376,7 @@ const readFallbackFile = ( /** * Resolves an access token for an explicit profile account: env token → keyring (profile * account, then legacy account) → fallback file. Used by commands that reconcile a - * pflag-effective profile after `CommandCredentials` already captured a different one at + * profile resolved from raw argv after `CommandCredentials` already captured a different one at * construction. Fails with the same validation error as `resolveAccessToken` and propagates * credential storage failures. */ diff --git a/apps/cli/src/auth/command-credentials.layer.unit.test.ts b/apps/cli/src/auth/command-credentials.layer.unit.test.ts index e05c9c7f57..4b45208fc4 100644 --- a/apps/cli/src/auth/command-credentials.layer.unit.test.ts +++ b/apps/cli/src/auth/command-credentials.layer.unit.test.ts @@ -156,10 +156,10 @@ afterEach(() => { const VALID_TOKEN = "sbp_" + "a".repeat(40); const VALID_OAUTH_TOKEN = "sbp_oauth_" + "b".repeat(40); const VALID_V0_TOKEN = "sbp_v0_" + "c".repeat(40); -const encodeGoKeyringBase64 = (token: string) => +const encodeKeyringBase64 = (token: string) => `go-keyring-base64:${Buffer.from(token).toString("base64")}`; -const goWindowsKey = (account: string) => `Supabase CLI:${account}/Supabase CLI/${account}`; -const encodeGoWindowsPassword = (token: string) => { +const windowsKey = (account: string) => `Supabase CLI:${account}/Supabase CLI/${account}`; +const encodeWindowsPassword = (token: string) => { const bytes = Buffer.from(token, "utf8"); let encoded = ""; for (let index = 0; index < bytes.length; index += 2) { @@ -252,8 +252,8 @@ describe("commandCredentialsLayer.getAccessToken", () => { ); }); - it.effect("decodes Go keyring base64 values from the keyring profile account", () => { - passwords.set("Supabase CLI/supabase", encodeGoKeyringBase64(VALID_TOKEN)); + it.effect("decodes legacy keyring base64 values from the keyring profile account", () => { + passwords.set("Supabase CLI/supabase", encodeKeyringBase64(VALID_TOKEN)); return Effect.gen(function* () { const { getAccessToken } = yield* CommandCredentials; const token = yield* getAccessToken; @@ -261,8 +261,8 @@ describe("commandCredentialsLayer.getAccessToken", () => { }).pipe(Effect.provide(makeLayer())); }); - it.effect("reads Windows credentials created by Go keyring", () => { - passwords.set(goWindowsKey("supabase"), encodeGoWindowsPassword(VALID_TOKEN)); + it.effect("reads Windows credentials stored in the legacy target format", () => { + passwords.set(windowsKey("supabase"), encodeWindowsPassword(VALID_TOKEN)); return Effect.gen(function* () { const { getAccessToken } = yield* CommandCredentials; const token = yield* getAccessToken; @@ -271,8 +271,8 @@ describe("commandCredentialsLayer.getAccessToken", () => { }).pipe(Effect.provide(makeLayer({ platform: "win32" }))); }); - it.effect("does not search Go Windows targets on other platforms", () => { - passwords.set(goWindowsKey("supabase"), VALID_TOKEN); + it.effect("does not search legacy Windows targets on other platforms", () => { + passwords.set(windowsKey("supabase"), VALID_TOKEN); return Effect.gen(function* () { const { getAccessToken } = yield* CommandCredentials; const token = yield* getAccessToken; @@ -436,11 +436,11 @@ describe("commandCredentialsLayer.saveAccessToken", () => { }).pipe(Effect.provide(makeLayer())), ); - it.effect("writes Windows credentials where Go keyring reads them", () => + it.effect("writes Windows credentials under the legacy target name", () => Effect.gen(function* () { const { saveAccessToken } = yield* CommandCredentials; yield* saveAccessToken(VALID_TOKEN); - expect(passwords.get(goWindowsKey("supabase"))).toBe(VALID_TOKEN); + expect(passwords.get(windowsKey("supabase"))).toBe(VALID_TOKEN); expect(passwords.has("Supabase CLI/supabase")).toBe(false); }).pipe(Effect.provide(makeLayer({ platform: "win32" }))), ); @@ -450,7 +450,7 @@ describe("commandCredentialsLayer.saveAccessToken", () => { return Effect.gen(function* () { const { saveAccessToken } = yield* CommandCredentials; yield* saveAccessToken(VALID_TOKEN); - expect(passwords.get(goWindowsKey("supabase")) ?? "").toBe(""); + expect(passwords.get(windowsKey("supabase")) ?? "").toBe(""); expect(passwords.has("Supabase CLI/supabase")).toBe(false); const content = readFileSync(join(tempHome, ".supabase", "access-token"), "utf-8"); expect(content).toBe(VALID_TOKEN); @@ -540,7 +540,7 @@ describe("commandCredentialsLayer.deleteAccessToken", () => { }, ); - it.effect("win32: no Go target credential → NotLoggedInError, nothing fabricated", () => { + it.effect("win32: no legacy target credential → NotLoggedInError, nothing fabricated", () => { return Effect.gen(function* () { const { deleteAccessToken } = yield* CommandCredentials; const exit = yield* Effect.exit(deleteAccessToken); @@ -548,13 +548,13 @@ describe("commandCredentialsLayer.deleteAccessToken", () => { if (exit._tag === "Failure") { expect(JSON.stringify(exit.cause)).toContain("NotLoggedInError"); } - expect(passwords.has(goWindowsKey("supabase"))).toBe(false); + expect(passwords.has(windowsKey("supabase"))).toBe(false); expect(withTargetCalls).toEqual([]); }).pipe(Effect.provide(makeLayer({ platform: "win32" }))); }); - it.effect("win32: an empty placeholder Go target → NotLoggedInError", () => { - passwords.set(goWindowsKey("supabase"), ""); + it.effect("win32: an empty placeholder legacy target → NotLoggedInError", () => { + passwords.set(windowsKey("supabase"), ""); return Effect.gen(function* () { const { deleteAccessToken } = yield* CommandCredentials; const exit = yield* Effect.exit(deleteAccessToken); @@ -648,43 +648,46 @@ describe("commandCredentialsLayer.deleteAccessToken", () => { }).pipe(Effect.provide(makeLayer())); }); - it.effect("win32: deletes both the plain and the Go Windows target entries", () => { + it.effect("win32: deletes both the plain and the legacy Windows target entries", () => { passwords.set("Supabase CLI/supabase", VALID_TOKEN); - passwords.set(goWindowsKey("supabase"), VALID_TOKEN); + passwords.set(windowsKey("supabase"), VALID_TOKEN); return Effect.gen(function* () { const { deleteAccessToken } = yield* CommandCredentials; yield* deleteAccessToken; expect(passwords.has("Supabase CLI/supabase")).toBe(false); - expect(passwords.has(goWindowsKey("supabase"))).toBe(false); + expect(passwords.has(windowsKey("supabase"))).toBe(false); }).pipe(Effect.provide(makeLayer({ platform: "win32" }))); }); it.effect( - "win32: deletes a Go target whose blob getPassword/findCredentials can't decode", + "win32: deletes a legacy target whose blob getPassword/findCredentials can't decode", () => { - passwords.set(goWindowsKey("supabase"), VALID_TOKEN); - opaqueAccounts.add(goWindowsKey("supabase")); + passwords.set(windowsKey("supabase"), VALID_TOKEN); + opaqueAccounts.add(windowsKey("supabase")); return Effect.gen(function* () { const { deleteAccessToken } = yield* CommandCredentials; yield* deleteAccessToken; - expect(passwords.has(goWindowsKey("supabase"))).toBe(false); + expect(passwords.has(windowsKey("supabase"))).toBe(false); }).pipe(Effect.provide(makeLayer({ platform: "win32" }))); }, ); - it.effect("win32: a confirmed Go target whose delete returns false → DeleteTokenError", () => { - passwords.set(goWindowsKey("supabase"), VALID_TOKEN); - failDeleteAccounts.add(goWindowsKey("supabase")); - return Effect.gen(function* () { - const { deleteAccessToken } = yield* CommandCredentials; - const exit = yield* Effect.exit(deleteAccessToken); - expect(exit._tag).toBe("Failure"); - if (exit._tag === "Failure") { - expect(JSON.stringify(exit.cause)).toContain("DeleteTokenError"); - } - expect(passwords.has(goWindowsKey("supabase"))).toBe(true); - }).pipe(Effect.provide(makeLayer({ platform: "win32" }))); - }); + it.effect( + "win32: a confirmed legacy target whose delete returns false → DeleteTokenError", + () => { + passwords.set(windowsKey("supabase"), VALID_TOKEN); + failDeleteAccounts.add(windowsKey("supabase")); + return Effect.gen(function* () { + const { deleteAccessToken } = yield* CommandCredentials; + const exit = yield* Effect.exit(deleteAccessToken); + expect(exit._tag).toBe("Failure"); + if (exit._tag === "Failure") { + expect(JSON.stringify(exit.cause)).toContain("DeleteTokenError"); + } + expect(passwords.has(windowsKey("supabase"))).toBe(true); + }).pipe(Effect.provide(makeLayer({ platform: "win32" }))); + }, + ); it.effect("win32: an enumeration hiccup alone does not block logout", () => { throwOnFindCredentials = true; @@ -699,7 +702,7 @@ describe("commandCredentialsLayer.deleteAccessToken", () => { "win32: an enumeration failure whose target also fails to delete → DeleteTokenError", () => { throwOnFindCredentials = true; - failDeleteAccounts.add(goWindowsKey("supabase")); + failDeleteAccounts.add(windowsKey("supabase")); return Effect.gen(function* () { const { deleteAccessToken } = yield* CommandCredentials; const exit = yield* Effect.exit(deleteAccessToken); @@ -754,50 +757,50 @@ describe("commandCredentialsLayer.deleteAllProjectCredentials", () => { }).pipe(Effect.provide(makeLayer())); }); - it.effect("win32: deletes Go target-shaped project credentials", () => { - passwords.set(goWindowsKey("abcdefghijklmnopqrs1"), "secret-1"); - passwords.set(goWindowsKey("abcdefghijklmnopqrs2"), "secret-2"); + it.effect("win32: deletes legacy target-shaped project credentials", () => { + passwords.set(windowsKey("abcdefghijklmnopqrs1"), "secret-1"); + passwords.set(windowsKey("abcdefghijklmnopqrs2"), "secret-2"); return Effect.gen(function* () { const { deleteAllProjectCredentials } = yield* CommandCredentials; yield* deleteAllProjectCredentials; - expect(passwords.has(goWindowsKey("abcdefghijklmnopqrs1"))).toBe(false); - expect(passwords.has(goWindowsKey("abcdefghijklmnopqrs2"))).toBe(false); + expect(passwords.has(windowsKey("abcdefghijklmnopqrs1"))).toBe(false); + expect(passwords.has(windowsKey("abcdefghijklmnopqrs2"))).toBe(false); }).pipe(Effect.provide(makeLayer({ platform: "win32" }))); }); it.effect( - "win32: a single undecodable entry aborts the Go target sweep without failing logout", + "win32: a single undecodable entry aborts the legacy target sweep without failing logout", () => { - passwords.set(goWindowsKey("abcdefghijklmnopqrs1"), "secret-1"); - passwords.set(goWindowsKey("abcdefghijklmnopqrs2"), "secret-2"); - opaqueAccounts.add(goWindowsKey("abcdefghijklmnopqrs1")); + passwords.set(windowsKey("abcdefghijklmnopqrs1"), "secret-1"); + passwords.set(windowsKey("abcdefghijklmnopqrs2"), "secret-2"); + opaqueAccounts.add(windowsKey("abcdefghijklmnopqrs1")); return Effect.gen(function* () { const { deleteAllProjectCredentials } = yield* CommandCredentials; const exit = yield* Effect.exit(deleteAllProjectCredentials); expect(exit._tag).toBe("Success"); - expect(passwords.has(goWindowsKey("abcdefghijklmnopqrs1"))).toBe(true); - expect(passwords.has(goWindowsKey("abcdefghijklmnopqrs2"))).toBe(true); + expect(passwords.has(windowsKey("abcdefghijklmnopqrs1"))).toBe(true); + expect(passwords.has(windowsKey("abcdefghijklmnopqrs2"))).toBe(true); }).pipe(Effect.provide(makeLayer({ platform: "win32" }))); }, ); }); describe("commandCredentialsLayer.deleteProjectCredential", () => { - it.effect("win32: deletes a Go target-shaped project credential", () => { - passwords.set(goWindowsKey("abcdefghijklmnopqrs1"), "db-password"); + it.effect("win32: deletes a legacy target-shaped project credential", () => { + passwords.set(windowsKey("abcdefghijklmnopqrs1"), "db-password"); return Effect.gen(function* () { const { deleteProjectCredential } = yield* CommandCredentials; const deleted = yield* deleteProjectCredential("abcdefghijklmnopqrs1"); expect(deleted).toBe(true); - expect(passwords.has(goWindowsKey("abcdefghijklmnopqrs1"))).toBe(false); + expect(passwords.has(windowsKey("abcdefghijklmnopqrs1"))).toBe(false); }).pipe(Effect.provide(makeLayer({ platform: "win32" }))); }); it.effect( "win32: a confirmed project credential whose delete fails → CredentialDeleteError", () => { - passwords.set(goWindowsKey("abcdefghijklmnopqrs1"), "db-password"); - throwOnDeleteAccounts.add(goWindowsKey("abcdefghijklmnopqrs1")); + passwords.set(windowsKey("abcdefghijklmnopqrs1"), "db-password"); + throwOnDeleteAccounts.add(windowsKey("abcdefghijklmnopqrs1")); return Effect.gen(function* () { const { deleteProjectCredential } = yield* CommandCredentials; const exit = yield* Effect.exit(deleteProjectCredential("abcdefghijklmnopqrs1")); diff --git a/apps/cli/src/auth/command-platform-api.layer.unit.test.ts b/apps/cli/src/auth/command-platform-api.layer.unit.test.ts index 84dc920389..33bb385f3f 100644 --- a/apps/cli/src/auth/command-platform-api.layer.unit.test.ts +++ b/apps/cli/src/auth/command-platform-api.layer.unit.test.ts @@ -278,35 +278,32 @@ describe("commandPlatformApiLayer", () => { }); }); - it.effect( - "fails with the invalid-token error when the env token is malformed (Go parity)", - () => { - const http = captureRequests(); - const layer = commandPlatformApiLayer.pipe( - Layer.provide(mockCliSettings({ accessToken: "sbp_not_a_valid_token" })), - Layer.provide(mockCredentials(Option.none())), - Layer.provide(http.layer), - withBaseDeps(), + it.effect("fails with the invalid-token error when the env token is malformed", () => { + const http = captureRequests(); + const layer = commandPlatformApiLayer.pipe( + Layer.provide(mockCliSettings({ accessToken: "sbp_not_a_valid_token" })), + Layer.provide(mockCredentials(Option.none())), + Layer.provide(http.layer), + withBaseDeps(), + ); + return Effect.gen(function* () { + const exit = yield* Effect.exit( + Effect.gen(function* () { + const api = yield* CommandPlatformApi; + return yield* api.v1.listAllProjects(); + }).pipe(Effect.provide(layer)), ); - return Effect.gen(function* () { - const exit = yield* Effect.exit( - Effect.gen(function* () { - const api = yield* CommandPlatformApi; - return yield* api.v1.listAllProjects(); - }).pipe(Effect.provide(layer)), - ); - expect(Exit.isFailure(exit)).toBe(true); - if (Exit.isFailure(exit)) { - const errorJson = JSON.stringify(exit.cause); - expect(errorJson).toContain("InvalidAccessTokenError"); - expect(errorJson).toContain("Invalid access token format"); - } - expect(http.requests).toHaveLength(0); - }); - }, - ); + expect(Exit.isFailure(exit)).toBe(true); + if (Exit.isFailure(exit)) { + const errorJson = JSON.stringify(exit.cause); + expect(errorJson).toContain("InvalidAccessTokenError"); + expect(errorJson).toContain("Invalid access token format"); + } + expect(http.requests).toHaveLength(0); + }); + }); - it.effect("sends Go-style User-Agent and no X-Supabase-Command headers", () => { + it.effect("sends the CLI User-Agent and no X-Supabase-Command headers", () => { const http = captureRequests(); const layer = commandPlatformApiLayer.pipe( Layer.provide( diff --git a/apps/cli/src/cli/complete.ts b/apps/cli/src/cli/complete.ts index c305a00051..ff4eaa4da0 100644 --- a/apps/cli/src/cli/complete.ts +++ b/apps/cli/src/cli/complete.ts @@ -7,7 +7,7 @@ import process from "node:process"; import { QUERY_OUTPUT_FORMATS, RESOURCE_OUTPUT_FORMATS, -} from "../command-internal/go-output-flag.ts"; +} from "../command-internal/output-formats.ts"; import { unwrapParam } from "../command-internal/param-introspection.ts"; import { isValidBase0Int64, parseUintBase0 } from "../command-internal/parse-uint.ts"; import { parseStringSliceFlag } from "../command-internal/string-slice-flag.ts"; @@ -28,7 +28,7 @@ import { analyticsLayer } from "../telemetry/analytics.layer.ts"; import { formatCliError, normalizeCliError } from "../shared/output/normalize-error.ts"; /** - * Implements the shell completion protocol that cobra-generated scripts (`supabase + * Implements the shell completion protocol that the generated scripts (`supabase * completion {bash,zsh,fish,powershell}`) call into via `supabase __complete`/ * `__completeNoDesc` on every tab press. Completion argv can contain partial or malformed * flag tokens (e.g. `--de` mid-word), so this bypasses the structured CLI parser and @@ -472,7 +472,7 @@ const COMPLETION_UINT_FLAGS: ReadonlySet = new Set([ ]); /** - * Flags validated against Go duration syntax (see `isValidGoDuration`) rather than this + * Flags validated against duration syntax (see `isValidDuration`) rather than this * tree's plain `Flag.string` declarations. */ const COMPLETION_DURATION_FLAGS: ReadonlySet = new Set([ @@ -483,8 +483,8 @@ const COMPLETION_DURATION_FLAGS: ReadonlySet = new Set([ /** `--exp` (`gen bearer-jwt`) is validated as an RFC 3339 timestamp, not a plain string. */ const COMPLETION_RFC3339_FLAGS: ReadonlySet = new Set(["gen bearer-jwt:exp"]); -/** Nanosecond scale for each unit the Go duration grammar accepts. */ -const GO_DURATION_UNIT_NANOS: ReadonlyMap = new Map([ +/** Nanosecond scale for each unit the duration grammar accepts. */ +const DURATION_UNIT_NANOS: ReadonlyMap = new Map([ ["ns", 1n], ["us", 1_000n], ["µs", 1_000n], // U+00B5 micro sign @@ -495,23 +495,23 @@ const GO_DURATION_UNIT_NANOS: ReadonlyMap = new Map([ ["h", 3_600_000_000_000n], ]); -// Go's duration grammar accumulates into a `uint64` (range-checked against `1<<63` +// The duration grammar accumulates into a `uint64` (range-checked against `1<<63` // mid-parse) and narrows to the `int64` max in the final non-negative check below. -const GO_DURATION_UINT64_OVERFLOW_BOUND = 1n << 63n; -const GO_DURATION_MAX_INT64 = (1n << 63n) - 1n; +const DURATION_UINT64_OVERFLOW_BOUND = 1n << 63n; +const DURATION_MAX_INT64 = (1n << 63n) - 1n; function isAsciiDigit(char: string | undefined): boolean { return char !== undefined && char >= "0" && char <= "9"; } /** - * Validates the Go duration syntax (`1h30m`, `1.5h`, `.5s`) these flags accept, returning a + * Validates the duration syntax (`1h30m`, `1.5h`, `.5s`) these flags accept, returning a * boolean verdict rather than a parsed value. Uses `BigInt` for the integer/fraction - * accumulators to match Go's `uint64` overflow semantics exactly, and a plain `Number` for - * the one step Go performs in `float64` — a JS `number` is itself an IEEE-754 double, so + * accumulators to get `uint64` overflow semantics, and a plain `Number` for + * the one step performed in `float64` — a JS `number` is itself an IEEE-754 double, so * this round-trips bit-for-bit rather than merely approximating it. */ -function isValidGoDuration(value: string): boolean { +function isValidDuration(value: string): boolean { let rest = value; let negative = false; if (rest.length > 0 && (rest[0] === "-" || rest[0] === "+")) { @@ -529,9 +529,9 @@ function isValidGoDuration(value: string): boolean { let i = 0; let intPart = 0n; while (isAsciiDigit(rest[i])) { - if (intPart > GO_DURATION_UINT64_OVERFLOW_BOUND / 10n) return false; + if (intPart > DURATION_UINT64_OVERFLOW_BOUND / 10n) return false; intPart = intPart * 10n + BigInt(rest[i] as string); - if (intPart > GO_DURATION_UINT64_OVERFLOW_BOUND) return false; + if (intPart > DURATION_UINT64_OVERFLOW_BOUND) return false; i++; } const hasIntDigits = i > 0; @@ -547,11 +547,11 @@ function isValidGoDuration(value: string): boolean { let fracOverflowed = false; while (isAsciiDigit(rest[j])) { if (!fracOverflowed) { - if (fracPart > GO_DURATION_MAX_INT64 / 10n) { + if (fracPart > DURATION_MAX_INT64 / 10n) { fracOverflowed = true; } else { const next = fracPart * 10n + BigInt(rest[j] as string); - if (next > GO_DURATION_UINT64_OVERFLOW_BOUND) { + if (next > DURATION_UINT64_OVERFLOW_BOUND) { fracOverflowed = true; } else { fracPart = next; @@ -570,24 +570,24 @@ function isValidGoDuration(value: string): boolean { let k = 0; while (k < rest.length && !(rest[k] === "." || isAsciiDigit(rest[k]))) k++; if (k === 0) return false; // missing unit - const unitNanos = GO_DURATION_UNIT_NANOS.get(rest.slice(0, k)); + const unitNanos = DURATION_UNIT_NANOS.get(rest.slice(0, k)); rest = rest.slice(k); if (unitNanos === undefined) return false; // unknown unit - if (intPart > GO_DURATION_UINT64_OVERFLOW_BOUND / unitNanos) return false; + if (intPart > DURATION_UINT64_OVERFLOW_BOUND / unitNanos) return false; let termNanos = intPart * unitNanos; if (fracPart > 0n) { const fractional = Number(fracPart) * (Number(unitNanos) / Number(scale)); termNanos += BigInt(Math.trunc(fractional)); - if (termNanos > GO_DURATION_UINT64_OVERFLOW_BOUND) return false; + if (termNanos > DURATION_UINT64_OVERFLOW_BOUND) return false; } total += termNanos; - if (total > GO_DURATION_UINT64_OVERFLOW_BOUND) return false; + if (total > DURATION_UINT64_OVERFLOW_BOUND) return false; } // The negative side already got the larger `1<<63` bound above (int64's two's-complement // asymmetry); only the non-negative case needs this final check. - return negative || total <= GO_DURATION_MAX_INT64; + return negative || total <= DURATION_MAX_INT64; } /** @@ -597,11 +597,11 @@ function isValidGoDuration(value: string): boolean { * through `Date#setUTCFullYear`, which — unlike the `Date` constructor — doesn't special-case * a 0-99 year into 1900+year. */ -const GO_RFC3339_PATTERN = +const RFC3339_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:[.,]\d+)?(?:Z|[+-](\d{2}):(\d{2}))$/; -function isValidGoRfc3339(value: string): boolean { - const match = GO_RFC3339_PATTERN.exec(value); +function isValidRfc3339(value: string): boolean { + const match = RFC3339_PATTERN.exec(value); if (match === null) return false; const [, year, month, day, hour, minute, second, offsetHour, offsetMinute] = match; const y = Number(year); @@ -670,10 +670,10 @@ function isValidFlagValue( return "value" in parseUintBase0(value); } if (COMPLETION_DURATION_FLAGS.has(key)) { - return isValidGoDuration(value); + return isValidDuration(value); } if (COMPLETION_RFC3339_FLAGS.has(key)) { - return isValidGoRfc3339(value); + return isValidRfc3339(value); } if ( flag.isVariadic && @@ -684,7 +684,7 @@ function isValidFlagValue( } switch (flag.primitiveTag) { case "Boolean": - return parseGoBool(value) !== undefined; + return parseBoolLiteral(value) !== undefined; case "Choice": if (flag.name === "output") { return outputFlagChoiceKeys(matchedPath).includes(value); @@ -1019,15 +1019,8 @@ export function respondToComplete( }); } -const GO_TRUE_BOOL_SPELLINGS: ReadonlySet = new Set([ - "1", - "t", - "T", - "TRUE", - "true", - "True", -]); -const GO_FALSE_BOOL_SPELLINGS: ReadonlySet = new Set([ +const TRUE_BOOL_SPELLINGS: ReadonlySet = new Set(["1", "t", "T", "TRUE", "true", "True"]); +const FALSE_BOOL_SPELLINGS: ReadonlySet = new Set([ "0", "f", "F", @@ -1036,16 +1029,15 @@ const GO_FALSE_BOOL_SPELLINGS: ReadonlySet = new Set([ "False", ]); -function parseGoBool(value: string): boolean | undefined { - if (GO_TRUE_BOOL_SPELLINGS.has(value)) return true; - if (GO_FALSE_BOOL_SPELLINGS.has(value)) return false; +function parseBoolLiteral(value: string): boolean | undefined { + if (TRUE_BOOL_SPELLINGS.has(value)) return true; + if (FALSE_BOOL_SPELLINGS.has(value)) return false; return undefined; } /** * `argv[0] === "__completeNoDesc"` always wins; otherwise `SUPABASE_COMPLETION_DESCRIPTIONS` - * is checked first, falling back to `COBRA_COMPLETION_DESCRIPTIONS` when unset or empty. An - * unparseable value is ignored, leaving the `argv[0]`-derived default in place. + * is consulted. An unparseable value is ignored, leaving the `argv[0]`-derived default in place. */ export function resolveIncludeDescriptions( argv0: string | undefined, @@ -1053,8 +1045,8 @@ export function resolveIncludeDescriptions( ): boolean { let includeDescriptions = argv0 !== "__completeNoDesc"; if (includeDescriptions) { - const raw = env.SUPABASE_COMPLETION_DESCRIPTIONS || env.COBRA_COMPLETION_DESCRIPTIONS || ""; - const parsed = parseGoBool(raw); + const raw = env.SUPABASE_COMPLETION_DESCRIPTIONS ?? ""; + const parsed = parseBoolLiteral(raw); if (parsed !== undefined) includeDescriptions = parsed; } return includeDescriptions; diff --git a/apps/cli/src/cli/complete.unit.test.ts b/apps/cli/src/cli/complete.unit.test.ts index 9e38f872b2..e4219d6b14 100644 --- a/apps/cli/src/cli/complete.unit.test.ts +++ b/apps/cli/src/cli/complete.unit.test.ts @@ -228,7 +228,7 @@ describe("respondToComplete", () => { ); }); - it("does not treat a surviving bare `-` leftover as an unmatched command (pflag's stripFlags drops it)", () => { + it("does not treat a surviving bare `-` leftover as an unmatched command (it is dropped)", () => { const result = respondToComplete(rootCommand, ["__complete", "-", "--d"]); expect(result?.candidates.map((c) => c.name)).toEqual( expect.arrayContaining(["--debug", "--dns-resolver"]), @@ -257,7 +257,7 @@ describe("respondToComplete", () => { }); }); - it("short-circuits on a flag Go marks required even though this port made it optional at parse time", () => { + it("short-circuits on a flag marked required even though it is optional at parse time", () => { // Required-ness comes from the explicit `COMPLETION_REQUIRED_FLAGS` table, not from // inferring it off `Flag.optional`. const result = respondToComplete(rootCommand, [ @@ -378,7 +378,7 @@ describe("respondToComplete", () => { }); }); - describe("attached shorthand values resolve via pflag's real strict parser (CLI-1965 review)", () => { + describe("attached shorthand values resolve via the strict flag parser (CLI-1965 review)", () => { it("parses a non-boolean shorthand's attached value instead of treating the token as unknown", () => { const result = respondToComplete(rootCommand, [ "__complete", @@ -504,7 +504,7 @@ describe("respondToComplete", () => { }); }); - describe("uint-backed flags reject a leading sign like real pflag's ParseUint (CLI-1965 review)", () => { + describe("uint-backed flags reject a leading sign (CLI-1965 review)", () => { it.each([ { path: ["functions", "deploy"], flag: "jobs" }, { path: ["migration", "down"], flag: "last" }, @@ -568,14 +568,14 @@ describe("respondToComplete", () => { expect(result?.candidates.map((c) => c.name)).toContain("branches"); }); - it("still accepts json everywhere — the one value both Go enums share", () => { + it("still accepts json everywhere — the one value every output format enum shares", () => { const result = respondToComplete(rootCommand, ["__complete", "--output", "json", ""]); expect(result?.directive).toBe(CompletionDirective.NoFileComp); expect(result?.candidates.map((c) => c.name)).toContain("branches"); }); }); - describe("flag values are validated the way real pflag parses them (CLI-1965 review)", () => { + describe("flag values are validated with the strict flag parser (CLI-1965 review)", () => { it("accepts a base-0 hex value for a plain (non-uint) integer flag", () => { const result = respondToComplete(rootCommand, [ "__complete", @@ -625,7 +625,7 @@ describe("respondToComplete", () => { expect(min?.candidates.map((c) => c.name)).toContain("--profile"); }); - it("rejects a malformed value for Go's DurationVar flags (gen types --query-timeout, gen bearer-jwt --valid-for)", () => { + it("rejects a malformed value for duration flags (gen types --query-timeout, gen bearer-jwt --valid-for)", () => { const queryTimeout = respondToComplete(rootCommand, [ "__complete", "gen", @@ -674,7 +674,7 @@ describe("respondToComplete", () => { expect(validForValid?.candidates.map((c) => c.name)).toContain("--profile"); }); - it("rejects a duration one unit past Go's int64 nanosecond range for Go's DurationVar flags", () => { + it("rejects a duration one unit past the int64 nanosecond range for duration flags", () => { const overflow = respondToComplete(rootCommand, [ "__complete", "gen", @@ -696,7 +696,7 @@ describe("respondToComplete", () => { expect(max?.candidates.map((c) => c.name)).toContain("--local"); }); - it("rejects a malformed value for Go's TimeVar flag (gen bearer-jwt --exp, RFC3339 only)", () => { + it("rejects a malformed value for the time flag (gen bearer-jwt --exp, RFC3339 only)", () => { const invalid = respondToComplete(rootCommand, [ "__complete", "gen", @@ -950,7 +950,7 @@ describe("respondToComplete", () => { }); }); - it("returns undefined for zero completion args (mirrors cobra's MinimumNArgs(1) failure)", () => { + it("returns undefined for zero completion args", () => { expect(respondToComplete(rootCommand, ["__complete"])).toBeUndefined(); }); @@ -1059,7 +1059,7 @@ describe("collectInScopeFlags", () => { ); }); - it("orders flags like cobra's InheritedFlags().VisitAll then NonInheritedFlags().VisitAll — alphabetical within each block, not declaration order (CLI-1965 review)", () => { + it("orders inherited flags before the command's own flags — alphabetical within each block, not declaration order (CLI-1965 review)", () => { const { commandChain } = resolveCommandPath(rootCommand, ["db", "dump"]); const names = collectInScopeFlags(rootCommand, commandChain).map((flag) => flag.name); @@ -1076,7 +1076,7 @@ describe("collectInScopeFlags", () => { expect(ownBlock).toContain("help"); }); - it("orders root's own flags alphabetically end-to-end (InheritedFlags() is empty at root)", () => { + it("orders root's own flags alphabetically end-to-end (no inherited flags at root)", () => { const atRoot = collectInScopeFlags( rootCommand, resolveCommandPath(rootCommand, []).commandChain, @@ -1119,7 +1119,6 @@ describe("resolveIncludeDescriptions", () => { expect( resolveIncludeDescriptions("__completeNoDesc", { SUPABASE_COMPLETION_DESCRIPTIONS: "true", - COBRA_COMPLETION_DESCRIPTIONS: "true", }), ).toBe(false); }); @@ -1130,9 +1129,9 @@ describe("resolveIncludeDescriptions", () => { ).toBe(false); }); - it("falls back to the generic COBRA_COMPLETION_DESCRIPTIONS when the program-specific var is unset", () => { + it("ignores COBRA_COMPLETION_DESCRIPTIONS", () => { expect(resolveIncludeDescriptions("__complete", { COBRA_COMPLETION_DESCRIPTIONS: "0" })).toBe( - false, + true, ); }); @@ -1143,15 +1142,6 @@ describe("resolveIncludeDescriptions", () => { }), ).toBe(true); }); - - it("prioritizes the program-specific var over the generic one when both are set and conflict", () => { - expect( - resolveIncludeDescriptions("__complete", { - SUPABASE_COMPLETION_DESCRIPTIONS: "true", - COBRA_COMPLETION_DESCRIPTIONS: "false", - }), - ).toBe(true); - }); }); describe("formatCompletionResponse", () => { diff --git a/apps/cli/src/cli/root.ts b/apps/cli/src/cli/root.ts index 7604fde4ff..f45c995b96 100644 --- a/apps/cli/src/cli/root.ts +++ b/apps/cli/src/cli/root.ts @@ -209,7 +209,7 @@ export const rootCommandForFeatures = (options: RootCommandFeatures = {}) => const outputFormat = resolveAgentOutputFormat({ explicitOutputFormat, agentDefaultOutputFormat: agentDefaultOutputFormatFor(options, cliArgs.args), - goOutputFormat: resourceOutput, + outputFlagFormat: resourceOutput, agentOverride: agent, detectedAgentName: aiTool.name, isBuiltInTextRequest: isBuiltInTextRequest(cliArgs.args), diff --git a/apps/cli/src/command-internal/api-url.ts b/apps/cli/src/command-internal/api-url.ts index 8a91bbf436..fad07ecae0 100644 --- a/apps/cli/src/command-internal/api-url.ts +++ b/apps/cli/src/command-internal/api-url.ts @@ -1,4 +1,4 @@ -import { goUrlParse } from "./storage-url.ts"; +import { parseUrl } from "./storage-url.ts"; /** * Derives the local API URL: an explicit `api.external_url` wins, otherwise @@ -34,6 +34,6 @@ export function resolveStudioApiUrl( hostname: string, apiExternalUrl: string, ): string { - const { host } = goUrlParse(rawApiUrl); + const { host } = parseUrl(rawApiUrl); return host === "" || host === hostname ? apiExternalUrl : rawApiUrl; } diff --git a/apps/cli/src/command-internal/pflag-reconcile.ts b/apps/cli/src/command-internal/argv-flag-reconcile.ts similarity index 79% rename from apps/cli/src/command-internal/pflag-reconcile.ts rename to apps/cli/src/command-internal/argv-flag-reconcile.ts index 412335c4db..ff1e6bcd39 100644 --- a/apps/cli/src/command-internal/pflag-reconcile.ts +++ b/apps/cli/src/command-internal/argv-flag-reconcile.ts @@ -1,7 +1,7 @@ import { Data, Effect, FileSystem, Option, Path, Result } from "effect"; import { CliArgs } from "../shared/cli/cli-args.service.ts"; -import { lastExplicitLongFlagValue, type PflagArgvScan } from "../shared/cli/cobra-flag-groups.ts"; +import { lastExplicitLongFlagValue, type ArgvFlagScan } from "../shared/cli/flag-groups.ts"; import { ProfileFlag, WorkdirFlag } from "./global-flags.ts"; import { RuntimeInfo } from "../shared/runtime/runtime-info.service.ts"; import { @@ -19,12 +19,12 @@ import { validateWorkdirIsDirectory } from "./workdir-validation.ts"; * validation or API call — the Effect CLI parser never validates this path itself, and can * miss the value entirely when `--workdir` consumed a flag-shaped token. * - * Flows through {@link validatePflagWorkdir}'s inferred Effect error channel; no call site - * imports the class by name. + * Flows through {@link validateArgvWorkdir}'s inferred Effect error channel; no call site + * imports the class by name. The tag is the telemetry fingerprint and must stay stable. * * @public */ -export class PflagWorkdirError extends Data.TaggedError("PflagWorkdirError")<{ +export class ArgvWorkdirError extends Data.TaggedError("PflagWorkdirError")<{ readonly message: string; }> { get [ErrorActionabilityId](): CliErrorActionabilityDeclaration { @@ -33,17 +33,16 @@ export class PflagWorkdirError extends Data.TaggedError("PflagWorkdirError")<{ } /** - * Reconciles an Effect-parsed option flag with pflag's raw-argv semantics: the flag is only - * set when the raw scan says pflag would have set it, using the scan's own value (last - * occurrence wins for a `StringVar`). + * Reconciles an Effect-parsed option flag with the raw-argv scan: the flag is only set when + * the scan shows it set, using the scan's own value (last occurrence wins). * - * The vendored Effect parser refuses to consume a flag-shaped token as a value, while pflag - * consumes it unconditionally — in `--project-ref --metadata-file x.xml --metadata-url u`, - * pflag hands `--metadata-file` to `--project-ref` and never sets `metadata-file`, so acting + * The vendored Effect parser refuses to consume a flag-shaped token as a value, while the raw + * scan consumes it unconditionally — in `--project-ref --metadata-file x.xml --metadata-url u`, + * the scan hands `--metadata-file` to `--project-ref` and never sets `metadata-file`, so acting * on the parsed options there would still read the metadata file unexpectedly. The two agree * on every normal invocation. */ -export function pflagStringValue( +export function argvStringValue( occurrences: ReadonlyMap>, flagName: string, ): Option.Option { @@ -52,7 +51,7 @@ export function pflagStringValue( } /** - * Like `pflagStringValue`, but for CSV-accumulating slice flags: every occurrence is + * Like `argvStringValue`, but for CSV-accumulating slice flags: every occurrence is * CSV-split and accumulated. An absent flag reconciles to `[]` even when the Effect parser * produced values (its tokens were consumed by another flag). * @@ -60,7 +59,7 @@ export function pflagStringValue( * practice, since the Effect parser rejects the same malformed input at parse time before the * handler runs; it just keeps a handler-level disagreement from crashing. */ -export function pflagSliceValue( +export function argvSliceValue( occurrences: ReadonlyMap>, flagName: string, parsedFallback: ReadonlyArray, @@ -83,7 +82,7 @@ export function pflagSliceValue( * default applies, which cannot fail. * * Resolution order: - * - the scan's last `--workdir` occurrence wins — pflag consumes flag-shaped tokens the + * - the scan's last `--workdir` occurrence wins — the scan consumes flag-shaped tokens the * Effect parser refuses (`--workdir --metadata-file` binds `"--metadata-file"`), so the * parsed flag cannot be trusted; * - when the `--workdir` token itself was consumed as another flag's value @@ -92,12 +91,12 @@ export function pflagSliceValue( * - otherwise the Effect-parsed value covers what the anchored scan cannot see: `--workdir` * placed before the command path (`supabase --workdir x sso add …`). */ -export function pflagWorkdirValue( - scan: Pick, +export function argvWorkdirValue( + scan: Pick, parsedWorkdir: Option.Option, envWorkdir: string | undefined, ): Option.Option { - const scanned = pflagStringValue(scan.occurrences, "workdir"); + const scanned = argvStringValue(scan.occurrences, "workdir"); // Same last-wins order as the profile resolver: post-path occurrence, then pre-path // occurrence, then consumed-discard, then the parsed fallback. const prePathValues = scan.prePathOccurrences.get("workdir"); @@ -121,7 +120,7 @@ export function pflagWorkdirValue( } /** - * Validates the workdir {@link pflagWorkdirValue} resolves, aborting before any API call when + * Validates the workdir {@link argvWorkdirValue} resolves, aborting before any API call when * it's missing or not a directory — the config layer only path-resolves the workdir it sees * and never validates this, and can miss the value entirely when `--workdir` consumed a * flag-shaped token. @@ -130,19 +129,19 @@ export function pflagWorkdirValue( * may have resolved the workdir from different sources, but they then issue the identical * request regardless. */ -export const validatePflagWorkdir = Effect.fnUntraced(function* ( - scan: Pick, +export const validateArgvWorkdir = Effect.fnUntraced(function* ( + scan: Pick, ) { // `serviceOption`: absent outside the real CLI tree (handler-level tests // provide argv via `Stdio.layerTest`, not the global flag settings). const parsedWorkdir = Option.flatten(yield* Effect.serviceOption(WorkdirFlag)); - const workdir = pflagWorkdirValue(scan, parsedWorkdir, process.env["SUPABASE_WORKDIR"]); + const workdir = argvWorkdirValue(scan, parsedWorkdir, process.env["SUPABASE_WORKDIR"]); if (Option.isNone(workdir)) { return; } const fs = yield* FileSystem.FileSystem; yield* validateWorkdirIsDirectory(workdir.value, fs).pipe( - Effect.mapError((cause) => new PflagWorkdirError({ message: cause.message })), + Effect.mapError((cause) => new ArgvWorkdirError({ message: cause.message })), ); }); @@ -151,8 +150,8 @@ export const validatePflagWorkdir = Effect.fnUntraced(function* ( * it falls through to the persisted `~/.supabase/profile` file and then the `supabase` * default. * - * Resolution order mirrors {@link pflagWorkdirValue}: - * - the scan's last `--profile` occurrence wins — pflag consumes flag-shaped tokens the + * Resolution order mirrors {@link argvWorkdirValue}: + * - the scan's last `--profile` occurrence wins — the scan consumes flag-shaped tokens the * Effect parser refuses (`--profile --metadata-url` binds `"--metadata-url"`), and a * scanned occurrence marks the flag changed even when its value is the `supabase` default * or empty; @@ -164,12 +163,12 @@ export const validatePflagWorkdir = Effect.fnUntraced(function* ( * distinguish an explicit `--profile supabase` from the flag's default, so that value is * treated as unset. */ -export function pflagProfileValue( - scan: Pick, +export function argvProfileValue( + scan: Pick, parsedProfile: Option.Option, envProfile: string | undefined, ): Option.Option { - const scanned = pflagStringValue(scan.occurrences, "profile"); + const scanned = argvStringValue(scan.occurrences, "profile"); // A post-path occurrence wins outright; otherwise a pre-path occurrence // (`--profile A sso add …`) stays effective even when a later profile-shaped token was // consumed as another flag's value. @@ -194,7 +193,7 @@ export function pflagProfileValue( } /** - * Reconciles the profile the Effect config layer resolved with the {@link pflagProfileValue} + * Reconciles the profile the Effect config layer resolved with the {@link argvProfileValue} * semantics, returning the API URL a request must target when they disagree — `Option.none` * means the layer's `CommandSettings.apiUrl` already matches. Loads before the workdir check * and any other flag validation or API request, since it can change which host every @@ -209,15 +208,15 @@ export function pflagProfileValue( * `Stdio.layerTest`) the flag settings and `RuntimeInfo` may be absent, and the reconcile then * only acts on what the scan itself shows. */ -export const resolvePflagProfile = Effect.fnUntraced(function* ( - scan: Pick, +export const resolveArgvProfile = Effect.fnUntraced(function* ( + scan: Pick, ) { const parsedRaw = yield* Effect.serviceOption(ProfileFlag); const parsedProfile = Option.filter(parsedRaw, (value) => value !== "supabase"); const env = process.env["SUPABASE_PROFILE"]; const envProfile = env !== undefined && env.length > 0 ? env : undefined; - // The explicit token pflag-equivalent semantics resolve, vs. the one the config layer's own + // The explicit token the raw-argv scan resolves, vs. the one the config layer's own // scan resolved (which treats the last raw `--profile` occurrence as explicit even when it // was consumed as another flag's value). When both agree on a non-empty token, the layer // already resolved the right profile. @@ -225,7 +224,7 @@ export const resolvePflagProfile = Effect.fnUntraced(function* ( onNone: () => undefined, onSome: ({ args }) => lastExplicitLongFlagValue(args, [], "profile"), }); - const goExplicit = pflagProfileValue(scan, parsedProfile, envProfile); + const argvExplicit = argvProfileValue(scan, parsedProfile, envProfile); const layerExplicit = scanExplicit !== undefined ? Option.some(scanExplicit) @@ -235,10 +234,10 @@ export const resolvePflagProfile = Effect.fnUntraced(function* ( ? Option.some(envProfile) : Option.none(); if ( - Option.isSome(goExplicit) && + Option.isSome(argvExplicit) && Option.isSome(layerExplicit) && - goExplicit.value === layerExplicit.value && - goExplicit.value !== "" + argvExplicit.value === layerExplicit.value && + argvExplicit.value !== "" ) { return Option.none(); } @@ -257,8 +256,8 @@ export const resolvePflagProfile = Effect.fnUntraced(function* ( .readFileString(profileFilePath(path.value, runtimeInfo.value.homeDir)) .pipe(Effect.option); - const goToken = Option.isSome(goExplicit) - ? goExplicit.value + const argvToken = Option.isSome(argvExplicit) + ? argvExplicit.value : Option.isSome(fileRaw) ? fileRaw.value : "supabase"; @@ -272,14 +271,14 @@ export const resolvePflagProfile = Effect.fnUntraced(function* ( }, }); - if (goToken === layerToken && goToken !== "") { + if (argvToken === layerToken && argvToken !== "") { return Option.none(); } - return Option.some(yield* loadProfile(goToken, fs.value)); + return Option.some(yield* loadProfile(argvToken, fs.value)); }); -/** Accepted literal spellings for a pflag boolean flag value (`strconv.ParseBool`). */ -const GO_PARSE_BOOL: ReadonlyMap = new Map([ +/** Accepted literal spellings for a boolean flag value: `1`, `t`, `T`, `TRUE`, `true`, `True`, `0`, `f`, `F`, `FALSE`, `false`, `False`. */ +const BOOL_LITERALS: ReadonlyMap = new Map([ ["1", true], ["t", true], ["T", true], @@ -295,17 +294,17 @@ const GO_PARSE_BOOL: ReadonlyMap = new Map([ ]); /** - * Like `pflagStringValue`, but for boolean flags: every occurrence is applied in argv order + * Like `argvStringValue`, but for boolean flags: every occurrence is applied in argv order * (a bare occurrence sets `true`; `--flag=value` is checked against the literal set above), * so the last occurrence wins and an absent flag is `false`. An occurrence with an * unrecognized literal fails immediately, before any other flag validation or API call. * * This can't be read off the Effect-parsed boolean: the Effect parser resolves repeated - * flags first-wins (pflag is last-wins) and accepts `yes`/`no`, which the literal set above + * flags first-wins (the scan is last-wins) and accepts `yes`/`no`, which the literal set above * rejects. The scan records a bare occurrence as `"true"` and an inline-empty `--flag=` as * `""`, so both go through the same literal-set check and fail consistently. */ -export function pflagBoolValue( +export function argvBoolValue( occurrences: ReadonlyMap>, flagName: string, ): Result.Result { @@ -315,10 +314,10 @@ export function pflagBoolValue( } let effective = false; for (const raw of values) { - const parsed = GO_PARSE_BOOL.get(raw); + const parsed = BOOL_LITERALS.get(raw); if (parsed === undefined) { return Result.fail( - `invalid argument ${JSON.stringify(raw)} for "--${flagName}" flag: strconv.ParseBool: parsing ${JSON.stringify(raw)}: invalid syntax`, + `invalid argument ${JSON.stringify(raw)} for "--${flagName}" flag: expected a boolean`, ); } effective = parsed; @@ -327,7 +326,7 @@ export function pflagBoolValue( } /** - * Like `pflagStringValue`, but for enum-valued flags: every occurrence must be in `allowed`, + * Like `argvStringValue`, but for enum-valued flags: every occurrence must be in `allowed`, * checked in argv order, and the first invalid one fails immediately — reachable here because * the Effect parser resolves repeats first-wins and never validates later occurrences (e.g. * `--type saml --type bogus` parses). The last occurrence wins; an absent flag is @@ -336,7 +335,7 @@ export function pflagBoolValue( * `flagLabel` names the flag in the error message: `--name` without a shorthand, `-s, --name` * with one. */ -export function pflagEnumValue( +export function argvEnumValue( occurrences: ReadonlyMap>, flagName: string, allowed: ReadonlyArray, diff --git a/apps/cli/src/command-internal/pflag-reconcile.unit.test.ts b/apps/cli/src/command-internal/argv-flag-reconcile.unit.test.ts similarity index 62% rename from apps/cli/src/command-internal/pflag-reconcile.unit.test.ts rename to apps/cli/src/command-internal/argv-flag-reconcile.unit.test.ts index 8b3e8f7d88..2d1d98c584 100644 --- a/apps/cli/src/command-internal/pflag-reconcile.unit.test.ts +++ b/apps/cli/src/command-internal/argv-flag-reconcile.unit.test.ts @@ -10,12 +10,12 @@ import { CliArgs } from "../shared/cli/cli-args.service.ts"; import { ProfileFlag } from "./global-flags.ts"; import { mockRuntimeInfo } from "../../tests/helpers/mocks.ts"; import { - pflagBoolValue, - pflagEnumValue, - pflagProfileValue, - pflagWorkdirValue, - resolvePflagProfile, -} from "./pflag-reconcile.ts"; + argvBoolValue, + argvEnumValue, + argvProfileValue, + argvWorkdirValue, + resolveArgvProfile, +} from "./argv-flag-reconcile.ts"; // Sample enum values for the generic enum-reconciliation helper under test. const NAME_ID_FORMATS = [ @@ -28,76 +28,70 @@ const NAME_ID_FORMATS = [ const occ = (entries: ReadonlyArray]>) => new Map(entries.map(([name, values]) => [name, [...values]])); -describe("pflagBoolValue", () => { - it("is false when the flag never occurs (Go default)", () => { - expect(pflagBoolValue(occ([]), "skip-url-validation")).toEqual(Result.succeed(false)); +describe("argvBoolValue", () => { + it("is false when the flag never occurs", () => { + expect(argvBoolValue(occ([]), "skip-url-validation")).toEqual(Result.succeed(false)); }); - it('treats a bare occurrence (recorded as pflag\'s NoOptDefVal "true") as true', () => { - expect(pflagBoolValue(occ([["skip-url-validation", ["true"]]]), "skip-url-validation")).toEqual( + it('treats a bare occurrence (recorded as "true") as true', () => { + expect(argvBoolValue(occ([["skip-url-validation", ["true"]]]), "skip-url-validation")).toEqual( Result.succeed(true), ); }); - it("resolves repeats last-wins, not first-wins (pflag Sets every occurrence)", () => { + it("resolves repeats last-wins, not first-wins", () => { // --skip-url-validation=false --skip-url-validation expect( - pflagBoolValue(occ([["skip-url-validation", ["false", "true"]]]), "skip-url-validation"), + argvBoolValue(occ([["skip-url-validation", ["false", "true"]]]), "skip-url-validation"), ).toEqual(Result.succeed(true)); // --skip-url-validation --skip-url-validation=false expect( - pflagBoolValue(occ([["skip-url-validation", ["true", "false"]]]), "skip-url-validation"), + argvBoolValue(occ([["skip-url-validation", ["true", "false"]]]), "skip-url-validation"), ).toEqual(Result.succeed(false)); }); - it("fails on an inline-empty occurrence exactly like Go's ParseBool", () => { + it("fails on an inline-empty occurrence", () => { // --skip-url-validation=false --skip-url-validation= expect( - pflagBoolValue(occ([["skip-url-validation", ["false", ""]]]), "skip-url-validation"), + argvBoolValue(occ([["skip-url-validation", ["false", ""]]]), "skip-url-validation"), ).toEqual( - Result.fail( - `invalid argument "" for "--skip-url-validation" flag: strconv.ParseBool: parsing "": invalid syntax`, - ), + Result.fail(`invalid argument "" for "--skip-url-validation" flag: expected a boolean`), ); }); - it("accepts exactly Go's strconv.ParseBool literal set", () => { + it("accepts exactly the boolean literal set", () => { for (const raw of ["1", "t", "T", "TRUE", "true", "True"]) { - expect(pflagBoolValue(occ([["f", [raw]]]), "f")).toEqual(Result.succeed(true)); + expect(argvBoolValue(occ([["f", [raw]]]), "f")).toEqual(Result.succeed(true)); } for (const raw of ["0", "f", "F", "FALSE", "false", "False"]) { - expect(pflagBoolValue(occ([["f", [raw]]]), "f")).toEqual(Result.succeed(false)); + expect(argvBoolValue(occ([["f", [raw]]]), "f")).toEqual(Result.succeed(false)); } }); - it("fails with pflag's byte-exact invalid-argument message on the first bad occurrence", () => { - expect(pflagBoolValue(occ([["skip-url-validation", ["yes"]]]), "skip-url-validation")).toEqual( - Result.fail( - `invalid argument "yes" for "--skip-url-validation" flag: strconv.ParseBool: parsing "yes": invalid syntax`, - ), + it("fails with the invalid-argument message on the first bad occurrence", () => { + expect(argvBoolValue(occ([["skip-url-validation", ["yes"]]]), "skip-url-validation")).toEqual( + Result.fail(`invalid argument "yes" for "--skip-url-validation" flag: expected a boolean`), ); expect( - pflagBoolValue(occ([["skip-url-validation", ["true", "no"]]]), "skip-url-validation"), + argvBoolValue(occ([["skip-url-validation", ["true", "no"]]]), "skip-url-validation"), ).toEqual( - Result.fail( - `invalid argument "no" for "--skip-url-validation" flag: strconv.ParseBool: parsing "no": invalid syntax`, - ), + Result.fail(`invalid argument "no" for "--skip-url-validation" flag: expected a boolean`), ); }); }); -describe("pflagEnumValue", () => { +describe("argvEnumValue", () => { it("is none when the flag never occurs", () => { - expect(pflagEnumValue(occ([]), "name-id-format", NAME_ID_FORMATS)).toEqual( + expect(argvEnumValue(occ([]), "name-id-format", NAME_ID_FORMATS)).toEqual( Result.succeed(Option.none()), ); }); - it("resolves repeats last-wins, matching pflag", () => { + it("resolves repeats last-wins", () => { const persistent = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"; const transient = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"; expect( - pflagEnumValue( + argvEnumValue( occ([["name-id-format", [transient, persistent]]]), "name-id-format", NAME_ID_FORMATS, @@ -105,10 +99,10 @@ describe("pflagEnumValue", () => { ).toEqual(Result.succeed(Option.some(persistent))); }); - it("fails with the Go enum Set message when any occurrence is invalid", () => { + it("fails with the enum message when any occurrence is invalid", () => { const persistent = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"; expect( - pflagEnumValue( + argvEnumValue( occ([["name-id-format", [persistent, "bogus"]]]), "name-id-format", NAME_ID_FORMATS, @@ -120,14 +114,14 @@ describe("pflagEnumValue", () => { ); }); - it("names the flag with its shorthand when a label is given (pflag errors.go:39-41)", () => { - expect(pflagEnumValue(occ([["type", ["bogus"]]]), "type", ["saml"], "-t, --type")).toEqual( + it("names the flag with its shorthand when a label is given", () => { + expect(argvEnumValue(occ([["type", ["bogus"]]]), "type", ["saml"], "-t, --type")).toEqual( Result.fail(`invalid argument "bogus" for "-t, --type" flag: must be one of [ saml ]`), ); }); }); -describe("pflagWorkdirValue", () => { +describe("argvWorkdirValue", () => { const scan = ( entries: ReadonlyArray]>, consumed: ReadonlyArray = [], @@ -138,10 +132,10 @@ describe("pflagWorkdirValue", () => { prePathOccurrences: occ(prePath), }); - it("resolves pre-path repeats last-wins, like pflag (the parser is first-wins)", () => { + it("resolves pre-path repeats last-wins (the parser is first-wins)", () => { // --workdir /existing --workdir /missing sso add … expect( - pflagWorkdirValue( + argvWorkdirValue( scan([], [], [["workdir", ["/existing", "/missing"]]]), Option.some("/existing"), undefined, @@ -151,17 +145,13 @@ describe("pflagWorkdirValue", () => { it("keeps a pre-path occurrence when the only post-path workdir token was consumed", () => { expect( - pflagWorkdirValue( - scan([], ["workdir"], [["workdir", ["/pre"]]]), - Option.some("/pre"), - "/env", - ), + argvWorkdirValue(scan([], ["workdir"], [["workdir", ["/pre"]]]), Option.some("/pre"), "/env"), ).toEqual(Option.some("/pre")); }); it("post-path occurrences still win over pre-path ones (argv-order last-wins)", () => { expect( - pflagWorkdirValue( + argvWorkdirValue( scan([["workdir", ["/post"]]], [], [["workdir", ["/pre"]]]), Option.some("/pre"), undefined, @@ -169,57 +159,57 @@ describe("pflagWorkdirValue", () => { ).toEqual(Option.some("/post")); }); - it("resolves nothing when no flag, parsed value, or env var is present (Go walks up)", () => { - expect(pflagWorkdirValue(scan([]), Option.none(), undefined)).toEqual(Option.none()); + it("resolves nothing when no flag, parsed value, or env var is present", () => { + expect(argvWorkdirValue(scan([]), Option.none(), undefined)).toEqual(Option.none()); }); it("prefers the scan's occurrence over the parsed flag and the env var", () => { // --workdir --metadata-file … expect( - pflagWorkdirValue(scan([["workdir", ["--metadata-file"]]]), Option.none(), "/env"), + argvWorkdirValue(scan([["workdir", ["--metadata-file"]]]), Option.none(), "/env"), ).toEqual(Option.some("--metadata-file")); }); - it("resolves repeats last-wins, matching pflag StringVar", () => { + it("resolves repeats last-wins", () => { expect( - pflagWorkdirValue(scan([["workdir", ["/a", "/b"]]]), Option.some("/a"), undefined), + argvWorkdirValue(scan([["workdir", ["/a", "/b"]]]), Option.some("/a"), undefined), ).toEqual(Option.some("/b")); }); it("falls back to the parsed flag when the anchored scan saw no occurrence (pre-path --workdir)", () => { - expect(pflagWorkdirValue(scan([]), Option.some("/pre-path"), "/env")).toEqual( + expect(argvWorkdirValue(scan([]), Option.some("/pre-path"), "/env")).toEqual( Option.some("/pre-path"), ); }); it("ignores the parsed flag when the --workdir token was consumed by another flag, falling to the env var", () => { // --domains --workdir /x - expect(pflagWorkdirValue(scan([], ["workdir"]), Option.some("/x"), "/env")).toEqual( + expect(argvWorkdirValue(scan([], ["workdir"]), Option.some("/x"), "/env")).toEqual( Option.some("/env"), ); - expect(pflagWorkdirValue(scan([], ["workdir"]), Option.some("/x"), undefined)).toEqual( + expect(argvWorkdirValue(scan([], ["workdir"]), Option.some("/x"), undefined)).toEqual( Option.none(), ); }); it("uses the env var when neither the scan nor the parser saw the flag", () => { - expect(pflagWorkdirValue(scan([]), Option.none(), "/env")).toEqual(Option.some("/env")); + expect(argvWorkdirValue(scan([]), Option.none(), "/env")).toEqual(Option.some("/env")); }); - it("treats a changed-but-empty flag as the walk-up default, shadowing the env var (viper precedence)", () => { + it("treats a changed-but-empty flag as the walk-up default, shadowing the env var", () => { // --workdir= - expect(pflagWorkdirValue(scan([["workdir", [""]]]), Option.none(), "/env")).toEqual( + expect(argvWorkdirValue(scan([["workdir", [""]]]), Option.none(), "/env")).toEqual( Option.none(), ); - expect(pflagWorkdirValue(scan([]), Option.some(""), "/env")).toEqual(Option.none()); + expect(argvWorkdirValue(scan([]), Option.some(""), "/env")).toEqual(Option.none()); }); it("treats an empty env var as unset", () => { - expect(pflagWorkdirValue(scan([]), Option.none(), "")).toEqual(Option.none()); + expect(argvWorkdirValue(scan([]), Option.none(), "")).toEqual(Option.none()); }); }); -describe("pflagProfileValue", () => { +describe("argvProfileValue", () => { const scan = ( entries: ReadonlyArray]>, consumed: ReadonlyArray = [], @@ -233,7 +223,7 @@ describe("pflagProfileValue", () => { it("keeps a pre-path occurrence when the only post-path profile token was consumed", () => { // --profile A sso add --type saml --domains --profile expect( - pflagProfileValue( + argvProfileValue( scan([], ["profile"], [["profile", ["a.yml"]]]), Option.some("a.yml"), "env.yml", @@ -241,9 +231,9 @@ describe("pflagProfileValue", () => { ).toEqual(Option.some("a.yml")); }); - it("resolves pre-path repeats last-wins, like pflag (the parser is first-wins)", () => { + it("resolves pre-path repeats last-wins (the parser is first-wins)", () => { expect( - pflagProfileValue( + argvProfileValue( scan([], [], [["profile", ["a.yml", "b.yml"]]]), Option.some("a.yml"), undefined, @@ -253,7 +243,7 @@ describe("pflagProfileValue", () => { it("post-path occurrences still win over pre-path ones (argv-order last-wins)", () => { expect( - pflagProfileValue( + argvProfileValue( scan([["profile", ["post.yml"]]], [], [["profile", ["pre.yml"]]]), Option.some("pre.yml"), undefined, @@ -261,37 +251,37 @@ describe("pflagProfileValue", () => { ).toEqual(Option.some("post.yml")); }); - it("resolves nothing when no flag, parsed value, or env var is present (Go falls to the file/default)", () => { - expect(pflagProfileValue(scan([]), Option.none(), undefined)).toEqual(Option.none()); + it("resolves nothing when no flag, parsed value, or env var is present (falls to the file/default)", () => { + expect(argvProfileValue(scan([]), Option.none(), undefined)).toEqual(Option.none()); }); it("prefers the scan's occurrence over the parsed flag and the env var", () => { // --profile --metadata-url … expect( - pflagProfileValue(scan([["profile", ["--metadata-url"]]]), Option.none(), "env.yml"), + argvProfileValue(scan([["profile", ["--metadata-url"]]]), Option.none(), "env.yml"), ).toEqual(Option.some("--metadata-url")); }); - it("resolves repeats last-wins, matching pflag StringVar (the parser is first-wins)", () => { + it("resolves repeats last-wins (the parser is first-wins)", () => { expect( - pflagProfileValue(scan([["profile", ["a.yml", "b.yml"]]]), Option.some("a.yml"), undefined), + argvProfileValue(scan([["profile", ["a.yml", "b.yml"]]]), Option.some("a.yml"), undefined), ).toEqual(Option.some("b.yml")); }); - it("keeps an explicit scanned `supabase` — pflag marks it changed, shadowing the env var", () => { - expect(pflagProfileValue(scan([["profile", ["supabase"]]]), Option.none(), "env.yml")).toEqual( + it("keeps an explicit scanned `supabase` — it counts as set, shadowing the env var", () => { + expect(argvProfileValue(scan([["profile", ["supabase"]]]), Option.none(), "env.yml")).toEqual( Option.some("supabase"), ); }); - it("keeps a changed-but-empty occurrence — Go fails LoadProfile on it, never falling to the env", () => { - expect(pflagProfileValue(scan([["profile", [""]]]), Option.none(), "env.yml")).toEqual( + it("keeps a changed-but-empty occurrence — profile loading fails on it, never falling to the env", () => { + expect(argvProfileValue(scan([["profile", [""]]]), Option.none(), "env.yml")).toEqual( Option.some(""), ); }); it("falls back to the parsed flag when the anchored scan saw no occurrence (pre-path --profile)", () => { - expect(pflagProfileValue(scan([]), Option.some("pre.yml"), "env.yml")).toEqual( + expect(argvProfileValue(scan([]), Option.some("pre.yml"), "env.yml")).toEqual( Option.some("pre.yml"), ); }); @@ -299,23 +289,23 @@ describe("pflagProfileValue", () => { it("ignores the parsed flag when the --profile token was consumed by another flag, falling to the env var", () => { // --domains --profile alternate.yml expect( - pflagProfileValue(scan([], ["profile"]), Option.some("alternate.yml"), "env.yml"), + argvProfileValue(scan([], ["profile"]), Option.some("alternate.yml"), "env.yml"), ).toEqual(Option.some("env.yml")); expect( - pflagProfileValue(scan([], ["profile"]), Option.some("alternate.yml"), undefined), + argvProfileValue(scan([], ["profile"]), Option.some("alternate.yml"), undefined), ).toEqual(Option.none()); }); it("uses the env var when neither the scan nor the parser saw the flag", () => { - expect(pflagProfileValue(scan([]), Option.none(), "env.yml")).toEqual(Option.some("env.yml")); + expect(argvProfileValue(scan([]), Option.none(), "env.yml")).toEqual(Option.some("env.yml")); }); it("treats an empty env var as unset", () => { - expect(pflagProfileValue(scan([]), Option.none(), "")).toEqual(Option.none()); + expect(argvProfileValue(scan([]), Option.none(), "")).toEqual(Option.none()); }); }); -describe("resolvePflagProfile", () => { +describe("resolveArgvProfile", () => { const withEnvProfile = (value: string, effect: Effect.Effect) => { const prev = process.env["SUPABASE_PROFILE"]; process.env["SUPABASE_PROFILE"] = value; @@ -343,7 +333,7 @@ describe("resolvePflagProfile", () => { it.effect( "re-loads the env profile when the layer's scan wrongly shadowed a consumed token", () => { - const dir = mkdtempSync(join(tmpdir(), "supabase-pflag-reconcile-")); + const dir = mkdtempSync(join(tmpdir(), "supabase-argv-flag-reconcile-")); const profilePath = join(dir, "env.yml"); writeFileSync( profilePath, @@ -357,7 +347,7 @@ describe("resolvePflagProfile", () => { return withEnvProfile( profilePath, Effect.gen(function* () { - const resolved = yield* resolvePflagProfile({ + const resolved = yield* resolveArgvProfile({ occurrences: new Map(), consumedFlagNames: new Set(["profile"]), prePathOccurrences: new Map(), @@ -378,11 +368,11 @@ describe("resolvePflagProfile", () => { ); it.effect("returns none when the scan and the layer agree on an explicit supabase", () => { - const dir = mkdtempSync(join(tmpdir(), "supabase-pflag-reconcile-")); + const dir = mkdtempSync(join(tmpdir(), "supabase-argv-flag-reconcile-")); return withEnvProfile( "rogue-profile", Effect.gen(function* () { - const resolved = yield* resolvePflagProfile({ + const resolved = yield* resolveArgvProfile({ occurrences: new Map([["profile", ["supabase"]]]), consumedFlagNames: new Set(), prePathOccurrences: new Map(), diff --git a/apps/cli/src/command-internal/go-quote.ts b/apps/cli/src/command-internal/byte-quote.ts similarity index 54% rename from apps/cli/src/command-internal/go-quote.ts rename to apps/cli/src/command-internal/byte-quote.ts index dd72c5646b..e444908f14 100644 --- a/apps/cli/src/command-internal/go-quote.ts +++ b/apps/cli/src/command-internal/byte-quote.ts @@ -1,19 +1,16 @@ /** - * `strconv.Quote` (the `%q` verb) over raw UTF-8 bytes, shared by every error message that must - * reproduce a `%q` interpolation byte-for-byte. + * Double-quotes a string over raw UTF-8 bytes for error messages. * - * Operates on bytes, not JS strings: a byte slice can split a multibyte rune (rendered as `\xNN` - * per orphan byte), and escaping decisions are made per decoded rune over those bytes. Callers - * with a whole JS string encode it first; invalid UTF-8 in `process.argv` has already been - * replaced with U+FFFD by then, so byte-identical output for invalid-UTF-8 argv is unattainable at - * that boundary. + * Operates on bytes, not JS strings: a byte slice can split a multibyte code point (rendered as + * `\xNN` per orphan byte). Callers with a whole JS string encode it first; invalid UTF-8 in + * `process.argv` has already been replaced with U+FFFD by then. */ /** - * `utf8.DecodeRune` semantics over a byte slice: returns the code point and byte size at `i`, or - * `cp: -1` with `size: 1` for an invalid byte — exactly the cases rendered as a lone `\xNN`. + * Decodes the UTF-8 code point at `i`, returning its value and byte length, or `cp: -1` with + * `size: 1` for an invalid or truncated sequence. */ -function decodeUtf8Rune( +function decodeUtf8CodePoint( bytes: Uint8Array, i: number, ): { readonly cp: number; readonly size: number } { @@ -47,12 +44,12 @@ function decodeUtf8Rune( return { cp, size: extra + 1 }; } -// Printable runes ≥ 0x80: letters, marks, numbers, punctuation, symbols (ASCII is handled -// explicitly in goQuote). Unicode-table drift between engines only affects which escape a garbage -// rune gets in one error message. -const GO_PRINTABLE_RE = /[\p{L}\p{M}\p{N}\p{P}\p{S}]/u; +// Printable code points ≥ 0x80: letters, marks, numbers, punctuation, symbols (ASCII is handled +// explicitly in quoteBytes). Unicode-table drift between engines only affects which escape a garbage +// code point gets in one error message. +const PRINTABLE_RE = /[\p{L}\p{M}\p{N}\p{P}\p{S}]/u; -const GO_ESCAPES: Readonly> = { +const ESCAPE_SHORTHANDS: Readonly> = { 0x07: "\\a", 0x08: "\\b", 0x0c: "\\f", @@ -63,26 +60,27 @@ const GO_ESCAPES: Readonly> = { }; /** - * `%q` (`strconv.Quote`) over raw UTF-8 bytes. Valid printable runes print literally; - * control/non-printable ones use the `\a…\v` shorthands then `\xNN`/`\uNNNN`/`\UNNNNNNNN`. + * Wraps the bytes in double quotes. Valid printable code points print literally, control + * characters use the `\a \b \f \n \r \t \v` shorthands, each invalid byte becomes `\xNN`, and + * other non-printable code points become `\uNNNN` or `\UNNNNNNNN`. */ -export function goQuote(bytes: Uint8Array): string { +export function quoteBytes(bytes: Uint8Array): string { let out = '"'; for (let i = 0; i < bytes.length;) { - const { cp, size } = decodeUtf8Rune(bytes, i); + const { cp, size } = decodeUtf8CodePoint(bytes, i); if (cp === -1) { out += `\\x${(bytes[i] ?? 0).toString(16).padStart(2, "0")}`; i += 1; continue; } i += size; - const escape = GO_ESCAPES[cp]; + const escape = ESCAPE_SHORTHANDS[cp]; const ch = String.fromCodePoint(cp); if (ch === '"' || ch === "\\") out += `\\${ch}`; else if (escape !== undefined) out += escape; else if (cp >= 0x20 && cp < 0x7f) out += ch; else if (cp < 0x80) out += `\\x${cp.toString(16).padStart(2, "0")}`; - else if (GO_PRINTABLE_RE.test(ch)) out += ch; + else if (PRINTABLE_RE.test(ch)) out += ch; else if (cp < 0x10000) out += `\\u${cp.toString(16).padStart(4, "0")}`; else out += `\\U${cp.toString(16).padStart(8, "0")}`; } diff --git a/apps/cli/src/command-internal/cli-config-load.ts b/apps/cli/src/command-internal/cli-config-load.ts new file mode 100644 index 0000000000..55dd5d2c85 --- /dev/null +++ b/apps/cli/src/command-internal/cli-config-load.ts @@ -0,0 +1,37 @@ +import type { CliProjectEnvironment } from "@supabase/config"; +import { + decodeCliConfigDocumentForValidationEffect as decodeDocumentWithCompat, + loadCliConfig as loadConfigWithCompat, + resolveCliConfigSubtree as resolveSubtreeWithCompat, + resolveCliConfigValue as resolveValueWithCompat, + type DecodeCliConfigDocumentForValidationEffectOptions, + type InternalLoadCliConfigOptions, +} from "@supabase/config/internal"; + +export type CliConfigLoadOptions = Omit; +export type CliConfigValidationOptions = Omit< + DecodeCliConfigDocumentForValidationEffectOptions, + "cliCompat" +>; + +/** The single `apps/cli` entrypoint that loads config with the CLI's own loader semantics. */ +export const loadCliConfig = (cwd: string, options?: CliConfigLoadOptions) => + loadConfigWithCompat(cwd, { ...options, cliCompat: true }); + +/** Decodes a pending document with the same semantics as {@link loadCliConfig}. */ +export const decodeCliConfigDocumentForValidation = ( + document: Record, + options: CliConfigValidationOptions, +) => decodeDocumentWithCompat(document, { ...options, cliCompat: true }); + +export const resolveCliConfigValue = ( + value: T, + cliProjectEnv: Pick, + configPath: string, +) => resolveValueWithCompat(value, cliProjectEnv, configPath, { cliCompat: true }); + +export const resolveCliConfigSubtree = ( + value: T, + cliProjectEnv: Pick, + pathPrefix: string, +) => resolveSubtreeWithCompat(value, cliProjectEnv, pathPrefix, { cliCompat: true }); diff --git a/apps/cli/src/command-internal/config-pull-run.ts b/apps/cli/src/command-internal/config-pull-run.ts index 8327855dba..167d3f415b 100644 --- a/apps/cli/src/command-internal/config-pull-run.ts +++ b/apps/cli/src/command-internal/config-pull-run.ts @@ -9,11 +9,9 @@ import { } from "@supabase/config/effect"; import { applyConfigEdits, - decodeCliConfigDocumentForValidationEffect, writeCliConfigDocumentText, type ConfigEdit, type ConfigEditRefusalReason, - type DecodeCliConfigDocumentForValidationEffectOptions, } from "@supabase/config/internal"; import type { ConfigChange } from "@supabase/config"; import { operationDefinitions } from "@supabase/api/effect"; @@ -22,6 +20,10 @@ import { Effect, FileSystem, Result, Schema, SchemaIssue } from "effect"; import { CommandPlatformApi } from "../auth/command-platform-api.service.ts"; import { CommandSettings } from "../config/command-settings.service.ts"; import { Output } from "../shared/output/output.service.ts"; +import { + decodeCliConfigDocumentForValidation, + type CliConfigValidationOptions, +} from "./cli-config-load.ts"; import { sanitizeErrorBody, sanitizeInlineName } from "./http-errors.ts"; import type { ConfigTarget } from "./project-target.ts"; import { BRANCH_UUID_PATTERN } from "./ref-patterns.ts"; @@ -298,7 +300,7 @@ function configPullChangeRelativeValue( } /** - * Converts a failed {@link decodeCliConfigDocumentForValidationEffect} attempt's + * Converts a failed {@link decodeCliConfigDocumentForValidation} attempt's * `SchemaIssue` paths into `ConfigChange.path`-relative segments. `isLabelPrefixed` strips * one leading segment for the raw/unmerged projection of a remote destination, whose issue * paths start with the `remotes` map's own key (the label) rather than already being @@ -372,16 +374,16 @@ function configPullFamiliesForChangePaths( } /** - * Runs {@link decodeCliConfigDocumentForValidationEffect}, capturing only its own + * Runs {@link decodeCliConfigDocumentForValidation}, capturing only its own * `CliConfigParseError` failure into a `Result`. A genuinely malformed `.env`/`.env.local`, * or a filesystem failure reading one, is not a decode-attribution failure, so those * propagate uncaught, matching how the real `loadCliConfig` call already handles them. */ function decodeConfigPullValidation( document: Record, - options: DecodeCliConfigDocumentForValidationEffectOptions, + options: CliConfigValidationOptions, ) { - return decodeCliConfigDocumentForValidationEffect(document, options).pipe( + return decodeCliConfigDocumentForValidation(document, options).pipe( Effect.map(Result.succeed), Effect.catchTag("CliConfigParseError", (cause) => Effect.succeed(Result.fail(cause))), ); @@ -389,7 +391,7 @@ function decodeConfigPullValidation( /** * The change-path keys ({@link configPathKey}) that already fail - * {@link decodeCliConfigDocumentForValidationEffect} in `rawDocument` as it sits on disk + * {@link decodeCliConfigDocumentForValidation} in `rawDocument` as it sits on disk * right now, before this pull's own writes are projected. {@link validateConfigPullPlan} * excludes these from the families it forms — pulling should never attribute, drop, or fail * over a pre-existing problem; it only needs to leave the file no worse than it was. Runs @@ -406,7 +408,6 @@ const configPullPreExistingFailingChangePathKeys = Effect.fnUntraced(function* ( const rawDecoded = yield* decodeConfigPullValidation(input.rawDocument, { path: input.configPath, format: input.format, - goViperCompat: true, }); if (Result.isFailure(rawDecoded)) { for (const path of configPullSchemaIssueChangePaths( @@ -420,7 +421,6 @@ const configPullPreExistingFailingChangePathKeys = Effect.fnUntraced(function* ( const mergedDecoded = yield* decodeConfigPullValidation(input.rawDocument, { path: input.configPath, format: input.format, - goViperCompat: true, remoteName: input.destination.label, }); if (Result.isFailure(mergedDecoded)) { @@ -471,14 +471,12 @@ const validateConfigPullPlan = Effect.fnUntraced(function* (input: { input.projectRef, ); const rawDecoded = yield* decodeConfigPullValidation(document, { - goViperCompat: true, path: input.configPath, format: input.format, }); const mergedDecoded = destination.kind === "remote" ? yield* decodeConfigPullValidation(document, { - goViperCompat: true, path: input.configPath, format: input.format, remoteName: destination.label, diff --git a/apps/cli/src/command-internal/config-validate.ts b/apps/cli/src/command-internal/config-validate.ts index 9549248426..d02d21efa3 100644 --- a/apps/cli/src/command-internal/config-validate.ts +++ b/apps/cli/src/command-internal/config-validate.ts @@ -9,7 +9,7 @@ import { } from "../shared/telemetry/error-actionability.ts"; import { canonicalPathForContainment, isPathContainedInRoot } from "./path-containment.ts"; import { BRANCH_PROJECT_REF_PATTERN } from "./ref-patterns.ts"; -import { goUrlParse } from "./storage-url.ts"; +import { parseUrl } from "./storage-url.ts"; /** * Single home for config validation, shared by the two config readers: @@ -45,19 +45,20 @@ export const HOOK_SECRET_PATTERN = /^v1,whsec_[A-Za-z0-9+/=]{32,88}$/u; export const CLERK_DOMAIN_PATTERN = /^(clerk([.][a-z0-9-]+){2,}|([a-z0-9-]+[.])+clerk[.]accounts[.]dev)$/u; -// Accepted boolean string forms, matching Go's `strconv.ParseBool`; any other value is a -// parse error. -const GO_BOOL_TRUE = new Set(["1", "t", "T", "TRUE", "true", "True"]); -const GO_BOOL_FALSE = new Set(["0", "f", "F", "FALSE", "false", "False", ""]); +// Accepted boolean string forms (`1`/`t`/`T`/`TRUE`/`true`/`True`, `0`/`f`/`F`/`FALSE`/`false`/`False`, +// and the empty string as false); any other value is a parse error. +const BOOL_TRUE_LITERALS = new Set(["1", "t", "T", "TRUE", "true", "True"]); +const BOOL_FALSE_LITERALS = new Set(["0", "f", "F", "FALSE", "false", "False", ""]); /** - * Parses a config bool value: accepts the same string forms as `strconv.ParseBool`, returns + * Parses a config bool value: accepts `1`/`t`/`T`/`TRUE`/`true`/`True` and + * `0`/`f`/`F`/`FALSE`/`false`/`False` (and the empty string as false), returns * `undefined` for anything else (surfaced by callers as a `failed to parse config` error). * Used by both D and L for `SUPABASE_*` bool-flavored env overrides and TOML bool decoding. */ -export function parseGoBool(value: string): boolean | undefined { - if (GO_BOOL_TRUE.has(value)) return true; - if (GO_BOOL_FALSE.has(value)) return false; +export function parseBoolLiteral(value: string): boolean | undefined { + if (BOOL_TRUE_LITERALS.has(value)) return true; + if (BOOL_FALSE_LITERALS.has(value)) return false; return undefined; } @@ -312,7 +313,7 @@ export function validateResolvedConfig(input: ConfigValidationInput): void { throw new ConfigValidateError("Missing required field in config: studio.port"); } try { - goUrlParse(input.studio.apiUrl); + parseUrl(input.studio.apiUrl); } catch (cause) { throw new ConfigValidateError(`Invalid config for studio.api_url: ${messageOf(cause)}`); } @@ -340,7 +341,7 @@ export function validateResolvedConfig(input: ConfigValidationInput): void { provider !== "turnstile" ) { throw new ConfigValidateError( - "failed to parse config: decoding failed due to the following error(s):\n\n'auth.captcha.provider' must be one of [hcaptcha turnstile]", + "failed to parse config:\nauth.captcha.provider: must be one of hcaptcha, turnstile", ); } if (auth.captcha.enabled) { @@ -377,12 +378,12 @@ export function validateResolvedConfig(input: ConfigValidationInput): void { `Missing required field in config: auth.hook.${hook.type}.uri`, ); } - // Uses `goUrlParse`'s stricter semantics (the same port used for `studio.api_url` + // Uses `parseUrl`'s stricter semantics (the same port used for `studio.api_url` // above) so a malformed URI like an unterminated IPv6 host (`http://[::1`) fails the // whole load instead of passing a bare scheme-prefix regex. let scheme: string; try { - scheme = goUrlParse(hook.uri).scheme; + scheme = parseUrl(hook.uri).scheme; } catch (cause) { throw new ConfigValidateError(`failed to parse template url: ${messageOf(cause)}`); } @@ -537,7 +538,7 @@ export function validateResolvedConfig(input: ConfigValidationInput): void { backend !== "bigquery" ) { throw new ConfigValidateError( - "failed to parse config: decoding failed due to the following error(s):\n\n'analytics.backend' must be one of [postgres bigquery]", + "failed to parse config:\nanalytics.backend: must be one of postgres, bigquery", ); } if (input.analytics.enabled && backend === "bigquery") { diff --git a/apps/cli/src/command-internal/config-validate.unit.test.ts b/apps/cli/src/command-internal/config-validate.unit.test.ts index 6140bcd76f..9ad09db7eb 100644 --- a/apps/cli/src/command-internal/config-validate.unit.test.ts +++ b/apps/cli/src/command-internal/config-validate.unit.test.ts @@ -21,27 +21,27 @@ import { ConfigValidateError, type AuthInput, type ConfigValidationInput, - parseGoBool, + parseBoolLiteral, resolveEmailTemplateContentPath, validateResolvedConfig, } from "./config-validate.ts"; -describe("parseGoBool", () => { - it("accepts Go's strconv.ParseBool true forms", () => { +describe("parseBoolLiteral", () => { + it("accepts the true forms", () => { for (const value of ["1", "t", "T", "TRUE", "true", "True"]) { - expect(parseGoBool(value)).toBe(true); + expect(parseBoolLiteral(value)).toBe(true); } }); - it("accepts Go's strconv.ParseBool false forms, including the empty string", () => { + it("accepts the false forms, including the empty string", () => { for (const value of ["0", "f", "F", "FALSE", "false", "False", ""]) { - expect(parseGoBool(value)).toBe(false); + expect(parseBoolLiteral(value)).toBe(false); } }); - it("returns undefined for a value outside Go's strconv.ParseBool acceptance set", () => { - expect(parseGoBool("yes")).toBeUndefined(); - expect(parseGoBool("2")).toBeUndefined(); + it("returns undefined for a value outside the accepted set", () => { + expect(parseBoolLiteral("yes")).toBeUndefined(); + expect(parseBoolLiteral("2")).toBeUndefined(); }); }); @@ -57,11 +57,11 @@ describe("PROJECT_REF_PATTERN", () => { }); describe("BUCKET_NAME_PATTERN", () => { - it("matches Go-legal bucket name characters", () => { + it("matches legal bucket name characters", () => { expect(BUCKET_NAME_PATTERN.test("my-bucket.1")).toBe(true); }); - it("rejects characters outside Go's bucketNamePattern", () => { + it("rejects characters outside the bucket name pattern", () => { expect(BUCKET_NAME_PATTERN.test("bad#name")).toBe(false); expect(BUCKET_NAME_PATTERN.test("bad/name")).toBe(false); }); @@ -84,7 +84,7 @@ describe("HOOK_SECRET_PATTERN", () => { expect(HOOK_SECRET_PATTERN.test(`v1,whsec_${"a".repeat(32)}`)).toBe(true); }); - it("rejects a secret that doesn't match Go's hookSecretPattern", () => { + it("rejects a secret that doesn't match the hook secret pattern", () => { expect(HOOK_SECRET_PATTERN.test("not-a-valid-secret")).toBe(false); }); }); @@ -98,7 +98,7 @@ describe("CLERK_DOMAIN_PATTERN", () => { expect(CLERK_DOMAIN_PATTERN.test("example.clerk.accounts.dev")).toBe(true); }); - it("rejects a domain that doesn't match Go's clerkDomainPattern", () => { + it("rejects a domain that doesn't match the clerk domain pattern", () => { expect(CLERK_DOMAIN_PATTERN.test("not-a-clerk-domain")).toBe(false); }); }); @@ -444,7 +444,7 @@ describe("validateResolvedConfig", () => { ).toThrow("Missing required field in config: db.major_version"); }); - it("rejects the unsupported Postgres 12.x major_version with Go's dedicated message", () => { + it("rejects the unsupported Postgres 12.x major_version with its dedicated message", () => { expect(() => validateResolvedConfig(minimalInput({ db: { port: 5432, majorVersion: 12 } })), ).toThrow("Postgres version 12.x is unsupported."); @@ -464,7 +464,7 @@ describe("validateResolvedConfig", () => { }); describe("storage.buckets", () => { - it("rejects a bucket name Go's ValidateBucketName refuses", () => { + it("rejects an invalid bucket name", () => { expect(() => validateResolvedConfig(minimalInput({ storageBucketNames: ["bad/name"] })), ).toThrow("Invalid Bucket name: bad/name."); @@ -948,7 +948,7 @@ describe("validateResolvedConfig", () => { ).toThrow("Missing required field in config: auth.hook.custom_access_token.secrets"); }); - it("rejects an http(s) hook secret that doesn't match Go's hookSecretPattern", () => { + it("rejects an http(s) hook secret that doesn't match the hook secret pattern", () => { expect(() => validateResolvedConfig( minimalInput({ @@ -1034,7 +1034,7 @@ describe("validateResolvedConfig", () => { ).toThrow("auth.hook.custom_access_token.uri should be a HTTP, HTTPS, or pg-functions URI"); }); - it("rejects a hook uri that fails Go's url.Parse (malformed IPv6 host)", () => { + it("rejects a hook uri that fails URL parsing (malformed IPv6 host)", () => { expect(() => validateResolvedConfig( minimalInput({ @@ -1150,7 +1150,7 @@ describe("validateResolvedConfig", () => { ).toThrow("Invalid config: auth.third_party.clerk is enabled but without a domain."); }); - it("rejects clerk enabled with a domain that doesn't match Go's clerkDomainPattern", () => { + it("rejects clerk enabled with a domain that doesn't match the clerk domain pattern", () => { expect(() => validateResolvedConfig( minimalInput({ @@ -1211,7 +1211,7 @@ describe("validateResolvedConfig", () => { }); describe("functions.*", () => { - it("rejects a function slug Go's ValidateFunctionSlug refuses", () => { + it("rejects an invalid function slug", () => { expect(() => validateResolvedConfig(minimalInput({ functionSlugs: ["1bad"] }))).toThrow( "Invalid Function name: 1bad.", ); @@ -1269,13 +1269,13 @@ describe("validateResolvedConfig", () => { // Coverage for behavior only meaningfully testable at this shared layer (e.g. the captcha // enum), not moved from either caller's own suite. describe("Config.Validate divergence regression coverage", () => { - it("throws the Go-parity missing-required message for db.major_version = 0 (regression for the D fix in 0c62a914)", () => { + it("throws the missing-required message for db.major_version = 0 (regression for the D fix in 0c62a914)", () => { expect(() => validateResolvedConfig({ ...minimalInput(), db: { port: 5432, majorVersion: 0 } }), ).toThrow("Missing required field in config: db.major_version"); }); - it("throws Go's decode-time enum message for an invalid auth.captcha.provider, regardless of enabled", () => { + it("throws the decode-time enum message for an invalid auth.captcha.provider, regardless of enabled", () => { // Unreachable through L's real flow: `@supabase/config`'s schema already narrows // `provider` to "hcaptcha" | "turnstile" | undefined, so an invalid value fails schema // decoding first. D's real TOML flow can reach this branch; its own suite covers that @@ -1289,7 +1289,7 @@ describe("validateResolvedConfig", () => { }), ), ).toThrow( - "failed to parse config: decoding failed due to the following error(s):\n\n'auth.captcha.provider' must be one of [hcaptcha turnstile]", + "failed to parse config:\nauth.captcha.provider: must be one of hcaptcha, turnstile", ); }); }); diff --git a/apps/cli/src/command-internal/connect-errors.ts b/apps/cli/src/command-internal/connect-errors.ts index d9eb65d081..fa0c8c05e3 100644 --- a/apps/cli/src/command-internal/connect-errors.ts +++ b/apps/cli/src/command-internal/connect-errors.ts @@ -1,6 +1,5 @@ /** - * Connection-error classification and rendering ported from the established - * connect helpers and pgconn's `connectError`. + * Connection-error classification and rendering. * Used by the container-level pooler fallback (`db dump --linked`) to decide * whether a failed pg_dump/pg container was an IPv6 connectivity failure that * warrants retrying through the IPv4 transaction pooler, and by the connection @@ -29,7 +28,7 @@ export function ipv6Suggestion(): string { const IPV6_LITERAL_PATTERN = /(?:\[[0-9a-fA-F:]+\]|\([0-9a-fA-F:]+\))/; // Node's dial-failure shape (`connect ENETUNREACH 2600:…:5432`). The port may be // followed by whitespace, end-of-string, or a closing paren — the connect-failure -// message renders the driver cause parenthesized (pgconn `dial error (…)` form). +// message renders the driver cause parenthesized (`dial error (…)` form). const NODE_ENETUNREACH_PATTERN = /\benetunreach\s+([0-9a-fA-F:]+):\d+(?:[\s)]|$)/i; /** @@ -86,8 +85,8 @@ export interface ConnectSuggestionContext { * and `code`. The `@effect/sql` `SqlError` wraps the node-postgres/node `net` driver error on * its `cause`; a multi-address dial wraps an `AggregateError` whose `errors[]` carry the * per-IP `ECONNREFUSED`/`ENETUNREACH` system errors — an aggregate node contributes nothing - * itself and only its last child is visited, since pgconn's own fallback loop overwrites its - * error on every attempt so only the last one survives. The parent's own fields are skipped: + * itself and only its last child is visited, since each attempt overwrites the previous error + * so only the last one survives. The parent's own fields are skipped: * node's `aggregateErrors` copies `errors[0].code` onto the aggregate itself (`lib/internal/ * errors.js`, Bun matches), so reading them would blame an abandoned first attempt. */ @@ -126,7 +125,7 @@ export interface ConnectFailureTarget { * Walks to the deepest underlying driver error: unwraps `cause` chains (the `@effect/sql` * `SqlError` exposes its `ConnectionError` reason as `cause`, and the reason exposes the * node-postgres error the same way) and descends into the last entry of an `AggregateError`'s - * `errors[]` — pgconn's multi-address fallback loop likewise surfaces the last attempt's error. + * `errors[]` — the last attempt's error is the one surfaced. */ function deepestConnectCause(error: unknown): unknown { let current: unknown = error; @@ -146,8 +145,8 @@ function deepestConnectCause(error: unknown): unknown { return current; } -// Node/Bun errno codes raised while dialing the server — pgconn wraps the -// equivalent net.Dial failures as `dial error (…)`. +// Node/Bun errno codes raised while dialing the server, rendered as +// `dial error (…)`. const DIAL_ERROR_CODES = new Set([ "ECONNREFUSED", "ETIMEDOUT", @@ -179,8 +178,7 @@ export function isDialFailure(error: unknown): boolean { // The complete documented Node/OpenSSL X509 certificate-verification code // family (Node tls docs "X509 certificate error codes", OpenSSL's // `X509_verify_cert_error` set), complemented by node's ERR_TLS_*/ERR_SSL_* -// prefixes at the use site. pgconn stages by connection phase — any `startTLS` -// failure becomes `tls error (…)` — but node exposes no +// prefixes at the use site. Any TLS-phase failure renders as `tls error (…)`, but node exposes no // phase marker, so the full code family is the proxy. These strings are unique // to TLS-layer verification: server SQLSTATEs and dial/DNS `E…` errnos are // classified by earlier branches. @@ -215,13 +213,13 @@ const TLS_ERROR_CODES = new Set([ "HOSTNAME_MISMATCH", ]); // node-postgres' own message when the server answers `N` to SSLRequest -// (`pg/lib/connection.js`); pgconn: `tls error (server refused TLS connection)`. +// (`pg/lib/connection.js`); rendered as `tls error (server refused TLS connection)`. const SERVER_REFUSED_SSL = "The server does not support SSL connections"; // Node/Bun's TLS-socket message when the server accepts SSLRequest but closes // the socket before the handshake completes (node `lib/_tls_wrap.js` // `onConnectEnd`; Bun emits the same text for both FIN and RST). Phase-specific // by construction — only ever raised pre-secure-connection — so it maps to -// pgconn's startTLS stage (`tls error (…)`). Its code is +// the TLS stage (`tls error (…)`). Its code is // ECONNRESET, absent from DIAL_ERROR_CODES: a raw // post-handshake `read ECONNRESET` is not phase-specific and stays verbatim. const TLS_DISCONNECT_MESSAGE = @@ -240,24 +238,17 @@ const SQLSTATE_PATTERN = /^[0-9A-Z]{5}$/; export const isSqlState = (code: string): boolean => SQLSTATE_PATTERN.test(code); /** - * Render the underlying driver failure the way pgconn stages its - * `connectError.msg` (`server error` / `hostname resolving error` / + * Render the underlying driver failure by stage (`server error` / `hostname resolving error` / * `dial error` / `tls error`, each with the cause parenthesized). - * A server ErrorResponse reproduces pgconn's `PgError` - * rendering byte-for-byte (`Severity: Message (SQLSTATE Code)`); - * for the other stages the parenthesized text is the node driver's own message, - * which cannot byte-match libpq/pgconn wording (e.g. node's - * `connect ECONNREFUSED 1.2.3.4:5432` vs - * `dial tcp 1.2.3.4:5432: connect: connection refused`). An unrecognized cause - * (e.g. node-postgres' `Connection terminated unexpectedly`, where pgconn would - * say `failed to receive message (unexpected EOF)`) is rendered verbatim rather + * A server ErrorResponse renders as `Severity: Message (SQLSTATE Code)`; + * for the other stages the parenthesized text is the node driver's own message + * (e.g. `connect ECONNREFUSED 1.2.3.4:5432`). An unrecognized cause + * (e.g. node-postgres' `Connection terminated unexpectedly`) is rendered verbatim rather * than guessing a stage. * - * Known stage-label caveat: pgconn labels by auth phase, which node-postgres - * does not expose — a wrong password over SCRAM arrives mid-SASL, so pgconn renders - * `failed SASL auth (FATAL: password authentication failed … (SQLSTATE 28P01))` - * where this renders `server error (…)` with the identical - * inner `PgError` bytes. The suggestion classifier keys off the inner text, so + * Known stage-label caveat: node-postgres does not expose the auth phase, so a wrong password + * over SCRAM arrives mid-SASL and renders as `server error (…)` with the same inner server-error + * text a SASL-stage label would carry. The suggestion classifier keys off the inner text, so * the `SUPABASE_DB_PASSWORD` hint fires identically either way. */ function connectCauseDetail(cause: unknown): string { @@ -293,10 +284,9 @@ function connectCauseDetail(cause: unknown): string { } /** - * Port of pgconn's `connectError.Error()`, the inner text of - * `failed to connect to postgres: %w` wrap: + * The inner text of the `failed to connect to postgres: ` wrap: * `` failed to connect to `host=… user=… database=…`: ``. - * Callers pass the connection config (pgconn embeds the config-level identity) + * Callers pass the connection config (its identity is embedded in the message) * and the raw failure — either the `@effect/sql` `SqlError` * from the pooled connect probe or the bare node-postgres error from the raw * client, both unwrapped by {@link deepestConnectCause}. @@ -344,7 +334,7 @@ function hasIPv6DialCause(error: unknown, depth = 0): boolean { * `SqlError` cause/aggregate chain. Returns `undefined` when no specific suggestion applies. * * The rendered message ({@link connectFailureMessage}) and this classifier inspect the same - * surfaced attempt: pgconn's fallback loop keeps only the last error on every attempt, so the + * surfaced attempt: only the last attempt's error survives, so the * collectors above descend into only the last aggregate child too — the displayed cause and * the suggestion can never disagree. */ diff --git a/apps/cli/src/command-internal/connect-errors.unit.test.ts b/apps/cli/src/command-internal/connect-errors.unit.test.ts index 5420f47ef3..7900811923 100644 --- a/apps/cli/src/command-internal/connect-errors.unit.test.ts +++ b/apps/cli/src/command-internal/connect-errors.unit.test.ts @@ -101,7 +101,7 @@ describe("connectFailureMessage", () => { ); }); - it("surfaces the last dial attempt of a dual-stack AggregateError (pgconn last-fallback parity)", () => { + it("surfaces the last dial attempt of a dual-stack AggregateError (last attempt wins)", () => { const aggregate = Object.assign(new AggregateError([], ""), { code: "ECONNREFUSED", errors: [ @@ -114,7 +114,7 @@ describe("connectFailureMessage", () => { ); }); - it("reproduces pgconn's server-error rendering byte-for-byte for a server ErrorResponse", () => { + it("renders a server ErrorResponse byte-for-byte", () => { expect(connectFailureMessage(target, realSqlConnectError(authFailedError()))).toBe( `${prefix} server error (FATAL: password authentication failed for user "postgres" (SQLSTATE 28P01))`, ); @@ -336,7 +336,7 @@ describe("connectSuggestion", () => { expect(connectSuggestion(realSqlConnectError(aggregate), ctx)).toBe(ipv6Suggestion()); }); - it("classifies only the LAST attempt of a mixed-family aggregate (pgconn last-fallback parity)", () => { + it("classifies only the LAST attempt of a mixed-family aggregate (last attempt wins)", () => { const aggregate = Object.assign(new AggregateError([], ""), { code: "EHOSTUNREACH", errors: [ @@ -382,7 +382,7 @@ describe("connectSuggestion", () => { ); }); - it("sets no suggestion for a mid-handshake TLS disconnect, like Go", () => { + it("sets no suggestion for a mid-handshake TLS disconnect", () => { const midHandshake = Object.assign( new Error("Client network socket disconnected before secure TLS connection was established"), { code: "ECONNRESET" }, @@ -390,7 +390,7 @@ describe("connectSuggestion", () => { expect(connectSuggestion(realSqlConnectError(midHandshake), ctx)).toBeUndefined(); }); - it("keeps an IPv4 EADDRNOTAVAIL unclassified, like Go without an IPv6 literal", () => { + it("keeps an IPv4 EADDRNOTAVAIL unclassified without an IPv6 literal", () => { const err = realSqlConnectError(dialError("EADDRNOTAVAIL", "10.1.2.3", 5432)); expect(connectSuggestion(err, ctx)).toBeUndefined(); }); diff --git a/apps/cli/src/command-internal/db-bootstrap/await-storage-ready.unit.test.ts b/apps/cli/src/command-internal/db-bootstrap/await-storage-ready.unit.test.ts index 55b5edb922..db37a3dd56 100644 --- a/apps/cli/src/command-internal/db-bootstrap/await-storage-ready.unit.test.ts +++ b/apps/cli/src/command-internal/db-bootstrap/await-storage-ready.unit.test.ts @@ -149,7 +149,7 @@ describe("awaitStorageReady", () => { ); it.effect( - "is still retrying after 29 seconds, but fails once the 30th second is exhausted — pins Go's hardcoded 30s constant", + "is still retrying after 29 seconds, but fails once the 30th second is exhausted — pins the hardcoded 30s timeout", () => Effect.gen(function* () { const mock = mockSpawner((args) => { diff --git a/apps/cli/src/command-internal/db-bootstrap/bootstrap-config.ts b/apps/cli/src/command-internal/db-bootstrap/bootstrap-config.ts index 577eb95193..eb2da95569 100644 --- a/apps/cli/src/command-internal/db-bootstrap/bootstrap-config.ts +++ b/apps/cli/src/command-internal/db-bootstrap/bootstrap-config.ts @@ -14,7 +14,7 @@ import { Effect, type FileSystem, type Path } from "effect"; import type { LocalServiceVersionOverrides } from "../../shared/services/services.shared.ts"; import { makeRemoteWins } from "../db-config.toml-read.ts"; import { resolveDbImage } from "../db-image.ts"; -import { resolveHealthTimeoutSeconds } from "../go-duration.ts"; +import { resolveHealthTimeoutSeconds } from "../duration.ts"; import { envOverride, envOverrideBool, diff --git a/apps/cli/src/command-internal/db-bootstrap/container-lifecycle.unit.test.ts b/apps/cli/src/command-internal/db-bootstrap/container-lifecycle.unit.test.ts index 5c250bf23b..d8d15c1280 100644 --- a/apps/cli/src/command-internal/db-bootstrap/container-lifecycle.unit.test.ts +++ b/apps/cli/src/command-internal/db-bootstrap/container-lifecycle.unit.test.ts @@ -720,17 +720,14 @@ describe("volumeExists", () => { ); }); - it.live( - "resolves true (protected, not fresh) on an ambiguous inspect failure, matching Go's IsNotFound gate", - () => { - const mock = mockSpawner(() => ({ exitCode: 1, stderr: "permission denied\n" })); - return volumeExists(mock.spawner, "supabase_db_proj").pipe( - Effect.map((exists) => { - expect(exists).toBe(true); - }), - ); - }, - ); + it.live("resolves true (protected, not fresh) on an ambiguous inspect failure", () => { + const mock = mockSpawner(() => ({ exitCode: 1, stderr: "permission denied\n" })); + return volumeExists(mock.spawner, "supabase_db_proj").pipe( + Effect.map((exists) => { + expect(exists).toBe(true); + }), + ); + }); it.live("fails with VolumeInspectError when no runtime can be spawned", () => { const spawner = ChildProcessSpawner.make(() => diff --git a/apps/cli/src/command-internal/db-bootstrap/db-setup.unit.test.ts b/apps/cli/src/command-internal/db-bootstrap/db-setup.unit.test.ts index 97ee24487e..4d850c9793 100644 --- a/apps/cli/src/command-internal/db-bootstrap/db-setup.unit.test.ts +++ b/apps/cli/src/command-internal/db-bootstrap/db-setup.unit.test.ts @@ -366,32 +366,29 @@ describe("startSetupLocalDatabase", () => { }, ); - it.effect( - "labels every one-shot job with the project's Docker labels, matching Go's DockerStart (review: Codex, PR #6022)", - () => { - const workdir = makeWorkdir(); - const { session } = fakeSession(); - const out = mockOutput(); - const docker = mockDockerRun(); - // Default config: realtime, storage, and auth are all enabled — 3 jobs. - return run( - baseInput(workdir, session, { majorVersion: 15, projectId: "labeled-proj" }), - out, - docker, - ).pipe( - Effect.map(() => { - expect(docker.runs.length).toBe(3); - for (const job of docker.runs) { - expect(job.labels).toEqual({ - "com.supabase.cli.project": "labeled-proj", - "com.docker.compose.project": "labeled-proj", - }); - } - rmSync(workdir, { recursive: true, force: true }); - }), - ); - }, - ); + it.effect("labels every one-shot job with the project's Docker labels", () => { + const workdir = makeWorkdir(); + const { session } = fakeSession(); + const out = mockOutput(); + const docker = mockDockerRun(); + // Default config: realtime, storage, and auth are all enabled — 3 jobs. + return run( + baseInput(workdir, session, { majorVersion: 15, projectId: "labeled-proj" }), + out, + docker, + ).pipe( + Effect.map(() => { + expect(docker.runs.length).toBe(3); + for (const job of docker.runs) { + expect(job.labels).toEqual({ + "com.supabase.cli.project": "labeled-proj", + "com.docker.compose.project": "labeled-proj", + }); + } + rmSync(workdir, { recursive: true, force: true }); + }), + ); + }); it.effect( "the realtime job's env matches `buildRealtimeEnv` on the internal db address + jwks", @@ -489,31 +486,28 @@ describe("startSetupLocalDatabase", () => { }, ); - it.effect( - "--debug tees every one-shot job's stderr, matching Go's utils.GetDebugLogger()", - () => { - const workdir = makeWorkdir(); - const { session } = fakeSession(); - const out = mockOutput(); - const docker = mockDockerRun(); - const config = decodeConfig({ - realtime: { enabled: true }, - storage: { enabled: false }, - auth: { enabled: true }, - }); - return run( - baseInput(workdir, session, { majorVersion: 15, config, debug: true }), - out, - docker, - ).pipe( - Effect.map(() => { - expect(docker.runs.length).toBe(2); - expect(docker.captureOptsCalls).toEqual([{ teeStderr: true }, { teeStderr: true }]); - rmSync(workdir, { recursive: true, force: true }); - }), - ); - }, - ); + it.effect("--debug tees every one-shot job's stderr", () => { + const workdir = makeWorkdir(); + const { session } = fakeSession(); + const out = mockOutput(); + const docker = mockDockerRun(); + const config = decodeConfig({ + realtime: { enabled: true }, + storage: { enabled: false }, + auth: { enabled: true }, + }); + return run( + baseInput(workdir, session, { majorVersion: 15, config, debug: true }), + out, + docker, + ).pipe( + Effect.map(() => { + expect(docker.runs.length).toBe(2); + expect(docker.captureOptsCalls).toEqual([{ teeStderr: true }, { teeStderr: true }]); + rmSync(workdir, { recursive: true, force: true }); + }), + ); + }); it.effect("without --debug, one-shot jobs run with teeStderr off", () => { const workdir = makeWorkdir(); @@ -679,46 +673,40 @@ describe("resolveDbSetupPrelude", () => { ); }); - it.effect( - 'prints the banner BEFORE a JWKS resolution failure — matching Go\'s "initSchema" printing the banner before ever calling "initSchema15" -> "ResolveJWKS" (review: PRRT_kwDOErm0O86W6R-O)', - () => { - const out = mockOutput(); - return run( - { - majorVersion: 15, - realtimeEnabledForSetup: true, - jwks: Effect.fail(new Error("jwks discovery failed")), - }, - out, - ).pipe( - Effect.flip, - Effect.map((error) => { - expect(error.message).toBe("jwks discovery failed"); - const banner = out.rawChunks.filter((c) => c.text === "Initialising schema...\n"); - expect(banner.length).toBe(1); - expect(banner[0]?.stream).toBe("stderr"); - }), - ); - }, - ); + it.effect("prints the banner BEFORE a JWKS resolution failure", () => { + const out = mockOutput(); + return run( + { + majorVersion: 15, + realtimeEnabledForSetup: true, + jwks: Effect.fail(new Error("jwks discovery failed")), + }, + out, + ).pipe( + Effect.flip, + Effect.map((error) => { + expect(error.message).toBe("jwks discovery failed"); + const banner = out.rawChunks.filter((c) => c.text === "Initialising schema...\n"); + expect(banner.length).toBe(1); + expect(banner[0]?.stream).toBe("stderr"); + }), + ); + }); - it.effect( - "does not resolve JWKS on PG <= 14 even with realtime enabled — Go's initSchema never reaches initSchema15 there", - () => { - const out = mockOutput(); - let jwksCalled = false; - const jwks = Effect.sync(() => { - jwksCalled = true; - return "unused"; - }); - return run({ majorVersion: 14, realtimeEnabledForSetup: true, jwks }, out).pipe( - Effect.map((resolved) => { - expect(jwksCalled).toBe(false); - expect(resolved.jwks).toBe(""); - }), - ); - }, - ); + it.effect("does not resolve JWKS on PG <= 14 even with realtime enabled", () => { + const out = mockOutput(); + let jwksCalled = false; + const jwks = Effect.sync(() => { + jwksCalled = true; + return "unused"; + }); + return run({ majorVersion: 14, realtimeEnabledForSetup: true, jwks }, out).pipe( + Effect.map((resolved) => { + expect(jwksCalled).toBe(false); + expect(resolved.jwks).toBe(""); + }), + ); + }); }); /** diff --git a/apps/cli/src/command-internal/db-bootstrap/docker-create-args.unit.test.ts b/apps/cli/src/command-internal/db-bootstrap/docker-create-args.unit.test.ts index 21f248b19a..39aa659629 100644 --- a/apps/cli/src/command-internal/db-bootstrap/docker-create-args.unit.test.ts +++ b/apps/cli/src/command-internal/db-bootstrap/docker-create-args.unit.test.ts @@ -226,7 +226,7 @@ describe("buildStartContainerCreateArgs", () => { ); }); - test("emits cmd tokens after the image even when entrypoint is absent (Pooler: start.go:1234-1237)", () => { + test("emits cmd tokens after the image even when entrypoint is absent", () => { const spec: StartContainerSpec = { image: "supabase/supavisor:2.0.0", containerName: "supabase_pooler_proj", @@ -247,7 +247,7 @@ describe("buildStartContainerCreateArgs", () => { ]); }); - test("omits health flags entirely when healthcheck is absent (Kong/PostgREST: start.go:975)", () => { + test("omits health flags entirely when healthcheck is absent", () => { const spec: StartContainerSpec = { image: "kong:3", containerName: "supabase_kong_proj", diff --git a/apps/cli/src/command-internal/db-bootstrap/local-container-inputs.ts b/apps/cli/src/command-internal/db-bootstrap/local-container-inputs.ts index ce589339a3..e0a320cfe9 100644 --- a/apps/cli/src/command-internal/db-bootstrap/local-container-inputs.ts +++ b/apps/cli/src/command-internal/db-bootstrap/local-container-inputs.ts @@ -20,7 +20,7 @@ import { DbConfigLoadError } from "../db-config.errors.ts"; import { InvalidServiceVersionTagError } from "../service-version-overrides.ts"; import { localDbContainerId } from "../docker-ids.ts"; import { resolveDockerNetworkMode } from "../../shared/functions/functions-docker.ts"; -import { viperEnvStringWithProjectFallback } from "../viper-env.ts"; +import { supabaseEnvStringWithProjectFallback } from "../supabase-env.ts"; import { isBitbucketPipeline } from "../bitbucket-pipeline.ts"; import { resolveAuthExternalUrl, @@ -136,7 +136,7 @@ export const buildLocalDbContainerInputs = ( // See {@link resolveDockerNetworkMode} for the full flag/env/fallback precedence. const networkId = resolveDockerNetworkMode({ explicit: Option.getOrUndefined(networkIdFlag), - envOverride: viperEnvStringWithProjectFallback("SUPABASE_NETWORK_ID", projectEnvValues), + envOverride: supabaseEnvStringWithProjectFallback("SUPABASE_NETWORK_ID", projectEnvValues), projectId, }); // Only needed on Linux; Docker Desktop already resolves `host.docker.internal` elsewhere. diff --git a/apps/cli/src/command-internal/db-bootstrap/postgres.service.ts b/apps/cli/src/command-internal/db-bootstrap/postgres.service.ts index be97398051..18b0b3b84d 100644 --- a/apps/cli/src/command-internal/db-bootstrap/postgres.service.ts +++ b/apps/cli/src/command-internal/db-bootstrap/postgres.service.ts @@ -11,7 +11,7 @@ import type { CliConfig } from "@supabase/config"; import { localDbContainerId } from "../docker-ids.ts"; import { toDockerMountPath } from "../docker-path.ts"; -import { encodeToml } from "../go-output.encoders.ts"; +import { encodeToml } from "../output.encoders.ts"; import { POSTGRES_DEFAULT_ROOT_KEY } from "../local-config-values.ts"; import type { StartContainerSpec } from "./docker-create-args.ts"; import { START_DB_RESTORE_SH } from "./templates/db-restore.sh.ts"; diff --git a/apps/cli/src/command-internal/db-bootstrap/postgres.service.unit.test.ts b/apps/cli/src/command-internal/db-bootstrap/postgres.service.unit.test.ts index f431f2181c..47f7bf755c 100644 --- a/apps/cli/src/command-internal/db-bootstrap/postgres.service.unit.test.ts +++ b/apps/cli/src/command-internal/db-bootstrap/postgres.service.unit.test.ts @@ -114,7 +114,7 @@ describe("buildPostgresStartContainerSpec", () => { expect(spec.secretFiles).toBeUndefined(); }); - test("uses a rootKey override instead of the Go default when provided", () => { + test("uses a rootKey override instead of the default when provided", () => { const spec = buildPostgresStartContainerSpec( baseInput({ db: baseDb({ major_version: 17 }), rootKey: "custom-root-key" }), ); @@ -163,7 +163,7 @@ describe("buildPostgresStartContainerSpec", () => { }); }); - test("OrioleDB branch defaults unset S3 fields to empty strings, matching Go's zero-value string fields", () => { + test("OrioleDB branch defaults unset S3 fields to empty strings", () => { const spec = buildPostgresStartContainerSpec( baseInput({ db: baseDb({ orioledb_version: "17.4.1.030" }) }), ); @@ -223,7 +223,7 @@ describe("buildPostgresStartContainerSpec", () => { expect(spec.env.POSTGRES_INITDB_ARGS).toBeUndefined(); }); - test("healthcheck matches Go's pg_isready probe", () => { + test("healthcheck uses the pg_isready probe", () => { const spec = buildPostgresStartContainerSpec(baseInput()); expect(spec.healthcheck).toEqual({ test: ["CMD", "pg_isready", "-U", "postgres", "-h", "127.0.0.1", "-p", "5432"], @@ -380,7 +380,7 @@ describe("postgresImageVersionTag", () => { expect(postgresImageVersionTag("supabase/postgres:17.4.1.030")).toBe("17.4.1.030"); }); - test("degrades to the whole string when there is no colon at all, matching Go's Image[i+1:] with i=-1", () => { + test("degrades to the whole string when there is no colon at all", () => { expect(postgresImageVersionTag("supabase/postgres")).toBe("supabase/postgres"); }); }); @@ -451,7 +451,7 @@ describe("buildShadowPostgresContainerSpec", () => { expect(spec.labels).toEqual({}); }); - test("initializes POSTGRES_PASSWORD from the resolved [db] password, not a hardcoded literal — the deliberate TS extension the input's own doc describes (Go rejects the toml key at config load and always uses 'postgres')", () => { + test("initializes POSTGRES_PASSWORD from the resolved [db] password, not a hardcoded literal", () => { const spec = buildShadowPostgresContainerSpec(baseShadowInput({ password: "hunter2" })); expect(spec.env?.["POSTGRES_PASSWORD"]).toBe("hunter2"); }); diff --git a/apps/cli/src/command-internal/db-bootstrap/realtime-env.unit.test.ts b/apps/cli/src/command-internal/db-bootstrap/realtime-env.unit.test.ts index 18ed9b0dc9..a4f456cd5f 100644 --- a/apps/cli/src/command-internal/db-bootstrap/realtime-env.unit.test.ts +++ b/apps/cli/src/command-internal/db-bootstrap/realtime-env.unit.test.ts @@ -24,7 +24,7 @@ describe("buildRealtimeEnv", () => { expect(env["API_JWT_JWKS"]).toBe(base.jwks); }); - test("matches Go's remaining static env values", () => { + test("sets the remaining static env values", () => { const env = buildRealtimeEnv(base); expect(env).toMatchObject({ PORT: "4000", diff --git a/apps/cli/src/command-internal/db-bootstrap/recreate-local-database.unit.test.ts b/apps/cli/src/command-internal/db-bootstrap/recreate-local-database.unit.test.ts index bc1313b5eb..c05c77d8b5 100644 --- a/apps/cli/src/command-internal/db-bootstrap/recreate-local-database.unit.test.ts +++ b/apps/cli/src/command-internal/db-bootstrap/recreate-local-database.unit.test.ts @@ -63,7 +63,7 @@ describe("resetDisconnectClients", () => { ); it.effect( - "is still retrying after 9 one-second backoffs, but fails once the 10th is exhausted — pins Go's `NewBackoffPolicy(ctx, 10*time.Second)` constant", + "is still retrying after 9 one-second backoffs, but fails once the 10th is exhausted — pins the 10-second backoff limit", () => Effect.gen(function* () { // Never drains — pegs the retry schedule to its hard 10-retry ceiling. diff --git a/apps/cli/src/command-internal/db-bootstrap/shadow-cache.ts b/apps/cli/src/command-internal/db-bootstrap/shadow-cache.ts index 1ffe855cc4..3a64dfddfa 100644 --- a/apps/cli/src/command-internal/db-bootstrap/shadow-cache.ts +++ b/apps/cli/src/command-internal/db-bootstrap/shadow-cache.ts @@ -20,7 +20,7 @@ import { } from "effect"; import type { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"; -import { viperEnvBoolWithProjectFallback } from "../viper-env.ts"; +import { supabaseEnvBoolWithProjectFallback } from "../supabase-env.ts"; import { Output } from "../../shared/output/output.service.ts"; import { containerCliExitCode, describeContainerCliFailure } from "../container-cli.ts"; import { DbConnection } from "../db-connection.service.ts"; @@ -60,7 +60,7 @@ import { type Spawner = ChildProcessSpawner["Service"]; -/** `SUPABASE_SHADOW_CACHE` — opt-out gate (viper bool when set; unset is ON). */ +/** `SUPABASE_SHADOW_CACHE` — opt-out gate (parsed as a boolean when set; unset is ON). */ export const SHADOW_CACHE_ENV = "SUPABASE_SHADOW_CACHE"; /** @@ -688,7 +688,7 @@ export const peekShadowBaseline = ( Effect.gen(function* () { if ( opts.bypassCache === true || - !viperEnvBoolWithProjectFallback(SHADOW_CACHE_ENV, input.setup.projectEnvValues ?? {}) + !supabaseEnvBoolWithProjectFallback(SHADOW_CACHE_ENV, input.setup.projectEnvValues ?? {}) ) { return { state: "uncachable" } as const; } @@ -868,7 +868,7 @@ export const acquireShadowDatabase = ( Effect.gen(function* () { if ( opts.bypassCache === true || - !viperEnvBoolWithProjectFallback(SHADOW_CACHE_ENV, input.setup.projectEnvValues ?? {}, { + !supabaseEnvBoolWithProjectFallback(SHADOW_CACHE_ENV, input.setup.projectEnvValues ?? {}, { whenUnset: true, }) ) { diff --git a/apps/cli/src/command-internal/db-bootstrap/shadow-database.ts b/apps/cli/src/command-internal/db-bootstrap/shadow-database.ts index 9451314dc1..b4317f66e1 100644 --- a/apps/cli/src/command-internal/db-bootstrap/shadow-database.ts +++ b/apps/cli/src/command-internal/db-bootstrap/shadow-database.ts @@ -751,8 +751,8 @@ const migrateShadowDatabaseWith = ( ); /** - * Migrates a shadow for migra and the legacy pg-delta engine. Those Go-backed - * workflows historically include `pg_net` in the platform baseline regardless of + * Migrates a shadow for migra and the legacy pg-delta engine. Those + * workflows include `pg_net` in the platform baseline regardless of * project config, so preserve that baseline while sharing the native TS setup path. */ export const migrateShadowDatabase = ( diff --git a/apps/cli/src/command-internal/db-bootstrap/start-local-database.ts b/apps/cli/src/command-internal/db-bootstrap/start-local-database.ts index db9961f353..72c96f3778 100644 --- a/apps/cli/src/command-internal/db-bootstrap/start-local-database.ts +++ b/apps/cli/src/command-internal/db-bootstrap/start-local-database.ts @@ -45,9 +45,9 @@ import { resolveLocalConfigValues, resolveThirdPartyProviders, } from "../local-config-values.ts"; -import { parseGoDuration, resolveHealthTimeoutSeconds } from "../go-duration.ts"; +import { parseDuration, resolveHealthTimeoutSeconds } from "../duration.ts"; import { ramInBytes } from "../size-units.ts"; -import { goUrlParse } from "../storage-url.ts"; +import { parseUrl } from "../storage-url.ts"; import { loadLocalProjectContext } from "../local-project-context.ts"; import { cliProjectFilterValue } from "../docker-ids.ts"; import { buildLocalDbContainerInputs } from "./local-container-inputs.ts"; @@ -137,7 +137,7 @@ export const startLocalDatabase = Effect.fn("DbBootstrap.startLocalDatabase")(fu resolveAuthEmail(config.auth.email, authDocForValidation, projectEnvValues), ); yield* wrapDbConfigOverride("auth.email.max_frequency", () => - parseGoDuration(resolvedEmailForValidation.max_frequency), + parseDuration(resolvedEmailForValidation.max_frequency), ); yield* wrapDbConfigOverride("auth.email.smtp", () => resolveAuthEmailSmtp(authDocForValidation, projectEnvValues), @@ -146,7 +146,7 @@ export const startLocalDatabase = Effect.fn("DbBootstrap.startLocalDatabase")(fu resolveAuthSms(authDocForValidation, config.auth.sms, projectEnvValues), ); yield* wrapDbConfigOverride("auth.sms.max_frequency", () => - parseGoDuration(smsForValidation.max_frequency), + parseDuration(smsForValidation.max_frequency), ); const authEnabledForValidation = envOverrideBool( "SUPABASE_AUTH_ENABLED", @@ -173,16 +173,16 @@ export const startLocalDatabase = Effect.fn("DbBootstrap.startLocalDatabase")(fu const gotrueSessionsForValidation = resolveGotrueSessions(config.auth.sessions, projectEnvValues); if (gotrueSessionsForValidation?.timebox !== undefined) { yield* wrapDbConfigOverride("auth.sessions.timebox", () => - parseGoDuration(gotrueSessionsForValidation.timebox!), + parseDuration(gotrueSessionsForValidation.timebox!), ); } if (gotrueSessionsForValidation?.inactivity_timeout !== undefined) { yield* wrapDbConfigOverride("auth.sessions.inactivity_timeout", () => - parseGoDuration(gotrueSessionsForValidation.inactivity_timeout!), + parseDuration(gotrueSessionsForValidation.inactivity_timeout!), ); } yield* wrapDbConfigOverride("auth.mfa.phone.max_frequency", () => - parseGoDuration(resolveAuthMfa(config.auth.mfa, projectEnvValues).phone.max_frequency), + parseDuration(resolveAuthMfa(config.auth.mfa, projectEnvValues).phone.max_frequency), ); yield* wrapDbConfigOverride("auth.rate_limit", () => resolveGotrueRateLimit(config.auth.rate_limit, projectEnvValues), @@ -532,7 +532,7 @@ export const startLocalDatabase = Effect.fn("DbBootstrap.startLocalDatabase")(fu config.studio.api_url; if (studioEnabledForValidation) { yield* Effect.try({ - try: () => goUrlParse(studioApiUrlForValidation), + try: () => parseUrl(studioApiUrlForValidation), catch: (cause) => new DbConfigLoadError({ message: `Invalid config for studio.api_url: ${cause instanceof Error ? cause.message : String(cause)}`, diff --git a/apps/cli/src/command-internal/db-config.integration.test.ts b/apps/cli/src/command-internal/db-config.integration.test.ts index a556e887e8..22a11e75aa 100644 --- a/apps/cli/src/command-internal/db-config.integration.test.ts +++ b/apps/cli/src/command-internal/db-config.integration.test.ts @@ -608,46 +608,43 @@ describe("dbConfigResolver (db-url under the stack backend)", () => { }); describe("dbConfigResolver (linked config ordering)", () => { - it.effect( - "validates the ref-merged config before any network work (Go ParseDatabaseConfig order)", - () => { - // The ref is sourced from the config's top-level project_id; the matching remote block - // sets an unsupported major_version. If validation happened after the connection work, - // `mockDbConnection.connect()` would die first. - const ref = "abcdefghijklmnopqrst"; - const dir = withWorkdir( - [ - `project_id = "${ref}"`, - "[db]", - "major_version = 15", - `[remotes.${ref.slice(0, 4)}]`, - `project_id = "${ref}"`, - `[remotes.${ref.slice(0, 4)}.db]`, - "major_version = 99", - "", - ].join("\n"), - ); - // The linked ref is sourced via the project-ref resolver's env fallback. - return resolve(dir, linkedFlags, { configEnv: { SUPABASE_PROJECT_ID: ref } }).pipe( - Effect.exit, - Effect.tap((exit) => - Effect.sync(() => { - expect(Exit.isFailure(exit)).toBe(true); - if (Exit.isFailure(exit)) { - expect(JSON.stringify(exit.cause)).toContain( - "Failed reading config: Invalid db.major_version: 99.", - ); - } - }), - ), - Effect.ensuring( - Effect.sync(() => { - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("validates the ref-merged config before any network work", () => { + // The ref is sourced from the config's top-level project_id; the matching remote block + // sets an unsupported major_version. If validation happened after the connection work, + // `mockDbConnection.connect()` would die first. + const ref = "abcdefghijklmnopqrst"; + const dir = withWorkdir( + [ + `project_id = "${ref}"`, + "[db]", + "major_version = 15", + `[remotes.${ref.slice(0, 4)}]`, + `project_id = "${ref}"`, + `[remotes.${ref.slice(0, 4)}.db]`, + "major_version = 99", + "", + ].join("\n"), + ); + // The linked ref is sourced via the project-ref resolver's env fallback. + return resolve(dir, linkedFlags, { configEnv: { SUPABASE_PROJECT_ID: ref } }).pipe( + Effect.exit, + Effect.tap((exit) => + Effect.sync(() => { + expect(Exit.isFailure(exit)).toBe(true); + if (Exit.isFailure(exit)) { + expect(JSON.stringify(exit.cause)).toContain( + "Failed reading config: Invalid db.major_version: 99.", + ); + } + }), + ), + Effect.ensuring( + Effect.sync(() => { + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); it.effect("surfaces a project-ref read failure instead of reporting not-linked", () => { // The ref file is seeded as a directory (not a file), with no project_id or env fallback, diff --git a/apps/cli/src/command-internal/db-config.parse.ts b/apps/cli/src/command-internal/db-config.parse.ts index 75ca603477..38031e3c3a 100644 --- a/apps/cli/src/command-internal/db-config.parse.ts +++ b/apps/cli/src/command-internal/db-config.parse.ts @@ -157,7 +157,7 @@ function unbracketIpv6(host: string): string { return host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host; } -/** Sentinel for a present-but-non-numeric `connect_timeout` (pgconn parse error). */ +/** Sentinel for a present-but-non-numeric `connect_timeout` (parse error). */ const CONNECT_TIMEOUT_INVALID = Symbol("connect-timeout-invalid"); /** diff --git a/apps/cli/src/command-internal/db-config.parse.unit.test.ts b/apps/cli/src/command-internal/db-config.parse.unit.test.ts index fdefee3402..c49f408dc3 100644 --- a/apps/cli/src/command-internal/db-config.parse.unit.test.ts +++ b/apps/cli/src/command-internal/db-config.parse.unit.test.ts @@ -40,7 +40,7 @@ describe("parseConnectionString (URL form)", () => { }); }); - it("defaults the user to the OS account when userinfo is omitted (libpq/pgconn parity)", () => { + it("defaults the user to the OS account when userinfo is omitted (libpq parity)", () => { expect(parseConnectionString("postgresql://localhost/mydb")).toEqual({ host: "localhost", port: 5432, @@ -92,7 +92,7 @@ describe("parseConnectionString (URL form)", () => { } }); - it("honors libpq query params (host/dbname) over the structural URL (pgconn parity)", () => { + it("honors libpq query params (host/dbname) over the structural URL", () => { expect(parseConnectionString("postgresql:///postgres?host=/var/run/postgresql")).toEqual({ host: "/var/run/postgresql", port: 5432, @@ -111,7 +111,7 @@ describe("parseConnectionString (URL form)", () => { }); }); - it("strips the brackets from an IPv6 literal host (Go url.Hostname parity)", () => { + it("strips the brackets from an IPv6 literal host", () => { expect(parseConnectionString("postgresql://postgres:pw@[::1]:5432/postgres")).toEqual({ host: "::1", port: 5432, @@ -135,7 +135,7 @@ describe("parseConnectionString (URL form)", () => { expect(parsed).not.toHaveProperty("options"); }); - it("collects non-structural query settings as runtimeParams (pgconn parity)", () => { + it("collects non-structural query settings as runtimeParams", () => { const parsed = parseConnectionString( "postgres://u:pw@h/db?search_path=tenant&statement_timeout=5000&sslmode=require&options=reference%3Dabc", ); @@ -149,13 +149,13 @@ describe("parseConnectionString (URL form)", () => { expect(parsed).not.toHaveProperty("runtimeParams"); }); - it("merges PGAPPNAME into runtimeParams as application_name (pgconn env merge)", () => { + it("merges PGAPPNAME into runtimeParams as application_name (env merge)", () => { const env = (name: string): string | undefined => (name === "PGAPPNAME" ? "myapp" : undefined); const parsed = parseConnectionString("postgres://u:pw@h/db", env); expect(parsed?.runtimeParams).toEqual({ application_name: "myapp" }); }); - it("lets a connection-string application_name override PGAPPNAME (pgconn precedence)", () => { + it("lets a connection-string application_name override PGAPPNAME (precedence)", () => { const env = (name: string): string | undefined => name === "PGAPPNAME" ? "from-env" : undefined; const parsed = parseConnectionString("postgres://u:pw@h/db?application_name=from-url", env); @@ -185,7 +185,7 @@ describe("parseConnectionString (URL form)", () => { expect(parsed).not.toHaveProperty("runtimeParams"); }); - it("resolves client certs from PGSSLCERT/PGSSLKEY env (pgconn precedence)", () => { + it("resolves client certs from PGSSLCERT/PGSSLKEY env (precedence)", () => { const env = (name: string): string | undefined => name === "PGSSLCERT" ? "/e/c.crt" : name === "PGSSLKEY" ? "/e/c.key" : undefined; const parsed = parseConnectionString("postgres://u:pw@h/db", env); @@ -193,7 +193,7 @@ describe("parseConnectionString (URL form)", () => { expect(parsed?.sslkey).toBe("/e/c.key"); }); - it("rejects a client cert with sslcert but no sslkey (pgconn both-or-neither)", () => { + it("rejects a client cert with sslcert but no sslkey (both or neither)", () => { expect(parseConnectionString("postgres://u:pw@h/db?sslcert=/c/client.crt")).toBeUndefined(); expect(parseConnectionString("host=h user=u sslkey=/c/client.key")).toBeUndefined(); }); @@ -206,7 +206,7 @@ describe("parseConnectionString (URL form)", () => { expect(parseConnectionString("postgres://user:p%zz@example.com/db")).toBeUndefined(); }); - it("rejects a non-numeric or empty ?port= query override (pgconn parsePort error)", () => { + it("rejects a non-numeric or empty ?port= query override (invalid port)", () => { expect(parseConnectionString("postgresql://host/db?port=abc")).toBeUndefined(); expect(parseConnectionString("postgresql://db.example.com/app?port=")).toBeUndefined(); }); @@ -223,7 +223,7 @@ describe("parseConnectionString (URL form)", () => { } }); - it("rejects an invalid sslmode value (pgconn 'sslmode is invalid')", () => { + it("rejects an invalid sslmode value ('sslmode is invalid')", () => { expect(parseConnectionString("postgres://h/db?sslmode=verifyfull")).toBeUndefined(); expect(parseConnectionString("host=h sslmode=bogus")).toBeUndefined(); }); @@ -237,7 +237,7 @@ describe("parseConnectionString (URL form)", () => { }); }); - it("fills sslmode from PGSSLMODE when the URL omits it (pgconn env default)", () => { + it("fills sslmode from PGSSLMODE when the URL omits it (env default)", () => { const prev = process.env["PGSSLMODE"]; process.env["PGSSLMODE"] = "verify-full"; try { @@ -292,7 +292,7 @@ describe("parseConnectionString (libpq keyword/value DSN)", () => { }); }); - it("prefers PGUSER over the OS account for the default user (pgconn env precedence)", () => { + it("prefers PGUSER over the OS account for the default user (env precedence)", () => { const prev = process.env["PGUSER"]; process.env["PGUSER"] = "pg_role"; try { @@ -311,7 +311,7 @@ describe("parseConnectionString (libpq keyword/value DSN)", () => { } }); - it("fills omitted DSN fields from PG* env vars (pgconn env defaults)", () => { + it("fills omitted DSN fields from PG* env vars (env defaults)", () => { const prev = { PGHOST: process.env["PGHOST"], PGPORT: process.env["PGPORT"], @@ -365,7 +365,7 @@ describe("parseConnectionString (libpq keyword/value DSN)", () => { }); }); -describe("empty-password precedence (pgconn parity)", () => { +describe("empty-password precedence", () => { // Points PGPASSFILE at a temp file we control and sets PGPASSWORD, to prove which one wins. let tmp: string; let pgpassPath: string; @@ -425,7 +425,7 @@ describe("empty-password precedence (pgconn parity)", () => { }); }); -describe("multi-host failover (pgconn parity)", () => { +describe("multi-host failover", () => { it("parses a comma-separated multi-host URL into primary + fallbacks", () => { expect(parseConnectionString("postgres://u:pw@h1:5432,h2:5433/db")).toEqual({ host: "h1", @@ -448,7 +448,7 @@ describe("multi-host failover (pgconn parity)", () => { }); }); - it("zips hosts to the (compacted) port list exactly as pgconn does", () => { + it("zips hosts to the (compacted) port list positionally", () => { // Empty ports are dropped before zipping, so a host that omits a port takes the next entry // in the compacted port list rather than inheriting the previous host's port; only a host // past the end reuses `ports[0]`. For `h1:5432,h2,h3:5544` the port list is `[5432, 5544]`: @@ -477,7 +477,7 @@ describe("multi-host failover (pgconn parity)", () => { ).toMatchObject({ host: "h1", sslmode: "require", fallbacks: [{ host: "h2", port: 5433 }] }); }); - it("rejects a multi-host URL with a non-numeric port (pgconn parsePort error)", () => { + it("rejects a multi-host URL with a non-numeric port (invalid port)", () => { expect(parseConnectionString("postgres://u:pw@h1:5432,h2:bad/db")).toBeUndefined(); }); @@ -494,7 +494,7 @@ describe("multi-host failover (pgconn parity)", () => { }); }); -describe("passfile= DSN setting (pgconn parity)", () => { +describe("passfile= DSN setting", () => { // Points PGPASSFILE at one file and `passfile=` at a different one, to prove the // connection-string setting wins. let tmp: string; @@ -591,7 +591,7 @@ describe("injected env lookup (project dotenv parity)", () => { }); }); -describe("pgservice resolution (pgconn parity)", () => { +describe("pgservice resolution", () => { // A `service=`/`PGSERVICE` resolves against the service file and merges its settings between // env and the explicit connection-string fields. `dbname` is remapped to `database`. An // unresolvable service is a hard parse error. @@ -669,7 +669,7 @@ describe("pgservice resolution (pgconn parity)", () => { }); }); -describe("keyword/value DSN backslash handling (pgconn parity)", () => { +describe("keyword/value DSN backslash handling", () => { it("preserves backslashes before ordinary chars (Windows cert paths)", () => { expect( parseConnectionString("host=h dbname=d user=u sslrootcert=C:\\certs\\root.pem")?.sslrootcert, @@ -693,7 +693,7 @@ describe("keyword/value DSN backslash handling (pgconn parity)", () => { ).toBe("C:\\certs\\root.pem"); }); - it("rejects an unquoted value ending in a lone backslash (pgconn 'invalid backslash')", () => { + it("rejects an unquoted value ending in a lone backslash ('invalid backslash')", () => { expect(parseConnectionString("host=h user=u password=secret\\")).toBeUndefined(); // A complete trailing `\\` escape pair is still accepted (→ single backslash). expect(parseConnectionString("host=h user=u dbname=d sslrootcert=C:\\\\")?.sslrootcert).toBe( @@ -702,7 +702,7 @@ describe("keyword/value DSN backslash handling (pgconn parity)", () => { }); }); -describe("connect_timeout (pgconn parity)", () => { +describe("connect_timeout", () => { it("parses connect_timeout from a URL query into connectTimeoutSeconds", () => { expect(parseConnectionString("postgres://u:p@h/db?connect_timeout=15")).toMatchObject({ connectTimeoutSeconds: 15, @@ -723,7 +723,7 @@ describe("connect_timeout (pgconn parity)", () => { }); }); - it("omits connectTimeoutSeconds when unset or zero (driver applies Go's default)", () => { + it("omits connectTimeoutSeconds when unset or zero (driver applies its default)", () => { expect(parseConnectionString("postgres://u:p@h/db")).not.toHaveProperty( "connectTimeoutSeconds", ); @@ -738,7 +738,7 @@ describe("connect_timeout (pgconn parity)", () => { }); }); -describe("empty URL query overrides (pgconn parity)", () => { +describe("empty URL query overrides", () => { it("an empty ?dbname= overrides the path with an empty database", () => { expect(parseConnectionString("postgres://u:p@host/production?dbname=")).toMatchObject({ database: "", @@ -757,7 +757,7 @@ describe("empty URL query overrides (pgconn parity)", () => { }); }); -describe("pgconn parse refinements", () => { +describe("DSN parse refinements", () => { it("accepts a comma-separated ?port= list for a multi-host URL", () => { expect(parseConnectionString("postgres://h1,h2/db?port=5432,5433")).toMatchObject({ host: "h1", @@ -799,7 +799,7 @@ describe("pgconn parse refinements", () => { }); }); -describe("database= alias and empty service values (pgconn parity)", () => { +describe("database= alias and empty service values", () => { let tmp: string; beforeEach(() => { @@ -821,7 +821,7 @@ describe("database= alias and empty service values (pgconn parity)", () => { }); }); - it("uses last-wins for dbname/database aliases in a DSN (pgconn remaps at parse time)", () => { + it("uses last-wins for dbname/database aliases in a DSN (remapped at parse time)", () => { expect(parseConnectionString("host=h user=u dbname=template1 database=appdb")).toMatchObject({ database: "appdb", }); @@ -862,7 +862,7 @@ describe("database= alias and empty service values (pgconn parity)", () => { }); }); -describe("more pgconn parse refinements", () => { +describe("more DSN parse refinements", () => { it("honors a present-but-empty ?host= as a literal empty host (overrides structural)", () => { expect(parseConnectionString("postgres://remote.example.com/postgres?host=")).toMatchObject({ host: "", @@ -879,7 +879,7 @@ describe("more pgconn parse refinements", () => { }); }); - it("does not dial hostaddr as the host (pgconn ignores hostaddr)", () => { + it("does not dial hostaddr as the host (hostaddr is ignored)", () => { const env = (name: string): string | undefined => (name === "PGHOST" ? "envhost" : undefined); const parsed = parseConnectionString("hostaddr=10.0.0.5 user=u", env); expect(parsed?.host).toBe("envhost"); diff --git a/apps/cli/src/command-internal/db-config.toml-read.ts b/apps/cli/src/command-internal/db-config.toml-read.ts index 60bcd879d9..d1532c6adb 100644 --- a/apps/cli/src/command-internal/db-config.toml-read.ts +++ b/apps/cli/src/command-internal/db-config.toml-read.ts @@ -12,7 +12,7 @@ import { type ExperimentalInput, type HookInput, type MfaFactorInput, - parseGoBool, + parseBoolLiteral, type PasskeyInput, resolveEmailTemplateContentPath, resolveSigningKeysPath, @@ -642,18 +642,18 @@ function expandEnv(value: string, lookup: (name: string) => string | undefined): return envRefValue(value, lookup(name)); } -/** `[db]` ports decode into `uint16`. */ +/** `[db]` ports are integers in 0-65535. */ const MAX_PORT = 65535; /** - * Resolve a `[db]` port field: the TOML value decodes into a `uint16`, and a + * Resolve a `[db]` port field: the TOML value must be an integer in 0-65535, and a * quoted `env(VAR)` reference is expanded first, then parsed as the port. * Resolution rules: * * - **Omitted** (`undefined`) → the schema default. - * - **Present and resolves to a `uint16`** (a plain integer in range, or an + * - **Present and resolves to a valid port** (a plain integer in range, or an * `env(VAR)` string that expands to one) → that value. - * - **Present but cannot unmarshal** (non-numeric, negative, out of range, or an + * - **Present but invalid** (non-numeric, negative, out of range, or an * unresolved `env(VAR)`) → `undefined`, signalling the caller to abort with * `DbConfigLoadError` rather than silently defaulting and running * against the default local database while hiding a broken config. @@ -733,7 +733,7 @@ function joinSupabaseSeedPath(pattern: string): string { * relative and joins under `supabase/` — unlike Node's win32 `isAbsolute`, * which treats it as rooted at the current drive. */ -const goIsAbs = (pathSvc: Path.Path, pattern: string): boolean => { +const isAbsolutePattern = (pathSvc: Path.Path, pattern: string): boolean => { if (process.platform !== "win32") { return pathSvc.isAbsolute(pattern); } @@ -756,7 +756,9 @@ const goIsAbs = (pathSvc: Path.Path, pattern: string): boolean => { * override — all three feed the glob the same resolved paths. */ export const resolveSeedSqlPath = (pathSvc: Path.Path, pattern: string): string => - pattern.length === 0 || goIsAbs(pathSvc, pattern) ? pattern : joinSupabaseSeedPath(pattern); + pattern.length === 0 || isAbsolutePattern(pathSvc, pattern) + ? pattern + : joinSupabaseSeedPath(pattern); /** `[db]` ports default through the development env unless `SUPABASE_ENV` overrides. */ const DEFAULT_SUPABASE_ENV = "development"; @@ -831,14 +833,14 @@ function nonEmptyString(value: unknown): Option.Option { /** * Resolve a `[section] enabled` style bool: a native TOML bool, or a string - * (including an `env(VAR)` reference) accepted by {@link parseGoBool}. + * (including an `env(VAR)` reference) accepted by {@link parseBoolLiteral}. * Returns `"invalid"` for a malformed string; applies `fallback` when the key * is absent. */ function resolveBool(value: unknown, fallback: boolean, lookup: EnvLookup): boolean | "invalid" { if (typeof value === "boolean") return value; if (typeof value === "string") { - const parsed = parseGoBool(expandEnv(value, lookup)); + const parsed = parseBoolLiteral(expandEnv(value, lookup)); return parsed ?? "invalid"; } // A numeric value decodes as a bool (`value != 0`), so `enabled = 0` is an explicit @@ -863,7 +865,7 @@ const resolveBoolOrFail = Effect.fnUntraced(function* ( envValue?: string, ) { if (envValue !== undefined) { - const parsed = parseGoBool(expandEnv(envValue, lookup)); + const parsed = parseBoolLiteral(expandEnv(envValue, lookup)); if (parsed === undefined) { return yield* Effect.fail( new DbConfigLoadError({ message: `failed to parse config: invalid ${field}.` }), @@ -883,7 +885,7 @@ const resolveBoolOrFail = Effect.fnUntraced(function* ( /** * Tri-state sibling of `resolveBoolOrFail` for fields that stay `None` (never * `false`) when absent. The `SUPABASE_*` env override wins when present; - * otherwise a present TOML bool/string is decoded with {@link parseGoBool}, + * otherwise a present TOML bool/string is decoded with {@link parseBoolLiteral}, * and a malformed value aborts the load. */ const resolveOptionalBoolOrFail = Effect.fnUntraced(function* ( @@ -893,7 +895,7 @@ const resolveOptionalBoolOrFail = Effect.fnUntraced(function* ( lookup: EnvLookup, ) { if (envValue !== undefined) { - const parsed = parseGoBool(expandEnv(envValue, lookup)); + const parsed = parseBoolLiteral(expandEnv(envValue, lookup)); if (parsed === undefined) { return yield* Effect.fail( new DbConfigLoadError({ message: `failed to parse config: invalid ${field}.` }), @@ -905,7 +907,7 @@ const resolveOptionalBoolOrFail = Effect.fnUntraced(function* ( // A numeric value decodes the same way: `value != 0`. if (typeof value === "number") return Option.some(value !== 0); if (typeof value === "string") { - const parsed = parseGoBool(expandEnv(value, lookup)); + const parsed = parseBoolLiteral(expandEnv(value, lookup)); if (parsed === undefined) { return yield* Effect.fail( new DbConfigLoadError({ message: `failed to parse config: invalid ${field}.` }), @@ -1218,7 +1220,7 @@ const readDbTomlCore = Effect.fnUntraced(function* ( ); } - // A present-but-unmarshalable port aborts rather than defaulting, so a broken `[db]` + // A present-but-invalid port aborts rather than defaulting, so a broken `[db]` // config never silently targets the default local database. const port = resolvePort( (remoteWins("db.port") ? undefined : envOverride("SUPABASE_DB_PORT")) ?? db?.["port"], @@ -1239,7 +1241,7 @@ const readDbTomlCore = Effect.fnUntraced(function* ( ); } // An explicit `db.port = 0` is a load error (an absent port is defaulted first); - // `resolvePort` accepts 0 as a valid uint16, so the zero check lives here. No + // `resolvePort` accepts 0 as a valid port, so the zero check lives here. No // equivalent check for `shadow_port`. if (port === 0) { return yield* Effect.fail( @@ -1344,7 +1346,7 @@ const readDbTomlCore = Effect.fnUntraced(function* ( let webhooksEnabled: boolean; if (webhooksEnabledEnv !== undefined) { const expandedWebhooksEnabledEnv = expandEnv(webhooksEnabledEnv, lookup); - const parsed = parseGoBool(expandedWebhooksEnabledEnv); + const parsed = parseBoolLiteral(expandedWebhooksEnabledEnv); if (parsed === undefined) { return yield* Effect.fail( new DbConfigLoadError({ @@ -1359,7 +1361,7 @@ const readDbTomlCore = Effect.fnUntraced(function* ( // A numeric `enabled = 1` is true (`value != 0`), same as `experimental.pgdelta.enabled` below. webhooksEnabled = webhooksEnabledRaw !== 0; } else if (typeof webhooksEnabledRaw === "string") { - const parsed = parseGoBool(expandEnv(webhooksEnabledRaw, lookup)); + const parsed = parseBoolLiteral(expandEnv(webhooksEnabledRaw, lookup)); if (parsed === undefined) { return yield* Effect.fail( new DbConfigLoadError({ @@ -1386,7 +1388,7 @@ const readDbTomlCore = Effect.fnUntraced(function* ( if (enabledEnv !== undefined) { // An `env(VAR)` indirection in the override is expanded before the bool parse. const expandedEnabledEnv = expandEnv(enabledEnv, lookup); - const parsed = parseGoBool(expandedEnabledEnv); + const parsed = parseBoolLiteral(expandedEnabledEnv); if (parsed === undefined) { return yield* Effect.fail( new DbConfigLoadError({ @@ -1401,7 +1403,7 @@ const readDbTomlCore = Effect.fnUntraced(function* ( // A numeric `enabled = 1` is true (`value != 0`), same rule as the generic `resolveBool`. enabled = enabledRaw !== 0; } else if (typeof enabledRaw === "string") { - const parsed = parseGoBool(expandEnv(enabledRaw, lookup)); + const parsed = parseBoolLiteral(expandEnv(enabledRaw, lookup)); if (parsed === undefined) { return yield* Effect.fail( new DbConfigLoadError({ @@ -1498,7 +1500,7 @@ const readDbTomlCore = Effect.fnUntraced(function* ( if (typeof value === "boolean") return value; if (typeof value === "number") return value !== 0; if (typeof value !== "string") return false; - const parsed = parseGoBool(expandEnv(value, lookup)); + const parsed = parseBoolLiteral(expandEnv(value, lookup)); if (parsed === undefined) return yield* fail(`failed to parse config: invalid ${field}.`); return parsed; }); @@ -1922,7 +1924,7 @@ const readDbTomlCore = Effect.fnUntraced(function* ( // trimming; empty → `[]`). An array is decoded element-by-element: each element is // expanded but not re-split, so `["env(SEEDS)"]` stays one pattern. The env override // wins over the TOML value; absent/invalid falls back to the caller's default. - const splitGoSeedPaths = (value: string): ReadonlyArray => { + const splitSeedPaths = (value: string): ReadonlyArray => { const expanded = expandEnv(value, lookup); return expanded.length === 0 ? [] : expanded.split(","); }; @@ -1932,7 +1934,7 @@ const readDbTomlCore = Effect.fnUntraced(function* ( * way, and a negative zero keeps its sign — so the resolved pattern's hash key matches * an existing recorded entry. */ - const formatGoWeakFloat = (value: number): string => { + const formatWeakFloat = (value: number): string => { if (Number.isNaN(value)) return "NaN"; if (value === Number.POSITIVE_INFINITY) return "+Inf"; if (value === Number.NEGATIVE_INFINITY) return "-Inf"; @@ -1953,28 +1955,28 @@ const readDbTomlCore = Effect.fnUntraced(function* ( const weakCoerceGlobEntry = (value: unknown): string | undefined => { if (typeof value === "string") return value; if (typeof value === "boolean") return value ? "1" : "0"; - if (typeof value === "number") return formatGoWeakFloat(value); + if (typeof value === "number") return formatWeakFloat(value); return undefined; }; // A non-scalar glob element (nested array/table, or a bare TOML datetime) fails with - // an "unconvertible type" error instead of being silently dropped; each datetime + // an "expected a string" error instead of being silently dropped; each datetime // variant reports its own type name for the message. `smol-toml` parses every TOML // datetime to a `TomlDate` (a `Date` subclass), exposing the `isDate`/`isTime`/ // `isLocal` discriminators needed to pick the right variant name. - const goTomlDateType = (value: SmolToml.TomlDate): string => { - if (value.isDate()) return "toml.LocalDate"; - if (value.isTime()) return "toml.LocalTime"; - return value.isLocal() ? "toml.LocalDateTime" : "time.Time"; + const tomlDateTypeName = (value: SmolToml.TomlDate): string => { + if (value.isDate()) return "local date"; + if (value.isTime()) return "local time"; + return value.isLocal() ? "local date-time" : "offset date-time"; }; - const goUnconvertibleType = (value: unknown): string | undefined => + const unsupportedTypeName = (value: unknown): string | undefined => value instanceof SmolToml.TomlDate - ? goTomlDateType(value) + ? tomlDateTypeName(value) : Array.isArray(value) - ? "[]interface {}" + ? "array" : typeof value === "object" && value !== null - ? "map[string]interface {}" + ? "table" : undefined; - // Returns the "unconvertible type" issue for each bad array element, without + // Returns the "expected a string" issue for each bad array element, without // failing — both `Glob` fields' issues are combined into one error afterward (see // `failOnGlobIssues`), with `db.migrations.schema_paths` ordered before `db.seed.sql_paths`. const globArrayIssues = ( @@ -1982,26 +1984,22 @@ const readDbTomlCore = Effect.fnUntraced(function* ( values: ReadonlyArray, ): ReadonlyArray => values.flatMap((value, index) => { - const goType = goUnconvertibleType(value); - return goType === undefined + const typeName = unsupportedTypeName(value); + return typeName === undefined ? [] - : [`'${keyPath}[${index}]' expected type 'string', got unconvertible type '${goType}'`]; + : [`${keyPath}[${index}]: expected a string, got ${typeName}`]; }); // Fails once with every issue collected across both `Glob` fields, instead of // failing on the first field checked. const failOnGlobIssues = ( issues: ReadonlyArray, ): Effect.Effect => - issues.length === 0 - ? Effect.void - : fail( - `failed to parse config: decoding failed due to the following error(s):\n\n${issues.join("\n")}`, - ); + issues.length === 0 ? Effect.void : fail(`failed to parse config:\n${issues.join("\n")}`); // A scalar top-level value (e.g. `schema_paths = 42`) is treated like a // single-element array: a zero-length map decodes to `[]`, anything else weakly - // coerces or reports an unconvertible-type issue. A `TomlDate` must not match the + // coerces or reports an unsupported-type issue. A `TomlDate` must not match the // zero-length-map case — its value is stored internally, so `Object.keys` is empty - // too, but it should still be treated as unconvertible. + // too, but it should still be reported as an unsupported type. const resolveScalarGlobFallback = ( keyPath: string, value: unknown, @@ -2032,7 +2030,7 @@ const readDbTomlCore = Effect.fnUntraced(function* ( absentDefault: ReadonlyArray, ): { readonly patterns: ReadonlyArray; readonly issues: ReadonlyArray } => { if (override !== undefined) { - return { patterns: splitGoSeedPaths(override), issues: [] }; + return { patterns: splitSeedPaths(override), issues: [] }; } if (Array.isArray(raw)) { return { @@ -2044,7 +2042,7 @@ const readDbTomlCore = Effect.fnUntraced(function* ( }; } if (typeof raw === "string") { - return { patterns: splitGoSeedPaths(raw), issues: [] }; + return { patterns: splitSeedPaths(raw), issues: [] }; } if (raw === undefined) { return { patterns: absentDefault, issues: [] }; diff --git a/apps/cli/src/command-internal/db-config.toml-read.unit.test.ts b/apps/cli/src/command-internal/db-config.toml-read.unit.test.ts index 37e65f0246..a4a58f2147 100644 --- a/apps/cli/src/command-internal/db-config.toml-read.unit.test.ts +++ b/apps/cli/src/command-internal/db-config.toml-read.unit.test.ts @@ -213,7 +213,7 @@ describe("readDbToml", () => { ); }); - it.effect("collapses. and .. in relative seed sql_paths like Go's path.Join", () => { + it.effect("collapses. and .. in relative seed sql_paths", () => { const dir = withConfig( ["[db.seed]", 'sql_paths = ["../seed.sql", "sub/../other.sql", "./plain.sql"]', ""].join( "\n", @@ -261,7 +261,7 @@ describe("readDbToml", () => { ); }); - it.effect("treats an empty-string db.seed.sql_paths as no patterns (Go []string{})", () => { + it.effect("treats an empty-string db.seed.sql_paths as no patterns", () => { const dir = withConfig(["[db.seed]", 'sql_paths = ""', ""].join("\n")); return read(dir).pipe( Effect.tap((v) => @@ -296,7 +296,7 @@ describe("readDbToml", () => { }, ); - it.effect("expands an env() array element but does NOT split it (Go array asymmetry)", () => { + it.effect("expands an env() array element but does NOT split it", () => { const previous = process.env["SEEDS"]; process.env["SEEDS"] = "a.sql,b.sql"; const dir = withConfig(["[db.seed]", 'sql_paths = ["env(SEEDS)"]', ""].join("\n")); @@ -316,20 +316,17 @@ describe("readDbToml", () => { ); }); - it.effect( - "weakly coerces non-string db.seed.sql_paths array elements (Go mapstructure parity)", - () => { - const dir = withConfig(["[db.seed]", 'sql_paths = [42, true, "seed.sql"]', ""].join("\n")); - return read(dir).pipe( - Effect.tap((v) => - Effect.sync(() => { - expect(v.seed.sqlPaths).toEqual(["supabase/42", "supabase/1", "supabase/seed.sql"]); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("weakly coerces non-string db.seed.sql_paths array elements", () => { + const dir = withConfig(["[db.seed]", 'sql_paths = [42, true, "seed.sql"]', ""].join("\n")); + return read(dir).pipe( + Effect.tap((v) => + Effect.sync(() => { + expect(v.seed.sqlPaths).toEqual(["supabase/42", "supabase/1", "supabase/seed.sql"]); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); it.effect( "honors SUPABASE_DB_MIGRATIONS_SCHEMA_PATHS over the TOML array (comma split, no trim)", @@ -370,7 +367,7 @@ describe("readDbToml", () => { ); it.effect( - "on Windows, resolves a leading-slash schema/seed path pattern under supabase/ instead of treating it as absolute (Go filepath.IsAbs parity)", + "on Windows, resolves a leading-slash schema/seed path pattern under supabase/ instead of treating it as absolute", () => { // A bare leading `/` has no Windows volume name, so it resolves as relative and joins to // `supabase/`, unlike Node's `path.win32.isAbsolute`, which treats a leading separator as @@ -394,31 +391,25 @@ describe("readDbToml", () => { }, ); - it.effect( - "weakly coerces non-string db.migrations.schema_paths array elements (Go mapstructure parity)", - () => { - // `v.UnmarshalExact` never sets `WeaklyTypedInput: false`, so viper's - // `defaultDecoderConfig` default of `true` stands — mapstructure's `decodeString` - // coerces a bool to "1"/"0" and a number to its decimal string rather than - // erroring or dropping the element. Verified empirically: - // `schema_paths = [42, true, "schemas/*.sql"]` resolves to - // `supabase/{42,1,schemas/*.sql}`, not a filtered two-element list. - const dir = withConfig( - ["[db.migrations]", 'schema_paths = [42, true, "schemas/*.sql"]', ""].join("\n"), - ); - return read(dir).pipe( - Effect.tap((v) => - Effect.sync(() => { - expect(v.schemaPaths).toEqual(["supabase/42", "supabase/1", "supabase/schemas/*.sql"]); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("weakly coerces non-string db.migrations.schema_paths array elements", () => { + // A bool coerces to "1"/"0" and a number to its decimal string rather than erroring or + // dropping the element: `schema_paths = [42, true, "schemas/*.sql"]` resolves to + // `supabase/{42,1,schemas/*.sql}`, not a filtered two-element list. + const dir = withConfig( + ["[db.migrations]", 'schema_paths = [42, true, "schemas/*.sql"]', ""].join("\n"), + ); + return read(dir).pipe( + Effect.tap((v) => + Effect.sync(() => { + expect(v.schemaPaths).toEqual(["supabase/42", "supabase/1", "supabase/schemas/*.sql"]); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); it.effect( - "formats a large numeric db.migrations.schema_paths entry as fixed decimal, not scientific notation (Go strconv.FormatFloat parity)", + "formats a large numeric db.migrations.schema_paths entry as fixed decimal, not scientific notation", () => { const dir = withConfig(["[db.migrations]", "schema_paths = [1e21]", ""].join("\n")); return read(dir).pipe( @@ -433,7 +424,7 @@ describe("readDbToml", () => { ); it.effect( - "formats TOML special-float db.migrations.schema_paths entries like Go's strconv.FormatFloat, not JS's toString (Go parity)", + "formats TOML special-float db.migrations.schema_paths entries as +Inf/-Inf/NaN, not JS's toString", () => { const dir = withConfig(["[db.migrations]", "schema_paths = [inf, -inf, nan]", ""].join("\n")); return read(dir).pipe( @@ -447,47 +438,41 @@ describe("readDbToml", () => { }, ); - it.effect( - "weakly coerces a TOP-LEVEL scalar db.migrations.schema_paths (Go mapstructure weak-decode of a []string field)", - () => { - const dirNumber = withConfig(["[db.migrations]", "schema_paths = 42", ""].join("\n")); - const dirBool = withConfig(["[db.migrations]", "schema_paths = true", ""].join("\n")); - return Effect.all([read(dirNumber), read(dirBool)]).pipe( - Effect.tap(([numberResult, boolResult]) => - Effect.sync(() => { - expect(numberResult.schemaPaths).toEqual(["supabase/42"]); - expect(boolResult.schemaPaths).toEqual(["supabase/1"]); - rmSync(dirNumber, { recursive: true, force: true }); - rmSync(dirBool, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("weakly coerces a TOP-LEVEL scalar db.migrations.schema_paths", () => { + const dirNumber = withConfig(["[db.migrations]", "schema_paths = 42", ""].join("\n")); + const dirBool = withConfig(["[db.migrations]", "schema_paths = true", ""].join("\n")); + return Effect.all([read(dirNumber), read(dirBool)]).pipe( + Effect.tap(([numberResult, boolResult]) => + Effect.sync(() => { + expect(numberResult.schemaPaths).toEqual(["supabase/42"]); + expect(boolResult.schemaPaths).toEqual(["supabase/1"]); + rmSync(dirNumber, { recursive: true, force: true }); + rmSync(dirBool, { recursive: true, force: true }); + }), + ), + ); + }); - it.effect( - "treats a TOP-LEVEL empty-table db.migrations.schema_paths as no patterns (Go mapstructure zero-length-map special case)", - () => { - const dir = withConfig(["[db.migrations]", "schema_paths = {}", ""].join("\n")); - return read(dir).pipe( - Effect.tap((v) => - Effect.sync(() => { - expect(v.schemaPaths).toEqual([]); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("treats a TOP-LEVEL empty-table db.migrations.schema_paths as no patterns", () => { + const dir = withConfig(["[db.migrations]", "schema_paths = {}", ""].join("\n")); + return read(dir).pipe( + Effect.tap((v) => + Effect.sync(() => { + expect(v.schemaPaths).toEqual([]); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); it.effect.each([ - { name: "offset date-time", literal: "1979-05-27T07:32:00Z", goType: "time.Time" }, - { name: "local date-time", literal: "1979-05-27T07:32:00", goType: "toml.LocalDateTime" }, - { name: "local date", literal: "1979-05-27", goType: "toml.LocalDate" }, - { name: "local time", literal: "07:32:00", goType: "toml.LocalTime" }, + { name: "offset date-time", literal: "1979-05-27T07:32:00Z", typeName: "offset date-time" }, + { name: "local date-time", literal: "1979-05-27T07:32:00", typeName: "local date-time" }, + { name: "local date", literal: "1979-05-27", typeName: "local date" }, + { name: "local time", literal: "07:32:00", typeName: "local time" }, ])( - "aborts the whole config load on a TOP-LEVEL bare $name db.migrations.schema_paths instead of silently treating it as empty (Go mapstructure UnconvertibleTypeError, review CLI-1958)", - ({ literal, goType }) => { + "aborts the whole config load on a TOP-LEVEL bare $name db.migrations.schema_paths instead of silently treating it as empty", + ({ literal, typeName }) => { // `smol-toml` parses every TOML datetime variant to a `TomlDate` (a `Date` subclass) // that stores its value internally, not as an enumerable own property, so // `Object.keys(tomlDate).length === 0` — same as a genuine empty inline table @@ -502,7 +487,7 @@ describe("readDbToml", () => { expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { expect(JSON.stringify(exit.cause)).toContain( - `'db.migrations.schema_paths[0]' expected type 'string', got unconvertible type '${goType}'`, + `db.migrations.schema_paths[0]: expected a string, got ${typeName}`, ); } rmSync(dir, { recursive: true, force: true }); @@ -513,7 +498,7 @@ describe("readDbToml", () => { ); it.effect( - "aborts the whole config load on a bare datetime db.migrations.schema_paths ARRAY element (Go mapstructure UnconvertibleTypeError, review CLI-1958)", + "aborts the whole config load on a bare datetime db.migrations.schema_paths ARRAY element", () => { // Same `TomlDate`-vs-generic-object collision as the top-level scalar case above, but // reached through the real-array branch instead of the scalar fallback: the valid glob @@ -530,7 +515,7 @@ describe("readDbToml", () => { expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { expect(JSON.stringify(exit.cause)).toContain( - "'db.migrations.schema_paths[1]' expected type 'string', got unconvertible type 'time.Time'", + "db.migrations.schema_paths[1]: expected a string, got offset date-time", ); } rmSync(dir, { recursive: true, force: true }); @@ -551,7 +536,7 @@ describe("readDbToml", () => { expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { expect(JSON.stringify(exit.cause)).toContain( - "'db.seed.sql_paths[0]' expected type 'string', got unconvertible type 'time.Time'", + "db.seed.sql_paths[0]: expected a string, got offset date-time", ); } rmSync(dir, { recursive: true, force: true }); @@ -562,7 +547,7 @@ describe("readDbToml", () => { ); it.effect( - "aborts the whole config load on a TOP-LEVEL table db.migrations.schema_paths (Go mapstructure UnconvertibleTypeError, synthetic index 0)", + "aborts the whole config load on a TOP-LEVEL table db.migrations.schema_paths (synthetic index 0)", () => { // A non-empty map isn't weakly coercible, so it fails decoding element 0 the same way a // nested-array/table array element does. @@ -574,7 +559,7 @@ describe("readDbToml", () => { expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { expect(JSON.stringify(exit.cause)).toContain( - "'db.migrations.schema_paths[0]' expected type 'string', got unconvertible type 'map[string]interface {}'", + "db.migrations.schema_paths[0]: expected a string, got table", ); } rmSync(dir, { recursive: true, force: true }); @@ -602,7 +587,7 @@ describe("readDbToml", () => { ); it.effect( - "aborts the whole config load on a non-scalar db.migrations.schema_paths element (Go mapstructure UnconvertibleTypeError)", + "aborts the whole config load on a non-scalar db.migrations.schema_paths element", () => { // Unlike a bool/number (weakly coerced above), a nested array/table fails the whole // config load rather than dropping just that element. @@ -614,7 +599,7 @@ describe("readDbToml", () => { expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { expect(JSON.stringify(exit.cause)).toContain( - "failed to parse config: decoding failed due to the following error(s):\\n\\n'db.migrations.schema_paths[0]' expected type 'string', got unconvertible type '[]interface {}'", + "failed to parse config:\\ndb.migrations.schema_paths[0]: expected a string, got array", ); } rmSync(dir, { recursive: true, force: true }); @@ -625,7 +610,7 @@ describe("readDbToml", () => { ); it.effect( - "aborts the whole config load on a table db.migrations.schema_paths element, reporting every bad index (Go mapstructure parity)", + "aborts the whole config load on a table db.migrations.schema_paths element, reporting every bad index", () => { const dir = withConfig( ["[db.migrations]", 'schema_paths = ["schemas/*.sql", { path = "x.sql" }]', ""].join("\n"), @@ -637,7 +622,7 @@ describe("readDbToml", () => { expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { expect(JSON.stringify(exit.cause)).toContain( - "'db.migrations.schema_paths[1]' expected type 'string', got unconvertible type 'map[string]interface {}'", + "db.migrations.schema_paths[1]: expected a string, got table", ); } rmSync(dir, { recursive: true, force: true }); @@ -658,7 +643,7 @@ describe("readDbToml", () => { expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { expect(JSON.stringify(exit.cause)).toContain( - "'db.seed.sql_paths[0]' expected type 'string', got unconvertible type '[]interface {}'", + "db.seed.sql_paths[0]: expected a string, got array", ); } rmSync(dir, { recursive: true, force: true }); @@ -669,7 +654,7 @@ describe("readDbToml", () => { ); it.effect( - "aggregates unconvertible-entry issues from BOTH db.seed.sql_paths and db.migrations.schema_paths in one error (Go UnmarshalExact single-pass parity, review CLI-1958)", + "aggregates non-string-entry issues from BOTH db.seed.sql_paths and db.migrations.schema_paths in one error", () => { const dir = withConfig( ["[db.seed]", "sql_paths = [[]]", "", "[db.migrations]", "schema_paths = [[]]", ""].join( @@ -683,10 +668,8 @@ describe("readDbToml", () => { expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { const message = JSON.stringify(exit.cause); - const schemaIssue = - "'db.migrations.schema_paths[0]' expected type 'string', got unconvertible type '[]interface {}'"; - const seedIssue = - "'db.seed.sql_paths[0]' expected type 'string', got unconvertible type '[]interface {}'"; + const schemaIssue = "db.migrations.schema_paths[0]: expected a string, got array"; + const seedIssue = "db.seed.sql_paths[0]: expected a string, got array"; expect(message).toContain(schemaIssue); expect(message).toContain(seedIssue); expect(message.indexOf(schemaIssue)).toBeLessThan(message.indexOf(seedIssue)); @@ -729,7 +712,7 @@ describe("readDbToml", () => { }, ); - it.effect("decodes a numeric db.seed.enabled = 0 as false (Go weak-bool decode)", () => { + it.effect("decodes a numeric db.seed.enabled = 0 as false", () => { const dir = withConfig(["[db.seed]", "enabled = 0", ""].join("\n")); return read(dir).pipe( Effect.tap((v) => @@ -753,22 +736,19 @@ describe("readDbToml", () => { ); }); - it.effect( - "decodes a numeric experimental.pgdelta.enabled = 1 as true (Go weak-bool decode)", - () => { - const dir = withConfig(["[experimental.pgdelta]", "enabled = 1", ""].join("\n")); - return read(dir).pipe( - Effect.tap((v) => - Effect.sync(() => { - expect(v.pgDelta.enabled).toBe(true); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("decodes a numeric experimental.pgdelta.enabled = 1 as true", () => { + const dir = withConfig(["[experimental.pgdelta]", "enabled = 1", ""].join("\n")); + return read(dir).pipe( + Effect.tap((v) => + Effect.sync(() => { + expect(v.pgDelta.enabled).toBe(true); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); - it.effect("rejects an explicit db.port = 0 (Go's Missing required field)", () => { + it.effect("rejects an explicit db.port = 0 as a missing required field", () => { const dir = withConfig(["[db]", "port = 0", ""].join("\n")); return read(dir).pipe( Effect.exit, @@ -787,8 +767,7 @@ describe("readDbToml", () => { }); it.effect("an explicit remote db.migrations.enabled beats SUPABASE_DB_MIGRATIONS_ENABLED", () => { - // Go applies each matched-remote key via v.Set (override tier) above AutomaticEnv, - // so an explicit remote value wins over the env var. + // A matched remote's keys override the environment, so an explicit remote value wins over the env var. const ref = "abcdefghijklmnopqrst"; const previous = process.env["SUPABASE_DB_MIGRATIONS_ENABLED"]; process.env["SUPABASE_DB_MIGRATIONS_ENABLED"] = "false"; @@ -832,7 +811,7 @@ describe("readDbToml", () => { ); }); - it.effect("collapses. and .. in relative db.migrations.schema_paths like Go's path.Join", () => { + it.effect("collapses. and .. in relative db.migrations.schema_paths", () => { const dir = withConfig( [ "[db.migrations]", @@ -877,7 +856,7 @@ describe("readDbToml", () => { }, ); - it.effect("defaults db.migrations.schema_paths to [] when absent (Go's Glob zero value)", () => { + it.effect("defaults db.migrations.schema_paths to [] when absent", () => { const dir = withConfig(["[db]", "port = 54322", ""].join("\n")); return read(dir).pipe( Effect.tap((v) => @@ -1385,7 +1364,7 @@ describe("readDbToml", () => { ); }); - it.effect("rejects deno_version = 0 with Go's missing-required message", () => { + it.effect("rejects deno_version = 0 with the missing-required message", () => { const dir = withConfig(["[edge_runtime]", "deno_version = 0", ""].join("\n")); return read(dir).pipe( Effect.exit, @@ -1494,7 +1473,7 @@ describe("readDbToml", () => { ); }); - it.effect("accepts an underscore bucket name like Go's permissive pattern", () => { + it.effect("accepts an underscore bucket name", () => { // The bucket-name pattern uses `\w` (includes `_`) and is not case-restricted despite the // prose, so `Bad_Name` actually passes: match the regex, not the message text. const dir = withConfig("[storage.buckets.Bad_Name]\n"); @@ -1534,7 +1513,7 @@ describe("readDbToml", () => { ); }); - it.effect("parses [api] auto_expose_new_tables string with Go bool tokens (TRUE → true)", () => { + it.effect("parses [api] auto_expose_new_tables string with bool tokens (TRUE → true)", () => { // `TRUE`/`1`/`t` are also accepted as true, not just lowercase `true`. const dir = withConfig('[api]\nauto_expose_new_tables = "TRUE"\n'); return read(dir).pipe( @@ -1655,7 +1634,7 @@ describe("readDbToml", () => { ); }); - it.effect("treats SUPABASE_EXPERIMENTAL_PGDELTA_ENABLED=1 as true (Go strconv.ParseBool)", () => { + it.effect("treats SUPABASE_EXPERIMENTAL_PGDELTA_ENABLED=1 as true", () => { const dir = withConfig(undefined); const saved = process.env["SUPABASE_EXPERIMENTAL_PGDELTA_ENABLED"]; process.env["SUPABASE_EXPERIMENTAL_PGDELTA_ENABLED"] = "1"; @@ -1675,7 +1654,7 @@ describe("readDbToml", () => { ); }); - it.effect("fails on a malformed SUPABASE_EXPERIMENTAL_PGDELTA_ENABLED (Go config error)", () => { + it.effect("fails on a malformed SUPABASE_EXPERIMENTAL_PGDELTA_ENABLED", () => { const dir = withConfig(undefined); const saved = process.env["SUPABASE_EXPERIMENTAL_PGDELTA_ENABLED"]; process.env["SUPABASE_EXPERIMENTAL_PGDELTA_ENABLED"] = "maybe"; @@ -1697,12 +1676,12 @@ describe("readDbToml", () => { ); }); - it.effect("parses [auth] enabled string forms via Go ParseBool and fails on malformed", () => { + it.effect("parses [auth] enabled string forms via bool parsing and fails on malformed", () => { const ok = withConfig(["[auth]", 'enabled = "0"', ""].join("\n")); const bad = withConfig(["[storage]", 'enabled = "nope"', ""].join("\n")); return Effect.gen(function* () { const v = yield* read(ok); - expect(v.baseline.authEnabled).toBe(false); // "0" → false (ParseBool) + expect(v.baseline.authEnabled).toBe(false); // "0" → false const exit = yield* read(bad).pipe(Effect.exit); expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { @@ -1774,7 +1753,7 @@ describe("readDbToml", () => { ); }); - it.effect("expands env(VAR) for password and port like Go's LoadEnvHook", () => { + it.effect("expands env(VAR) for password and port", () => { process.env["DB_PW"] = "from-env"; process.env["DB_PORT"] = "6000"; const dir = withConfig( @@ -1807,7 +1786,7 @@ describe("readDbToml", () => { ); }); - it.effect("honors SUPABASE_DB_SEED_ENABLED over the TOML value (Go AutomaticEnv)", () => { + it.effect("honors SUPABASE_DB_SEED_ENABLED over the TOML value", () => { process.env["SUPABASE_DB_SEED_ENABLED"] = "false"; const dir = withConfig(["[db.seed]", "enabled = true", ""].join("\n")); return read(dir).pipe( @@ -1821,7 +1800,7 @@ describe("readDbToml", () => { ); }); - it.effect("expands an env() indirection in SUPABASE_DB_SEED_ENABLED (Go LoadEnvHook)", () => { + it.effect("expands an env() indirection in SUPABASE_DB_SEED_ENABLED", () => { process.env["SUPABASE_DB_SEED_ENABLED"] = "env(SEED_ON)"; process.env["SEED_ON"] = "false"; const dir = withConfig(["[db.seed]", "enabled = true", ""].join("\n")); @@ -1837,7 +1816,7 @@ describe("readDbToml", () => { ); }); - it.effect("honors SUPABASE_DB_MIGRATIONS_ENABLED over the default (Go AutomaticEnv)", () => { + it.effect("honors SUPABASE_DB_MIGRATIONS_ENABLED over the default", () => { process.env["SUPABASE_DB_MIGRATIONS_ENABLED"] = "false"; const dir = withConfig(undefined); return read(dir).pipe( @@ -1866,22 +1845,19 @@ describe("readDbToml", () => { ); }); - it.effect( - "expands env(VAR) for the top-level project_id (Go config.Load before Docker IDs)", - () => { - process.env["PROJECT_REF"] = "abcdefghijklmnopqrst"; - const dir = withConfig(['project_id = "env(PROJECT_REF)"', ""].join("\n")); - return read(dir).pipe( - Effect.tap((v) => - Effect.sync(() => { - expect(Option.getOrNull(v.projectId)).toBe("abcdefghijklmnopqrst"); - delete process.env["PROJECT_REF"]; - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("expands env(VAR) for the top-level project_id", () => { + process.env["PROJECT_REF"] = "abcdefghijklmnopqrst"; + const dir = withConfig(['project_id = "env(PROJECT_REF)"', ""].join("\n")); + return read(dir).pipe( + Effect.tap((v) => + Effect.sync(() => { + expect(Option.getOrNull(v.projectId)).toBe("abcdefghijklmnopqrst"); + delete process.env["PROJECT_REF"]; + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); it.effect("does not merge a remote block whose project_id is a TOML env() literal", () => { // Remote matching happens on the raw `env(...)` literal, before expansion, so this block is @@ -2200,7 +2176,7 @@ describe("readDbToml", () => { }, ); - it.effect("fails when a present shadow_port cannot unmarshal into a uint16", () => { + it.effect("fails when a present shadow_port is not a valid port number", () => { const dir = withConfig(["[db]", "port = 5000", 'shadow_port = "nope"', ""].join("\n")); return read(dir).pipe( Effect.exit, @@ -2216,7 +2192,7 @@ describe("readDbToml", () => { ); }); - it.effect("resolves env(VAR) from the project supabase/.env file (Go loadNestedEnv)", () => { + it.effect("resolves env(VAR) from the project supabase/.env file", () => { delete process.env["DB_FILEVAR"]; const dir = withConfig( ["[db]", 'port = "env(DB_FILEVAR)"', 'password = "env(DB_FILEVAR)"', ""].join("\n"), @@ -2233,7 +2209,7 @@ describe("readDbToml", () => { ); }); - it.effect("lets the shell env win over a project .env value (godotenv no-override)", () => { + it.effect("lets the shell env win over a project .env value (no override)", () => { process.env["DB_FILEVAR"] = "shell-wins"; const dir = withConfig(["[db]", 'password = "env(DB_FILEVAR)"', ""].join("\n")); writeFileSync(join(dir, "supabase", ".env"), "DB_FILEVAR=from-file\n"); @@ -2248,7 +2224,7 @@ describe("readDbToml", () => { ); }); - it.effect("lets supabase/.env win over a repo-root .env (Go walks supabase/ first)", () => { + it.effect("lets supabase/.env win over a repo-root .env", () => { delete process.env["DB_FILEVAR"]; const dir = withConfig(["[db]", 'password = "env(DB_FILEVAR)"', ""].join("\n")); writeFileSync(join(dir, ".env"), "DB_FILEVAR=root\n"); @@ -2298,7 +2274,7 @@ describe("readDbToml", () => { ); }); - it.effect("lets SUPABASE_DB_* env vars override the [db] config (viper AutomaticEnv)", () => { + it.effect("lets SUPABASE_DB_* env vars override the [db] config", () => { const prev = { PORT: process.env["SUPABASE_DB_PORT"], SHADOW: process.env["SUPABASE_DB_SHADOW_PORT"], @@ -2347,7 +2323,7 @@ describe("readDbToml", () => { ); }); - it.effect("rejects db.major_version = 0 with Go's missing-required message", () => { + it.effect("rejects db.major_version = 0 with the missing-required message", () => { const dir = withConfig(["[db]", "major_version = 0", ""].join("\n")); return read(dir).pipe( Effect.exit, @@ -2365,7 +2341,7 @@ describe("readDbToml", () => { ); }); - it.effect("rejects db.major_version = 12 with Go's 12.x message", () => { + it.effect("rejects db.major_version = 12 with the 12.x message", () => { const dir = withConfig(["[db]", "major_version = 12", ""].join("\n")); return read(dir).pipe( Effect.exit, @@ -2429,7 +2405,7 @@ describe("readDbToml", () => { ); }); - it.effect("expands env(VAR) for db.major_version like Go's LoadEnvHook", () => { + it.effect("expands env(VAR) for db.major_version", () => { process.env["PG_MAJOR"] = "15"; const dir = withConfig(["[db]", 'major_version = "env(PG_MAJOR)"', ""].join("\n")); return read(dir).pipe( @@ -2493,7 +2469,7 @@ describe("readDbToml", () => { ); }); - it.effect("rejects a malformed [remotes.*] project_id on every load (Go Validate)", () => { + it.effect("rejects a malformed [remotes.*] project_id on every load", () => { const dir = withConfig(["[remotes.staging]", 'project_id = "staging"', ""].join("\n")); return read(dir).pipe( Effect.exit, @@ -2525,7 +2501,7 @@ describe("readDbToml", () => { ); }); - it.effect("ignores an empty SUPABASE_DB_PORT override (viper AllowEmptyEnv=false)", () => { + it.effect("ignores an empty SUPABASE_DB_PORT override", () => { const prev = process.env["SUPABASE_DB_PORT"]; process.env["SUPABASE_DB_PORT"] = ""; const dir = withConfig(["[db]", "port = 55555", ""].join("\n")); @@ -2543,7 +2519,7 @@ describe("readDbToml", () => { it.effect("loadProjectEnv surfaces SUPABASE_DB_PASSWORD from .env (linked-path source)", () => { // The --linked resolver reads SUPABASE_DB_PASSWORD via this map, so a value - // defined only in supabase/.env must be visible (Go's loadNestedEnv parity). + // defined only in supabase/.env must be visible. delete process.env["SUPABASE_DB_PASSWORD"]; const dir = mkdtempSync(join(tmpdir(), "db-toml-")); mkdirSync(join(dir, "supabase"), { recursive: true }); @@ -2612,7 +2588,7 @@ describe("readDbToml", () => { }, ); - it.effect("ignores a [db.pooler] connection_string in config.toml (Go reads .temp only)", () => { + it.effect("ignores a [db.pooler] connection_string in config.toml (only .temp is read)", () => { const dir = withConfig( [ "[db.pooler]", @@ -2727,7 +2703,7 @@ describe("resolveDeclarativeDir", () => { ); }); -describe("readDbToml auth.Enabled validation (Go config.Validate parity)", () => { +describe("readDbToml auth.Enabled validation", () => { // Fails the config load with `message` contained in the surfaced error. const failsWith = ( lines: ReadonlyArray, @@ -2754,7 +2730,7 @@ describe("readDbToml auth.Enabled validation (Go config.Validate parity)", () => it.effect("rejects an explicit empty auth.site_url", () => failsWith(["[auth]", 'site_url = ""'], "Missing required field in config: auth.site_url"), ); - it.effect("defaults an absent auth.site_url (Go template default) — no error", () => + it.effect("defaults an absent auth.site_url — no error", () => succeeds(["[auth]", "enabled = true"]), ); it.effect("skips all auth validation when auth.enabled = false", () => @@ -2933,7 +2909,7 @@ describe("readDbToml auth.Enabled validation (Go config.Validate parity)", () => ), ); - it.effect("defaults [auth.email.smtp] enabled=true when the table omits enabled (Go merge)", () => + it.effect("defaults [auth.email.smtp] enabled=true when the table omits enabled", () => failsWith( ["[auth.email.smtp]", 'user = "u"'], "Missing required field in config: auth.email.smtp.host", @@ -2968,15 +2944,15 @@ describe("readDbToml auth.Enabled validation (Go config.Validate parity)", () => ), ); - it.effect("rejects an unknown captcha provider (Go enum, regardless of enabled)", () => + it.effect("rejects an unknown captcha provider, regardless of enabled", () => failsWith( ["[auth.captcha]", "enabled = false", 'provider = "cloudflare"'], - "'auth.captcha.provider' must be one of [hcaptcha turnstile]", + "auth.captcha.provider: must be one of hcaptcha, turnstile", ), ); }); -describe("readDbToml encrypted secret decryption (Go DecryptSecretHookFunc parity)", () => { +describe("readDbToml encrypted secret decryption", () => { // An undecryptable `encrypted:` value anywhere in config.toml aborts the load with // `failed to parse config: `. const expectFails = (lines: ReadonlyArray, message: string) => @@ -3015,7 +2991,7 @@ describe("readDbToml encrypted secret decryption (Go DecryptSecretHookFunc parit it.effect("accepts a plain (non-encrypted) secret value", () => expectLoads(["[db]", 'root_key = "plaintext-not-encrypted"']), ); - it.effect("treats an unset env() secret as a no-op (verbatim, like Go's hook)", () => + it.effect("treats an unset env() secret as a no-op (verbatim)", () => expectLoads(["[db]", 'root_key = "env(SOME_UNSET_ROOT_KEY)"']), ); it.effect("does NOT decrypt a non-secret string that starts with encrypted:", () => @@ -3060,7 +3036,7 @@ describe("readDbToml encrypted secret decryption (Go DecryptSecretHookFunc parit ); }); -describe("readDbToml non-scalar config booleans (Go UnmarshalExact parity)", () => { +describe("readDbToml non-scalar config booleans", () => { // A present non-scalar boolean must fail the config load rather than falling through to the // schema default, which would let `db reset` prompt and drop schemas. const failsInvalid = (lines: ReadonlyArray, field: string) => @@ -3081,7 +3057,7 @@ describe("readDbToml non-scalar config booleans (Go UnmarshalExact parity)", () ); }); -describe("readDbToml empty project_id (Go config.Validate parity)", () => { +describe("readDbToml empty project_id", () => { it.effect("rejects a present-but-empty top-level project_id", () => { const dir = withConfig('project_id = ""\n'); return read(dir).pipe( @@ -3113,7 +3089,7 @@ describe("readDbToml empty project_id (Go config.Validate parity)", () => { }); }); -describe("readDbToml [analytics] validation (Go config.Validate parity)", () => { +describe("readDbToml [analytics] validation", () => { const failsWith = (lines: ReadonlyArray, message: string) => Effect.gen(function* () { const dir = withConfig(lines.join("\n")); @@ -3133,7 +3109,7 @@ describe("readDbToml [analytics] validation (Go config.Validate parity)", () => it.effect("rejects an unknown analytics.backend regardless of enabled", () => failsWith( ["[analytics]", "enabled = false", 'backend = "clickhouse"'], - "'analytics.backend' must be one of [postgres bigquery]", + "analytics.backend: must be one of postgres, bigquery", ), ); it.effect("rejects bigquery analytics missing gcp_project_id", () => @@ -3196,7 +3172,7 @@ describe("readDbToml [analytics] validation (Go config.Validate parity)", () => }); }); -describe("readDbToml SUPABASE_PROJECT_ID override (Go AutomaticEnv parity)", () => { +describe("readDbToml SUPABASE_PROJECT_ID override", () => { const restore = (previous: string | undefined) => Effect.sync(() => { if (previous === undefined) delete process.env["SUPABASE_PROJECT_ID"]; @@ -3241,7 +3217,7 @@ describe("readDbToml SUPABASE_PROJECT_ID override (Go AutomaticEnv parity)", () ); }); - it.effect("ignores an empty SUPABASE_PROJECT_ID (viper AllowEmptyEnv=false)", () => { + it.effect("ignores an empty SUPABASE_PROJECT_ID", () => { const previous = process.env["SUPABASE_PROJECT_ID"]; process.env["SUPABASE_PROJECT_ID"] = ""; const dir = withConfig(['project_id = "toml-project"', ""].join("\n")); diff --git a/apps/cli/src/command-internal/db-connection.service.ts b/apps/cli/src/command-internal/db-connection.service.ts index abba632ff3..e743401536 100644 --- a/apps/cli/src/command-internal/db-connection.service.ts +++ b/apps/cli/src/command-internal/db-connection.service.ts @@ -143,7 +143,7 @@ export interface QueryResult { readonly fields: ReadonlyArray; /** * Postgres type OID per column. Lets the table/CSV formatter render `float4`/`float8` columns - * with `%g`-style formatting while integer columns stay plain. Optional so other `queryRaw` + * in shortest round-trip form (exponent notation for very large or small magnitudes) while integer columns stay plain. Optional so other `queryRaw` * callers/mocks need not set it. */ readonly fieldTypeIds?: ReadonlyArray; diff --git a/apps/cli/src/command-internal/db-connection.sql-pg.integration.test.ts b/apps/cli/src/command-internal/db-connection.sql-pg.integration.test.ts index 721ff27bc7..d91b73dd5a 100644 --- a/apps/cli/src/command-internal/db-connection.sql-pg.integration.test.ts +++ b/apps/cli/src/command-internal/db-connection.sql-pg.integration.test.ts @@ -428,37 +428,35 @@ describe("dbConnectionLayer connect failures", () => { }), ); - it.live( - "reproduces pgconn's server-error rendering for an auth failure and suggests SUPABASE_DB_PASSWORD", - () => - Effect.gen(function* () { - const server = yield* Effect.promise(() => - fakePostgresServer((socket) => { - socket.write( - errorResponse({ - S: "FATAL", - V: "FATAL", - C: "28P01", - M: 'password authentication failed for user "postgres"', - F: "auth.c", - L: "326", - R: "auth_failed", - }), - ); - socket.end(); - }), - ); - const error = yield* connectFailure({ port: server.port }).pipe( - Effect.ensuring(Effect.sync(server.close)), - ); - expect(error.message).toBe( - "failed to connect to postgres: failed to connect to `host=127.0.0.1 user=postgres database=postgres`: " + - 'server error (FATAL: password authentication failed for user "postgres" (SQLSTATE 28P01))', - ); - expect(error.suggestion).toBe(SUGGEST_ENV_VAR); - expect(error.message).not.toContain(SENTINEL_PASSWORD); - expect(error.retryable).toBeUndefined(); - }), + it.live("renders the server error for an auth failure and suggests SUPABASE_DB_PASSWORD", () => + Effect.gen(function* () { + const server = yield* Effect.promise(() => + fakePostgresServer((socket) => { + socket.write( + errorResponse({ + S: "FATAL", + V: "FATAL", + C: "28P01", + M: 'password authentication failed for user "postgres"', + F: "auth.c", + L: "326", + R: "auth_failed", + }), + ); + socket.end(); + }), + ); + const error = yield* connectFailure({ port: server.port }).pipe( + Effect.ensuring(Effect.sync(server.close)), + ); + expect(error.message).toBe( + "failed to connect to postgres: failed to connect to `host=127.0.0.1 user=postgres database=postgres`: " + + 'server error (FATAL: password authentication failed for user "postgres" (SQLSTATE 28P01))', + ); + expect(error.suggestion).toBe(SUGGEST_ENV_VAR); + expect(error.message).not.toContain(SENTINEL_PASSWORD); + expect(error.retryable).toBeUndefined(); + }), ); it.live( @@ -485,7 +483,7 @@ describe("dbConnectionLayer connect failures", () => { describe("dbConnectionLayer exec failures", () => { it.live( - "maps a real wire ErrorResponse to pgconn's PgError rendering with detail and position", + "maps a real wire ErrorResponse to the server-error rendering with detail and position", () => // Tripwire for the server-error extraction: drives the real driver stack (node-postgres // wire parsing → `DatabaseError` → `@effect/sql-pg`'s `SqlError` cause chain → diff --git a/apps/cli/src/command-internal/db-connection.sql-pg.layer.ts b/apps/cli/src/command-internal/db-connection.sql-pg.layer.ts index e8d99fea43..b6db3a0133 100644 --- a/apps/cli/src/command-internal/db-connection.sql-pg.layer.ts +++ b/apps/cli/src/command-internal/db-connection.sql-pg.layer.ts @@ -1031,7 +1031,7 @@ const connect = ( return { fields: result.fields.map((field) => field.name), // Surface the column type OIDs so the table/CSV formatter can render float4/float8 - // with %g-style formatting while integer columns stay plain. + // in shortest round-trip form while integer columns stay plain. fieldTypeIds: result.fields.map((field) => field.dataTypeID), rows: result.rows, commandTag, diff --git a/apps/cli/src/command-internal/db-connection.sql-pg.unit.test.ts b/apps/cli/src/command-internal/db-connection.sql-pg.unit.test.ts index 72e59e03d1..1aaf92ac78 100644 --- a/apps/cli/src/command-internal/db-connection.sql-pg.unit.test.ts +++ b/apps/cli/src/command-internal/db-connection.sql-pg.unit.test.ts @@ -76,7 +76,7 @@ describe("buildConnectionUrl", () => { ); }); - it("forwards runtimeParams as -c flags in the options startup param (Go RuntimeParams)", () => { + it("forwards runtimeParams as -c flags in the options startup param", () => { const url = buildConnectionUrl( { user: "postgres", @@ -135,7 +135,7 @@ describe("sslOptionFor", () => { expect(sslOptionFor(undefined, false, undefined)).toEqual({ rejectUnauthorized: false }); }); - it("treats prefer/require as TLS without verification (their pgconn primary)", () => { + it("treats prefer/require as TLS without verification (their primary mode)", () => { expect(sslOptionFor("prefer", false, undefined)).toEqual({ rejectUnauthorized: false }); expect(sslOptionFor("require", false, undefined)).toEqual({ rejectUnauthorized: false }); }); @@ -151,7 +151,7 @@ describe("sslOptionFor", () => { }); }); - it("verifies the CA chain but skips hostname for verify-ca (pgconn parity)", () => { + it("verifies the CA chain but skips hostname for verify-ca", () => { const ssl = sslOptionFor("verify-ca", false, undefined); expect(ssl).toMatchObject({ rejectUnauthorized: true }); if (typeof ssl === "object" && ssl !== null) { @@ -160,7 +160,7 @@ describe("sslOptionFor", () => { } }); - it("attaches the client cert (cert/key/passphrase) to every TLS mode (pgconn parity)", () => { + it("attaches the client cert (cert/key/passphrase) to every TLS mode", () => { const clientCert = { cert: "CERT", key: "KEY", passphrase: "pw" }; expect(sslOptionFor("verify-full", false, undefined, undefined, clientCert)).toMatchObject({ cert: "CERT", @@ -181,7 +181,7 @@ describe("sslOptionFor", () => { }); }); - it("carries the servername for non-verifying TLS modes too (Go enables sslsni by default)", () => { + it("carries the servername for non-verifying TLS modes too", () => { expect(sslOptionFor("require", false, "db.example.com")).toEqual({ rejectUnauthorized: false, servername: "db.example.com", @@ -203,7 +203,7 @@ describe("sslOptionFor", () => { }); }); -describe("sslConfigsFor (pgconn fallback list)", () => { +describe("sslConfigsFor (fallback list)", () => { it("local connections try a single plaintext (no-TLS) config", () => { expect(sslConfigsFor(undefined, true, undefined)).toEqual([false]); }); @@ -212,7 +212,7 @@ describe("sslConfigsFor (pgconn fallback list)", () => { expect(sslConfigsFor("disable", false, undefined)).toEqual([false]); }); - it("allow is plaintext primary with a TLS fallback ({nil, tlsConfig})", () => { + it("allow is plaintext primary with a TLS fallback (plaintext first, then TLS)", () => { expect(sslConfigsFor("allow", false, undefined)).toEqual([ false, { rejectUnauthorized: false }, @@ -433,7 +433,7 @@ describe("poolStepDownVerify", () => { expect(done).toBeUndefined(); }); - it("propagates a failing step-down to the pool callback so the checkout fails (Go AfterConnect parity)", async () => { + it("propagates a failing step-down to the pool callback so the checkout fails", async () => { const failure = new Error("permission denied to set role"); const client = { query: () => Promise.reject(failure) }; const done = await new Promise((resolve) => { @@ -520,14 +520,14 @@ describe("isUnixSocketHost", () => { expect(isUnixSocketHost("::1")).toBe(false); }); - it("treats an uppercase Windows drive path as a socket, lowercase as TCP (pgconn parity)", () => { + it("treats an uppercase Windows drive path as a socket, lowercase as TCP", () => { expect(isUnixSocketHost("C:\\pgsql")).toBe(true); expect(isUnixSocketHost("c:\\pgsql")).toBe(false); expect(isUnixSocketHost("C:")).toBe(false); }); }); -describe("isTerminalConnectError (pgconn fallback termination)", () => { +describe("isTerminalConnectError (fallback termination)", () => { it("terminates on auth/catalog/privilege SQLSTATEs carried on the error cause", () => { // The pg driver attaches the SQLSTATE as `code`; @effect/sql wraps it in `cause`. expect(isTerminalConnectError({ cause: { code: "28P01" } }, false)).toBe(true); @@ -535,7 +535,7 @@ describe("isTerminalConnectError (pgconn fallback termination)", () => { expect(isTerminalConnectError({ code: "42501" }, false)).toBe(true); }); - it("gates 28000 on the attempt having used TLS (pgconn fc.TLSConfig != nil)", () => { + it("gates 28000 on the attempt having used TLS (a TLS attempt is required)", () => { expect(isTerminalConnectError({ code: "28000" }, true)).toBe(true); expect(isTerminalConnectError({ code: "28000" }, false)).toBe(false); }); @@ -568,7 +568,7 @@ describe("toExecError (pg server-error extraction)", () => { }), }); - it("renders pgconn's PgError message and carries code, detail, and position", () => { + it("renders the server error message and carries code, detail, and position", () => { const error = toExecError( sqlErrorChain({ message: 'type "ltree" does not exist', @@ -584,7 +584,7 @@ describe("toExecError (pg server-error extraction)", () => { expect(error.position).toBe(25); }); - it("omits detail and position when the server sent none (Go's non-empty gates)", () => { + it("omits detail and position when the server sent none", () => { const error = toExecError( sqlErrorChain({ message: 'syntax error at or near "BOOM"', diff --git a/apps/cli/src/command-internal/db-image.unit.test.ts b/apps/cli/src/command-internal/db-image.unit.test.ts index d66aabd1b2..b66d3de899 100644 --- a/apps/cli/src/command-internal/db-image.unit.test.ts +++ b/apps/cli/src/command-internal/db-image.unit.test.ts @@ -68,7 +68,7 @@ describe("resolveDbImage", () => { }); }); - it.effect("rewrites to the OrioleDB image on a 15/17 project (Go config.Validate)", () => { + it.effect("rewrites to the OrioleDB image on a 15/17 project", () => { const dir = withTemp(); return Effect.gen(function* () { // > 15.1.1.13 → `-orioledb` diff --git a/apps/cli/src/command-internal/db-target-flags.unit.test.ts b/apps/cli/src/command-internal/db-target-flags.unit.test.ts index bc7f841570..6f5abb6126 100644 --- a/apps/cli/src/command-internal/db-target-flags.unit.test.ts +++ b/apps/cli/src/command-internal/db-target-flags.unit.test.ts @@ -135,7 +135,7 @@ describe("resolveDbTargetFlags", () => { expect(result.setFlags).toEqual(["local"]); }); - it("--schema -- --linked: -- consumed as schema value, --linked is a real flag (Go pflag parity)", () => { + it("--schema -- --linked: -- consumed as schema value, --linked is a real flag", () => { const result = resolveDbTargetFlags(["db", "lint", "--schema", "--", "--linked"]); expect(result.connType).toBe("linked"); expect(result.setFlags).toEqual(["linked"]); @@ -271,7 +271,7 @@ describe("changedLinkedLocalFlags", () => { expect(changedLinkedLocalFlags(["seed", "buckets", "--local"])).toEqual(["local"]); }); - it("returns both selectors in cobra's sorted order when both are set", () => { + it("returns both selectors in sorted order when both are set", () => { expect(changedLinkedLocalFlags(["seed", "buckets", "--local", "--linked"])).toEqual([ "linked", "local", diff --git a/apps/cli/src/command-internal/debug-logger.layer.ts b/apps/cli/src/command-internal/debug-logger.layer.ts index bc3c156247..8bf81d628c 100644 --- a/apps/cli/src/command-internal/debug-logger.layer.ts +++ b/apps/cli/src/command-internal/debug-logger.layer.ts @@ -5,7 +5,7 @@ import { DebugLogger } from "./debug-logger.service.ts"; const pad = (n: number): string => String(n).padStart(2, "0"); -/** Formats a timestamp matching Go's `log.LstdFlags`: `YYYY/MM/DD HH:MM:SS`. */ +/** Formats a timestamp as `YYYY/MM/DD HH:MM:SS`. */ function formatTimestamp(now: Date): string { return ( `${now.getFullYear()}/${pad(now.getMonth() + 1)}/${pad(now.getDate())} ` + diff --git a/apps/cli/src/command-internal/debug-logger.layer.unit.test.ts b/apps/cli/src/command-internal/debug-logger.layer.unit.test.ts index 8207e64115..67e7ef8628 100644 --- a/apps/cli/src/command-internal/debug-logger.layer.unit.test.ts +++ b/apps/cli/src/command-internal/debug-logger.layer.unit.test.ts @@ -46,7 +46,7 @@ describe("debugLoggerLayer", () => { ); }); - it.effect("http emits Go timestamp order and method/url format", () => { + it.effect("http emits timestamp, method and url in order", () => { vi.useFakeTimers(); vi.setSystemTime(new Date(2026, 5, 4, 8, 24, 47)); const stderr = captureStderr(); diff --git a/apps/cli/src/command-internal/detect-content-type.ts b/apps/cli/src/command-internal/detect-content-type.ts index 2c01986594..f6550503eb 100644 --- a/apps/cli/src/command-internal/detect-content-type.ts +++ b/apps/cli/src/command-internal/detect-content-type.ts @@ -1,6 +1,6 @@ /** * Content-type sniffer for the `seed buckets` upload path: Storage's `Content-Type` metadata is - * byte-driven, not extension-driven, matching Go's `net/http.DetectContentType` byte-for-byte so + * byte-driven, not extension-driven, so * stored metadata stays reproducible. Signature order matters: first match wins. */ diff --git a/apps/cli/src/command-internal/detect-content-type.unit.test.ts b/apps/cli/src/command-internal/detect-content-type.unit.test.ts index 70fc8d0483..8c93db206f 100644 --- a/apps/cli/src/command-internal/detect-content-type.unit.test.ts +++ b/apps/cli/src/command-internal/detect-content-type.unit.test.ts @@ -10,8 +10,6 @@ function bytes(s: string): Uint8Array { } describe("detectContentType", () => { - // Expected values were produced by running Go's `http.DetectContentType` over these same - // byte inputs. const corpus: ReadonlyArray = [ ["png", "\x89PNG\x0D\x0A\x1A\x0A\x00\x00", "image/png"], ["pdf", "%PDF-1.4\n...", "application/pdf"], @@ -37,7 +35,7 @@ describe("detectContentType", () => { ]; for (const [name, input, expected] of corpus) { - it(`matches Go http.DetectContentType for ${name}`, () => { + it(`detects the expected content type for ${name}`, () => { expect(detectContentType(bytes(input))).toBe(expected); }); } diff --git a/apps/cli/src/command-internal/diff-engine.unit.test.ts b/apps/cli/src/command-internal/diff-engine.unit.test.ts index 0c24c29cc4..c1b1978798 100644 --- a/apps/cli/src/command-internal/diff-engine.unit.test.ts +++ b/apps/cli/src/command-internal/diff-engine.unit.test.ts @@ -83,7 +83,7 @@ describe("resolvePullDiffEngine", () => { }); describe("parseBoolEnv", () => { - it("accepts only strconv.ParseBool truthy strings", () => { + it("accepts only the boolean truthy strings", () => { for (const v of ["1", "t", "T", "TRUE", "true", "True"]) { expect(parseBoolEnv(v)).toBe(true); } @@ -108,14 +108,14 @@ describe("resolveDeclarativeFromArgs", () => { expect(resolveDeclarativeFromArgs(["db", "pull", "--use-pg-delta"])).toBe(true); }); - it("parses an =value with strconv.ParseBool semantics", () => { + it("parses an =value with boolean semantics", () => { expect(resolveDeclarativeFromArgs(["--declarative=false"])).toBe(false); expect(resolveDeclarativeFromArgs(["--declarative=true"])).toBe(true); expect(resolveDeclarativeFromArgs(["--use-pg-delta=0"])).toBe(false); expect(resolveDeclarativeFromArgs(["--use-pg-delta=1"])).toBe(true); }); - it("lets the last occurrence win across both flag names (pflag single-variable bind)", () => { + it("lets the last occurrence win across both flag names", () => { expect(resolveDeclarativeFromArgs(["--declarative", "--use-pg-delta=false"])).toBe(false); expect(resolveDeclarativeFromArgs(["--use-pg-delta", "--declarative=false"])).toBe(false); expect(resolveDeclarativeFromArgs(["--declarative=false", "--use-pg-delta"])).toBe(true); diff --git a/apps/cli/src/command-internal/docker-ids.unit.test.ts b/apps/cli/src/command-internal/docker-ids.unit.test.ts index 701e0740a6..a61cc4f195 100644 --- a/apps/cli/src/command-internal/docker-ids.unit.test.ts +++ b/apps/cli/src/command-internal/docker-ids.unit.test.ts @@ -10,7 +10,7 @@ import { localNetworkId, } from "./docker-ids.ts"; import { resolveDockerNetworkMode } from "../shared/functions/functions-docker.ts"; -import { viperEnvStringWithProjectFallback } from "./viper-env.ts"; +import { supabaseEnvStringWithProjectFallback } from "./supabase-env.ts"; describe("resolveLocalProjectId", () => { it("prefers SUPABASE_PROJECT_ID (env) over config.toml and the basename", () => { @@ -34,7 +34,7 @@ describe("resolveLocalProjectId", () => { }); describe("serviceContainerIds", () => { - it("returns the 13 service container ids in Go's GetDockerIds() order", () => { + it("returns the 13 service container ids in order", () => { expect(serviceContainerIds("my-app")).toEqual([ "supabase_kong_my-app", "supabase_auth_my-app", @@ -59,7 +59,7 @@ describe("serviceContainerIds", () => { }); describe("cliProjectFilterValue", () => { - it("returns the bare label when the project id is empty (Go's --all path)", () => { + it("returns the bare label when the project id is empty (the --all path)", () => { expect(cliProjectFilterValue("")).toBe(CLI_PROJECT_LABEL); }); @@ -74,7 +74,7 @@ describe("cliProjectFilterValue", () => { }); }); -describe("resolveDockerNetworkMode composed with viperEnvStringWithProjectFallback (start/db start call shape)", () => { +describe("resolveDockerNetworkMode composed with supabaseEnvStringWithProjectFallback (start/db start call shape)", () => { const KEY = "SUPABASE_NETWORK_ID"; afterEach(() => { @@ -84,7 +84,7 @@ describe("resolveDockerNetworkMode composed with viperEnvStringWithProjectFallba function resolve(flagValue: string | undefined, projectEnv: Record) { return resolveDockerNetworkMode({ explicit: flagValue, - envOverride: viperEnvStringWithProjectFallback(KEY, projectEnv), + envOverride: supabaseEnvStringWithProjectFallback(KEY, projectEnv), projectId: "my-app", }); } @@ -104,7 +104,7 @@ describe("resolveDockerNetworkMode composed with viperEnvStringWithProjectFallba expect(resolve(undefined, { [KEY]: "project-network" })).toBe("project-network"); }); - it("prefers the shell value over the project .env value (presence wins, matching godotenv.Load)", () => { + it("prefers the shell value over the project .env value (presence wins)", () => { process.env[KEY] = "shell-network"; expect(resolve(undefined, { [KEY]: "project-network" })).toBe("shell-network"); }); @@ -115,7 +115,7 @@ describe("resolveDockerNetworkMode composed with viperEnvStringWithProjectFallba expect(resolve("", {})).toBe(localNetworkId("my-app")); }); - it("an explicit-but-empty --network-id= skips the env var entirely (viper: a Changed pflag resolves before AutomaticEnv)", () => { + it("an explicit-but-empty --network-id= skips the env var entirely (an explicit flag beats the env var)", () => { process.env[KEY] = "env-network"; expect(resolve("", { [KEY]: "project-network" })).toBe(localNetworkId("my-app")); }); diff --git a/apps/cli/src/command-internal/docker-lifecycle.unit.test.ts b/apps/cli/src/command-internal/docker-lifecycle.unit.test.ts index 97165a31c4..16ef30d51a 100644 --- a/apps/cli/src/command-internal/docker-lifecycle.unit.test.ts +++ b/apps/cli/src/command-internal/docker-lifecycle.unit.test.ts @@ -286,19 +286,16 @@ describe("inspectContainerState", () => { ); }); - it.live( - "treats a paused/restarting container as running, matching Go's boolean-based gate", - () => { - const mock = mockSpawner({ - stdout: JSON.stringify({ Status: "paused", Running: true, ExitCode: 0 }), - }); - return inspectContainerState(mock.spawner, "supabase_db_my-app").pipe( - Effect.map((state) => { - expect(state).toEqual({ running: true, status: "paused", exitCode: 0, oomKilled: false }); - }), - ); - }, - ); + it.live("treats a paused/restarting container as running", () => { + const mock = mockSpawner({ + stdout: JSON.stringify({ Status: "paused", Running: true, ExitCode: 0 }), + }); + return inspectContainerState(mock.spawner, "supabase_db_my-app").pipe( + Effect.map((state) => { + expect(state).toEqual({ running: true, status: "paused", exitCode: 0, oomKilled: false }); + }), + ); + }); it.live( "fails with DockerLifecycleInspectError, preserving the real stderr, when the container does not exist", diff --git a/apps/cli/src/command-internal/docker-registry.unit.test.ts b/apps/cli/src/command-internal/docker-registry.unit.test.ts index 632948ed05..2253e9a04e 100644 --- a/apps/cli/src/command-internal/docker-registry.unit.test.ts +++ b/apps/cli/src/command-internal/docker-registry.unit.test.ts @@ -95,7 +95,7 @@ describe("getRegistryImageUrl", () => { ).toEqual(["my.mirror.example/supabase/postgres:17.6.1.138"]); }); - it("honors a projectEnvValues (dotenv)-only registry override, matching Go's post-Load os.Getenv", () => { + it("honors a projectEnvValues (dotenv)-only registry override", () => { expect( withRegistry(undefined, () => resolveImage("supabase/pg_prove:3.36", { diff --git a/apps/cli/src/command-internal/docker-run.args.unit.test.ts b/apps/cli/src/command-internal/docker-run.args.unit.test.ts index f38831461b..f448a9bc43 100644 --- a/apps/cli/src/command-internal/docker-run.args.unit.test.ts +++ b/apps/cli/src/command-internal/docker-run.args.unit.test.ts @@ -26,7 +26,7 @@ describe("applyBitbucketDockerFilter", () => { expect(applyBitbucketDockerFilter(pgDelta, false)).toBe(pgDelta); }); - test("drops named-volume binds and clears security-opt under Bitbucket (Go DockerStart)", () => { + test("drops named-volume binds and clears security-opt under Bitbucket", () => { const filtered = applyBitbucketDockerFilter(pgDelta, true); // Named Deno-cache volume dropped; the /repo:/workspace bind mount kept. expect(filtered.binds).toEqual(["/repo:/workspace"]); @@ -35,7 +35,7 @@ describe("applyBitbucketDockerFilter", () => { }); describe("buildDockerArgs", () => { - test("assembles run args in Go-parity order for a named network", () => { + test("assembles run args in order for a named network", () => { expect(buildDockerArgs(base)).toEqual([ "run", "--rm", @@ -114,7 +114,7 @@ describe("buildDockerArgs", () => { expect(buildDockerArgs(base)).not.toContain("--label"); }); - test("emits --label k=v for each entry, before the image (Go's DockerStart project labels)", () => { + test("emits --label k=v for each entry, before the image", () => { const args = buildDockerArgs({ ...base, labels: { diff --git a/apps/cli/src/command-internal/docker-suggest.unit.test.ts b/apps/cli/src/command-internal/docker-suggest.unit.test.ts index eb857679d8..52791dcf4d 100644 --- a/apps/cli/src/command-internal/docker-suggest.unit.test.ts +++ b/apps/cli/src/command-internal/docker-suggest.unit.test.ts @@ -4,7 +4,7 @@ import { containerRuntimeNotFoundMessage } from "./container-cli.ts"; import { SUGGEST_DOCKER_INSTALL, isDockerDaemonUnreachable } from "./docker-suggest.ts"; describe("isDockerDaemonUnreachable", () => { - it("detects the docker/podman daemon-down CLI messages (Go's IsErrConnectionFailed)", () => { + it("detects the docker/podman daemon-down CLI messages", () => { expect( isDockerDaemonUnreachable( "Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?", @@ -35,7 +35,7 @@ describe("isDockerDaemonUnreachable", () => { expect(isDockerDaemonUnreachable("")).toBe(false); }); - it("exposes Go's install hint verbatim", () => { + it("exposes the install hint verbatim", () => { expect(SUGGEST_DOCKER_INSTALL).toContain("https://docs.docker.com/desktop"); }); }); diff --git a/apps/cli/src/command-internal/dotenv.ts b/apps/cli/src/command-internal/dotenv.ts index 2fb5fbc016..77af4075c4 100644 --- a/apps/cli/src/command-internal/dotenv.ts +++ b/apps/cli/src/command-internal/dotenv.ts @@ -1,9 +1,9 @@ -// godotenv-compatible: `KEY=VALUE`/`KEY="VALUE"` lines, `#` comments, blank lines, and an +// Dotenv syntax: `KEY=VALUE`/`KEY="VALUE"` lines, `#` comments, blank lines, and an // optional `export ` prefix. An empty or invalid variable name throws. const EXPORT_PREFIX = /^\s*export\s+/; /** - * Minimal godotenv-compatible parser for project `.env` files. Throws when a variable name is + * Minimal dotenv parser for project `.env` files. Throws when a variable name is * empty or contains an invalid character. */ export function parseDotEnv(contents: string): Record { diff --git a/apps/cli/src/command-internal/dotenv.unit.test.ts b/apps/cli/src/command-internal/dotenv.unit.test.ts index 9f976b150c..c12e6f0ee8 100644 --- a/apps/cli/src/command-internal/dotenv.unit.test.ts +++ b/apps/cli/src/command-internal/dotenv.unit.test.ts @@ -11,7 +11,7 @@ describe("parseDotEnv", () => { }); }); - it("expands escape sequences in double-quoted values (godotenv parity)", () => { + it("expands escape sequences in double-quoted values", () => { expect(parseDotEnv('A="line1\\nline2"\nB="a\\"b\\\\c"')).toEqual({ A: "line1\nline2", B: 'a"b\\c', @@ -22,7 +22,7 @@ describe("parseDotEnv", () => { expect(parseDotEnv("A='line1\\nline2'")).toEqual({ A: "line1\\nline2" }); }); - it("strips unquoted inline comments preceded by whitespace (godotenv parity)", () => { + it("strips unquoted inline comments preceded by whitespace", () => { // A `#` after whitespace begins a comment; a `#` with no leading space is // part of the value. expect(parseDotEnv("DB_PORT=54323 # local db")).toEqual({ DB_PORT: "54323" }); @@ -35,7 +35,7 @@ describe("parseDotEnv", () => { expect(parseDotEnv("B='has # hash inside' # comment")).toEqual({ B: "has # hash inside" }); }); - it("accepts godotenv YAML-style colon assignments", () => { + it("accepts YAML-style colon assignments", () => { expect(parseDotEnv("SUPABASE_DB_PASSWORD: secret\nDB_PORT:54323")).toEqual({ SUPABASE_DB_PASSWORD: "secret", DB_PORT: "54323", @@ -48,11 +48,11 @@ describe("parseDotEnv", () => { expect(() => parseDotEnv('A="unterminated')).toThrow(/unterminated quoted value/); }); - it("throws Go's 'unexpected character' error on a malformed variable name", () => { + it("throws an 'unexpected character' error on a malformed variable name", () => { expect(() => parseDotEnv("!=")).toThrow(/unexpected character "!" in variable name/); }); - describe("multiline quoted values (godotenv parity)", () => { + describe("multiline quoted values", () => { it("scans a double-quoted value across newlines, preserving embedded newlines", () => { const pem = "-----BEGIN KEY-----\nline2\nline3\n-----END KEY-----"; const env = parseDotEnv(`KEY="${pem}"\nDB_PORT=54323`); @@ -78,7 +78,7 @@ describe("parseDotEnv", () => { }); }); - describe("variable expansion (godotenv parity)", () => { + describe("variable expansion", () => { it("expands $VAR and ${VAR} from earlier same-file definitions", () => { expect(parseDotEnv("DB_PORT=54323\nSUPABASE_DB_PORT=$DB_PORT\nURL=${DB_PORT}/db")).toEqual({ DB_PORT: "54323", diff --git a/apps/cli/src/command-internal/go-duration.ts b/apps/cli/src/command-internal/duration.ts similarity index 91% rename from apps/cli/src/command-internal/go-duration.ts rename to apps/cli/src/command-internal/duration.ts index f9ab1f3526..0e11cd8fc7 100644 --- a/apps/cli/src/command-internal/go-duration.ts +++ b/apps/cli/src/command-internal/duration.ts @@ -1,6 +1,5 @@ /** - * Duration string parsing and formatting matching Go's `time.ParseDuration()`/`Duration.String()` - * grammar and rounding — the syntax the hosted API and container env vars expect. + * Duration string parsing and formatting in the `1h30m0s` grammar with nanosecond rounding — the syntax the hosted API and container env vars expect. * * Several `config.toml` duration fields (e.g. `auth.sessions.timebox = "1h"`) are stored as the * raw string, but a container's env var is built from the parsed-then-reserialized value, which @@ -15,7 +14,7 @@ const NS_PER_HOUR = 60 * NS_PER_MINUTE; const NS_PER_MS = 1_000_000; const NS_PER_US = 1_000; -// `BigInt` constants for `parseGoDuration`'s accumulator: near the int64 nanosecond ceiling +// `BigInt` constants for `parseDuration`'s accumulator: near the int64 nanosecond ceiling // (~9.223e18, past `Number.MAX_SAFE_INTEGER`), a `number` accumulator would silently round a // legitimate boundary value into a false overflow or vice versa. const NS_PER_SECOND_BIG = 1_000_000_000n; @@ -34,7 +33,7 @@ const MAX_INT64_NS = 9223372036854775807n; const UINT64_ACCUMULATOR_BOUND_NS = 1n << 63n; /** - * Parses a Go-style duration string to nanoseconds: a possibly-signed sequence of decimal + * Parses a duration string to nanoseconds: a possibly-signed sequence of decimal * numbers, each with a unit suffix (`"ns"`, `"us"`/`"µs"`/`"μs"`, `"ms"`, `"s"`, `"m"`, `"h"`), e.g. * `"5s"`, `"1h30m"`, `"300ms"`. Throws on invalid input, including an overflowing magnitude. * @@ -43,7 +42,7 @@ const UINT64_ACCUMULATOR_BOUND_NS = 1n << 63n; * ceiling, but every real config duration is nowhere near it, and the overflow check itself * doesn't depend on `number` precision. */ -export function parseGoDuration(value: string): number { +export function parseDuration(value: string): number { const orig = value; let s = value; let neg = false; @@ -139,11 +138,11 @@ export function parseGoDuration(value: string): number { } /** - * Formats nanoseconds as a Go-style duration string. `0` formats as `"0s"`; otherwise only the + * Formats nanoseconds as a duration string. `0` formats as `"0s"`; otherwise only the * needed units show, with minutes/seconds always trailing an hours component (`"1h0m0s"`), and a * sub-second remainder as a fraction of its largest applicable unit (`"1.5s"`, `"300ms"`). */ -export function formatGoDuration(nanoseconds: number): string { +export function formatDuration(nanoseconds: number): string { if (nanoseconds === 0) return "0s"; let ns = nanoseconds; @@ -202,10 +201,10 @@ function formatFraction(totalNs: number, unitNs: number): string { /** * Seconds form of a `Db.HealthTimeout` duration string (`"2m"` default). Throws on a malformed - * value, matching {@link parseGoDuration}; the caller wraps that into a typed config-load-failure + * value, matching {@link parseDuration}; the caller wraps that into a typed config-load-failure * error so rollback/cleanup still fires. A degenerate value like `"0s"` isn't special-cased: it * means exactly one immediate health probe with no wait, not a 30s fallback. */ export function resolveHealthTimeoutSeconds(healthTimeout: string): number { - return Math.trunc(parseGoDuration(healthTimeout) / 1_000_000_000); + return Math.trunc(parseDuration(healthTimeout) / 1_000_000_000); } diff --git a/apps/cli/src/command-internal/duration.unit.test.ts b/apps/cli/src/command-internal/duration.unit.test.ts new file mode 100644 index 0000000000..c1390e67b5 --- /dev/null +++ b/apps/cli/src/command-internal/duration.unit.test.ts @@ -0,0 +1,146 @@ +import { describe, expect, it } from "vitest"; + +import { formatDuration, parseDuration } from "./duration.ts"; + +describe("parseDuration", () => { + it("parses a single unit", () => { + expect(parseDuration("5s")).toBe(5_000_000_000); + expect(parseDuration("300ms")).toBe(300_000_000); + expect(parseDuration("1h")).toBe(3_600_000_000_000); + }); + + it("parses multiple units concatenated", () => { + expect(parseDuration("1h30m")).toBe(5_400_000_000_000); + }); + + it("parses a fractional value", () => { + expect(parseDuration("1.5s")).toBe(1_500_000_000); + }); + + it("parses a signed value", () => { + expect(parseDuration("-5s")).toBe(-5_000_000_000); + expect(parseDuration("+5s")).toBe(5_000_000_000); + }); + + it('treats a bare "0" as zero regardless of sign', () => { + expect(parseDuration("0")).toBe(0); + }); + + it("rejects an empty string", () => { + expect(() => parseDuration("")).toThrow('time: invalid duration ""'); + }); + + it.each(["s", "m", "h", "ms", "us", "µs", "μs", "ns"])( + 'rejects a bare unit with no preceding digit ("%s")', + (input) => { + expect(() => parseDuration(input)).toThrow(`time: invalid duration "${input}"`); + }, + ); + + it.each(["us", "µs", "μs"])('accepts every microsecond unit spelling ("1%s")', (unit) => { + expect(parseDuration(`1${unit}`)).toBe(1_000); + }); + + it('rejects a bare unit following a valid unit ("1hs")', () => { + expect(() => parseDuration("1hs")).toThrow('time: invalid duration "1hs"'); + }); + + it('rejects a negative bare unit ("-s")', () => { + expect(() => parseDuration("-s")).toThrow('time: invalid duration "-s"'); + }); + + it.each([".s", ".", "-.", "+.", ".h"])('rejects a lone "." with no digits ("%s")', (input) => { + expect(() => parseDuration(input)).toThrow(`time: invalid duration "${input}"`); + }); + + it('accepts a fraction-only value with no leading digit (".5s")', () => { + expect(parseDuration(".5s")).toBe(500_000_000); + }); + + it("rejects a number with no unit", () => { + expect(() => parseDuration("5")).toThrow('time: missing unit in duration "5"'); + }); + + it("rejects an unknown unit", () => { + expect(() => parseDuration("5x")).toThrow('time: unknown unit in duration "5x"'); + }); + + it('truncates a sub-nanosecond fraction instead of rounding ("0.5ns")', () => { + expect(parseDuration("0.5ns")).toBe(0); + }); + + it('truncates a sub-nanosecond fraction instead of rounding ("1.9ns")', () => { + expect(parseDuration("1.9ns")).toBe(1); + }); + + it('rounds a long fractional remainder up ("0.999999999999999999s")', () => { + expect(parseDuration("0.999999999999999999s")).toBe(1_000_000_000); + }); + + it('rejects a duration that overflows math.MaxInt64 nanoseconds ("2562048h")', () => { + expect(() => parseDuration("2562048h")).toThrow('time: invalid duration "2562048h"'); + }); + + it("accepts the maximum parseable duration without overflowing", () => { + expect(() => parseDuration("2562047h47m16.854775807s")).not.toThrow(); + }); + + it('rejects a duration exactly 1ns past the int64 maximum ("9223372036854775808ns")', () => { + expect(() => parseDuration("9223372036854775808ns")).toThrow( + 'time: invalid duration "9223372036854775808ns"', + ); + }); + + it("accepts a duration exactly at the int64 maximum in nanoseconds", () => { + expect(() => parseDuration("9223372036854775807ns")).not.toThrow(); + }); + + it("accepts the minimum representable negative duration (int64 minimum ns)", () => { + expect(parseDuration("-9223372036854775808ns")).toBe(-9223372036854775808); + }); + + it("accepts the equivalent hours/minutes/seconds form of math.MinInt64 ns", () => { + expect(parseDuration("-2562047h47m16.854775808s")).toBe(-9223372036854775808); + }); + + it("rejects a duration 1ns past math.MinInt64", () => { + expect(() => parseDuration("-9223372036854775809ns")).toThrow( + 'time: invalid duration "-9223372036854775809ns"', + ); + }); +}); + +describe("formatDuration", () => { + it('formats zero as "0s"', () => { + expect(formatDuration(0)).toBe("0s"); + }); + + it("formats whole hours/minutes/seconds", () => { + expect(formatDuration(3_600_000_000_000)).toBe("1h0m0s"); + }); + + it("normalizes 90s to 1m30s", () => { + expect(formatDuration(90_000_000_000)).toBe("1m30s"); + }); + + it("formats a sub-second fraction", () => { + expect(formatDuration(1_500_000_000)).toBe("1.5s"); + }); + + it("round-trips through parseDuration", () => { + expect(formatDuration(parseDuration("1h"))).toBe("1h0m0s"); + expect(formatDuration(parseDuration("90s"))).toBe("1m30s"); + }); + + it("includes a sub-second fraction alongside minutes", () => { + expect(formatDuration(parseDuration("1m0.5s"))).toBe("1m0.5s"); + }); + + it("includes a sub-second fraction alongside hours", () => { + expect(formatDuration(parseDuration("1h0.5s"))).toBe("1h0m0.5s"); + }); + + it("includes a sub-second fraction alongside hours and minutes", () => { + expect(formatDuration(parseDuration("1h1m0.5s"))).toBe("1h1m0.5s"); + }); +}); diff --git a/apps/cli/src/command-internal/edge-runtime-script.unit.test.ts b/apps/cli/src/command-internal/edge-runtime-script.unit.test.ts index 4a2ede6846..d7f3e67eff 100644 --- a/apps/cli/src/command-internal/edge-runtime-script.unit.test.ts +++ b/apps/cli/src/command-internal/edge-runtime-script.unit.test.ts @@ -16,7 +16,7 @@ describe("buildEdgeRuntimeStartCmd", () => { ]); }); - it("drops --port when allocation failed (Go preserves prior behaviour)", () => { + it("drops --port when allocation failed", () => { expect(buildEdgeRuntimeStartCmd({ port: Option.none(), debug: false })).toEqual([ "edge-runtime", "start", diff --git a/apps/cli/src/command-internal/experimental-gate.unit.test.ts b/apps/cli/src/command-internal/experimental-gate.unit.test.ts index c0cdc0f60d..d34e016979 100644 --- a/apps/cli/src/command-internal/experimental-gate.unit.test.ts +++ b/apps/cli/src/command-internal/experimental-gate.unit.test.ts @@ -14,7 +14,7 @@ describe("requireExperimental", () => { requireExperimental.pipe(Effect.provide(withFlag(true))), ); - it.effect("fails with Go's byte-exact message when neither flag nor env is set", () => + it.effect("fails with the gate message when neither flag nor env is set", () => Effect.gen(function* () { const saved = process.env[ENV]; delete process.env[ENV]; @@ -26,7 +26,7 @@ describe("requireExperimental", () => { }), ); - it.effect("passes when SUPABASE_EXPERIMENTAL=1 even without the flag (viper AutomaticEnv)", () => + it.effect("passes when SUPABASE_EXPERIMENTAL=1 even without the flag", () => Effect.gen(function* () { const saved = process.env[ENV]; process.env[ENV] = "1"; @@ -37,20 +37,18 @@ describe("requireExperimental", () => { }), ); - it.effect( - "fails even with SUPABASE_EXPERIMENTAL=1 when --experimental=false is explicit (viper Changed wins)", - () => - Effect.gen(function* () { - const saved = process.env[ENV]; - process.env[ENV] = "1"; - const error = yield* requireExperimental.pipe( - Effect.provide(withFlag(false, ["--experimental=false"])), - Effect.flip, - ); - if (saved === undefined) delete process.env[ENV]; - else process.env[ENV] = saved; - expect(error).toBeInstanceOf(ExperimentalRequiredError); - }), + it.effect("fails even with SUPABASE_EXPERIMENTAL=1 when --experimental=false is explicit", () => + Effect.gen(function* () { + const saved = process.env[ENV]; + process.env[ENV] = "1"; + const error = yield* requireExperimental.pipe( + Effect.provide(withFlag(false, ["--experimental=false"])), + Effect.flip, + ); + if (saved === undefined) delete process.env[ENV]; + else process.env[ENV] = saved; + expect(error).toBeInstanceOf(ExperimentalRequiredError); + }), ); it.effect( @@ -69,32 +67,28 @@ describe("requireExperimental", () => { }), ); - it.effect( - "a repeated --experimental=false --experimental=true keeps the LAST occurrence (viper Set() wins)", - () => - Effect.gen(function* () { - const exit = yield* requireExperimental.pipe( - Effect.provide( - withFlag(false, ["db", "pull", "--experimental=false", "--experimental=true"]), - ), - Effect.exit, - ); - expect(exit._tag).toBe("Success"); - }), + it.effect("a repeated --experimental=false --experimental=true keeps the LAST occurrence", () => + Effect.gen(function* () { + const exit = yield* requireExperimental.pipe( + Effect.provide( + withFlag(false, ["db", "pull", "--experimental=false", "--experimental=true"]), + ), + Effect.exit, + ); + expect(exit._tag).toBe("Success"); + }), ); - it.effect( - "a repeated --experimental=true --experimental=false keeps the LAST occurrence (viper Set() wins)", - () => - Effect.gen(function* () { - const error = yield* requireExperimental.pipe( - Effect.provide( - withFlag(true, ["db", "pull", "--experimental=true", "--experimental=false"]), - ), - Effect.flip, - ); - expect(error).toBeInstanceOf(ExperimentalRequiredError); - }), + it.effect("a repeated --experimental=true --experimental=false keeps the LAST occurrence", () => + Effect.gen(function* () { + const error = yield* requireExperimental.pipe( + Effect.provide( + withFlag(true, ["db", "pull", "--experimental=true", "--experimental=false"]), + ), + Effect.flip, + ); + expect(error).toBeInstanceOf(ExperimentalRequiredError); + }), ); it.effect("a repeated --experimental=false --experimental (bare) keeps the LAST occurrence", () => diff --git a/apps/cli/src/command-internal/fail-on.unit.test.ts b/apps/cli/src/command-internal/fail-on.unit.test.ts index 5bf34de451..497cdf2b10 100644 --- a/apps/cli/src/command-internal/fail-on.unit.test.ts +++ b/apps/cli/src/command-internal/fail-on.unit.test.ts @@ -35,7 +35,7 @@ describe("makeLevelEnum (exact-ci matcher — db advisors)", () => { expect(advisors.toEnum("ERROR")).toBe(2); }); - it("does NOT match a mixed-case level (Go's switch is exact)", () => { + it("does NOT match a mixed-case level (matching is exact)", () => { expect(advisors.toEnum("Info")).toBe(-1); expect(advisors.toEnum("warning")).toBe(-1); }); diff --git a/apps/cli/src/command-internal/go-float.ts b/apps/cli/src/command-internal/format-float.ts similarity index 61% rename from apps/cli/src/command-internal/go-float.ts rename to apps/cli/src/command-internal/format-float.ts index 286cef0f98..d180250dff 100644 --- a/apps/cli/src/command-internal/go-float.ts +++ b/apps/cli/src/command-internal/format-float.ts @@ -1,14 +1,14 @@ /** - * Formats a number the way Go's `fmt.Sprintf("%v", float64)` does: shortest `%g`, exponent - * notation when the decimal exponent is `< -4` or `>= 6` (`1000000` → `1e+06`), fixed notation - * otherwise, with a signed, at-least-two-digit exponent. + * Formats a number in its shortest round-trip form, switching to exponent notation when the + * decimal exponent is `< -4` or `>= 6` (`1000000` → `1e+06`), with a signed, at-least-two-digit + * exponent. * * Shared by `db query`'s value formatter and `postgres-config`'s pretty table. */ -export function goFormatFloat(n: number): string { +export function formatGeneralFloat(n: number): string { if (Number.isNaN(n)) return "NaN"; if (!Number.isFinite(n)) return n > 0 ? "+Inf" : "-Inf"; - // Go's `%v` preserves the sign of negative zero; `n === 0` is true for both `+0` and `-0`. + // Negative zero keeps its sign; `n === 0` is true for both `+0` and `-0`. if (Object.is(n, -0)) return "-0"; if (n === 0) return "0"; const neg = n < 0; diff --git a/apps/cli/src/command-internal/format-float.unit.test.ts b/apps/cli/src/command-internal/format-float.unit.test.ts new file mode 100644 index 0000000000..d82379056d --- /dev/null +++ b/apps/cli/src/command-internal/format-float.unit.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "vitest"; + +import { formatGeneralFloat } from "./format-float.ts"; + +describe("formatGeneralFloat", () => { + it("renders fixed notation within the [-4, 6) decimal-exponent range", () => { + expect(formatGeneralFloat(100)).toBe("100"); + expect(formatGeneralFloat(100000)).toBe("100000"); + expect(formatGeneralFloat(0.5)).toBe("0.5"); + expect(formatGeneralFloat(0.0001)).toBe("0.0001"); + }); + + it("switches to signed exponent notation at exponent >= 6 or < -4", () => { + expect(formatGeneralFloat(1000000)).toBe("1e+06"); + expect(formatGeneralFloat(100000000000)).toBe("1e+11"); + expect(formatGeneralFloat(123456789)).toBe("1.23456789e+08"); + expect(formatGeneralFloat(0.00001)).toBe("1e-05"); + }); + + it("preserves the sign for negative exponent-notation values", () => { + expect(formatGeneralFloat(-1000000)).toBe("-1e+06"); + }); + + it("renders zero as a bare 0", () => { + expect(formatGeneralFloat(0)).toBe("0"); + }); +}); diff --git a/apps/cli/src/command-internal/functions-go-config.ts b/apps/cli/src/command-internal/functions-local-config.ts similarity index 94% rename from apps/cli/src/command-internal/functions-go-config.ts rename to apps/cli/src/command-internal/functions-local-config.ts index d107110529..50ff4991eb 100644 --- a/apps/cli/src/command-internal/functions-go-config.ts +++ b/apps/cli/src/command-internal/functions-local-config.ts @@ -1,5 +1,5 @@ import { Effect } from "effect"; -import type { FunctionsGoConfigCompat } from "../shared/functions/functions-config.ts"; +import type { FunctionsLocalConfigLoader } from "../shared/functions/functions-config.ts"; import { loadLocalProjectContext } from "./local-project-context.ts"; import { resolveLocalConfigValues } from "./local-config-values.ts"; @@ -17,7 +17,7 @@ function toError(cause: unknown): Error { * local-dev values (JWTs, URLs) are discarded here; only `projectId`/`edgeRuntimeDenoVersion` and * the validation side effect (throws on the first failure) matter to these three commands. */ -export const functionsGoConfigCompat: FunctionsGoConfigCompat = { +export const functionsLocalConfigLoader: FunctionsLocalConfigLoader = { load: ({ projectRoot, projectRef }) => Effect.gen(function* () { const context = yield* loadLocalProjectContext(projectRoot, toError, projectRef); diff --git a/apps/cli/src/command-internal/glob.unit.test.ts b/apps/cli/src/command-internal/glob.unit.test.ts index 5fa2f0164d..20325040d7 100644 --- a/apps/cli/src/command-internal/glob.unit.test.ts +++ b/apps/cli/src/command-internal/glob.unit.test.ts @@ -157,7 +157,7 @@ describe("walkSqlFiles", () => { }).pipe(Effect.provide(layer)); }); - it.effect("recurses into subdirectories, already sorted in Go's byte order", () => { + it.effect("recurses into subdirectories, already sorted in byte order", () => { const layer = fakeWalkFs( { "/schemas": ["nested", "top.sql"], "/schemas/nested": ["inner.sql"] }, { @@ -194,22 +194,19 @@ describe("walkSqlFiles", () => { }, ); - it.effect( - "does not descend into a symlinked subdirectory (Go's fs.WalkDir/afero.Walk no-follow)", - () => { - const layer = fakeWalkFs( - { "/schemas": ["linked", "top.sql"], "/schemas/linked": ["secret.sql"] }, - { "/schemas/linked": "Directory", "/schemas/top.sql": "File" }, - undefined, - new Set(["/schemas/linked"]), - ); - return Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem; - const files = yield* walkSqlFiles(fs, "/schemas", ""); - expect([...files]).toEqual(["top.sql"]); - }).pipe(Effect.provide(layer)); - }, - ); + it.effect("does not descend into a symlinked subdirectory", () => { + const layer = fakeWalkFs( + { "/schemas": ["linked", "top.sql"], "/schemas/linked": ["secret.sql"] }, + { "/schemas/linked": "Directory", "/schemas/top.sql": "File" }, + undefined, + new Set(["/schemas/linked"]), + ); + return Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const files = yield* walkSqlFiles(fs, "/schemas", ""); + expect([...files]).toEqual(["top.sql"]); + }).pipe(Effect.provide(layer)); + }); it.effect("excludes a symlinked .sql file instead of applying its target", () => { const layer = fakeWalkFs( diff --git a/apps/cli/src/command-internal/global-flags.ts b/apps/cli/src/command-internal/global-flags.ts index df8b051ac9..9e5c4e242f 100644 --- a/apps/cli/src/command-internal/global-flags.ts +++ b/apps/cli/src/command-internal/global-flags.ts @@ -3,7 +3,7 @@ import { Flag, GlobalFlag } from "effect/unstable/cli"; import { CliArgs } from "../shared/cli/cli-args.service.ts"; import { VALUE_CONSUMING_LONG_FLAGS, VALUE_CONSUMING_SHORT_FLAGS } from "./db-target-flags.ts"; -import { viperEnvBool, viperEnvBoolWithProjectFallback } from "./viper-env.ts"; +import { supabaseEnvBool, supabaseEnvBoolWithProjectFallback } from "./supabase-env.ts"; // The CLI's global-flag registry is tree-wide, so `-o/--output` can't be redeclared per command to // vary its allowed values; this models it as the union of every command's accepted values, and @@ -39,7 +39,7 @@ export const OutputFlag = GlobalFlag.setting("output")({ * The TS-only `--output-format` global, accepted on any subcommand. * * A value-taking global flag must also be registered in `PERSISTENT_VALUE_FLAG_NAMES` - * (`shared/cli/cobra-flag-groups.ts`), or the pre-parse argv scanners won't consume its value. + * (`shared/cli/flag-groups.ts`), or the pre-parse argv scanners won't consume its value. */ export const OutputFormatFlag = GlobalFlag.setting("output-format")({ flag: Flag.choice("output-format", ["text", "json", "stream-json"]).pipe( @@ -120,7 +120,7 @@ export const AgentFlag = GlobalFlag.setting("agent")({ * Every global/persistent flag declared above. * * A value-taking flag added here must also be added to `PERSISTENT_VALUE_FLAG_NAMES` - * (`shared/cli/cobra-flag-groups.ts`), which the handler-side pflag scans and pre-parse token + * (`shared/cli/flag-groups.ts`), which the handler-side argv scans and pre-parse token * scanners both derive their token set from. A flag missed there fails silently: an unregistered * value flag won't consume its following token, so that token gets misread as positional. */ @@ -165,7 +165,7 @@ export const globalFlagValues = Effect.gen(function* () { return values; }); -const PFLAG_FALSE_VALUES = new Set(["0", "f", "F", "false", "FALSE", "False"]); +const FALSE_VALUES = new Set(["0", "f", "F", "false", "FALSE", "False"]); /** * Raw argv truncated at the first bare `--` operand terminator. This CLI's own lexer stops parsing @@ -180,7 +180,7 @@ const argsBeforeOperandTerminator = (args: ReadonlyArray): ReadonlyArray }; /** - * Drops tokens that pflag would consume as a value-consuming flag's value in space-separated form + * Drops tokens consumed as a value-consuming flag's value in space-separated form * (`--flag value` / `-f value`), so the `--yes`/`--experimental`/`--debug` argv scanners below * don't mistake a consumed value token for an explicit occurrence of the global flag — e.g. * `db pull --password --experimental=false` treats `--experimental=false` as `--password`'s @@ -214,7 +214,7 @@ const nonValueConsumedTokens = (args: ReadonlyArray): ReadonlyArray): boolean => nonValueConsumedTokens(argsBeforeOperandTerminator(args)).some( - (arg) => arg.startsWith("--yes=") && PFLAG_FALSE_VALUES.has(arg.slice("--yes=".length)), + (arg) => arg.startsWith("--yes=") && FALSE_VALUES.has(arg.slice("--yes=".length)), ); /** @@ -228,14 +228,14 @@ export const resolveYes = Effect.gen(function* () { if (yesFlagExplicitlyFalse(cliArgs.args)) { return false; } - return flag || viperEnvBool("SUPABASE_YES"); + return flag || supabaseEnvBool("SUPABASE_YES"); }); /** * `--yes` resolved with the project `.env` consulted too, for commands that load the nested * project env before prompting (`migration down`, `migration repair --all`). Shell env * *presence* (any value) suppresses the file value entirely (see - * {@link viperEnvBoolWithProjectFallback}); an explicit `--yes` wins over both. `projectEnv` is + * {@link supabaseEnvBoolWithProjectFallback}); an explicit `--yes` wins over both. `projectEnv` is * the loaded map from `loadProjectEnv`. */ export const resolveYesWithProjectEnv = (projectEnv: Record) => @@ -245,7 +245,7 @@ export const resolveYesWithProjectEnv = (projectEnv: Record) => if (yesFlagExplicitlyFalse(cliArgs.args)) { return false; } - return flag || viperEnvBoolWithProjectFallback("SUPABASE_YES", projectEnv); + return flag || supabaseEnvBoolWithProjectFallback("SUPABASE_YES", projectEnv); }); /** @@ -261,7 +261,7 @@ const experimentalFlagFromArgs = (args: ReadonlyArray): boolean | undefi if (arg === "--experimental") { result = true; } else if (arg.startsWith("--experimental=")) { - result = !PFLAG_FALSE_VALUES.has(arg.slice("--experimental=".length)); + result = !FALSE_VALUES.has(arg.slice("--experimental=".length)); } } return result; @@ -279,12 +279,12 @@ export const resolveExperimental = Effect.gen(function* () { if (explicit !== undefined) { return explicit; } - return flag || viperEnvBool("SUPABASE_EXPERIMENTAL"); + return flag || supabaseEnvBool("SUPABASE_EXPERIMENTAL"); }); /** * `--experimental` with project `.env` fallback. Shell-env presence wins over the file; - * an explicit flag wins over both. See {@link viperEnvBoolWithProjectFallback}. + * an explicit flag wins over both. See {@link supabaseEnvBoolWithProjectFallback}. */ export const resolveExperimentalWithProjectEnv = (projectEnv: Record) => Effect.gen(function* () { @@ -294,7 +294,7 @@ export const resolveExperimentalWithProjectEnv = (projectEnv: Record): boolean => { if (arg === "--debug") { lastExplicitlyFalse = false; } else if (arg.startsWith("--debug=")) { - lastExplicitlyFalse = PFLAG_FALSE_VALUES.has(arg.slice("--debug=".length)); + lastExplicitlyFalse = FALSE_VALUES.has(arg.slice("--debug=".length)); } } return lastExplicitlyFalse; @@ -330,5 +330,5 @@ export const resolveDebugWithProjectEnv = (projectEnv: Record) = if (debugFlagExplicitlyFalse(cliArgs.args)) { return false; } - return flag || viperEnvBoolWithProjectFallback("SUPABASE_DEBUG", projectEnv); + return flag || supabaseEnvBoolWithProjectFallback("SUPABASE_DEBUG", projectEnv); }); diff --git a/apps/cli/src/command-internal/go-duration.unit.test.ts b/apps/cli/src/command-internal/go-duration.unit.test.ts deleted file mode 100644 index b7b58558a0..0000000000 --- a/apps/cli/src/command-internal/go-duration.unit.test.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { formatGoDuration, parseGoDuration } from "./go-duration.ts"; - -describe("parseGoDuration", () => { - it("parses a single unit", () => { - expect(parseGoDuration("5s")).toBe(5_000_000_000); - expect(parseGoDuration("300ms")).toBe(300_000_000); - expect(parseGoDuration("1h")).toBe(3_600_000_000_000); - }); - - it("parses multiple units concatenated", () => { - expect(parseGoDuration("1h30m")).toBe(5_400_000_000_000); - }); - - it("parses a fractional value", () => { - expect(parseGoDuration("1.5s")).toBe(1_500_000_000); - }); - - it("parses a signed value", () => { - expect(parseGoDuration("-5s")).toBe(-5_000_000_000); - expect(parseGoDuration("+5s")).toBe(5_000_000_000); - }); - - it('treats a bare "0" as zero regardless of sign', () => { - expect(parseGoDuration("0")).toBe(0); - }); - - it("rejects an empty string", () => { - expect(() => parseGoDuration("")).toThrow('time: invalid duration ""'); - }); - - it.each(["s", "m", "h", "ms", "us", "µs", "μs", "ns"])( - 'rejects a bare unit with no preceding digit ("%s")', - (input) => { - expect(() => parseGoDuration(input)).toThrow(`time: invalid duration "${input}"`); - }, - ); - - it.each(["us", "µs", "μs"])('accepts every microsecond unit spelling ("1%s")', (unit) => { - expect(parseGoDuration(`1${unit}`)).toBe(1_000); - }); - - it('rejects a bare unit following a valid unit ("1hs")', () => { - expect(() => parseGoDuration("1hs")).toThrow('time: invalid duration "1hs"'); - }); - - it('rejects a negative bare unit ("-s")', () => { - expect(() => parseGoDuration("-s")).toThrow('time: invalid duration "-s"'); - }); - - it.each([".s", ".", "-.", "+.", ".h"])('rejects a lone "." with no digits ("%s")', (input) => { - expect(() => parseGoDuration(input)).toThrow(`time: invalid duration "${input}"`); - }); - - it('accepts a fraction-only value with no leading digit (".5s")', () => { - expect(parseGoDuration(".5s")).toBe(500_000_000); - }); - - it("rejects a number with no unit", () => { - expect(() => parseGoDuration("5")).toThrow('time: missing unit in duration "5"'); - }); - - it("rejects an unknown unit", () => { - expect(() => parseGoDuration("5x")).toThrow('time: unknown unit in duration "5x"'); - }); - - it('truncates a sub-nanosecond fraction instead of rounding ("0.5ns")', () => { - expect(parseGoDuration("0.5ns")).toBe(0); - }); - - it('truncates a sub-nanosecond fraction instead of rounding ("1.9ns")', () => { - expect(parseGoDuration("1.9ns")).toBe(1); - }); - - it('rounds a long fractional remainder up like Go\'s float64 conversion ("0.999999999999999999s")', () => { - expect(parseGoDuration("0.999999999999999999s")).toBe(1_000_000_000); - }); - - it('rejects a duration that overflows math.MaxInt64 nanoseconds ("2562048h")', () => { - expect(() => parseGoDuration("2562048h")).toThrow('time: invalid duration "2562048h"'); - }); - - it("accepts Go's actual maximum parseable duration without overflowing", () => { - expect(() => parseGoDuration("2562047h47m16.854775807s")).not.toThrow(); - }); - - it('rejects a duration exactly 1ns past Go\'s true math.MaxInt64 ceiling ("9223372036854775808ns")', () => { - expect(() => parseGoDuration("9223372036854775808ns")).toThrow( - 'time: invalid duration "9223372036854775808ns"', - ); - }); - - it("accepts a duration exactly at Go's true math.MaxInt64 ceiling in nanoseconds", () => { - expect(() => parseGoDuration("9223372036854775807ns")).not.toThrow(); - }); - - it("accepts Go's exact minimum representable negative duration (math.MinInt64 ns)", () => { - expect(parseGoDuration("-9223372036854775808ns")).toBe(-9223372036854775808); - }); - - it("accepts the equivalent hours/minutes/seconds form of math.MinInt64 ns", () => { - expect(parseGoDuration("-2562047h47m16.854775808s")).toBe(-9223372036854775808); - }); - - it("rejects a duration 1ns past math.MinInt64", () => { - expect(() => parseGoDuration("-9223372036854775809ns")).toThrow( - 'time: invalid duration "-9223372036854775809ns"', - ); - }); -}); - -describe("formatGoDuration", () => { - it('formats zero as "0s"', () => { - expect(formatGoDuration(0)).toBe("0s"); - }); - - it("formats whole hours/minutes/seconds", () => { - expect(formatGoDuration(3_600_000_000_000)).toBe("1h0m0s"); - }); - - it("normalizes 90s to 1m30s", () => { - expect(formatGoDuration(90_000_000_000)).toBe("1m30s"); - }); - - it("formats a sub-second fraction", () => { - expect(formatGoDuration(1_500_000_000)).toBe("1.5s"); - }); - - it("round-trips through parseGoDuration", () => { - expect(formatGoDuration(parseGoDuration("1h"))).toBe("1h0m0s"); - expect(formatGoDuration(parseGoDuration("90s"))).toBe("1m30s"); - }); - - it("includes a sub-second fraction alongside minutes", () => { - expect(formatGoDuration(parseGoDuration("1m0.5s"))).toBe("1m0.5s"); - }); - - it("includes a sub-second fraction alongside hours", () => { - expect(formatGoDuration(parseGoDuration("1h0.5s"))).toBe("1h0m0.5s"); - }); - - it("includes a sub-second fraction alongside hours and minutes", () => { - expect(formatGoDuration(parseGoDuration("1h1m0.5s"))).toBe("1h1m0.5s"); - }); -}); diff --git a/apps/cli/src/command-internal/go-float.unit.test.ts b/apps/cli/src/command-internal/go-float.unit.test.ts deleted file mode 100644 index 7ebaa15172..0000000000 --- a/apps/cli/src/command-internal/go-float.unit.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { goFormatFloat } from "./go-float.ts"; - -describe("goFormatFloat", () => { - it("renders fixed notation within Go's [-4, 6) decimal-exponent range", () => { - expect(goFormatFloat(100)).toBe("100"); - expect(goFormatFloat(100000)).toBe("100000"); - expect(goFormatFloat(0.5)).toBe("0.5"); - expect(goFormatFloat(0.0001)).toBe("0.0001"); - }); - - it("switches to signed exponent notation at exponent >= 6 or < -4", () => { - expect(goFormatFloat(1000000)).toBe("1e+06"); - expect(goFormatFloat(100000000000)).toBe("1e+11"); - expect(goFormatFloat(123456789)).toBe("1.23456789e+08"); - expect(goFormatFloat(0.00001)).toBe("1e-05"); - }); - - it("preserves the sign for negative exponent-notation values", () => { - expect(goFormatFloat(-1000000)).toBe("-1e+06"); - }); - - it("renders zero as a bare 0", () => { - expect(goFormatFloat(0)).toBe("0"); - }); -}); diff --git a/apps/cli/src/command-internal/go-json.unit.test.ts b/apps/cli/src/command-internal/go-json.unit.test.ts deleted file mode 100644 index d00c4618f3..0000000000 --- a/apps/cli/src/command-internal/go-json.unit.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { - encodeGoJsonCompact, - encodeGoJsonIndented, - escapeGoJsonString, - goJsonKindName, -} from "./go-json.ts"; - -describe("escapeGoJsonString", () => { - it("escapes quotes and backslashes like Go", () => { - expect(escapeGoJsonString(`a"b\\c`)).toBe('"a\\"b\\\\c"'); - }); - - it("HTML-escapes <, > and & (Go's default escapeHTML)", () => { - expect(escapeGoJsonString(" & ")).toBe('"\\u003ca\\u003e \\u0026 \\u003cb\\u003e"'); - }); - - it("uses short escapes for tab/newline/carriage-return", () => { - expect(escapeGoJsonString("a\tb\nc\rd")).toBe('"a\\tb\\nc\\rd"'); - }); - - it("uses \\u00xx for other control characters (no \\b / \\f shorthand)", () => { - expect(escapeGoJsonString("\b\f")).toBe('"\\u0008\\u000c"'); - }); - - it("escapes U+2028 and U+2029", () => { - expect(escapeGoJsonString("

")).toBe('"\\u2028\\u2029"'); - }); -}); - -describe("encodeGoJsonIndented", () => { - it("preserves object key insertion order (not alphabetical)", () => { - expect(encodeGoJsonIndented({ level: "error", message: "boom" })).toBe( - `{\n "level": "error",\n "message": "boom"\n}\n`, - ); - }); - - it("renders nested arrays of objects with 2-space indent and a trailing newline", () => { - const value = [{ function: "public.f1", issues: [{ level: "error", message: "test 1b" }] }]; - expect(encodeGoJsonIndented(value)).toBe( - [ - "[", - " {", - ' "function": "public.f1",', - ' "issues": [', - " {", - ' "level": "error",', - ' "message": "test 1b"', - " }", - " ]", - " }", - "]", - "", - ].join("\n"), - ); - }); - - it("renders empty arrays and objects compactly", () => { - expect(encodeGoJsonIndented([])).toBe("[]\n"); - expect(encodeGoJsonIndented({})).toBe("{}\n"); - expect(encodeGoJsonIndented({ issues: [] })).toBe(`{\n "issues": []\n}\n`); - }); -}); - -describe("encodeGoJsonCompact", () => { - it("matches Go's json.Marshal compact shape with HTML escaping", () => { - expect(encodeGoJsonCompact({ metadata_xml: "&stuff", type: "saml" })).toBe( - '{"metadata_xml":"\\u003cxml\\u003e\\u0026stuff\\u003c/xml\\u003e","type":"saml"}', - ); - }); - - it("keeps insertion order, compact separators, and no trailing newline", () => { - expect(encodeGoJsonCompact({ b: [1, 2], a: { c: true } })).toBe('{"b":[1,2],"a":{"c":true}}'); - expect(encodeGoJsonCompact([])).toBe("[]"); - expect(encodeGoJsonCompact(null)).toBe("null"); - }); - - it("preserves negative zero's sign, unlike plain JSON.stringify", () => { - expect(encodeGoJsonCompact({ extra: -0 })).toBe('{"extra":-0}'); - expect(encodeGoJsonCompact({ extra: Number("-1e-10000") })).toBe('{"extra":-0}'); - expect(encodeGoJsonCompact({ extra: 0 })).toBe('{"extra":0}'); - }); - - it("iterates a Map in true insertion order, unlike a plain object with integer-like keys", () => { - const map = new Map([ - ["10", "a"], - ["2", "b"], - ]); - expect(encodeGoJsonCompact(map)).toBe('{"10":"a","2":"b"}'); - }); -}); - -describe("goJsonKindName", () => { - it("names every JSON-representable kind, including the generic fallback", () => { - expect(goJsonKindName([])).toBe("array"); - expect(goJsonKindName(1)).toBe("number"); - expect(goJsonKindName("s")).toBe("string"); - expect(goJsonKindName(true)).toBe("bool"); - expect(goJsonKindName(undefined)).toBe("value"); - }); -}); diff --git a/apps/cli/src/command-internal/go-json.ts b/apps/cli/src/command-internal/html-safe-json.ts similarity index 65% rename from apps/cli/src/command-internal/go-json.ts rename to apps/cli/src/command-internal/html-safe-json.ts index 1d9d8a3b89..39652e6361 100644 --- a/apps/cli/src/command-internal/go-json.ts +++ b/apps/cli/src/command-internal/html-safe-json.ts @@ -1,13 +1,12 @@ /** - * Byte-faithful reproduction of `encoding/json`'s encoder, for commands that must match Go's - * stdout exactly. + * Byte-stable JSON encoder for commands whose stdout format is fixed. * * Doesn't sort object keys — the caller builds objects whose key insertion order is already the * desired order; a `Map` is preserved as true insertion order (a plain object reorders * integer-like string keys into ascending numeric order, which would undo a lexicographic sort). * * Differs from `JSON.stringify(x, null, 2)` in escaping `<`, `>`, `&`, and control characters - * (`\u0008`/`\u000c` instead of `\b`/`\f`) the way Go's default encoder does. + * (`\u0008`/`\u000c` instead of `\b`/`\f`). */ const HEX = "0123456789abcdef"; @@ -17,11 +16,11 @@ function unicodeEscape(codeUnit: number): string { } /** - * Quotes and escapes a string exactly as `encoding/json` does with default HTML escaping. - * Iterates by UTF-16 code unit; the only non-ASCII runes Go escapes (U+2028, U+2029) are single + * Quotes and escapes a string with HTML escaping. + * Iterates by UTF-16 code unit; the only non-ASCII characters escaped (U+2028, U+2029) are single * BMP code units, so code units suffice. */ -export function escapeGoJsonString(value: string): string { +export function escapeHtmlSafeJsonString(value: string): string { let out = '"'; for (let i = 0; i < value.length; i++) { const code = value.charCodeAt(i); @@ -65,10 +64,10 @@ function walk(value: unknown, depth: number, pretty: boolean): string { if (value === null || value === undefined) return "null"; switch (typeof value) { case "string": - return escapeGoJsonString(value); + return escapeHtmlSafeJsonString(value); case "number": - // JSON.stringify collapses `-0` to `"0"`, but `encoding/json` marshals a float64 negative - // zero as `-0` — reachable via `gen bearer-jwt`'s signed payload, where the signed bytes + // JSON.stringify collapses `-0` to `"0"`, but a float64 negative + // zero marshals as `-0` — reachable via `gen bearer-jwt`'s signed payload, where the signed bytes // must match. if (!Number.isFinite(value)) return "null"; return Object.is(value, -0) ? "-0" : JSON.stringify(value); @@ -86,7 +85,7 @@ function walk(value: unknown, depth: number, pretty: boolean): string { return `[${open}${items.join(separator)}${close}${closeIndent}]`; } // A plain object reorders integer-like string keys ("2", "10") into ascending numeric order on - // enumeration, unlike a real Go map's lexicographic order; callers needing that order (e.g. + // enumeration, unlike lexicographic order; callers needing that order (e.g. // `sortKeysDeep`) pass a `Map`, whose iteration order is true insertion order. const entries = value instanceof Map @@ -95,34 +94,34 @@ function walk(value: unknown, depth: number, pretty: boolean): string { if (entries.length === 0) return "{}"; const colon = pretty ? ": " : ":"; const lines = entries.map( - ([key, val]) => `${indent}${escapeGoJsonString(key)}${colon}${walk(val, depth + 1, pretty)}`, + ([key, val]) => + `${indent}${escapeHtmlSafeJsonString(key)}${colon}${walk(val, depth + 1, pretty)}`, ); return `{${open}${lines.join(separator)}${close}${closeIndent}}`; } /** - * Encodes a value the way `json.Encoder` with `SetIndent("", " ")` + - * `Encode` does: 2-space indentation, object keys in insertion (struct) order, - * Go string escaping, and a trailing newline. + * Encodes a value with 2-space indentation, object keys in insertion order, + * HTML-safe string escaping, and a trailing newline. */ -export function encodeGoJsonIndented(value: unknown): string { +export function encodeHtmlSafeJsonIndented(value: unknown): string { return walk(value, 0, true) + "\n"; } /** - * Encodes a value the way `json.Marshal` does: compact separators - * (`{"k":v}`), object keys in insertion (struct) order, Go string escaping - * (HTML characters included), and no trailing newline. + * Encodes a value with compact separators + * (`{"k":v}`), object keys in insertion order, HTML-safe string escaping, + * and no trailing newline. */ -export function encodeGoJsonCompact(value: unknown): string { +export function encodeHtmlSafeJsonCompact(value: unknown): string { return walk(value, 0, false); } /** - * `encoding/json` type names for the JSON-representable kinds `json.Unmarshal` rejects. Used to - * reproduce Go's exact `"json: cannot unmarshal into Go value of type "` message. + * Type names for the JSON-representable kinds a decode rejects, used in "expected X, got " + * decode errors. */ -export function goJsonKindName(value: unknown): string { +export function jsonKindName(value: unknown): string { if (Array.isArray(value)) return "array"; switch (typeof value) { case "number": @@ -130,7 +129,7 @@ export function goJsonKindName(value: unknown): string { case "string": return "string"; case "boolean": - return "bool"; + return "boolean"; default: return "value"; } diff --git a/apps/cli/src/command-internal/html-safe-json.unit.test.ts b/apps/cli/src/command-internal/html-safe-json.unit.test.ts new file mode 100644 index 0000000000..733067c7c8 --- /dev/null +++ b/apps/cli/src/command-internal/html-safe-json.unit.test.ts @@ -0,0 +1,104 @@ +import { describe, expect, it } from "vitest"; + +import { + encodeHtmlSafeJsonCompact, + encodeHtmlSafeJsonIndented, + escapeHtmlSafeJsonString, + jsonKindName, +} from "./html-safe-json.ts"; + +describe("escapeHtmlSafeJsonString", () => { + it("escapes quotes and backslashes", () => { + expect(escapeHtmlSafeJsonString(`a"b\\c`)).toBe('"a\\"b\\\\c"'); + }); + + it("HTML-escapes <, > and &", () => { + expect(escapeHtmlSafeJsonString(" & ")).toBe('"\\u003ca\\u003e \\u0026 \\u003cb\\u003e"'); + }); + + it("uses short escapes for tab/newline/carriage-return", () => { + expect(escapeHtmlSafeJsonString("a\tb\nc\rd")).toBe('"a\\tb\\nc\\rd"'); + }); + + it("uses \\u00xx for other control characters (no \\b / \\f shorthand)", () => { + expect(escapeHtmlSafeJsonString("\b\f")).toBe('"\\u0008\\u000c"'); + }); + + it("escapes U+2028 and U+2029", () => { + expect(escapeHtmlSafeJsonString("

")).toBe('"\\u2028\\u2029"'); + }); +}); + +describe("encodeHtmlSafeJsonIndented", () => { + it("preserves object key insertion order (not alphabetical)", () => { + expect(encodeHtmlSafeJsonIndented({ level: "error", message: "boom" })).toBe( + `{\n "level": "error",\n "message": "boom"\n}\n`, + ); + }); + + it("renders nested arrays of objects with 2-space indent and a trailing newline", () => { + const value = [{ function: "public.f1", issues: [{ level: "error", message: "test 1b" }] }]; + expect(encodeHtmlSafeJsonIndented(value)).toBe( + [ + "[", + " {", + ' "function": "public.f1",', + ' "issues": [', + " {", + ' "level": "error",', + ' "message": "test 1b"', + " }", + " ]", + " }", + "]", + "", + ].join("\n"), + ); + }); + + it("renders empty arrays and objects compactly", () => { + expect(encodeHtmlSafeJsonIndented([])).toBe("[]\n"); + expect(encodeHtmlSafeJsonIndented({})).toBe("{}\n"); + expect(encodeHtmlSafeJsonIndented({ issues: [] })).toBe(`{\n "issues": []\n}\n`); + }); +}); + +describe("encodeHtmlSafeJsonCompact", () => { + it("emits the compact shape with HTML escaping", () => { + expect(encodeHtmlSafeJsonCompact({ metadata_xml: "&stuff", type: "saml" })).toBe( + '{"metadata_xml":"\\u003cxml\\u003e\\u0026stuff\\u003c/xml\\u003e","type":"saml"}', + ); + }); + + it("keeps insertion order, compact separators, and no trailing newline", () => { + expect(encodeHtmlSafeJsonCompact({ b: [1, 2], a: { c: true } })).toBe( + '{"b":[1,2],"a":{"c":true}}', + ); + expect(encodeHtmlSafeJsonCompact([])).toBe("[]"); + expect(encodeHtmlSafeJsonCompact(null)).toBe("null"); + }); + + it("preserves negative zero's sign, unlike plain JSON.stringify", () => { + expect(encodeHtmlSafeJsonCompact({ extra: -0 })).toBe('{"extra":-0}'); + expect(encodeHtmlSafeJsonCompact({ extra: Number("-1e-10000") })).toBe('{"extra":-0}'); + expect(encodeHtmlSafeJsonCompact({ extra: 0 })).toBe('{"extra":0}'); + }); + + it("iterates a Map in true insertion order, unlike a plain object with integer-like keys", () => { + const map = new Map([ + ["10", "a"], + ["2", "b"], + ]); + expect(encodeHtmlSafeJsonCompact(map)).toBe('{"10":"a","2":"b"}'); + }); +}); + +describe("jsonKindName", () => { + it("names every JSON-representable kind, including the generic fallback", () => { + expect(jsonKindName([])).toBe("array"); + expect(jsonKindName(1)).toBe("number"); + expect(jsonKindName("s")).toBe("string"); + expect(jsonKindName(true)).toBe("boolean"); + expect(jsonKindName(undefined)).toBe("value"); + }); +}); diff --git a/apps/cli/src/command-internal/linked-state.ts b/apps/cli/src/command-internal/linked-state.ts index c7ad6cc1a3..aaea7a668d 100644 --- a/apps/cli/src/command-internal/linked-state.ts +++ b/apps/cli/src/command-internal/linked-state.ts @@ -202,7 +202,7 @@ export function formatLinkedStateBlock(state: LinkedState): string { * what signals "not linked" for these formats. A degraded branch-linked * state still emits every field it knows; only `linked_branch` is absent. */ -export function linkedStateGoFields(state: LinkedState): Readonly> { +export function linkedStateFields(state: LinkedState): Readonly> { if (!state.linked) return {}; return { linked_project_ref: state.projectRef, @@ -216,7 +216,7 @@ export function linkedStateGoFields(state: LinkedState): Readonly BigInt(MAX_PORT)) { throw new InvalidPortEnvOverrideError(dottedFieldPath, value); } @@ -241,7 +246,7 @@ export function envOverride( export class InvalidBoolEnvOverrideError extends Error { static readonly [ErrorActionabilityFingerprintId] = "InvalidBoolEnvOverrideError"; constructor(dottedFieldPath: string, value: string) { - super(`Invalid config for ${dottedFieldPath}: cannot parse "${value}" as a bool`); + super(`Invalid config for ${dottedFieldPath}: "${value}" is not a valid boolean`); this.name = "InvalidBoolEnvOverrideError"; } get [ErrorActionabilityId](): CliErrorActionabilityDeclaration { @@ -263,7 +268,7 @@ export function envOverrideBool( ): boolean { const value = envOverride(name, undefined, projectEnvValues); if (value === undefined) return configured; - const parsed = parseGoBool(value); + const parsed = parseBoolLiteral(value); if (parsed === undefined) { throw new InvalidBoolEnvOverrideError(dottedFieldPath, value); } @@ -275,7 +280,7 @@ export class InvalidAnalyticsBackendEnvOverrideError extends Error { static readonly [ErrorActionabilityFingerprintId] = "InvalidAnalyticsBackendEnvOverrideError"; constructor(dottedFieldPath: string, value: string) { super( - `Invalid config for ${dottedFieldPath}: cannot parse "${value}" as one of "postgres", "bigquery"`, + `Invalid config for ${dottedFieldPath}: "${value}" must be one of "postgres", "bigquery"`, ); this.name = "InvalidAnalyticsBackendEnvOverrideError"; } @@ -308,9 +313,7 @@ export function envOverrideAnalyticsBackend( export class InvalidRealtimeIpVersionEnvOverrideError extends Error { static readonly [ErrorActionabilityFingerprintId] = "InvalidRealtimeIpVersionEnvOverrideError"; constructor(dottedFieldPath: string, value: string) { - super( - `Invalid config for ${dottedFieldPath}: cannot parse "${value}" as one of "IPv4", "IPv6"`, - ); + super(`Invalid config for ${dottedFieldPath}: "${value}" must be one of "IPv4", "IPv6"`); this.name = "InvalidRealtimeIpVersionEnvOverrideError"; } @@ -357,7 +360,7 @@ export class InvalidPoolModeEnvOverrideError extends Error { static readonly [ErrorActionabilityFingerprintId] = "InvalidPoolModeEnvOverrideError"; constructor(dottedFieldPath: string, value: string) { super( - `Invalid config for ${dottedFieldPath}: cannot parse "${value}" as one of "transaction", "session"`, + `Invalid config for ${dottedFieldPath}: "${value}" must be one of "transaction", "session"`, ); this.name = "InvalidPoolModeEnvOverrideError"; } @@ -384,7 +387,7 @@ export class InvalidEdgeRuntimePolicyEnvOverrideError extends Error { static readonly [ErrorActionabilityFingerprintId] = "InvalidEdgeRuntimePolicyEnvOverrideError"; constructor(dottedFieldPath: string, value: string) { super( - `Invalid config for ${dottedFieldPath}: cannot parse "${value}" as one of "per_worker", "oneshot"`, + `Invalid config for ${dottedFieldPath}: "${value}" must be one of "per_worker", "oneshot"`, ); this.name = "InvalidEdgeRuntimePolicyEnvOverrideError"; } @@ -624,8 +627,8 @@ function resolveSignedKey( ): string { if (configured !== undefined && configured.length > 0) return configured; return signingKey !== undefined - ? generateAsymmetricGoJwt(signingKey, role) - : generateGoJwt(jwtSecret, role); + ? generateAsymmetricLocalJwt(signingKey, role) + : generateLocalJwt(jwtSecret, role); } /** JWK fields, matching {@link Jwk}. */ @@ -952,11 +955,11 @@ const UINT_MAX = 18446744073709551615n; // 2^64 - 1 /** * Base-0 unsigned integer literal parsing (`0b`/`0o`/`0x` prefixes, and a bare leading zero - * also meaning octal — so `"010"` parses as `8`), matching Go's `strconv.ParseUint(str, 0, …)` - * grammar. Underscores between digits are allowed; a leading sign is never accepted. Returns - * `undefined` for anything invalid instead of throwing, leaving bit-width bounds to the caller. + * also meaning octal — so `"010"` parses as `8`). Underscores between digits are allowed; a + * leading sign is never accepted. Returns `undefined` for anything invalid instead of throwing, + * leaving bit-width bounds to the caller. */ -function parseGoBaseZeroUint(value: string): bigint | undefined { +function parseBaseZeroUint(value: string): bigint | undefined { if (value.length === 0 || value.startsWith("+") || value.startsWith("-")) return undefined; let literal: string | undefined; @@ -985,7 +988,7 @@ function parseGoBaseZeroUint(value: string): bigint | undefined { /** * `SUPABASE_` sibling of {@link envOverridePort} for uncapped `uint`-typed fields - * (`db.major_version`, `auth.jwt_expiry`, …). Parses with {@link parseGoBaseZeroUint} and + * (`db.major_version`, `auth.jwt_expiry`, …). Parses with {@link parseBaseZeroUint} and * folds an invalid or out-of-{@link UINT_MAX} override into the generic "Invalid " * error message. */ @@ -997,7 +1000,7 @@ export function envOverrideUint( ): number { const value = envOverride(name, undefined, projectEnvValues); if (value === undefined) return configured; - const parsed = parseGoBaseZeroUint(value); + const parsed = parseBaseZeroUint(value); if (parsed === undefined || parsed > UINT_MAX) { throw new Error(`Failed reading config: Invalid ${dottedFieldPath}: ${value}.`); } @@ -1044,7 +1047,7 @@ function envOverrideOptionalUint( ): number | undefined { const value = envOverride(name, undefined, projectEnvValues); if (value === undefined) return configured; - const parsed = parseGoBaseZeroUint(value); + const parsed = parseBaseZeroUint(value); if (parsed === undefined || parsed > UINT_MAX) { throw new Error(`Failed reading config: Invalid ${dottedFieldPath}: ${value}.`); } @@ -1063,7 +1066,7 @@ function envOverrideOptionalBool( ): boolean | undefined { const value = envOverride(name, undefined, projectEnvValues); if (value === undefined) return configured; - const parsed = parseGoBool(value); + const parsed = parseBoolLiteral(value); if (parsed === undefined) { throw new InvalidBoolEnvOverrideError(dottedFieldPath, value); } @@ -1076,7 +1079,7 @@ export class InvalidSessionReplicationRoleEnvOverrideError extends Error { "InvalidSessionReplicationRoleEnvOverrideError"; constructor(dottedFieldPath: string, value: string) { super( - `Invalid config for ${dottedFieldPath}: cannot parse "${value}" as one of "origin", "replica", "local"`, + `Invalid config for ${dottedFieldPath}: "${value}" must be one of "origin", "replica", "local"`, ); this.name = "InvalidSessionReplicationRoleEnvOverrideError"; } @@ -2132,7 +2135,7 @@ export function rawUnmodeledBool(value: unknown, dottedFieldPath: string): boole if (typeof value === "boolean") return value; if (typeof value === "number") return value !== 0; if (typeof value === "string") { - const parsed = parseGoBool(value); + const parsed = parseBoolLiteral(value); if (parsed === undefined) { throw new InvalidBoolEnvOverrideError(dottedFieldPath, value); } @@ -2302,7 +2305,7 @@ function validateAuthExternalProviders( * {@link readApiTlsFiles}. * @throws when `auth.signing_keys_path` is set, auth is enabled, and the file is missing, * malformed, or its first key uses an unsupported algorithm — see - * {@link resolveConfiguredSigningKeys} and {@link generateAsymmetricGoJwt}. + * {@link resolveConfiguredSigningKeys} and {@link generateAsymmetricLocalJwt}. * @throws when an email template's `content` is present without `content_path`, or a * configured `content_path` file can't be read — see {@link readAuthEmailTemplateContent}. * @throws {InvalidAnalyticsBackendEnvOverrideError} when `SUPABASE_ANALYTICS_BACKEND` doesn't @@ -2417,9 +2420,7 @@ export function resolveLocalConfigValues( // `/etc/postgresql-custom/pgsodium_root.key` on every start. const rawRootKeyValue = asRecord(document?.["db"])?.["root_key"]; if (rawRootKeyValue !== undefined && typeof rawRootKeyValue !== "string") { - throw new ConfigValidateError( - "failed to parse config: decoding failed due to the following error(s):\n\n'db.root_key' expected a map or struct", - ); + throw new ConfigValidateError("failed to parse config:\ndb.root_key: expected a table"); } const rawRootKey = remoteWins("db.root_key") ? rawRootKeyValue diff --git a/apps/cli/src/command-internal/local-config-values.unit.test.ts b/apps/cli/src/command-internal/local-config-values.unit.test.ts index b0d58ddb37..dfec0c434f 100644 --- a/apps/cli/src/command-internal/local-config-values.unit.test.ts +++ b/apps/cli/src/command-internal/local-config-values.unit.test.ts @@ -9,7 +9,7 @@ import { importJWK, jwtVerify } from "jose"; import { afterEach, describe, expect, it, vi } from "vitest"; import { useTempWorkdir } from "../../tests/helpers/command-mocks.ts"; -import { DEFAULT_SIGNING_KEY } from "./go-jwt.ts"; +import { DEFAULT_SIGNING_KEY } from "./local-jwt.ts"; import { POSTGRES_DEFAULT_ROOT_KEY, InvalidAnalyticsBackendEnvOverrideError, @@ -189,21 +189,21 @@ describe("resolveLocalConfigValues", () => { expect(values.publishableKey).toBe("value"); }); - it("fails config loading for an encrypted: secret with no private key, matching Go", () => { + it("fails config loading for an encrypted: secret with no private key", () => { const config = baseConfig({ auth: { publishable_key: VAULT_ENCRYPTED } }); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( "failed to parse config: missing private key", ); }); - it("decrypts an encrypted: auth.email.smtp.pass, matching Go's Secret-typed Smtp.Pass field", () => { + it("decrypts an encrypted: auth.email.smtp.pass", () => { process.env["DOTENV_PRIVATE_KEY"] = VAULT_PRIVATE_KEY; const document = { auth: { email: { smtp: { enabled: true, pass: VAULT_ENCRYPTED } } } }; const resolved = resolveAuthEmailSmtp(document.auth, undefined); expect(resolved?.pass).toBe("value"); }); - it("decrypts an encrypted: studio.openai_api_key, matching Go's Secret-typed OpenaiApiKey field", () => { + it("decrypts an encrypted: studio.openai_api_key", () => { process.env["DOTENV_PRIVATE_KEY"] = VAULT_PRIVATE_KEY; const config = baseConfig({ studio: { openai_api_key: VAULT_ENCRYPTED } }); const values = resolveLocalConfigValues(config, "127.0.0.1", WORKDIR); @@ -221,7 +221,7 @@ describe("resolveLocalConfigValues", () => { }); }); - it("rejects an explicit empty project_id, matching Go's Config.Validate", () => { + it("rejects an explicit empty project_id", () => { // An explicit `project_id = ""` overwrites the workdir-basename default with the literal // empty string, unlike an absent key. const config = baseConfig({ project_id: "" }); @@ -235,7 +235,7 @@ describe("resolveLocalConfigValues", () => { expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).not.toThrow(); }); - it("rejects an absent project_id when the workdir basename sanitizes to empty, matching Go", () => { + it("rejects an absent project_id when the workdir basename sanitizes to empty", () => { // The workdir-basename default still applies with no `project_id` key present, so a workdir // whose basename sanitizes to empty (e.g. `!!!`) still fails validation. const config = Schema.decodeUnknownSync(CliConfigSchema)({}); @@ -262,7 +262,7 @@ describe("resolveLocalConfigValues", () => { ).not.toThrow(); }); - it("hardcodes the Go-parity local S3 credentials", () => { + it("hardcodes the local S3 credentials", () => { const config = baseConfig(); const values = resolveLocalConfigValues(config, "127.0.0.1", WORKDIR); expect(values.storageS3AccessKeyId).toBe("625729a08b95bf1b7ff351a663f3a23c"); @@ -317,7 +317,7 @@ describe("resolveLocalConfigValues", () => { expect(values.serviceRoleKey).toBe("env-service-role"); }); - it("treats an empty env var as unset, matching Viper's default", () => { + it("treats an empty env var as unset", () => { process.env["SUPABASE_AUTH_JWT_SECRET"] = ""; const config = baseConfig({ auth: { jwt_secret: "a".repeat(32) } }); const values = resolveLocalConfigValues(config, "127.0.0.1", WORKDIR); @@ -361,7 +361,7 @@ describe("resolveLocalConfigValues", () => { }); }); - describe("SUPABASE_* env(VAR) indirection (Go's LoadEnvHook)", () => { + describe("SUPABASE_* env(VAR) indirection ", () => { // `env(VAR)` indirection resolves inside any string field, including a `SUPABASE_*` override // value itself, not just a config.toml literal. const ENV_KEYS = ["SUPABASE_AUTH_JWT_SECRET", "SUPABASE_DB_PORT", "SUPABASE_API_ENABLED"]; @@ -398,7 +398,7 @@ describe("resolveLocalConfigValues", () => { expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).not.toThrow(); }); - it("preserves the env(VAR) literal when the indirected var is unset, matching Go", () => { + it("preserves the env(VAR) literal when the indirected var is unset", () => { process.env["SUPABASE_AUTH_JWT_SECRET"] = "env(INDIRECT_JWT_SECRET)"; const config = baseConfig({ auth: { jwt_secret: "a".repeat(32) } }); const values = resolveLocalConfigValues(config, "127.0.0.1", WORKDIR); @@ -457,7 +457,7 @@ describe("resolveLocalConfigValues", () => { expect(values.apiUrl).toBe("https://env-override.example"); }); - it("treats an empty non-auth env var as unset, matching Viper's default", () => { + it("treats an empty non-auth env var as unset", () => { process.env["SUPABASE_DB_PORT"] = ""; const config = baseConfig({ db: { port: 54322 } }); const values = resolveLocalConfigValues(config, "127.0.0.1", WORKDIR); @@ -477,7 +477,7 @@ describe("resolveLocalConfigValues", () => { ); }); - it("rejects a SUPABASE_DB_PORT override above the uint16 range", () => { + it("rejects a SUPABASE_DB_PORT override above the 65535 port maximum", () => { process.env["SUPABASE_DB_PORT"] = "99999"; const config = baseConfig(); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( @@ -507,7 +507,7 @@ describe("resolveLocalConfigValues", () => { expect(values.dbPort).toBe(5432); }); - it("rejects a 0x-prefixed SUPABASE_DB_PORT override exceeding the uint16 range", () => { + it("rejects a 0x-prefixed SUPABASE_DB_PORT override exceeding the 65535 port maximum", () => { process.env["SUPABASE_DB_PORT"] = "0x1FFFF"; const config = baseConfig(); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( @@ -517,7 +517,7 @@ describe("resolveLocalConfigValues", () => { // Unlike the malformed/out-of-range cases above, db.port=0 is a required-field failure with // no `enabled` gate, unlike api.port/studio.port/local_smtp.port. - it("rejects a zero SUPABASE_DB_PORT override, matching Go's required-field check", () => { + it("rejects a zero SUPABASE_DB_PORT override", () => { process.env["SUPABASE_DB_PORT"] = "0"; const config = baseConfig(); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( @@ -646,7 +646,7 @@ describe("resolveLocalConfigValues", () => { ); }); - it("treats an empty SUPABASE_DB_MAJOR_VERSION override as unset, matching Viper's default", () => { + it("treats an empty SUPABASE_DB_MAJOR_VERSION override as unset", () => { process.env["SUPABASE_DB_MAJOR_VERSION"] = ""; const config = baseConfig({ db: { major_version: 17 } }); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).not.toThrow(); @@ -673,7 +673,7 @@ describe("resolveLocalConfigValues", () => { InvalidBoolEnvOverrideError, ); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( - 'Invalid config for db.network_restrictions.enabled: cannot parse "notabool" as a bool', + 'Invalid config for db.network_restrictions.enabled: "notabool" is not a valid boolean', ); }); }); @@ -692,14 +692,12 @@ describe("resolveLocalConfigValues", () => { expect(values.rootKey).toBe("custom-root-key"); }); - it("rejects a non-string root_key (e.g. a bare TOML integer), matching Go's Secret decode failure", () => { + it("rejects a non-string root_key (e.g. a bare TOML integer)", () => { const config = baseConfig(); const document = { db: { root_key: 12345 } }; expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR, undefined, document), - ).toThrow( - "failed to parse config: decoding failed due to the following error(s):\n\n'db.root_key' expected a map or struct", - ); + ).toThrow("failed to parse config:\ndb.root_key: expected a table"); }); }); @@ -738,7 +736,7 @@ describe("resolveLocalConfigValues", () => { ); }); - it("treats an empty SUPABASE_EDGE_RUNTIME_DENO_VERSION override as unset, matching Viper's default", () => { + it("treats an empty SUPABASE_EDGE_RUNTIME_DENO_VERSION override as unset", () => { process.env["SUPABASE_EDGE_RUNTIME_DENO_VERSION"] = ""; const config = baseConfig({ edge_runtime: { deno_version: 2 } }); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).not.toThrow(); @@ -787,7 +785,7 @@ describe("resolveLocalConfigValues", () => { InvalidAnalyticsBackendEnvOverrideError, ); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( - 'Invalid config for analytics.backend: cannot parse "mysql" as one of "postgres", "bigquery"', + 'Invalid config for analytics.backend: "mysql" must be one of "postgres", "bigquery"', ); }); }); @@ -826,7 +824,7 @@ describe("resolveLocalConfigValues", () => { expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR, undefined, document), ).toThrow( - 'Invalid config for experimental.webhooks.enabled: cannot parse "notabool" as a bool', + 'Invalid config for experimental.webhooks.enabled: "notabool" is not a valid boolean', ); }); @@ -898,7 +896,7 @@ describe("resolveLocalConfigValues", () => { ); }); - it("treats an empty override as unset, matching Viper's default", () => { + it("treats an empty override as unset", () => { process.env["SUPABASE_API_TLS_ENABLED"] = ""; const config = baseConfig({ api: { tls: { enabled: true }, port: 54321 } }); const values = resolveLocalConfigValues(config, "127.0.0.1", WORKDIR); @@ -926,7 +924,7 @@ describe("resolveLocalConfigValues", () => { InvalidRealtimeIpVersionEnvOverrideError, ); expect(() => envOverrideRealtimeIpVersion("IPv4", undefined)).toThrow( - 'Invalid config for realtime.ip_version: cannot parse "IPv5" as one of "IPv4", "IPv6"', + 'Invalid config for realtime.ip_version: "IPv5" must be one of "IPv4", "IPv6"', ); }); }); @@ -953,7 +951,7 @@ describe("resolveLocalConfigValues", () => { ).toBe(16384); }); - it("rejects an override exceeding the uint64 max (2^64), matching Go's ParseUint failure", () => { + it("rejects an override exceeding the uint64 max (2^64)", () => { process.env["SUPABASE_REALTIME_MAX_HEADER_LENGTH"] = "18446744073709551616"; expect(() => envOverrideRealtimeMaxHeaderLength(4096, undefined)).toThrow( "Failed reading config: Invalid realtime.max_header_length: 18446744073709551616.", @@ -965,7 +963,7 @@ describe("resolveLocalConfigValues", () => { expect(() => envOverrideRealtimeMaxHeaderLength(4096, undefined)).not.toThrow(); }); - it("rejects a hex override exceeding the uint64 max (2^64), matching Go's ParseUint failure", () => { + it("rejects a hex override exceeding the uint64 max (2^64)", () => { process.env["SUPABASE_REALTIME_MAX_HEADER_LENGTH"] = "0x10000000000000000"; expect(() => envOverrideRealtimeMaxHeaderLength(4096, undefined)).toThrow( "Failed reading config: Invalid realtime.max_header_length: 0x10000000000000000.", @@ -1060,7 +1058,7 @@ describe("resolveLocalConfigValues", () => { InvalidPoolModeEnvOverrideError, ); expect(() => envOverridePoolMode("transaction", undefined)).toThrow( - 'Invalid config for db.pooler.pool_mode: cannot parse "invalid" as one of "transaction", "session"', + 'Invalid config for db.pooler.pool_mode: "invalid" must be one of "transaction", "session"', ); }); }); @@ -1085,7 +1083,7 @@ describe("resolveLocalConfigValues", () => { InvalidEdgeRuntimePolicyEnvOverrideError, ); expect(() => envOverrideEdgeRuntimePolicy("oneshot", undefined)).toThrow( - 'Invalid config for edge_runtime.policy: cannot parse "invalid" as one of "per_worker", "oneshot"', + 'Invalid config for edge_runtime.policy: "invalid" must be one of "per_worker", "oneshot"', ); }); }); @@ -1194,7 +1192,7 @@ describe("resolveLocalConfigValues", () => { { enabled: false, provider: "hcaptcha", secret: "shh" }, undefined, ), - ).toThrow('cannot parse "not-a-bool" as a bool'); + ).toThrow('"not-a-bool" is not a valid boolean'); }); it("suppresses a malformed SUPABASE_AUTH_CAPTCHA_SECRET when a remote block already set auth.captcha.secret", () => { @@ -1360,7 +1358,7 @@ describe("resolveLocalConfigValues", () => { process.env["SUPABASE_AUTH_HOOK_CUSTOM_ACCESS_TOKEN_ENABLED"] = "not-a-bool"; const authDocument = { hook: { custom_access_token: { enabled: false } } }; expect(() => resolveAuthHooks(authDocument, allHooks, undefined)).toThrow( - 'cannot parse "not-a-bool" as a bool', + '"not-a-bool" is not a valid boolean', ); }); @@ -1424,7 +1422,7 @@ describe("resolveLocalConfigValues", () => { it("still rejects a malformed SUPABASE_AUTH_MFA_TOTP_ENROLL_ENABLED when no remote block matched", () => { process.env["SUPABASE_AUTH_MFA_TOTP_ENROLL_ENABLED"] = "not-a-bool"; const mfa = baseConfig().auth.mfa; - expect(() => resolveAuthMfa(mfa, undefined)).toThrow('cannot parse "not-a-bool" as a bool'); + expect(() => resolveAuthMfa(mfa, undefined)).toThrow('"not-a-bool" is not a valid boolean'); }); it("prefers a remote-set auth.mfa.phone.template over a conflicting SUPABASE_AUTH_MFA_PHONE_TEMPLATE", () => { @@ -1490,7 +1488,7 @@ describe("resolveLocalConfigValues", () => { process.env["SUPABASE_AUTH_EMAIL_SMTP_ENABLED"] = "not-a-bool"; const authDocument = { email: { smtp: { enabled: true } } }; expect(() => resolveAuthEmailSmtp(authDocument, undefined)).toThrow( - 'cannot parse "not-a-bool" as a bool', + '"not-a-bool" is not a valid boolean', ); }); @@ -1626,7 +1624,7 @@ describe("resolveLocalConfigValues", () => { }; expect(() => resolveAuthExternalProviders(authDocument, baseConfig().auth.external, undefined), - ).toThrow('cannot parse "not-a-bool" as a bool'); + ).toThrow('"not-a-bool" is not a valid boolean'); }); it("leaves an absent custom-provider boolean field at its schema default without throwing", () => { @@ -1641,7 +1639,7 @@ describe("resolveLocalConfigValues", () => { expect(resolved["my_custom"]?.enabled).toBe(false); }); - it("weakly coerces a raw numeric custom-provider boolean by truthiness, matching Go's WeaklyTypedInput decode", () => { + it("weakly coerces a raw numeric custom-provider boolean by truthiness", () => { const authDocument = { external: { my_custom: { enabled: 1, client_id: "custom-client-id" } }, }; @@ -1659,10 +1657,10 @@ describe("resolveLocalConfigValues", () => { }; expect(() => resolveAuthExternalProviders(authDocument, baseConfig().auth.external, undefined), - ).toThrow('cannot parse "1,2" as a bool'); + ).toThrow('"1,2" is not a valid boolean'); }); - it("resolves apple purely from env overrides even with no config.toml [auth.external] section at all, matching Go's ejected default template", () => { + it("resolves apple purely from env overrides even with no config.toml [auth.external] section at all", () => { const projectEnvValues = { SUPABASE_AUTH_EXTERNAL_APPLE_ENABLED: "true", SUPABASE_AUTH_EXTERNAL_APPLE_CLIENT_ID: "apple-client-id", @@ -1685,7 +1683,7 @@ describe("resolveLocalConfigValues", () => { }); }); - it("does not synthesize any other provider purely from an env override with no TOML table, only apple gets Go's default-template exception", () => { + it("does not synthesize any other provider purely from an env override with no TOML table, only apple gets the default-template exception", () => { const projectEnvValues = { SUPABASE_AUTH_EXTERNAL_GOOGLE_ENABLED: "true", SUPABASE_AUTH_EXTERNAL_GOOGLE_CLIENT_ID: "google-client-id", @@ -1743,7 +1741,7 @@ describe("resolveLocalConfigValues", () => { const projectEnvValues = { SUPABASE_AUTH_EXTERNAL_MY_CUSTOM_ENABLED: "not-a-bool" }; expect(() => resolveAuthExternalProviders(authDocument, baseConfig().auth.external, projectEnvValues), - ).toThrow('cannot parse "not-a-bool" as a bool'); + ).toThrow('"not-a-bool" is not a valid boolean'); }); it("prefers a remote-set auth.external..client_id over a conflicting SUPABASE_AUTH_EXTERNAL__CLIENT_ID", () => { @@ -1764,7 +1762,7 @@ describe("resolveLocalConfigValues", () => { }); describe("rawUnmodeledBool", () => { - it("returns false for an absent value, matching Go's zero-value bool default", () => { + it("returns false for an absent value", () => { expect(rawUnmodeledBool(undefined, "auth.passkey.enabled")).toBe(false); }); @@ -1773,13 +1771,13 @@ describe("resolveLocalConfigValues", () => { expect(rawUnmodeledBool(false, "auth.passkey.enabled")).toBe(false); }); - it("weakly coerces a raw number by truthiness, matching mapstructure's WeaklyTypedInput decodeBool", () => { + it("weakly coerces a raw number by truthiness", () => { expect(rawUnmodeledBool(123, "auth.passkey.enabled")).toBe(true); expect(rawUnmodeledBool(0, "auth.passkey.enabled")).toBe(false); expect(rawUnmodeledBool(1.5, "auth.passkey.enabled")).toBe(true); }); - it("parses a valid boolean-ish string the way Go's strconv.ParseBool does", () => { + it("parses a valid boolean-ish string", () => { expect(rawUnmodeledBool("true", "auth.passkey.enabled")).toBe(true); expect(rawUnmodeledBool("False", "auth.passkey.enabled")).toBe(false); expect(rawUnmodeledBool("", "auth.passkey.enabled")).toBe(false); @@ -1787,11 +1785,11 @@ describe("resolveLocalConfigValues", () => { it("throws on an unparsable string instead of silently disabling it", () => { expect(() => rawUnmodeledBool("not-a-bool", "auth.passkey.enabled")).toThrow( - 'cannot parse "not-a-bool" as a bool', + '"not-a-bool" is not a valid boolean', ); }); - it("throws on an array or table value — mapstructure's decodeBool errors on these unconditionally, never weakly coerced", () => { + it("throws on an array or table value — never weakly coerced", () => { expect(() => rawUnmodeledBool([1, 2], "auth.passkey.enabled")).toThrow( InvalidBoolEnvOverrideError, ); @@ -1874,7 +1872,7 @@ describe("resolveLocalConfigValues", () => { ).toBe(16); }); - it("rejects a uint override exceeding the uint64 max (2^64), matching Go's ParseUint failure", () => { + it("rejects a uint override exceeding the uint64 max (2^64)", () => { process.env["SUPABASE_DB_SETTINGS_MAX_CONNECTIONS"] = "18446744073709551616"; expect(() => resolveDbSettingsEnvOverrides({}, undefined)).toThrow( "Failed reading config: Invalid db.settings.max_connections: 18446744073709551616.", @@ -1914,7 +1912,7 @@ describe("resolveLocalConfigValues", () => { InvalidSessionReplicationRoleEnvOverrideError, ); expect(() => resolveDbSettingsEnvOverrides({}, undefined)).toThrow( - 'Invalid config for db.settings.session_replication_role: cannot parse "invalid" as one of "origin", "replica", "local"', + 'Invalid config for db.settings.session_replication_role: "invalid" must be one of "origin", "replica", "local"', ); }); @@ -1986,14 +1984,14 @@ describe("resolveLocalConfigValues", () => { }); }); - it("throws a Go-worded error when the signing keys file does not exist", () => { + it("throws a descriptive error when the signing keys file does not exist", () => { const config = baseConfig({ auth: { signing_keys_path: "missing.json" } }); expect(() => resolveLocalConfigValues(config, "127.0.0.1", tempRoot.current)).toThrow( "failed to read signing keys: ", ); }); - it("throws a Go-worded error when the signing keys file is malformed JSON", () => { + it("throws a descriptive error when the signing keys file is malformed JSON", () => { const supabaseDir = join(tempRoot.current, "supabase"); mkdirSync(supabaseDir, { recursive: true }); writeFileSync(join(supabaseDir, "signing_keys.json"), "not valid json"); @@ -2173,7 +2171,7 @@ describe("resolveLocalConfigValues", () => { expect(values.authPasswordRequirements).toBe("lower_upper_letters_digits"); }); - it("rejects an unrecognized SUPABASE_AUTH_PASSWORD_REQUIREMENTS override, matching Go's UnmarshalText", () => { + it("rejects an unrecognized SUPABASE_AUTH_PASSWORD_REQUIREMENTS override", () => { process.env["SUPABASE_AUTH_PASSWORD_REQUIREMENTS"] = "bogus"; const config = baseConfig(); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( @@ -2270,7 +2268,7 @@ describe("resolveLocalConfigValues", () => { const document = { auth: { passkey: { enabled: "not-a-bool" } } }; expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR, undefined, document), - ).toThrow('cannot parse "not-a-bool" as a bool'); + ).toThrow('"not-a-bool" is not a valid boolean'); }); }); @@ -2879,7 +2877,7 @@ describe("resolveLocalConfigValues", () => { ).not.toThrow(); }); - it("only validates the first enabled provider in Go's fixed priority order", () => { + it("only validates the first enabled provider in fixed priority order", () => { process.env["SUPABASE_AUTH_SMS_TWILIO_ENABLED"] = "false"; process.env["SUPABASE_AUTH_SMS_MESSAGEBIRD_ENABLED"] = "true"; const config = baseConfig(); @@ -3024,7 +3022,7 @@ describe("resolveLocalConfigValues", () => { vonage: { ...baseConfig().auth.sms.vonage, enabled: true }, }; expect(() => resolveAuthSms(undefined, configured, undefined)).toThrow( - 'cannot parse "not-a-bool" as a bool', + '"not-a-bool" is not a valid boolean', ); }); @@ -3215,7 +3213,7 @@ describe("resolveLocalConfigValues", () => { }, }; expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( - 'cannot parse "not-a-bool" as a bool', + '"not-a-bool" is not a valid boolean', ); expect(() => resolveLocalConfigValues( @@ -3247,7 +3245,7 @@ describe("resolveLocalConfigValues", () => { // The "exactly one of cert/key set" checks live in config-validate.unit.test.ts; the file-read // behavior below is tested here. - it("throws a Go-worded error when the configured cert file does not exist", () => { + it("throws a descriptive error when the configured cert file does not exist", () => { writeTlsFile(tempRoot.current, "key.pem"); const config = baseConfig({ api: { tls: { enabled: true, cert_path: "missing-cert.pem", key_path: "key.pem" } }, @@ -3257,7 +3255,7 @@ describe("resolveLocalConfigValues", () => { ); }); - it("throws a Go-worded error when the configured key file does not exist", () => { + it("throws a descriptive error when the configured key file does not exist", () => { writeTlsFile(tempRoot.current, "cert.pem"); const config = baseConfig({ api: { tls: { enabled: true, cert_path: "cert.pem", key_path: "missing-key.pem" } }, @@ -3704,7 +3702,7 @@ describe("resolveLocalConfigValues — remoteOverrideKeys (linked shadow provisi process.env["SUPABASE_AUTH_ENABLED"] = "not-a-bool"; const config = baseConfig({ auth: { enabled: false } }); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( - 'Invalid config for auth.enabled: cannot parse "not-a-bool" as a bool', + 'Invalid config for auth.enabled: "not-a-bool" is not a valid boolean', ); }); @@ -3727,7 +3725,7 @@ describe("resolveLocalConfigValues — remoteOverrideKeys (linked shadow provisi process.env["SUPABASE_ANALYTICS_ENABLED"] = "not-a-bool"; const config = baseConfig({ analytics: { enabled: false } }); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( - 'Invalid config for analytics.enabled: cannot parse "not-a-bool" as a bool', + 'Invalid config for analytics.enabled: "not-a-bool" is not a valid boolean', ); }); @@ -3942,7 +3940,7 @@ describe("resolveLocalConfigValues — remoteOverrideKeys (linked shadow provisi auth: { enabled: true, third_party: { firebase: { enabled: false } } }, }); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( - 'Invalid config for auth.third_party.firebase.enabled: cannot parse "not-a-bool" as a bool', + 'Invalid config for auth.third_party.firebase.enabled: "not-a-bool" is not a valid boolean', ); }); @@ -3988,7 +3986,7 @@ describe("resolveLocalConfigValues — remoteOverrideKeys (linked shadow provisi process.env["SUPABASE_API_ENABLED"] = "not-a-bool"; const config = baseConfig({ api: { enabled: false } }); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( - 'Invalid config for api.enabled: cannot parse "not-a-bool" as a bool', + 'Invalid config for api.enabled: "not-a-bool" is not a valid boolean', ); }); @@ -4011,7 +4009,7 @@ describe("resolveLocalConfigValues — remoteOverrideKeys (linked shadow provisi process.env["SUPABASE_STUDIO_ENABLED"] = "not-a-bool"; const config = baseConfig({ studio: { enabled: false } }); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( - 'Invalid config for studio.enabled: cannot parse "not-a-bool" as a bool', + 'Invalid config for studio.enabled: "not-a-bool" is not a valid boolean', ); }); @@ -4049,7 +4047,7 @@ describe("resolveLocalConfigValues — remoteOverrideKeys (linked shadow provisi process.env["SUPABASE_LOCAL_SMTP_ENABLED"] = "not-a-bool"; const config = baseConfig({ local_smtp: { enabled: false } }); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( - 'Invalid config for local_smtp.enabled: cannot parse "not-a-bool" as a bool', + 'Invalid config for local_smtp.enabled: "not-a-bool" is not a valid boolean', ); }); @@ -4072,7 +4070,7 @@ describe("resolveLocalConfigValues — remoteOverrideKeys (linked shadow provisi process.env["SUPABASE_AUTH_ENABLE_SIGNUP"] = "not-a-bool"; const config = baseConfig({ auth: { enable_signup: false } }); expect(() => resolveLocalConfigValues(config, "127.0.0.1", WORKDIR)).toThrow( - 'Invalid config for auth.enable_signup: cannot parse "not-a-bool" as a bool', + 'Invalid config for auth.enable_signup: "not-a-bool" is not a valid boolean', ); }); @@ -4224,14 +4222,14 @@ describe("resolveLocalJwks", () => { }); }); - it("throws a Go-worded error when the signing keys file does not exist", async () => { + it("throws a descriptive error when the signing keys file does not exist", async () => { const config = baseConfig({ auth: { signing_keys_path: "missing.json" } }); await expect(runLocalJwks(config, tempRoot.current, "a".repeat(32))).rejects.toThrow( "failed to read signing keys: ", ); }); - it("throws a Go-worded error when the signing keys file is malformed JSON", async () => { + it("throws a descriptive error when the signing keys file is malformed JSON", async () => { const supabaseDir = join(tempRoot.current, "supabase"); mkdirSync(supabaseDir, { recursive: true }); writeFileSync(join(supabaseDir, "signing_keys.json"), "not valid json"); @@ -4267,7 +4265,7 @@ describe("resolveLocalJwks", () => { ); }); - it("does not validate third-party providers when auth is disabled, matching Go's ResolveJWKS/IssuerURL", async () => { + it("does not validate third-party providers when auth is disabled", async () => { const remoteKeys = [{ kty: "RSA", kid: "firebase-key", n: "abc", e: "AQAB" }]; const issuerUrl = "https://securetoken.google.com/my-project"; const fetchMock = vi.spyOn(globalThis, "fetch").mockImplementation(async (input) => { @@ -4302,7 +4300,7 @@ describe("resolveLocalJwks", () => { fetchMock.mockRestore(); }); - it('does not attempt a remote JWKS fetch for an enabled third-party provider with an empty issuer_url, matching Go\'s issuerURL != "" check', async () => { + it("does not attempt a remote JWKS fetch for an enabled third-party provider with an empty issuer_url", async () => { const fetchMock = vi.spyOn(globalThis, "fetch"); const config = baseConfig({ auth: { @@ -4453,7 +4451,7 @@ describe("resolveLocalJwks", () => { process.env["SUPABASE_AUTH_ENABLED"] = "not-a-bool"; const config = baseConfig({ auth: { enabled: false } }); await expect(runLocalJwks(config, WORKDIR, "a".repeat(32))).rejects.toThrow( - 'Invalid config for auth.enabled: cannot parse "not-a-bool" as a bool', + 'Invalid config for auth.enabled: "not-a-bool" is not a valid boolean', ); }); }); @@ -4524,7 +4522,7 @@ describe("resolveConfiguredSigningKeys — remoteOverrideKeys (linked shadow pro process.env["SUPABASE_AUTH_ENABLED"] = "not-a-bool"; const config = baseConfig({ auth: { enabled: false } }); expect(() => resolveConfiguredSigningKeys(config, tempRoot.current, undefined)).toThrow( - 'Invalid config for auth.enabled: cannot parse "not-a-bool" as a bool', + 'Invalid config for auth.enabled: "not-a-bool" is not a valid boolean', ); }); }); diff --git a/apps/cli/src/command-internal/go-jwt.ts b/apps/cli/src/command-internal/local-jwt.ts similarity index 83% rename from apps/cli/src/command-internal/go-jwt.ts rename to apps/cli/src/command-internal/local-jwt.ts index 175e399816..8526da8332 100644 --- a/apps/cli/src/command-internal/go-jwt.ts +++ b/apps/cli/src/command-internal/local-jwt.ts @@ -1,5 +1,5 @@ import { createHmac, createPrivateKey, createSign } from "node:crypto"; -import { encodeGoJsonCompact } from "./go-json.ts"; +import { encodeHtmlSafeJsonCompact } from "./html-safe-json.ts"; export { DEFAULT_SIGNING_KEY } from "@supabase/stack/defaults"; @@ -33,7 +33,7 @@ export interface Jwk { /** * HS256 signer for the default local-dev `anon`/`service_role` keys. {@link - * generateAsymmetricGoJwt} below covers the RS256/ES256 branch, taken when + * generateAsymmetricLocalJwt} below covers the RS256/ES256 branch, taken when * `auth.signing_keys_path` is configured. * * Does not reuse `@supabase/stack`'s `generateJwt`: that helper uses a different issuer, a @@ -42,17 +42,17 @@ export interface Jwk { * "now". */ -const GO_JWT_ISSUER = "supabase-demo"; -const GO_JWT_FIXED_EXP = 1983812996; +const LOCAL_JWT_ISSUER = "supabase-demo"; +const LOCAL_JWT_FIXED_EXP = 1983812996; function base64UrlEncode(input: string): string { return Buffer.from(input).toString("base64url"); } -export function generateGoJwt(secret: string, role: "anon" | "service_role"): string { +export function generateLocalJwt(secret: string, role: "anon" | "service_role"): string { const header = base64UrlEncode(JSON.stringify({ alg: "HS256", typ: "JWT" })); const payload = base64UrlEncode( - JSON.stringify({ iss: GO_JWT_ISSUER, role, exp: GO_JWT_FIXED_EXP }), + JSON.stringify({ iss: LOCAL_JWT_ISSUER, role, exp: LOCAL_JWT_FIXED_EXP }), ); const data = `${header}.${payload}`; const signature = createHmac("sha256", secret).update(data).digest("base64url"); @@ -60,7 +60,7 @@ export function generateGoJwt(secret: string, role: "anon" | "service_role"): st } /** Asymmetric-JWT expiry: 10 years from now. */ -const GO_JWT_ASYMMETRIC_EXPIRY_SECONDS = 60 * 60 * 24 * 365 * 10; +const LOCAL_JWT_ASYMMETRIC_EXPIRY_SECONDS = 60 * 60 * 24 * 365 * 10; function base64UrlToBigInt(value: string): bigint { const hex = Buffer.from(value, "base64url").toString("hex"); @@ -87,7 +87,7 @@ function modInverse(a: bigint, m: bigint): bigint { /** * Backfills the RSA CRT parameters (`dp`, `dq`, `qi`) when absent: Node's `createPrivateKey` - * rejects an RSA JWK without them, unlike Go, which derives them lazily from `p`/`q`/`d` before + * rejects an RSA JWK without them, so they are derived from `p`/`q`/`d` before * signing. Returns the key unchanged if all three are already present, or if `d`/`p`/`q` are * missing (an invalid key either way). */ @@ -140,13 +140,11 @@ function assertSupportedKty(jwk: Jwk): void { } /** - * Reproduces `encoding/base64`'s `CorruptInputError` message for each numeric field, since Go's - * unpadded base64 decoder rejects input Node's own JWK importer would silently accept (e.g. a - * `=`-padded coordinate) and sign a token Go could never have produced. + * Produces a `CorruptInputError` message for each numeric field, since unpadded base64 decoding + * rejects input Node's own JWK importer would silently accept (e.g. a `=`-padded coordinate). * - * Checks fields in Go's exact order (EC: x, y, d; RSA: n, e, d, p, q) so the first invalid field - * matches Go's first-failure-wins order. An absent field is skipped, matching Go's zero value - * decoding to zero bytes. + * Checks fields in a fixed order (EC: x, y, d; RSA: n, e, d, p, q) so the first invalid field + * is reported. An absent field is skipped and decodes to zero bytes. */ function assertDecodableJwkNumericFields(jwk: Jwk): void { const assertField = (label: string, value: string | undefined): void => { @@ -196,8 +194,8 @@ function assertKeyMatchesAlgorithm(jwk: Jwk, algorithm: SupportedJwtAlgorithm): * * `dsaEncoding: "ieee-p1363"` is required for ES256: Node's default ECDSA signature is * DER-encoded, not the raw (r‖s) format JWS requires. The header is serialized with - * {@link encodeGoJsonCompact}, not `JSON.stringify`, since a `kid` containing `<`/`>`/`&` must - * HTML-escape to sign the same bytes Go would. + * {@link encodeHtmlSafeJsonCompact}, not `JSON.stringify`, since a `kid` containing `<`/`>`/`&` must + * HTML-escape so the signed bytes are stable. */ export function signJwtWithJwk(jwk: Jwk, payloadJson: string): string { try { @@ -226,7 +224,7 @@ export function signJwtWithJwk(jwk: Jwk, payloadJson: string): string { jwk.kid !== undefined && jwk.kid.length > 0 ? { alg: algorithm, kid: jwk.kid, typ: "JWT" } : { alg: algorithm, typ: "JWT" }; - const headerEncoded = base64UrlEncode(encodeGoJsonCompact(header)); + const headerEncoded = base64UrlEncode(encodeHtmlSafeJsonCompact(header)); const payloadEncoded = base64UrlEncode(payloadJson); const data = `${headerEncoded}.${payloadEncoded}`; @@ -248,9 +246,9 @@ export function signJwtWithJwk(jwk: Jwk, payloadJson: string): string { /** * The RS256/ES256 signing path, used when `auth.signing_keys_path` resolves to a non-empty JWK * array — the first key in the file signs both the anon and service_role tokens. Same claim - * shape as {@link generateGoJwt} (`iss`/`role`/`exp`), except the expiry is 10 years from now. + * shape as {@link generateLocalJwt} (`iss`/`role`/`exp`), except the expiry is 10 years from now. */ -export function generateAsymmetricGoJwt(jwk: Jwk, role: "anon" | "service_role"): string { - const expiresAt = Math.floor(Date.now() / 1000) + GO_JWT_ASYMMETRIC_EXPIRY_SECONDS; - return signJwtWithJwk(jwk, JSON.stringify({ iss: GO_JWT_ISSUER, role, exp: expiresAt })); +export function generateAsymmetricLocalJwt(jwk: Jwk, role: "anon" | "service_role"): string { + const expiresAt = Math.floor(Date.now() / 1000) + LOCAL_JWT_ASYMMETRIC_EXPIRY_SECONDS; + return signJwtWithJwk(jwk, JSON.stringify({ iss: LOCAL_JWT_ISSUER, role, exp: expiresAt })); } diff --git a/apps/cli/src/command-internal/go-jwt.unit.test.ts b/apps/cli/src/command-internal/local-jwt.unit.test.ts similarity index 76% rename from apps/cli/src/command-internal/go-jwt.unit.test.ts rename to apps/cli/src/command-internal/local-jwt.unit.test.ts index c67c88212f..13cb67ab7c 100644 --- a/apps/cli/src/command-internal/go-jwt.unit.test.ts +++ b/apps/cli/src/command-internal/local-jwt.unit.test.ts @@ -4,11 +4,11 @@ import { describe, expect, it } from "vitest"; import { assertDecodableJwkAlgorithm, - generateAsymmetricGoJwt, - generateGoJwt, + generateAsymmetricLocalJwt, + generateLocalJwt, signJwtWithJwk, type Jwk, -} from "./go-jwt.ts"; +} from "./local-jwt.ts"; const SECRET = "super-secret-jwt-token-with-at-least-32-characters-long"; @@ -33,16 +33,16 @@ function decodeSegment(segment: string): string { return Buffer.from(segment, "base64url").toString("utf8"); } -describe("generateGoJwt", () => { - it("emits Go's exact JWT header (no extra fields, alg before typ)", () => { - const token = generateGoJwt(SECRET, "anon"); +describe("generateLocalJwt", () => { + it("emits the JWT header (no extra fields, alg before typ)", () => { + const token = generateLocalJwt(SECRET, "anon"); const [header] = token.split("."); expect(header).toBeDefined(); expect(decodeSegment(header ?? "")).toBe('{"alg":"HS256","typ":"JWT"}'); }); - it("emits the anon payload with Go's exact key order and fixed claims", () => { - const token = generateGoJwt(SECRET, "anon"); + it("emits the anon payload with the exact key order and fixed claims", () => { + const token = generateLocalJwt(SECRET, "anon"); const [, payload] = token.split("."); expect(payload).toBeDefined(); const raw = decodeSegment(payload ?? ""); @@ -55,15 +55,15 @@ describe("generateGoJwt", () => { expect(Object.keys(parsed)).not.toContain("is_anonymous"); }); - it("emits the service_role payload with Go's exact key order and fixed claims", () => { - const token = generateGoJwt(SECRET, "service_role"); + it("emits the service_role payload with the exact key order and fixed claims", () => { + const token = generateLocalJwt(SECRET, "service_role"); const [, payload] = token.split("."); const raw = decodeSegment(payload ?? ""); expect(raw).toBe('{"iss":"supabase-demo","role":"service_role","exp":1983812996}'); }); it("signs with plain HMAC-SHA256 over the base64url header.payload, base64url-encoded", () => { - const token = generateGoJwt(SECRET, "anon"); + const token = generateLocalJwt(SECRET, "anon"); const [header, payload, signature] = token.split("."); const expectedSignature = createHmac("sha256", SECRET) .update(`${header}.${payload}`) @@ -72,32 +72,32 @@ describe("generateGoJwt", () => { }); it("is deterministic across calls (no timestamp derived from Date.now())", () => { - const first = generateGoJwt(SECRET, "anon"); - const second = generateGoJwt(SECRET, "anon"); + const first = generateLocalJwt(SECRET, "anon"); + const second = generateLocalJwt(SECRET, "anon"); expect(first).toBe(second); }); it("produces different tokens for different secrets", () => { - const a = generateGoJwt(SECRET, "anon"); - const b = generateGoJwt("a-different-secret-value-1234567", "anon"); + const a = generateLocalJwt(SECRET, "anon"); + const b = generateLocalJwt("a-different-secret-value-1234567", "anon"); expect(a).not.toBe(b); }); }); -describe("generateAsymmetricGoJwt", () => { +describe("generateAsymmetricLocalJwt", () => { it("signs and verifies an RS256 token from an RSA JWK", async () => { const jwk = generateRsaJwk("rsa-kid"); - const token = generateAsymmetricGoJwt(jwk, "anon"); + const token = generateAsymmetricLocalJwt(jwk, "anon"); const publicKey = await importJWK(publicJwkOf(jwk), "RS256"); const { payload, protectedHeader } = await jwtVerify(token, publicKey); expect(payload).toMatchObject({ iss: "supabase-demo", role: "anon" }); expect(protectedHeader).toEqual({ alg: "RS256", kid: "rsa-kid", typ: "JWT" }); }); - it("signs an RS256 token from an RSA JWK missing CRT exponents (dp/dq/qi), matching Go", async () => { + it("signs an RS256 token from an RSA JWK missing CRT exponents (dp/dq/qi)", async () => { const jwk = generateRsaJwk("rsa-kid"); const { dp: _dp, dq: _dq, qi: _qi, ...jwkWithoutCrtParams } = jwk; - const token = generateAsymmetricGoJwt(jwkWithoutCrtParams, "anon"); + const token = generateAsymmetricLocalJwt(jwkWithoutCrtParams, "anon"); const publicKey = await importJWK(publicJwkOf(jwk), "RS256"); const { payload, protectedHeader } = await jwtVerify(token, publicKey); expect(payload).toMatchObject({ iss: "supabase-demo", role: "anon" }); @@ -106,7 +106,7 @@ describe("generateAsymmetricGoJwt", () => { it("signs and verifies an ES256 token from an EC JWK", async () => { const jwk = generateEcJwk("ec-kid"); - const token = generateAsymmetricGoJwt(jwk, "service_role"); + const token = generateAsymmetricLocalJwt(jwk, "service_role"); const publicKey = await importJWK(publicJwkOf(jwk), "ES256"); const { payload, protectedHeader } = await jwtVerify(token, publicKey); expect(payload).toMatchObject({ iss: "supabase-demo", role: "service_role" }); @@ -115,7 +115,7 @@ describe("generateAsymmetricGoJwt", () => { it("omits the kid header entirely when the JWK has no kid", () => { const jwk = generateRsaJwk(); - const token = generateAsymmetricGoJwt(jwk, "anon"); + const token = generateAsymmetricLocalJwt(jwk, "anon"); const [header] = token.split("."); const decoded = JSON.parse(Buffer.from(header ?? "", "base64url").toString()); expect(decoded).toEqual({ alg: "RS256", typ: "JWT" }); @@ -124,7 +124,7 @@ describe("generateAsymmetricGoJwt", () => { it("sets a ~10-year expiry computed from the current time, not a fixed timestamp", () => { const jwk = generateRsaJwk(); const before = Math.floor(Date.now() / 1000); - const token = generateAsymmetricGoJwt(jwk, "anon"); + const token = generateAsymmetricLocalJwt(jwk, "anon"); const [, payload] = token.split("."); const decoded = JSON.parse(Buffer.from(payload ?? "", "base64url").toString()); const tenYearsSeconds = 60 * 60 * 24 * 365 * 10; @@ -134,39 +134,41 @@ describe("generateAsymmetricGoJwt", () => { it("rejects an unsupported algorithm", () => { const jwk = { ...generateRsaJwk(), alg: "RS512" }; - expect(() => generateAsymmetricGoJwt(jwk, "anon")).toThrow("unsupported algorithm: RS512"); + expect(() => generateAsymmetricLocalJwt(jwk, "anon")).toThrow("unsupported algorithm: RS512"); }); it("rejects a JWK with no algorithm", () => { const { alg: _alg, ...jwkWithoutAlg } = generateRsaJwk(); - expect(() => generateAsymmetricGoJwt(jwkWithoutAlg, "anon")).toThrow("unsupported algorithm: "); + expect(() => generateAsymmetricLocalJwt(jwkWithoutAlg, "anon")).toThrow( + "unsupported algorithm: ", + ); }); it("rejects an EC key forged with alg: RS256 instead of signing garbage", () => { const jwk = { ...generateEcJwk(), alg: "RS256" }; - expect(() => generateAsymmetricGoJwt(jwk, "anon")).toThrow( + expect(() => generateAsymmetricLocalJwt(jwk, "anon")).toThrow( "failed to sign JWT: key is of invalid type: RSA sign expects *rsa.PrivateKey", ); }); it("rejects an RSA key forged with alg: ES256 instead of signing garbage", () => { const jwk = { ...generateRsaJwk(), alg: "ES256" }; - expect(() => generateAsymmetricGoJwt(jwk, "anon")).toThrow( + expect(() => generateAsymmetricLocalJwt(jwk, "anon")).toThrow( "failed to sign JWT: key is of invalid type: ECDSA sign expects *ecdsa.PrivateKey", ); }); - it("rejects an ES256 EC key whose curve is not P-256, wrapped like Go's GenerateAsymmetricJWT", () => { + it("rejects an ES256 EC key whose curve is not P-256, wrapped in the private-key conversion error", () => { const { privateKey } = generateKeyPairSync("ec", { namedCurve: "P-384" }); const jwk = { ...privateKey.export({ format: "jwk" }), kty: "EC", alg: "ES256" }; - expect(() => generateAsymmetricGoJwt(jwk, "anon")).toThrow( + expect(() => generateAsymmetricLocalJwt(jwk, "anon")).toThrow( "failed to convert JWK to private key: unsupported curve: P-384", ); }); - it("rejects a JWK with no kty at all, wrapped like Go's GenerateAsymmetricJWT", () => { + it("rejects a JWK with no kty at all, wrapped in the private-key conversion error", () => { const jwk = { kty: "oct" } as Jwk; - expect(() => generateAsymmetricGoJwt(jwk, "anon")).toThrow( + expect(() => generateAsymmetricLocalJwt(jwk, "anon")).toThrow( "failed to convert JWK to private key: unsupported key type: oct", ); }); @@ -174,15 +176,15 @@ describe("generateAsymmetricGoJwt", () => { it("rejects an ES256 EC key with no curve at all", () => { const jwk = generateEcJwk(); const { crv: _crv, ...jwkWithoutCurve } = jwk; - expect(() => generateAsymmetricGoJwt(jwkWithoutCurve, "anon")).toThrow( + expect(() => generateAsymmetricLocalJwt(jwkWithoutCurve, "anon")).toThrow( "failed to convert JWK to private key: unsupported curve: ", ); }); - it("rejects a padded EC coordinate instead of signing a token Go would refuse to produce (CLI-1961 Codex review finding)", () => { + it("rejects a padded EC coordinate instead of signing an invalid token", () => { const jwk = generateEcJwk("ec-kid"); const padded = { ...jwk, x: `${jwk.x}=` }; - expect(() => generateAsymmetricGoJwt(padded, "anon")).toThrow( + expect(() => generateAsymmetricLocalJwt(padded, "anon")).toThrow( /^failed to convert JWK to private key: failed to decode x coordinate: illegal base64 data at input byte \d+$/, ); }); @@ -190,21 +192,21 @@ describe("generateAsymmetricGoJwt", () => { it("rejects a padded RSA modulus the same way", () => { const jwk = generateRsaJwk("rsa-kid"); const padded = { ...jwk, n: `${jwk.n}=` }; - expect(() => generateAsymmetricGoJwt(padded, "anon")).toThrow( + expect(() => generateAsymmetricLocalJwt(padded, "anon")).toThrow( /^failed to convert JWK to private key: failed to decode modulus: illegal base64 data at input byte \d+$/, ); }); it("still signs successfully for unpadded (correctly-encoded) coordinates", () => { const jwk = generateEcJwk("ec-kid"); - expect(() => generateAsymmetricGoJwt(jwk, "anon")).not.toThrow(); + expect(() => generateAsymmetricLocalJwt(jwk, "anon")).not.toThrow(); }); }); describe("signJwtWithJwk", () => { it("signs the caller's exact pre-encoded payload string verbatim (no re-serialization)", async () => { const jwk = generateEcJwk("ec-kid"); - // Unsorted and containing `&`, which Go's encoder would normally HTML-escape: this function + // Unsorted and containing `&`, which the encoder would normally HTML-escape: this function // must sign exactly the bytes it's given. const payloadJson = '{"role":"postgres","sb-role":"mgmt-api & co"}'; const token = signJwtWithJwk(jwk, payloadJson); @@ -216,7 +218,7 @@ describe("signJwtWithJwk", () => { expect(verified).toEqual({ role: "postgres", "sb-role": "mgmt-api & co" }); }); - it("HTML-escapes the kid in the header like Go's json.Marshal, unlike JSON.stringify (CLI-1961 Codex review finding)", () => { + it("HTML-escapes the kid in the header, unlike JSON.stringify", () => { const jwk = generateEcJwk("ac&d"); const token = signJwtWithJwk(jwk, '{"role":"anon"}'); const [header] = token.split("."); @@ -236,7 +238,7 @@ describe("assertDecodableJwkAlgorithm", () => { expect(() => assertDecodableJwkAlgorithm(undefined)).not.toThrow(); }); - it("rejects an unsupported algorithm with Go's exact UnmarshalText message", () => { + it("rejects an unsupported algorithm with the allowed-values message", () => { expect(() => assertDecodableJwkAlgorithm("HS256")).toThrow("must be one of [RS256 ES256]"); }); }); diff --git a/apps/cli/src/command-internal/local-project-context.ts b/apps/cli/src/command-internal/local-project-context.ts index 98487222a8..989d186fb4 100644 --- a/apps/cli/src/command-internal/local-project-context.ts +++ b/apps/cli/src/command-internal/local-project-context.ts @@ -4,7 +4,7 @@ import { type LoadedCliConfig, type CliConfig, } from "@supabase/config/effect"; -import { loadCliConfig } from "@supabase/config/internal"; +import { loadCliConfig } from "./cli-config-load.ts"; import { Crypto, Effect, FileSystem, Path, Schema } from "effect"; import { recordOrioleDbTelemetry } from "./db-image.ts"; @@ -68,7 +68,6 @@ export const loadLocalProjectContext = ( // Restricts resolution to `supabase/config.toml`; without this, a workdir with a stray // `config.json` would be preferred over it. tomlOnly: true, - goViperCompat: true, projectRef, }).pipe( Effect.mapError((cause) => mapConfigLoadError(`failed to read config: ${String(cause)}`)), diff --git a/apps/cli/src/command-internal/local-project-context.unit.test.ts b/apps/cli/src/command-internal/local-project-context.unit.test.ts index 58ac1a6667..27490c4b86 100644 --- a/apps/cli/src/command-internal/local-project-context.unit.test.ts +++ b/apps/cli/src/command-internal/local-project-context.unit.test.ts @@ -77,21 +77,18 @@ describe("loadLocalProjectContext", () => { ); }); - it.effect( - "does NOT install a project .env's DOCKER_HOST into process.env, matching Go's Docker client being frozen at binary startup, before godotenv.Load ever runs", - () => { - delete process.env[DOCKER_HOST_KEY]; - const workdir = tempRoot.current; - writeDotEnv(workdir, `DOCKER_HOST=tcp://project-dotenv-host:2375\n`); + it.effect("does NOT install a project .env's DOCKER_HOST into process.env", () => { + delete process.env[DOCKER_HOST_KEY]; + const workdir = tempRoot.current; + writeDotEnv(workdir, `DOCKER_HOST=tcp://project-dotenv-host:2375\n`); - return loadLocalProjectContext(workdir, (message) => new Error(message)).pipe( - Effect.map(() => { - expect(process.env[DOCKER_HOST_KEY]).toBeUndefined(); - }), - Effect.provide(Layer.mergeAll(BunServices.layer, runtimeInfoLayer)), - ); - }, - ); + return loadLocalProjectContext(workdir, (message) => new Error(message)).pipe( + Effect.map(() => { + expect(process.env[DOCKER_HOST_KEY]).toBeUndefined(); + }), + Effect.provide(Layer.mergeAll(BunServices.layer, runtimeInfoLayer)), + ); + }); it.effect( "leaves an already-set shell DOCKER_HOST untouched regardless of a conflicting project .env value", diff --git a/apps/cli/src/command-internal/login-crypto.layer.ts b/apps/cli/src/command-internal/login-crypto.layer.ts index 63ab3fc5fb..bcd98f57f4 100644 --- a/apps/cli/src/command-internal/login-crypto.layer.ts +++ b/apps/cli/src/command-internal/login-crypto.layer.ts @@ -27,7 +27,7 @@ export const loginCryptoLayer = Layer.sync(LoginCrypto, () => const host = hostname(); if (user && host) return `cli_${user}@${host}_${ts}`; } catch { - /* fall through to the fallback name (Go's generateTokenNameWithFallback) */ + /* fall through to the fallback name */ } return `cli_${ts}`; }), @@ -35,7 +35,7 @@ export const loginCryptoLayer = Layer.sync(LoginCrypto, () => Effect.try({ try: () => { const sharedSecret = ecdh.computeSecret(Buffer.from(payload.publicKey, "hex")); - // Go's `aesgcm.Open` expects the 16-byte GCM tag appended to the + // The payload carries the 16-byte GCM tag appended to the // ciphertext; Node wants it supplied separately via `setAuthTag`. const ciphertextHex = payload.ciphertext.slice(0, -32); const authTagHex = payload.ciphertext.slice(-32); diff --git a/apps/cli/src/command-internal/migrate-and-seed.unit.test.ts b/apps/cli/src/command-internal/migrate-and-seed.unit.test.ts index 9676f442d1..c864bc3e84 100644 --- a/apps/cli/src/command-internal/migrate-and-seed.unit.test.ts +++ b/apps/cli/src/command-internal/migrate-and-seed.unit.test.ts @@ -421,38 +421,35 @@ describe("migrateAndSeed experimental declarative-schema branch", () => { }, ); - it.effect( - "attaches the failing schema file as Go's CmdSuggestion (See schema file: )", - () => { - const workdir = makeWorkdir(); - const schemaPath = "supabase/schemas/broken.sql"; - writeFile(workdir, schemaPath, "totally not valid sql;"); - const { session } = failingExecSession(); - const out = mockOutput(); - return run( - workdir, - "", - { - ...baseConfig, - experimental: true, - schemaPaths: [schemaPath], - }, - session, - out, - ).pipe( - Effect.flip, - Effect.tap((error) => - Effect.sync(() => { - expect(error).toBeInstanceOf(MigrationApplyError); - const suggestion = (error as MigrationApplyError).suggestion; - expect(suggestion).toBeDefined(); - expect(stripAnsi(suggestion ?? "")).toBe(`See schema file: ${schemaPath}`); - rmSync(workdir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("attaches the failing schema file as a suggestion (See schema file: )", () => { + const workdir = makeWorkdir(); + const schemaPath = "supabase/schemas/broken.sql"; + writeFile(workdir, schemaPath, "totally not valid sql;"); + const { session } = failingExecSession(); + const out = mockOutput(); + return run( + workdir, + "", + { + ...baseConfig, + experimental: true, + schemaPaths: [schemaPath], + }, + session, + out, + ).pipe( + Effect.flip, + Effect.tap((error) => + Effect.sync(() => { + expect(error).toBeInstanceOf(MigrationApplyError); + const suggestion = (error as MigrationApplyError).suggestion; + expect(suggestion).toBeDefined(); + expect(stripAnsi(suggestion ?? "")).toBe(`See schema file: ${schemaPath}`); + rmSync(workdir, { recursive: true, force: true }); + }), + ), + ); + }); }); describe("migrateAndSeed local pg_net remediation", () => { diff --git a/apps/cli/src/command-internal/migration-apply.ts b/apps/cli/src/command-internal/migration-apply.ts index 96aea47001..25331aae58 100644 --- a/apps/cli/src/command-internal/migration-apply.ts +++ b/apps/cli/src/command-internal/migration-apply.ts @@ -143,14 +143,14 @@ const utf8ByteLength = (value: string): number => new TextEncoder().encode(value // The scanner buffer starts at this size before applying the configured/default max, so a // statement must reach at least this many bytes before an oversized-token error can fire, // regardless of how small an override is set. -const GO_SCANNER_START_BUF_SIZE = 4096; +const SCANNER_START_BUF_SIZE = 4096; // Fallback cap when `SUPABASE_SCANNER_BUFFER_SIZE` is set but parses to a non-positive size, // including a value that can't be parsed at all (e.g. a bare "5M" with no trailing "B"). -const GO_DEFAULT_MAX_SCANNER_CAPACITY = 256 * 1024; +const DEFAULT_MAX_SCANNER_CAPACITY = 256 * 1024; -const GO_MAX_INT64 = 9223372036854775807n; -const GO_MIN_INT64 = -9223372036854775808n; +const INT64_MAX = 9223372036854775807n; +const INT64_MIN = -9223372036854775808n; /** * Parses a base-0 integer literal: decimal, or `0x`/`0o`/`0b`-prefixed hex/octal/binary, or a @@ -158,7 +158,7 @@ const GO_MIN_INT64 = -9223372036854775808n; * the same base (never leading, trailing, or doubled). Returns `undefined` for anything invalid * or outside the 64-bit signed integer range. */ -const parseGoBaseZeroInt = (value: string): number | undefined => { +const parseBaseZeroInt = (value: string): number | undefined => { const negative = value.startsWith("-"); const unsigned = negative || value.startsWith("+") ? value.slice(1) : value; if (unsigned.length === 0) return undefined; @@ -198,7 +198,7 @@ const parseGoBaseZeroInt = (value: string): number | undefined => { const bigPrefix = base === 16 ? "0x" : base === 8 ? "0o" : base === 2 ? "0b" : ""; const magnitude = BigInt(`${bigPrefix}${cleanDigits}`); const signedMagnitude = negative ? -magnitude : magnitude; - if (signedMagnitude > GO_MAX_INT64 || signedMagnitude < GO_MIN_INT64) return undefined; + if (signedMagnitude > INT64_MAX || signedMagnitude < INT64_MIN) return undefined; const n = Number.parseInt(cleanDigits, base); return negative ? -n : n; @@ -206,8 +206,8 @@ const parseGoBaseZeroInt = (value: string): number | undefined => { // Drops a decimal literal's fractional part rather than rounding (`"5.5"` → `"5"`); a // `0x`/`0o`/`0b` literal (which contains letters) never matches and passes through unchanged for -// {@link parseGoBaseZeroInt} to accept or reject. -const trimGoDecimal = (value: string): string => { +// {@link parseBaseZeroInt} to accept or reject. +const trimDecimalFraction = (value: string): string => { if (!value.includes(".")) return value; const match = /^([+-]?\d*)(?:\.\d*)?$/.exec(value); if (!match) return value; @@ -220,7 +220,7 @@ const trimGoDecimal = (value: string): string => { * `SUPABASE_SCANNER_BUFFER_SIZE` accepts an integer byte count, optionally suffixed * `k`/`m`/`g` (× 1024/1024²/1024³) immediately followed by a trailing `b`/`B` (e.g. `"5MB"`). A * bare `"5M"` (no trailing `B`) is NOT 5 MiB — it fails to parse as an integer and is treated as - * unset (`0`), same as any other unparseable or non-positive value. See {@link parseGoBaseZeroInt} + * unset (`0`), same as any other unparseable or non-positive value. See {@link parseBaseZeroInt} * for the accepted integer-literal grammar (hex/octal/binary prefixes, `_` separators). */ const parseScannerBufferSize = (raw: string): number => { @@ -246,7 +246,7 @@ const parseScannerBufferSize = (raw: string): number => { break; } } - const size = parseGoBaseZeroInt(trimGoDecimal(value)); + const size = parseBaseZeroInt(trimDecimalFraction(value)); return size !== undefined && Number.isFinite(size) && size > 0 ? size * multiplier : 0; }; @@ -269,16 +269,16 @@ export const checkScannerBufferSize = ( if (raw === undefined) return Effect.void; const configuredLimit = parseScannerBufferSize(raw); // Covers both an explicit non-positive size and an unparseable value (see - // `GO_DEFAULT_MAX_SCANNER_CAPACITY` above) — both fall back to the hardcoded default cap, not + // `DEFAULT_MAX_SCANNER_CAPACITY` above) — both fall back to the hardcoded default cap, not // to "no limit". const limit = configuredLimit > 0 - ? Math.max(configuredLimit, GO_SCANNER_START_BUF_SIZE) - : GO_DEFAULT_MAX_SCANNER_CAPACITY; - // The reported limit is the raw configured value, even below the `GO_SCANNER_START_BUF_SIZE` + ? Math.max(configuredLimit, SCANNER_START_BUF_SIZE) + : DEFAULT_MAX_SCANNER_CAPACITY; + // The reported limit is the raw configured value, even below the `SCANNER_START_BUF_SIZE` // floor (which only affects when the too-long error can fire, not the number reported), or the // hardcoded default once that's been fallen back to. - const reportedLimit = configuredLimit > 0 ? configuredLimit : GO_DEFAULT_MAX_SCANNER_CAPACITY; + const reportedLimit = configuredLimit > 0 ? configuredLimit : DEFAULT_MAX_SCANNER_CAPACITY; let emitted = 0; let lastRaw = ""; for (const token of splitSqlTokens(content)) { @@ -292,7 +292,7 @@ export const checkScannerBufferSize = ( const suggestion = `Try setting SUPABASE_SCANNER_BUFFER_SIZE=5MB (current size is ${Math.floor(reportedLimit / 1024)}KB)`; return Effect.fail( mapError( - `bufio.Scanner: token too long\nAfter statement ${emitted}: ${lastRaw}\n${suggestion}`, + `scanner: token too long\nAfter statement ${emitted}: ${lastRaw}\n${suggestion}`, "read", ), ); diff --git a/apps/cli/src/command-internal/migration-apply.unit.test.ts b/apps/cli/src/command-internal/migration-apply.unit.test.ts index e1b33615b0..552fc8e478 100644 --- a/apps/cli/src/command-internal/migration-apply.unit.test.ts +++ b/apps/cli/src/command-internal/migration-apply.unit.test.ts @@ -292,27 +292,24 @@ describe("applyMigrationFile", () => { ); }); - it.effect( - "wraps a read failure with Go's parse-file error text (Go NewMigrationFromFile parity)", - () => { - const dir = mkdtempSync(join(tmpdir(), "apply-read-fail-")); - const missingFile = join(dir, "20240101120000_missing.sql"); - const { session } = fakeSession(); - return run(session, missingFile).pipe( - Effect.exit, - Effect.tap((exit) => - Effect.sync(() => { - expect(Exit.isFailure(exit)).toBe(true); - if (Exit.isFailure(exit)) { - const msg = JSON.stringify(exit.cause); - expect(msg).toContain("failed to open migration file: "); - } - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("wraps a read failure with the parse-file error text", () => { + const dir = mkdtempSync(join(tmpdir(), "apply-read-fail-")); + const missingFile = join(dir, "20240101120000_missing.sql"); + const { session } = fakeSession(); + return run(session, missingFile).pipe( + Effect.exit, + Effect.tap((exit) => + Effect.sync(() => { + expect(Exit.isFailure(exit)).toBe(true); + if (Exit.isFailure(exit)) { + const msg = JSON.stringify(exit.cause); + expect(msg).toContain("failed to open migration file: "); + } + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); it.effect("runs a pipeline-incompatible statement outside the surrounding transaction", () => { const dir = mkdtempSync(join(tmpdir(), "apply-")); @@ -887,7 +884,7 @@ describe("hasTransactionControl", () => { }); }); -describe("migration failure rendering (Go ExecBatch parity)", () => { +describe("migration failure rendering", () => { // Error message layout: `\n[Detail]\n[42704 hint]\n` then // `At statement: \n`. const failing = ( @@ -1029,7 +1026,7 @@ describe("markError", () => { expect(markError("abcde", 5)).toBe("abcde\n ^"); }); - it("consumes the position in UTF-8 bytes like Go, not characters", () => { + it("consumes the position in UTF-8 bytes, not characters", () => { expect(markError("héllo\nworld", 8)).toBe("héllo\nworld\n^"); expect(markError("sélect 1", 3)).toBe("sélect 1\n ^"); }); @@ -1152,7 +1149,7 @@ describe("seedGlobals", () => { describe("applySchemaFiles", () => { it.effect( - "reports a read failure with the workdir-relative path, not the absolute path used to read it (Go open supabase/... parity)", + "reports a read failure with the workdir-relative path, not the absolute path used to read it", () => { // An unreadable file (permission denied, not missing) still passes the glob's own // stat/type check, since that only needs directory execute permission, not read access to @@ -1189,7 +1186,7 @@ describe("applySchemaFiles", () => { ); it.effect( - "rejects an oversized statement when SUPABASE_SCANNER_BUFFER_SIZE is configured (Go bufio.Scanner: token too long parity)", + "rejects an oversized statement when SUPABASE_SCANNER_BUFFER_SIZE is configured", () => { const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-")); mkdirSync(join(dir, "supabase"), { recursive: true }); @@ -1215,7 +1212,7 @@ describe("applySchemaFiles", () => { expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { const msg = JSON.stringify(exit.cause); - expect(msg).toContain("bufio.Scanner: token too long"); + expect(msg).toContain("scanner: token too long"); expect(msg).toContain("After statement 1: SELECT 1;"); expect(msg).toContain("Try setting SUPABASE_SCANNER_BUFFER_SIZE=5MB"); } @@ -1233,7 +1230,7 @@ describe("applySchemaFiles", () => { ); it.effect( - "reports the last scanned RAW token in the too-long error even when it trimmed to empty (Go scanner.Text() parity, review CLI-1958)", + "reports the last scanned RAW token in the too-long error even when it trimmed to empty", () => { const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-empty-token-")); mkdirSync(join(dir, "supabase"), { recursive: true }); @@ -1257,7 +1254,7 @@ describe("applySchemaFiles", () => { expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { const msg = JSON.stringify(exit.cause); - expect(msg).toContain("bufio.Scanner: token too long"); + expect(msg).toContain("scanner: token too long"); expect(msg).toContain("After statement 0: ;"); } }).pipe( @@ -1274,7 +1271,7 @@ describe("applySchemaFiles", () => { ); it.effect( - "applies an oversized statement fine when SUPABASE_SCANNER_BUFFER_SIZE is unset (Go's default auto-grows to file size)", + "applies an oversized statement fine when SUPABASE_SCANNER_BUFFER_SIZE is unset (the buffer auto-grows to file size)", () => { const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-default-")); mkdirSync(join(dir, "supabase"), { recursive: true }); @@ -1309,7 +1306,7 @@ describe("applySchemaFiles", () => { ); it.effect( - "falls back to Go's hardcoded default cap when SUPABASE_SCANNER_BUFFER_SIZE is set but unparseable (viper parity, not '5M' == 5MiB)", + "falls back to the hardcoded default cap when SUPABASE_SCANNER_BUFFER_SIZE is set but unparseable (not '5M' == 5MiB)", () => { const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-garbage-")); mkdirSync(join(dir, "supabase"), { recursive: true }); @@ -1333,7 +1330,7 @@ describe("applySchemaFiles", () => { expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { const msg = JSON.stringify(exit.cause); - expect(msg).toContain("bufio.Scanner: token too long"); + expect(msg).toContain("scanner: token too long"); expect(msg).toContain( "Try setting SUPABASE_SCANNER_BUFFER_SIZE=5MB (current size is 256KB)", ); @@ -1351,94 +1348,84 @@ describe("applySchemaFiles", () => { }, ); - it.effect( - "accepts a hex-literal SUPABASE_SCANNER_BUFFER_SIZE (Go strconv.ParseInt base-0 parity, review CLI-1958)", - () => { - const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-hex-")); - mkdirSync(join(dir, "supabase"), { recursive: true }); - const file = join(dir, "supabase", "big.sql"); - writeFileSync(file, `SELECT 1;\nSELECT '${"a".repeat(5116)}';\n`); - const { session } = fakeSession(); - const previous = process.env["SUPABASE_SCANNER_BUFFER_SIZE"]; - process.env["SUPABASE_SCANNER_BUFFER_SIZE"] = "0x1400"; - return Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem; - const path = yield* Path.Path; - const exit = yield* applySchemaFiles( - session, - fs, - path, - dir, - ["supabase/big.sql"], - (message, suggestion) => - new TestError({ message: suggestion ? `${message} (${suggestion})` : message }), - ).pipe(Effect.exit); - expect(Exit.isFailure(exit)).toBe(true); - if (Exit.isFailure(exit)) { - const msg = JSON.stringify(exit.cause); - expect(msg).toContain("bufio.Scanner: token too long"); - expect(msg).toContain( - "Try setting SUPABASE_SCANNER_BUFFER_SIZE=5MB (current size is 5KB)", - ); - } - }).pipe( - Effect.ensuring( - Effect.sync(() => { - if (previous === undefined) delete process.env["SUPABASE_SCANNER_BUFFER_SIZE"]; - else process.env["SUPABASE_SCANNER_BUFFER_SIZE"] = previous; - rmSync(dir, { recursive: true, force: true }); - }), - ), - Effect.provide(BunServices.layer), - ); - }, - ); + it.effect("accepts a hex-literal SUPABASE_SCANNER_BUFFER_SIZE", () => { + const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-hex-")); + mkdirSync(join(dir, "supabase"), { recursive: true }); + const file = join(dir, "supabase", "big.sql"); + writeFileSync(file, `SELECT 1;\nSELECT '${"a".repeat(5116)}';\n`); + const { session } = fakeSession(); + const previous = process.env["SUPABASE_SCANNER_BUFFER_SIZE"]; + process.env["SUPABASE_SCANNER_BUFFER_SIZE"] = "0x1400"; + return Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const exit = yield* applySchemaFiles( + session, + fs, + path, + dir, + ["supabase/big.sql"], + (message, suggestion) => + new TestError({ message: suggestion ? `${message} (${suggestion})` : message }), + ).pipe(Effect.exit); + expect(Exit.isFailure(exit)).toBe(true); + if (Exit.isFailure(exit)) { + const msg = JSON.stringify(exit.cause); + expect(msg).toContain("scanner: token too long"); + expect(msg).toContain("Try setting SUPABASE_SCANNER_BUFFER_SIZE=5MB (current size is 5KB)"); + } + }).pipe( + Effect.ensuring( + Effect.sync(() => { + if (previous === undefined) delete process.env["SUPABASE_SCANNER_BUFFER_SIZE"]; + else process.env["SUPABASE_SCANNER_BUFFER_SIZE"] = previous; + rmSync(dir, { recursive: true, force: true }); + }), + ), + Effect.provide(BunServices.layer), + ); + }); - it.effect( - "accepts underscore digit separators in a decimal SUPABASE_SCANNER_BUFFER_SIZE (Go strconv.ParseInt base-0 underscore-literal parity, review CLI-1958)", - () => { - const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-underscore-")); - mkdirSync(join(dir, "supabase"), { recursive: true }); - const file = join(dir, "supabase", "big.sql"); - writeFileSync(file, `SELECT 1;\nSELECT '${"a".repeat(5116)}';\n`); - const { session } = fakeSession(); - const previous = process.env["SUPABASE_SCANNER_BUFFER_SIZE"]; - process.env["SUPABASE_SCANNER_BUFFER_SIZE"] = "5_120"; - return Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem; - const path = yield* Path.Path; - const exit = yield* applySchemaFiles( - session, - fs, - path, - dir, - ["supabase/big.sql"], - (message, suggestion) => - new TestError({ message: suggestion ? `${message} (${suggestion})` : message }), - ).pipe(Effect.exit); - expect(Exit.isFailure(exit)).toBe(true); - if (Exit.isFailure(exit)) { - const msg = JSON.stringify(exit.cause); - expect(msg).toContain("bufio.Scanner: token too long"); - expect(msg).toContain( - "Try setting SUPABASE_SCANNER_BUFFER_SIZE=5MB (current size is 5KB)", - ); - } - }).pipe( - Effect.ensuring( - Effect.sync(() => { - if (previous === undefined) delete process.env["SUPABASE_SCANNER_BUFFER_SIZE"]; - else process.env["SUPABASE_SCANNER_BUFFER_SIZE"] = previous; - rmSync(dir, { recursive: true, force: true }); - }), - ), - Effect.provide(BunServices.layer), - ); - }, - ); + it.effect("accepts underscore digit separators in a decimal SUPABASE_SCANNER_BUFFER_SIZE", () => { + const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-underscore-")); + mkdirSync(join(dir, "supabase"), { recursive: true }); + const file = join(dir, "supabase", "big.sql"); + writeFileSync(file, `SELECT 1;\nSELECT '${"a".repeat(5116)}';\n`); + const { session } = fakeSession(); + const previous = process.env["SUPABASE_SCANNER_BUFFER_SIZE"]; + process.env["SUPABASE_SCANNER_BUFFER_SIZE"] = "5_120"; + return Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const exit = yield* applySchemaFiles( + session, + fs, + path, + dir, + ["supabase/big.sql"], + (message, suggestion) => + new TestError({ message: suggestion ? `${message} (${suggestion})` : message }), + ).pipe(Effect.exit); + expect(Exit.isFailure(exit)).toBe(true); + if (Exit.isFailure(exit)) { + const msg = JSON.stringify(exit.cause); + expect(msg).toContain("scanner: token too long"); + expect(msg).toContain("Try setting SUPABASE_SCANNER_BUFFER_SIZE=5MB (current size is 5KB)"); + } + }).pipe( + Effect.ensuring( + Effect.sync(() => { + if (previous === undefined) delete process.env["SUPABASE_SCANNER_BUFFER_SIZE"]; + else process.env["SUPABASE_SCANNER_BUFFER_SIZE"] = previous; + rmSync(dir, { recursive: true, force: true }); + }), + ), + Effect.provide(BunServices.layer), + ); + }); it.effect( - "rejects an invalid underscore placement in SUPABASE_SCANNER_BUFFER_SIZE, unlike a valid digit separator (Go strconv.ParseInt underscore-grammar parity, review CLI-1958)", + "rejects an invalid underscore placement in SUPABASE_SCANNER_BUFFER_SIZE, unlike a valid digit separator", () => { const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-bad-underscore-")); mkdirSync(join(dir, "supabase"), { recursive: true }); @@ -1474,7 +1461,7 @@ describe("applySchemaFiles", () => { ); it.effect( - "falls back to Go's hardcoded default cap when SUPABASE_SCANNER_BUFFER_SIZE overflows Go's signed int range (strconv.ParseInt/cast.ToInt range-error parity, review CLI-1958)", + "falls back to the hardcoded default cap when SUPABASE_SCANNER_BUFFER_SIZE overflows the signed int range", () => { const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-int64-overflow-")); mkdirSync(join(dir, "supabase"), { recursive: true }); @@ -1498,7 +1485,7 @@ describe("applySchemaFiles", () => { expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { const msg = JSON.stringify(exit.cause); - expect(msg).toContain("bufio.Scanner: token too long"); + expect(msg).toContain("scanner: token too long"); expect(msg).toContain( "Try setting SUPABASE_SCANNER_BUFFER_SIZE=5MB (current size is 256KB)", ); @@ -1517,7 +1504,7 @@ describe("applySchemaFiles", () => { ); it.effect( - "still accepts the exact int64 boundary magnitudes for SUPABASE_SCANNER_BUFFER_SIZE (Go strconv.ParseInt range-boundary parity, review CLI-1958)", + "still accepts the exact int64 boundary magnitudes for SUPABASE_SCANNER_BUFFER_SIZE", () => { const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-int64-boundary-")); mkdirSync(join(dir, "supabase"), { recursive: true }); @@ -1553,7 +1540,7 @@ describe("applySchemaFiles", () => { ); it.effect( - "rejects an oversized statement when SUPABASE_SCANNER_BUFFER_SIZE is set only in the project env (Go loadNestedEnv parity)", + "rejects an oversized statement when SUPABASE_SCANNER_BUFFER_SIZE is set only in the project env", () => { const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-projectenv-")); mkdirSync(join(dir, "supabase"), { recursive: true }); @@ -1578,7 +1565,7 @@ describe("applySchemaFiles", () => { expect(Exit.isFailure(exit)).toBe(true); if (Exit.isFailure(exit)) { const msg = JSON.stringify(exit.cause); - expect(msg).toContain("bufio.Scanner: token too long"); + expect(msg).toContain("scanner: token too long"); } }).pipe( Effect.ensuring( @@ -1592,44 +1579,41 @@ describe("applySchemaFiles", () => { }, ); - it.effect( - "shell env still wins over the project env for SUPABASE_SCANNER_BUFFER_SIZE (Go godotenv 'never overrides' parity)", - () => { - const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-shellwins-")); - mkdirSync(join(dir, "supabase"), { recursive: true }); - const file = join(dir, "supabase", "big.sql"); - writeFileSync(file, `SELECT '${"a".repeat(5000)}';\n`); - const { session, calls } = fakeSession(); - const previous = process.env["SUPABASE_SCANNER_BUFFER_SIZE"]; - // "0" is treated as unset (no check); the shell value must still win over the project - // env's tiny limit. - process.env["SUPABASE_SCANNER_BUFFER_SIZE"] = "0"; - return Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem; - const path = yield* Path.Path; - yield* applySchemaFiles( - session, - fs, - path, - dir, - ["supabase/big.sql"], - (message, suggestion) => - new TestError({ message: suggestion ? `${message} (${suggestion})` : message }), - { SUPABASE_SCANNER_BUFFER_SIZE: "100b" }, - ); - expect(executedSql(calls).some((sql) => sql.startsWith("SELECT 'a"))).toBe(true); - }).pipe( - Effect.ensuring( - Effect.sync(() => { - if (previous === undefined) delete process.env["SUPABASE_SCANNER_BUFFER_SIZE"]; - else process.env["SUPABASE_SCANNER_BUFFER_SIZE"] = previous; - rmSync(dir, { recursive: true, force: true }); - }), - ), - Effect.provide(BunServices.layer), + it.effect("shell env still wins over the project env for SUPABASE_SCANNER_BUFFER_SIZE", () => { + const dir = mkdtempSync(join(tmpdir(), "schema-files-scanner-shellwins-")); + mkdirSync(join(dir, "supabase"), { recursive: true }); + const file = join(dir, "supabase", "big.sql"); + writeFileSync(file, `SELECT '${"a".repeat(5000)}';\n`); + const { session, calls } = fakeSession(); + const previous = process.env["SUPABASE_SCANNER_BUFFER_SIZE"]; + // "0" is treated as unset (no check); the shell value must still win over the project + // env's tiny limit. + process.env["SUPABASE_SCANNER_BUFFER_SIZE"] = "0"; + return Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + yield* applySchemaFiles( + session, + fs, + path, + dir, + ["supabase/big.sql"], + (message, suggestion) => + new TestError({ message: suggestion ? `${message} (${suggestion})` : message }), + { SUPABASE_SCANNER_BUFFER_SIZE: "100b" }, ); - }, - ); + expect(executedSql(calls).some((sql) => sql.startsWith("SELECT 'a"))).toBe(true); + }).pipe( + Effect.ensuring( + Effect.sync(() => { + if (previous === undefined) delete process.env["SUPABASE_SCANNER_BUFFER_SIZE"]; + else process.env["SUPABASE_SCANNER_BUFFER_SIZE"] = previous; + rmSync(dir, { recursive: true, force: true }); + }), + ), + Effect.provide(BunServices.layer), + ); + }); }); describe("revertsToLoginRole", () => { diff --git a/apps/cli/src/command-internal/migration-history.unit.test.ts b/apps/cli/src/command-internal/migration-history.unit.test.ts index 944292147d..cd147689ae 100644 --- a/apps/cli/src/command-internal/migration-history.unit.test.ts +++ b/apps/cli/src/command-internal/migration-history.unit.test.ts @@ -69,24 +69,24 @@ describe("reconcileMigrations", () => { }); }); - it("skips empty / whitespace versions (matches strconv.Atoi, not Number())", () => { + it("skips empty / whitespace versions (not Number() coercion)", () => { expect(reconcileMigrations(["", "20240101000000"], [" ", "20240101000000"])).toEqual({ kind: "in-sync", }); }); - it("treats a version within Go's int64 range as a real conflict (BigInt parity)", () => { + it("treats a version within the int64 range as a real conflict", () => { expect(reconcileMigrations(["9999999999999999"], []).kind).toBe("conflict"); }); - it("skips a version beyond Go's int64 range instead of hanging the scan", () => { + it("skips a version beyond the int64 range instead of hanging the scan", () => { expect( reconcileMigrations(["20240101000000", "9999999999999999999"], ["20240101000000"]), ).toEqual({ kind: "in-sync" }); }); }); -describe("listRemoteMigrations (suppress only undefined_table, like Go)", () => { +describe("listRemoteMigrations (suppress only undefined_table)", () => { const run = (error: DbExecError) => Effect.runPromiseExit(listRemoteMigrations(failingSession(error))); @@ -122,7 +122,7 @@ describe("listRemoteMigrations (suppress only undefined_table, like Go)", () => }); }); -describe("findPendingMigrations (Go TestPendingMigrations / TestIgnoreVersionMismatch)", () => { +describe("findPendingMigrations", () => { it("returns the local paths after the remote count when in sync", () => { const local = ["0", "1", "2"].map(mig); const result = findPendingMigrations(local, ["0"]); @@ -185,7 +185,7 @@ describe("suggestRevertHistory", () => { }); }); -describe("resolveMigrationFile (byte-ordered match, Go's sort.Strings via afero match.go:91)", () => { +describe("resolveMigrationFile (byte-ordered match)", () => { it("picks the UTF-8-byte-first match, not JS's default UTF-16 code-unit order", async () => { // A supplementary-plane character (U+1F600, a UTF-16 surrogate pair) alongside a BMP // private-use character (U+E000): JS's default `.sort()` ranks the surrogate pair first — diff --git a/apps/cli/src/command-internal/migration-timestamp.format.unit.test.ts b/apps/cli/src/command-internal/migration-timestamp.format.unit.test.ts index 76f8b6be16..d94efe30b9 100644 --- a/apps/cli/src/command-internal/migration-timestamp.format.unit.test.ts +++ b/apps/cli/src/command-internal/migration-timestamp.format.unit.test.ts @@ -11,7 +11,7 @@ describe("formatTimestampVersion", () => { expect(formatTimestampVersion("20220727064246")).toBe("2022-07-27 06:42:46"); }); - it("passes through short numeric versions (Go time.Parse errors → input)", () => { + it("passes through short numeric versions (parse errors → input)", () => { expect(formatTimestampVersion("0")).toBe("0"); expect(formatTimestampVersion("1")).toBe("1"); }); @@ -20,7 +20,7 @@ describe("formatTimestampVersion", () => { expect(formatTimestampVersion("abc")).toBe("abc"); }); - it("passes through out-of-range months/days like Go's strict parse", () => { + it("passes through out-of-range months/days", () => { expect(formatTimestampVersion("20221327064246")).toBe("20221327064246"); // month 13 expect(formatTimestampVersion("20220230000000")).toBe("20220230000000"); // Feb 30 expect(formatTimestampVersion("20220727256046")).toBe("20220727256046"); // hour 25 @@ -37,7 +37,7 @@ describe("parseMigrationVersion", () => { expect(parseMigrationVersion("0")).toBe(0n); }); - it("rejects non-integer versions like Go's strconv.Atoi", () => { + it("rejects non-integer versions", () => { expect(parseMigrationVersion("")).toBeUndefined(); expect(parseMigrationVersion("abc")).toBeUndefined(); expect(parseMigrationVersion(" 12")).toBeUndefined(); @@ -46,17 +46,17 @@ describe("parseMigrationVersion", () => { expect(parseMigrationVersion("+")).toBeUndefined(); }); - it("accepts a leading +/- sign like strconv.Atoi (for malformed history rows)", () => { - // Go's `Atoi` == `ParseInt(s, 10, 0)` accepts a leading sign, so `migration repair -1` + it("accepts a leading +/- sign (for malformed history rows)", () => { + // A leading sign is accepted, so `migration repair -1` // can delete a malformed `-1` row and the two-pointer merge orders `-1` before `0`. expect(parseMigrationVersion("-1")).toBe(-1n); expect(parseMigrationVersion("+5")).toBe(5n); }); it("accepts the full int64 range but rejects values outside it", () => { - // 16 digits Go accepts and surfaces as a conflict — must NOT be skipped. + // 16 digits parse and surface as a conflict — must NOT be skipped. expect(parseMigrationVersion("9999999999999999")).toBe(9999999999999999n); - // int64 max/min parse; one beyond either bound is rejected (Go's Atoi range error). + // int64 max/min parse; one beyond either bound is rejected. expect(parseMigrationVersion(MIGRATION_VERSION_MAX.toString())).toBe(MIGRATION_VERSION_MAX); expect(parseMigrationVersion((MIGRATION_VERSION_MAX + 1n).toString())).toBeUndefined(); expect(parseMigrationVersion("-9223372036854775808")).toBe(-9223372036854775808n); diff --git a/apps/cli/src/command-internal/go-output-flag.ts b/apps/cli/src/command-internal/output-formats.ts similarity index 89% rename from apps/cli/src/command-internal/go-output-flag.ts rename to apps/cli/src/command-internal/output-formats.ts index 8e6ad00f44..52d4e85d8b 100644 --- a/apps/cli/src/command-internal/go-output-flag.ts +++ b/apps/cli/src/command-internal/output-formats.ts @@ -20,7 +20,7 @@ export const QUERY_OUTPUT_FORMATS = ["json", "table", "csv"] as const; /** * Raised when `-o`/`--output` carries a value the active command doesn't accept. The message - * matches the established pflag rejection format: `invalid argument %q for %q flag: %v`. + * has the form `invalid argument "" for "" flag: `. */ export class InvalidOutputFormatError extends Data.TaggedError("InvalidOutputFormatError")<{ readonly message: string; @@ -36,8 +36,8 @@ export function outputFormatEnumMessage(allowed: ReadonlyArray): string } /** - * The full rejection message for an invalid `-o` value, matching the established - * `invalid argument %q for %q flag: %v` pflag format. + * The full rejection message for an invalid `-o` value, in the + * `invalid argument "" for "" flag: ` format. */ export function invalidOutputFormatMessage(value: string, allowed: ReadonlyArray): string { return `invalid argument "${value}" for "-o, --output" flag: ${outputFormatEnumMessage(allowed)}`; diff --git a/apps/cli/src/command-internal/go-output-flag.unit.test.ts b/apps/cli/src/command-internal/output-formats.unit.test.ts similarity index 84% rename from apps/cli/src/command-internal/go-output-flag.unit.test.ts rename to apps/cli/src/command-internal/output-formats.unit.test.ts index 41b440f43f..d39cc59273 100644 --- a/apps/cli/src/command-internal/go-output-flag.unit.test.ts +++ b/apps/cli/src/command-internal/output-formats.unit.test.ts @@ -5,10 +5,10 @@ import { invalidOutputFormatMessage, outputFormatEnumMessage, unsupportedOutputFlagMessage, -} from "./go-output-flag.ts"; +} from "./output-formats.ts"; -describe("go-output-flag", () => { - it("joins the allowed set with Go's ` | ` bracket format", () => { +describe("output-formats", () => { + it("joins the allowed set with the ` | ` bracket format", () => { expect(outputFormatEnumMessage(RESOURCE_OUTPUT_FORMATS)).toBe( "must be one of [ env | pretty | json | toml | yaml ]", ); @@ -17,8 +17,7 @@ describe("go-output-flag", () => { ); }); - it("reproduces Go's pflag rejection message byte-for-byte", () => { - // pflag: `invalid argument %q for %q flag: %v`, shorthand-prefixed `-o, --output`. + it("formats the rejection message with the shorthand-prefixed flag name", () => { expect(invalidOutputFormatMessage("table", RESOURCE_OUTPUT_FORMATS)).toBe( 'invalid argument "table" for "-o, --output" flag: must be one of [ env | pretty | json | toml | yaml ]', ); diff --git a/apps/cli/src/command-internal/go-output.encoders.ts b/apps/cli/src/command-internal/output.encoders.ts similarity index 79% rename from apps/cli/src/command-internal/go-output.encoders.ts rename to apps/cli/src/command-internal/output.encoders.ts index 935e0c48b9..4bef8f0e48 100644 --- a/apps/cli/src/command-internal/go-output.encoders.ts +++ b/apps/cli/src/command-internal/output.encoders.ts @@ -1,17 +1,17 @@ import { stringify as stringifyToml } from "smol-toml"; import { stringify as stringifyYaml } from "yaml"; -import { encodeGoJsonCompact, encodeGoJsonIndented } from "./go-json.ts"; -import { goStringCompare } from "./go-struct-output.encoders.ts"; +import { encodeHtmlSafeJsonCompact, encodeHtmlSafeJsonIndented } from "./html-safe-json.ts"; +import { compareByCodepoint } from "./struct-output.encoders.ts"; /** - * Reproduces `json.Encoder` output for `-o json`: alphabetical key order, Go string escaping, and + * Encodes `-o json` output: alphabetical key order, HTML-safe string escaping, and * a trailing newline. * * `nullForEmptyArrays` re-substitutes `null` for an empty array at the listed keys, for a schema * that decodes both `null` and `[]` to `[]` upstream (e.g. `backups list`'s `"backups": null`). */ -export function encodeGoJson( +export function encodeSortedJson( value: T, options?: { readonly nullForEmptyArrays?: ReadonlyArray }, ): string { @@ -33,24 +33,24 @@ export function encodeGoJson( } source = patched; } - return encodeGoJsonIndented(sortKeysDeep(source)); + return encodeHtmlSafeJsonIndented(sortKeysDeep(source)); } function sortKeysDeep(value: unknown): unknown { if (Array.isArray(value)) return value.map(sortKeysDeep); if (value === null || typeof value !== "object") return value; // A plain object reorders integer-like string keys ("2", "10") into ascending numeric order on - // enumeration; building a `Map` instead carries a lexicographic sort through to `go-json.ts`'s + // enumeration; building a `Map` instead carries a lexicographic sort through to `html-safe-json.ts`'s // `walk` intact, since `Map` iteration order is true insertion order. const sorted = new Map(); - // JS's default string sort orders by UTF-16 code unit, which diverges from Go's byte/codepoint + // JS's default string sort orders by UTF-16 code unit, which diverges from byte/codepoint // order once an astral character (a UTF-16 surrogate pair) meets a high-BMP one. - // `goStringCompare` reproduces Go's real map-key order instead — this matters here since `gen + // `compareByCodepoint` sorts by byte/codepoint order instead — this matters here since `gen // bearer-jwt`'s custom claims flow through this same sort before signing. - for (const key of Object.keys(value as Record).sort(goStringCompare)) { + for (const key of Object.keys(value as Record).sort(compareByCodepoint)) { const child = (value as Record)[key]; - // Drop `undefined` properties here, matching `JSON.stringify`'s behavior (the Go-faithful - // walker below would otherwise render them as `null`). + // Drop `undefined` properties here, matching `JSON.stringify`'s behavior (the walker + // below would otherwise render them as `null`). if (child === undefined) continue; sorted.set(key, sortKeysDeep(child)); } @@ -58,29 +58,29 @@ function sortKeysDeep(value: unknown): unknown { } /** - * Serializes an outbound API request body with sorted keys, Go string escaping, no indentation, - * and no trailing newline, matching `json.Marshal`'s struct output. + * Serializes an outbound API request body with sorted keys, HTML-safe string escaping, no + * indentation, and no trailing newline. * * Used on the raw-HTTP code path (`sso add`/`sso update`) whose request bodies the cli-e2e replay * server compares by string equality against recorded bodies, so key order and escaping must - * match exactly. {@link encodeGoJson} is the parallel for human-facing `--output json`. + * match exactly. {@link encodeSortedJson} is the parallel for human-facing `--output json`. */ -export function encodeGoStructJsonBody(value: unknown): string { - return encodeGoJsonCompact(sortKeysDeep(value)); +export function encodeSortedJsonBody(value: unknown): string { + return encodeHtmlSafeJsonCompact(sortKeysDeep(value)); } /** * YAML for map payloads (`branches get` envs, `sso info`, `status`, `postgres-config`). Struct - * payloads must use `encodeGoYaml` in `go-struct-output.encoders.ts` instead, since Go's yaml.v3 - * derives keys from Go field names, not JSON tags. + * payloads must use `encodeStructYaml` in `struct-output.encoders.ts` instead, since YAML keys + * derive from struct field names, not JSON tags. */ export function encodeYaml(value: unknown): string { return stringifyYaml(value); } /** - * TOML for map payloads. Struct payloads must use `encodeGoToml` in - * `go-struct-output.encoders.ts` instead, since BurntSushi emits PascalCase Go field names with + * TOML for map payloads. Struct payloads must use `encodeStructToml` in + * `struct-output.encoders.ts` instead, since the struct output uses PascalCase field names with * 2-space table indentation. */ export function encodeToml(value: unknown): string { @@ -92,7 +92,7 @@ export function encodeToml(value: unknown): string { } /** - * Reproduces the established `godotenv.Marshal` byte shape for `--output env`. + * Emits the established dotenv byte shape for `--output env`. * * Nested maps flatten to dotted paths, then uppercase with `.` replaced by `_`. Flattening does * not descend into slices: an array value becomes a single empty-string leaf. Integer-parseable diff --git a/apps/cli/src/command-internal/go-output.encoders.unit.test.ts b/apps/cli/src/command-internal/output.encoders.unit.test.ts similarity index 81% rename from apps/cli/src/command-internal/go-output.encoders.unit.test.ts rename to apps/cli/src/command-internal/output.encoders.unit.test.ts index 98738ed362..f43e55018f 100644 --- a/apps/cli/src/command-internal/go-output.encoders.unit.test.ts +++ b/apps/cli/src/command-internal/output.encoders.unit.test.ts @@ -1,7 +1,7 @@ import { V1ListAllBackupsOutput } from "@supabase/api/effect"; import { describe, expect, it } from "vitest"; -import { encodeEnv, encodeGoJson, encodeToml, encodeYaml } from "./go-output.encoders.ts"; +import { encodeEnv, encodeSortedJson, encodeToml, encodeYaml } from "./output.encoders.ts"; // Shaped like the backups response because the `nullForEmptyArrays` byte-parity assertions were // extracted from that port; see the `{ items, name }` fixtures below for plain-object coverage. @@ -23,9 +23,9 @@ const SAMPLE_RESPONSE: typeof V1ListAllBackupsOutput.Type = { }, }; -describe("encodeGoJson", () => { - it("emits Go's alphabetical struct-field order and trailing newline for a populated response", () => { - const out = encodeGoJson(SAMPLE_RESPONSE, { nullForEmptyArrays: ["backups"] }); +describe("encodeSortedJson", () => { + it("emits alphabetical struct-field order and trailing newline for a populated response", () => { + const out = encodeSortedJson(SAMPLE_RESPONSE, { nullForEmptyArrays: ["backups"] }); expect(out).toBe( `{ "backups": [ @@ -49,7 +49,7 @@ describe("encodeGoJson", () => { }); it("emits backups: null and an empty physical_backup_data object for a PITR-only response", () => { - const out = encodeGoJson( + const out = encodeSortedJson( { region: "ap-southeast-1", walg_enabled: false, @@ -72,7 +72,7 @@ describe("encodeGoJson", () => { }); it("leaves arrays intact when nullForEmptyArrays is not provided", () => { - const out = encodeGoJson({ items: [], name: "x" }); + const out = encodeSortedJson({ items: [], name: "x" }); expect(out).toBe( `{ "items": [], @@ -83,7 +83,7 @@ describe("encodeGoJson", () => { }); it("does not substitute null for non-empty arrays even when listed in nullForEmptyArrays", () => { - const out = encodeGoJson({ items: [1, 2], name: "x" }, { nullForEmptyArrays: ["items"] }); + const out = encodeSortedJson({ items: [1, 2], name: "x" }, { nullForEmptyArrays: ["items"] }); expect(out).toBe( `{ "items": [ @@ -96,8 +96,8 @@ describe("encodeGoJson", () => { ); }); - it("keeps Go's true lexicographic order for numeric-looking keys (CLI-1961 Codex review finding)", () => { - const out = encodeGoJson({ 10: "a", 2: "b", role: "anon" }); + it("keeps true lexicographic order for numeric-looking keys", () => { + const out = encodeSortedJson({ 10: "a", 2: "b", role: "anon" }); expect(out).toBe( `{ "10": "a", @@ -108,10 +108,10 @@ describe("encodeGoJson", () => { ); }); - it("sorts keys by Go's byte/code-point order, not JS's UTF-16 code-unit order (CLI-1961 Codex review finding)", () => { + it("sorts keys by byte/code-point order, not JS's UTF-16 code-unit order", () => { const highBmp = String.fromCodePoint(0xe000); const astral = String.fromCodePoint(0x10000); - const out = encodeGoJson({ [astral]: 2, [highBmp]: 1 }); + const out = encodeSortedJson({ [astral]: 2, [highBmp]: 1 }); expect(out).toBe(`{\n "${highBmp}": 1,\n "${astral}": 2\n}\n`); }); }); @@ -145,21 +145,21 @@ describe("encodeEnv", () => { expect(lines).toContain('PITR_ENABLED="true"'); }); - it("emits integer-parseable values unquoted (matches godotenv strconv.Atoi branch)", () => { + it("emits integer-parseable values unquoted", () => { const out = encodeEnv(SAMPLE_RESPONSE); const lines = out.split("\n"); expect(lines).toContain("PHYSICAL_BACKUP_DATA_EARLIEST_PHYSICAL_BACKUP_DATE_UNIX=1700000000"); expect(lines).toContain("PHYSICAL_BACKUP_DATA_LATEST_PHYSICAL_BACKUP_DATE_UNIX=1700001000"); }); - it("collapses arrays to a single empty leaf (Go viper does not descend into slices)", () => { + it("collapses arrays to a single empty leaf", () => { const out = encodeEnv(SAMPLE_RESPONSE); const lines = out.split("\n"); expect(lines).toContain('BACKUPS=""'); expect(lines.some((line) => line.startsWith("BACKUPS_0_"))).toBe(false); }); - it("matches Go's full env output for the sample backup response", () => { + it("emits the full env output for the sample backup response", () => { expect(encodeEnv(SAMPLE_RESPONSE)).toBe( [ 'BACKUPS=""', @@ -177,7 +177,7 @@ describe("encodeEnv", () => { expect(out).toBe('MESSAGE="with \\"quotes\\" and \\\\backslash"'); }); - it("escapes embedded newlines, carriage returns, and tabs (Go %q parity)", () => { + it("escapes embedded newlines, carriage returns, and tabs", () => { const out = encodeEnv({ description: "line one\nline two\rwith\ttab" }); expect(out).toBe('DESCRIPTION="line one\\nline two\\rwith\\ttab"'); }); @@ -187,11 +187,11 @@ describe("encodeEnv", () => { expect(out.split("\n")).toEqual(["A=2", "M=3", "Z=1"]); }); - it("omits empty nested maps entirely (Go viper parity)", () => { + it("omits empty nested maps entirely", () => { expect(encodeEnv({ physical_backup_data: {} })).toBe(""); }); - it("matches Go for the PITR-only response shape with empty physical_backup_data", () => { + it("handles the PITR-only response shape with empty physical_backup_data", () => { expect( encodeEnv({ region: "ap-southeast-1", diff --git a/apps/cli/src/command-internal/parse-uint.ts b/apps/cli/src/command-internal/parse-uint.ts index e70a46761b..b644e664a8 100644 --- a/apps/cli/src/command-internal/parse-uint.ts +++ b/apps/cli/src/command-internal/parse-uint.ts @@ -1,5 +1,5 @@ /** - * Parses a numeric flag token the way `pflag`'s `uint64`/`int64` types do: base-0 prefixes + * Parses a numeric flag token as a `uint64`/`int64`: base-0 prefixes * (`0x`, `0o`/leading `0`, `0b`), underscore digit separators, and strict int64/uint64 range * checks, rejecting any sign on an unsigned value (including `-0`, which a naive numeric parse * would treat as non-negative) and preserving the original token spelling. Used by shell diff --git a/apps/cli/src/command-internal/parse-uint.unit.test.ts b/apps/cli/src/command-internal/parse-uint.unit.test.ts index c110eb1e9e..f97ecd0814 100644 --- a/apps/cli/src/command-internal/parse-uint.unit.test.ts +++ b/apps/cli/src/command-internal/parse-uint.unit.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest"; import { isValidBase0Int64, parseUintBase0 } from "./parse-uint.ts"; -describe("parseUintBase0 (Go strconv.ParseUint(s, 0, 64) parity)", () => { +describe("parseUintBase0", () => { it("parses plain decimal", () => { expect(parseUintBase0("0")).toEqual({ value: 0 }); expect(parseUintBase0("1")).toEqual({ value: 1 }); @@ -16,7 +16,7 @@ describe("parseUintBase0 (Go strconv.ParseUint(s, 0, 64) parity)", () => { expect(parseUintBase0("+1")).toEqual({ cause: "invalid syntax" }); }); - it("parses Go's base-0 prefix forms: hex, octal (bare leading zero!), binary", () => { + it("parses base-0 prefix forms: hex, octal (bare leading zero!), binary", () => { expect(parseUintBase0("0x10")).toEqual({ value: 16 }); expect(parseUintBase0("0X10")).toEqual({ value: 16 }); expect(parseUintBase0("0o10")).toEqual({ value: 8 }); @@ -58,7 +58,7 @@ describe("parseUintBase0 (Go strconv.ParseUint(s, 0, 64) parity)", () => { }); }); -describe("isValidBase0Int64 (Go strconv.ParseInt(s, 0, 64) parity)", () => { +describe("isValidBase0Int64", () => { it("accepts int64's exact bounds, both signs", () => { expect(isValidBase0Int64("9223372036854775807")).toBe(true); // int64 max expect(isValidBase0Int64("-9223372036854775808")).toBe(true); // int64 min @@ -73,7 +73,7 @@ describe("isValidBase0Int64 (Go strconv.ParseInt(s, 0, 64) parity)", () => { expect(isValidBase0Int64("18446744073709551616")).toBe(false); // one past uint64 max }); - it("accepts plain decimals and Go's base-0 prefix forms, signed", () => { + it("accepts plain decimals and base-0 prefix forms, signed", () => { expect(isValidBase0Int64("0")).toBe(true); expect(isValidBase0Int64("42")).toBe(true); expect(isValidBase0Int64("-42")).toBe(true); diff --git a/apps/cli/src/command-internal/path-match.ts b/apps/cli/src/command-internal/path-match.ts index 58898ffcb1..9258219760 100644 --- a/apps/cli/src/command-internal/path-match.ts +++ b/apps/cli/src/command-internal/path-match.ts @@ -1,11 +1,11 @@ /** - * Matches a glob pattern against a path the way Go's `path.Match` does, for the seed-file - * globber's `[db.seed] sql_paths` expansion. Hand-ported rather than compiled to a `RegExp`, + * Matches a glob pattern against a path using shell-pattern semantics (`*` and `?` never cross + * `/`), for the seed-file globber's `[db.seed] sql_paths` expansion. Hand-ported rather than compiled to a `RegExp`, * since glob character classes diverge from JS regex classes and a malformed pattern must * report `badPattern` instead of being silently reinterpreted. * * Byte-wise (UTF-8), not UTF-16 code units: the `*`-retry loop can land mid-character, whose - * byte decodes as a single invalid rune that a `?` in the retried chunk can then consume. + * byte decodes as a single invalid code point that a `?` in the retried chunk can then consume. */ /** `badPattern` reports a malformed pattern instead of throwing. */ @@ -28,7 +28,7 @@ const UTF8_ENCODER = new TextEncoder(); */ type Bytes = Uint8Array; -const RUNE_ERROR = 0xfffd; +const REPLACEMENT_CHAR = 0xfffd; const SLASH = 0x2f; const STAR = 0x2a; const QUESTION = 0x3f; @@ -38,19 +38,19 @@ const CARET = 0x5e; const HYPHEN = 0x2d; const BACKSLASH = 0x5c; -interface DecodedRune { +interface DecodedCodePoint { readonly r: number; readonly size: number; } /** - * Decodes the UTF-8 rune starting at byte offset `i` of `b`. Any invalid or truncated - * sequence decodes as `(RUNE_ERROR, 1)` — never throws, and never consumes more than the + * Decodes the UTF-8 code point starting at byte offset `i` of `b`. Any invalid or truncated + * sequence decodes as `(REPLACEMENT_CHAR, 1)` — never throws, and never consumes more than the * single invalid lead byte. */ -const decodeRune = (b: Bytes, i: number): DecodedRune => { +const decodeCodePoint = (b: Bytes, i: number): DecodedCodePoint => { const n = b.length - i; - if (n <= 0) return { r: RUNE_ERROR, size: 0 }; + if (n <= 0) return { r: REPLACEMENT_CHAR, size: 0 }; const b0 = b[i]!; if (b0 < 0x80) return { r: b0, size: 1 }; let size: number; @@ -87,17 +87,17 @@ const decodeRune = (b: Bytes, i: number): DecodedRune => { } else { // 0x80-0xC1: a bare continuation byte or an overlong 2-byte lead. 0xF5-0xFF: past // the max valid lead byte. Both are invalid lead bytes. - return { r: RUNE_ERROR, size: 1 }; + return { r: REPLACEMENT_CHAR, size: 1 }; } - if (n < size) return { r: RUNE_ERROR, size: 1 }; + if (n < size) return { r: REPLACEMENT_CHAR, size: 1 }; const b1 = b[i + 1]!; - if (b1 < lo || b1 > hi) return { r: RUNE_ERROR, size: 1 }; + if (b1 < lo || b1 > hi) return { r: REPLACEMENT_CHAR, size: 1 }; if (size === 2) return { r: ((b0 & 0x1f) << 6) | (b1 & 0x3f), size: 2 }; const b2 = b[i + 2]!; - if (b2 < 0x80 || b2 > 0xbf) return { r: RUNE_ERROR, size: 1 }; + if (b2 < 0x80 || b2 > 0xbf) return { r: REPLACEMENT_CHAR, size: 1 }; if (size === 3) return { r: ((b0 & 0x0f) << 12) | ((b1 & 0x3f) << 6) | (b2 & 0x3f), size: 3 }; const b3 = b[i + 3]!; - if (b3 < 0x80 || b3 > 0xbf) return { r: RUNE_ERROR, size: 1 }; + if (b3 < 0x80 || b3 > 0xbf) return { r: REPLACEMENT_CHAR, size: 1 }; return { r: ((b0 & 0x07) << 18) | ((b1 & 0x3f) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3f), size: 4, @@ -150,9 +150,9 @@ const getEsc = (chunk: Bytes): GetEsc => { c = c.subarray(1); if (c.length === 0) return { r: 0, rest: c, bad: true }; } - const { r, size } = decodeRune(c, 0); + const { r, size } = decodeCodePoint(c, 0); // A genuinely invalid byte, not a literal (valid, 3-byte-encoded) U+FFFD character. - if (r === RUNE_ERROR && size === 1) return { r, rest: c.subarray(1), bad: true }; + if (r === REPLACEMENT_CHAR && size === 1) return { r, rest: c.subarray(1), bad: true }; const rest = c.subarray(size); return { r, rest, bad: rest.length === 0 }; }; @@ -181,7 +181,7 @@ const matchChunk = (chunkIn: Bytes, sIn: Bytes): MatchChunk => { if (op === LBRACKET) { let r = 0; if (!failed) { - const decoded = decodeRune(s, 0); + const decoded = decodeCodePoint(s, 0); r = decoded.r; s = s.subarray(decoded.size); } @@ -215,7 +215,7 @@ const matchChunk = (chunkIn: Bytes, sIn: Bytes): MatchChunk => { } else if (op === QUESTION) { if (!failed) { if (s[0] === SLASH) failed = true; - const { size } = decodeRune(s, 0); + const { size } = decodeCodePoint(s, 0); s = s.subarray(size); } chunk = chunk.subarray(1); @@ -239,8 +239,7 @@ const matchChunk = (chunkIn: Bytes, sIn: Bytes): MatchChunk => { }; /** - * Reports whether `name` matches the shell pattern `pattern`, using Go's `path.Match` - * semantics. `badPattern` is set (instead of throwing) for a malformed pattern. + * Reports whether `name` matches the shell pattern `pattern`. `badPattern` is set (instead of throwing) for a malformed pattern. */ export const pathMatch = (pattern: string, name: string): PathMatchResult => { let pat = UTF8_ENCODER.encode(pattern); diff --git a/apps/cli/src/command-internal/path-match.unit.test.ts b/apps/cli/src/command-internal/path-match.unit.test.ts index 6d0f8ab3d4..d8e60c9d41 100644 --- a/apps/cli/src/command-internal/path-match.unit.test.ts +++ b/apps/cli/src/command-internal/path-match.unit.test.ts @@ -18,7 +18,7 @@ describe("pathMatch", () => { }); }); - describe("`*` and `?` never cross `/` (Go's non-`/` rule)", () => { + describe("`*` and `?` never cross `/`", () => { it.each([ ["*.sql", "sub/seed.sql", false], ["seed?.sql", "seed/.sql", false], @@ -40,14 +40,14 @@ describe("pathMatch", () => { expect(pathMatch(pattern, name).matched).toBe(expected); }); - it("treats a leading `!` as a literal class member, NOT negation (Go parity)", () => { + it("treats a leading `!` as a literal class member, NOT negation", () => { expect(pathMatch("[!a].sql", "!.sql").matched).toBe(true); expect(pathMatch("[!a].sql", "a.sql").matched).toBe(true); expect(pathMatch("[!a].sql", "b.sql").matched).toBe(false); }); }); - describe("byte-offset `*` retry against multibyte characters (Go path.Match parity)", () => { + describe("byte-offset `*` retry against multibyte characters", () => { it.each([ // U+FF01 (fullwidth `!`) is 3 UTF-8 bytes; U+1F600 (😀) is 4 UTF-8 bytes. ["*??.sql", "!.sql", true], @@ -70,7 +70,7 @@ describe("pathMatch", () => { }); }); - describe("malformed patterns report badPattern (Go's path.ErrBadPattern)", () => { + describe("malformed patterns report badPattern", () => { it.each([ "[", // unterminated class "[a", // unterminated class with member diff --git a/apps/cli/src/command-internal/pg-dump.run.ts b/apps/cli/src/command-internal/pg-dump.run.ts index 8e50a6cf6c..1bfe0ea920 100644 --- a/apps/cli/src/command-internal/pg-dump.run.ts +++ b/apps/cli/src/command-internal/pg-dump.run.ts @@ -6,7 +6,7 @@ type StackRuntimePreference = | { readonly kind: "container"; readonly engine: "docker" | "podman" }; import { NetworkIdFlag } from "./global-flags.ts"; -import { viperEnvStringWithProjectFallback } from "./viper-env.ts"; +import { supabaseEnvStringWithProjectFallback } from "./supabase-env.ts"; import { RuntimeInfo } from "../shared/runtime/runtime-info.service.ts"; import { DockerRun } from "./docker-run.service.ts"; import { DockerRunError } from "./docker-run.errors.ts"; @@ -113,7 +113,10 @@ export const dumpNetworkMode = ( ): { readonly _tag: "named"; readonly name: string } | { readonly _tag: "host" } => { if (networkId !== undefined && networkId.length > 0) return { _tag: "named", name: networkId }; if (forceHostNetwork) return { _tag: "host" }; - const envNetworkId = viperEnvStringWithProjectFallback("SUPABASE_NETWORK_ID", projectEnvValues); + const envNetworkId = supabaseEnvStringWithProjectFallback( + "SUPABASE_NETWORK_ID", + projectEnvValues, + ); return envNetworkId.length > 0 ? { _tag: "named", name: envNetworkId } : { _tag: "host" }; }; diff --git a/apps/cli/src/command-internal/pgdelta.paths.ts b/apps/cli/src/command-internal/pgdelta.paths.ts index f4f10f139c..0523e8f2b6 100644 --- a/apps/cli/src/command-internal/pgdelta.paths.ts +++ b/apps/cli/src/command-internal/pgdelta.paths.ts @@ -3,7 +3,7 @@ * * Two roots: * - {@link pgDeltaTempPath}: project-local (`supabase/.temp/pgdelta`) — catalog - * snapshots and debug bundles (Go-shared, workspace-mounted). + * snapshots and debug bundles (workspace-mounted). * - {@link shadowBaselineCacheDir}: global under `SUPABASE_HOME` — the shadow * baseline PGDATA tars, shared across worktrees with the same settings. */ diff --git a/apps/cli/src/command-internal/postgres-url.ts b/apps/cli/src/command-internal/postgres-url.ts index 02aa1b22ef..bf72a4c761 100644 --- a/apps/cli/src/command-internal/postgres-url.ts +++ b/apps/cli/src/command-internal/postgres-url.ts @@ -13,7 +13,7 @@ function isIPv6Host(host: string): boolean { * and turns space into `+` (`application/x-www-form-urlencoded` query-escaping, not * `encodeURIComponent`, which differs on space and `!*'()`). */ -function goQueryEscape(value: string): string { +function queryEscape(value: string): string { let out = ""; for (const ch of value) { if (/[A-Za-z0-9\-_.~]/.test(ch)) { @@ -74,7 +74,7 @@ export function toPostgresURL(conn: PostgresUrlInput): string { // tenant on pooler fallback. const optionsParam = conn.options !== undefined && conn.options.length > 0 - ? `&options=${goQueryEscape(conn.options)}` + ? `&options=${queryEscape(conn.options)}` : ""; // Every other runtime param (search_path, statement_timeout, …), sorted for stable // serialization. @@ -83,7 +83,7 @@ export function toPostgresURL(conn: PostgresUrlInput): string { ? "" : Object.keys(conn.runtimeParams) .sort() - .map((key) => `&${goQueryEscape(key)}=${goQueryEscape(conn.runtimeParams![key]!)}`) + .map((key) => `&${queryEscape(key)}=${queryEscape(conn.runtimeParams![key]!)}`) .join(""); return `postgresql://${userinfo}@${host}:${conn.port}/${encodeURIComponent( conn.database, diff --git a/apps/cli/src/command-internal/postgres-url.unit.test.ts b/apps/cli/src/command-internal/postgres-url.unit.test.ts index 2088bdad7c..a8a948bbca 100644 --- a/apps/cli/src/command-internal/postgres-url.unit.test.ts +++ b/apps/cli/src/command-internal/postgres-url.unit.test.ts @@ -47,14 +47,14 @@ describe("toPostgresURL", () => { }); it("appends the pooler `options` runtime param after connect_timeout", () => { - // Go's ToPostgresURL appends RuntimeParams; the Supavisor tenant routing + // The Supavisor tenant routing // `options=reference=` must reach pg-delta (`=` escaped to %3D). expect(toPostgresURL({ ...base, options: "reference=abcdefghijklmnop" })).toBe( "postgresql://postgres:postgres@127.0.0.1:54322/postgres?connect_timeout=10&options=reference%3Dabcdefghijklmnop", ); }); - it("matches Go's url.QueryEscape for options (space → +)", () => { + it("query-escapes options (space → +)", () => { expect(toPostgresURL({ ...base, options: "-c search_path=public" })).toContain( "&options=-c+search_path%3Dpublic", ); @@ -67,7 +67,7 @@ describe("toPostgresURL", () => { ); }); - it("appends every runtimeParams entry (sorted) after options, like Go ToPostgresURL", () => { + it("appends every runtimeParams entry (sorted) after options", () => { expect( toPostgresURL({ ...base, @@ -79,7 +79,7 @@ describe("toPostgresURL", () => { ); }); - it("escapes runtimeParams values like Go's url.QueryEscape", () => { + it("query-escapes runtimeParams values", () => { expect(toPostgresURL({ ...base, runtimeParams: { search_path: "a b,c" } })).toContain( "&search_path=a+b%2Cc", ); diff --git a/apps/cli/src/command-internal/profile-load.ts b/apps/cli/src/command-internal/profile-load.ts index 28a1c66c65..9cd2e8d312 100644 --- a/apps/cli/src/command-internal/profile-load.ts +++ b/apps/cli/src/command-internal/profile-load.ts @@ -64,14 +64,14 @@ export function loadProfile( }; } - // An empty token falls back to the established "no config file" search-path error. + // An empty token has no config file to read. if (token === "") { - return yield* failRead(`Config File "config" Not Found in "[]"`); + return yield* failRead("no profile config file specified"); } - const ext = goFilepathExt(token); - if (!VIPER_SUPPORTED_EXTS.has(ext)) { - return yield* failRead(`Unsupported Config Type ${JSON.stringify(ext)}`); + const ext = fileExtension(token); + if (!PROFILE_FILE_EXTS.has(ext)) { + return yield* failRead(`unsupported config file type ${JSON.stringify(ext)}`); } const content = yield* fs @@ -94,17 +94,13 @@ export function loadProfile( try { parsed = parseYaml(content); } catch (cause) { - return yield* failRead(`While parsing config: ${parseDetail(cause)}`); + return yield* failRead(`invalid config file: ${parseDetail(cause)}`); } if (parsed === null || parsed === undefined) { parsed = {}; } if (typeof parsed !== "object" || Array.isArray(parsed)) { - // A non-mapping YAML document (e.g. a scalar or list) is rejected; detail text is - // best-effort. - return yield* failRead( - `While parsing config: yaml: unmarshal errors:\n cannot unmarshal into map[string]interface {}`, - ); + return yield* failRead("invalid config file: expected a YAML mapping at the top level"); } // Configuration keys are lowercased before decoding, so `API_URL:`/`Name:` behave like // their lowercase spellings, and unknown-key errors report the lowercased form. A @@ -114,15 +110,15 @@ export function loadProfile( config[key.toLowerCase()] = value; } - // Unknown keys abort decoding, reported sorted in a padded multi-line block. + // Unknown keys abort decoding, reported sorted. const invalidKeys = Object.keys(config) .filter((key) => !PROFILE_STRUCT_KEYS.has(key)) .sort(); if (invalidKeys.length > 0) { - return yield* failDecode(`'utils.Profile' has invalid keys: ${invalidKeys.join(", ")}`); + return yield* failDecode(`unknown keys: ${invalidKeys.join(", ")}`); } - // Weak scalar decoding + per-field tag validation, in struct field order. + // Weak scalar decoding + per-field format validation, in field order. const decodeErrors: string[] = []; const validationErrors: string[] = []; const values = new Map(); @@ -131,26 +127,26 @@ export function loadProfile( const weak = weakString(raw); if (weak === undefined) { decodeErrors.push( - `'${field.goName}' expected type 'string', got unconvertible type '${goTypeName(raw)}', value: '${goValueString(raw)}'`, + `${field.key}: expected a string, got ${valueTypeName(raw)}: ${JSON.stringify(raw)}`, ); continue; } values.set(field.key, weak); if (weak === "") { if (field.required) { - validationErrors.push(validatorLine(field.goName, "required")); + validationErrors.push(`${field.key} is required`); } continue; } if (field.format !== undefined && !FORMAT_TAG_CHECKS[field.format](weak)) { - validationErrors.push(validatorLine(field.goName, field.format)); + validationErrors.push(`${field.key} ${FORMAT_TAG_DESCRIPTIONS[field.format]}`); } } if (decodeErrors.length > 0) { return yield* failDecode(decodeErrors.join("\n")); } if (validationErrors.length > 0) { - return yield* fail(padGoErrorBlock(`invalid profile: ${validationErrors.join("\n")}`)); + return yield* fail(`invalid profile:\n${validationErrors.join("\n")}`); } // All required fields passed validation above; pooler_host stays "" when absent. @@ -169,15 +165,10 @@ const fail = (message: string) => Effect.fail(new ProfileLoadError({ message })) const failRead = (detail: string) => fail(`failed to read profile: ${detail}`); /** Aggregate decode-error template: multiple failing fields render as one block. */ -const failDecode = (detail: string) => - fail( - padGoErrorBlock( - `failed to parse profile: decoding failed due to the following error(s):\n\n${detail}`, - ), - ); +const failDecode = (detail: string) => fail(`failed to parse profile:\n${detail}`); /** Recognized config file extensions, checked case-sensitively. */ -const VIPER_SUPPORTED_EXTS: ReadonlySet = new Set([ +const PROFILE_FILE_EXTS: ReadonlySet = new Set([ "json", "toml", "yaml", @@ -196,7 +187,7 @@ const VIPER_SUPPORTED_EXTS: ReadonlySet = new Set([ * Returns everything after the last `.` in the final path segment, including for dot-files * (`.yml` → `yml`), where Node's `path.extname` returns `""`. */ -function goFilepathExt(token: string): string { +function fileExtension(token: string): string { for (let i = token.length - 1; i >= 0 && token[i] !== "/"; i--) { if (token[i] === ".") { return token.slice(i + 1); @@ -222,7 +213,6 @@ type FormatTag = "http_url" | "hostname_rfc1123" | "uuid4"; interface ProfileStringField { readonly key: string; - readonly goName: string; readonly required: boolean; readonly format?: FormatTag; } @@ -232,19 +222,29 @@ interface ProfileStringField { * `regions` (a slice) is exempt from weak string decoding and never validated. */ const PROFILE_STRING_FIELDS: ReadonlyArray = [ - { key: "name", goName: "Name", required: true }, - { key: "api_url", goName: "APIURL", required: true, format: "http_url" }, - { key: "dashboard_url", goName: "DashboardURL", required: true, format: "http_url" }, - { key: "docs_url", goName: "DocsURL", required: false, format: "http_url" }, - { key: "project_host", goName: "ProjectHost", required: true, format: "hostname_rfc1123" }, - { key: "pooler_host", goName: "PoolerHost", required: false, format: "hostname_rfc1123" }, - { key: "client_id", goName: "AuthClientID", required: false, format: "uuid4" }, - { key: "studio_image", goName: "StudioImage", required: false }, + { key: "name", required: true }, + { key: "api_url", required: true, format: "http_url" }, + { key: "dashboard_url", required: true, format: "http_url" }, + { key: "docs_url", required: false, format: "http_url" }, + { + key: "project_host", + required: true, + format: "hostname_rfc1123", + }, + { + key: "pooler_host", + required: false, + format: "hostname_rfc1123", + }, + { key: "client_id", required: false, format: "uuid4" }, + { key: "studio_image", required: false }, ]; -function validatorLine(goName: string, tag: string): string { - return `Key: 'Profile.${goName}' Error:Field validation for '${goName}' failed on the '${tag}' tag`; -} +const FORMAT_TAG_DESCRIPTIONS: Record = { + http_url: "must be an http(s) URL", + hostname_rfc1123: "must be a valid hostname", + uuid4: "must be a lowercase UUID v4", +}; /** RFC 1123 hostname pattern. */ const HOSTNAME_RFC1123 = /^([a-zA-Z0-9][a-zA-Z0-9-]{0,62})(\.[a-zA-Z0-9][a-zA-Z0-9-]{0,62})*?$/; @@ -268,7 +268,7 @@ const FORMAT_TAG_CHECKS: Record boolean> = { /** * Weak string coercion: strings pass through, booleans become `"1"`/`"0"`, numbers are - * stringified, and `null`/`undefined` decode to `""`. Arrays/objects are unconvertible + * stringified, and `null`/`undefined` decode to `""`. Arrays/objects are unsupported * (`undefined`, a decode error). */ function weakString(value: unknown): string | undefined { @@ -279,34 +279,12 @@ function weakString(value: unknown): string | undefined { return undefined; } -function goTypeName(value: unknown): string { - if (Array.isArray(value)) return "[]interface {}"; - if (typeof value === "object" && value !== null) return "map[string]interface {}"; +function valueTypeName(value: unknown): string { + if (Array.isArray(value)) return "array"; + if (typeof value === "object" && value !== null) return "object"; return typeof value; } -/** Best-effort rendering of an invalid field's value for the error message. */ -function goValueString(value: unknown): string { - if (Array.isArray(value)) return `[${value.map(goValueString).join(" ")}]`; - if (typeof value === "object" && value !== null) { - const entries = Object.entries(value as Record) - .map(([key, entry]) => `${key}:${goValueString(entry)}`) - .join(" "); - return `map[${entries}]`; - } - return String(value); -} - -/** - * Pads every line (including blank ones) with trailing spaces to the longest line's width, to - * match the CLI's established multi-line error rendering exactly. - */ -export function padGoErrorBlock(message: string): string { - const lines = message.split("\n"); - const width = Math.max(...lines.map((line) => line.length)); - return lines.map((line) => line.padEnd(width)).join("\n"); -} - function parseDetail(cause: unknown): string { return cause instanceof Error ? cause.message : String(cause); } diff --git a/apps/cli/src/command-internal/profile-load.unit.test.ts b/apps/cli/src/command-internal/profile-load.unit.test.ts index d4978e6521..4cdd1c4e5e 100644 --- a/apps/cli/src/command-internal/profile-load.unit.test.ts +++ b/apps/cli/src/command-internal/profile-load.unit.test.ts @@ -6,7 +6,7 @@ import { BunServices } from "@effect/platform-bun"; import { afterAll, describe, expect, it } from "@effect/vitest"; import { Effect, FileSystem } from "effect"; -import { loadProfile, padGoErrorBlock, type ProfileLoadError } from "./profile-load.ts"; +import { loadProfile, type ProfileLoadError } from "./profile-load.ts"; const tempRoot = mkdtempSync(join(tmpdir(), "supabase-profile-load-")); afterAll(() => rmSync(tempRoot, { recursive: true, force: true })); @@ -30,7 +30,7 @@ const writeProfile = (name: string, content: string): string => { }; describe("loadProfile", () => { - it.effect("resolves built-in profile names case-insensitively (Go strings.EqualFold)", () => + it.effect("resolves built-in profile names case-insensitively", () => Effect.gen(function* () { expect(yield* load("SUPABASE-LOCAL")).toBe("http://localhost:8080"); expect(yield* load("supabase")).toBe("https://api.supabase.com"); @@ -39,11 +39,9 @@ describe("loadProfile", () => { }), ); - it.effect("fails on an empty token with viper's search-mode error (Go `--profile=`)", () => + it.effect("fails an empty --profile= token with the search-mode error", () => Effect.gen(function* () { - expect(yield* loadError("")).toBe( - `failed to read profile: Config File "config" Not Found in "[]"`, - ); + expect(yield* loadError("")).toBe(`failed to read profile: no profile config file specified`); }), ); @@ -51,15 +49,15 @@ describe("loadProfile", () => { Effect.gen(function* () { // --profile --metadata-url … expect(yield* loadError("--metadata-url")).toBe( - `failed to read profile: Unsupported Config Type ""`, + `failed to read profile: unsupported config file type ""`, ); expect(yield* loadError("profile.txt")).toBe( - `failed to read profile: Unsupported Config Type "txt"`, + `failed to read profile: unsupported config file type "txt"`, ); }), ); - it.effect("uses Go filepath.Ext semantics for dot-files (`.yml` IS extension `yml`)", () => + it.effect("treats dot-files as having an extension (`.yml` IS extension `yml`)", () => Effect.gen(function* () { expect(yield* loadError(join(tempRoot, ".yml"))).toBe( `failed to read profile: open ${join(tempRoot, ".yml")}: no such file or directory`, @@ -67,7 +65,7 @@ describe("loadProfile", () => { }), ); - it.effect("fails on a missing file with Go's os.Open error", () => + it.effect("fails on a missing file with the open error", () => Effect.gen(function* () { expect(yield* loadError("missing.yml")).toBe( `failed to read profile: open missing.yml: no such file or directory`, @@ -75,7 +73,7 @@ describe("loadProfile", () => { }), ); - it.effect("fails on a directory with Go's read error", () => + it.effect("fails on a directory with the read error", () => Effect.gen(function* () { const dir = join(tempRoot, "dir.yml"); mkdirSync(dir, { recursive: true }); @@ -98,7 +96,7 @@ describe("loadProfile", () => { }), ); - it.effect("accepts mixed-case keys like viper's insensitive decode (probed on go1.26)", () => + it.effect("accepts mixed-case keys case-insensitively", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const file = writeProfile( @@ -141,7 +139,7 @@ describe("loadProfile", () => { }).pipe(Effect.provide(BunServices.layer)), ); - it.effect("reports unknown keys LOWERCASED, like viper's pre-decode normalization", () => + it.effect("reports unknown keys LOWERCASED", () => Effect.gen(function* () { const file = writeProfile( "bogus-upper.yml", @@ -153,30 +151,28 @@ describe("loadProfile", () => { "BOGUS_KEY: x", ].join("\n"), ); - expect(yield* loadError(file)).toContain("'utils.Profile' has invalid keys: bogus_key"); + expect(yield* loadError(file)).toContain("unknown keys: bogus_key"); }), ); - it.effect( - "returns Go's CurrentProfile.Name — the canonical built-in or the file's name field", - () => - Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem; - expect((yield* loadProfile("SUPABASE-LOCAL", fs)).name).toBe("supabase-local"); - const file = writeProfile( - "named.yml", - [ - "name: harness", - "api_url: http://127.0.0.1:44444", - "dashboard_url: http://127.0.0.1:44444/dashboard", - "project_host: supabase.co", - ].join("\n"), - ); - expect((yield* loadProfile(file, fs)).name).toBe("harness"); - }).pipe(Effect.provide(BunServices.layer)), + it.effect("returns the profile name — the canonical built-in or the file's name field", () => + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + expect((yield* loadProfile("SUPABASE-LOCAL", fs)).name).toBe("supabase-local"); + const file = writeProfile( + "named.yml", + [ + "name: harness", + "api_url: http://127.0.0.1:44444", + "dashboard_url: http://127.0.0.1:44444/dashboard", + "project_host: supabase.co", + ].join("\n"), + ); + expect((yield* loadProfile(file, fs)).name).toBe("harness"); + }).pipe(Effect.provide(BunServices.layer)), ); - it.effect("rejects unknown keys with mapstructure's padded UnmarshalExact block", () => + it.effect("rejects unknown keys with a sorted unknown-keys line", () => Effect.gen(function* () { const file = writeProfile( "extra-keys.yml", @@ -189,27 +185,24 @@ describe("loadProfile", () => { "db_url: postgres://localhost:5432/db", ].join("\n"), ); - const line1 = "failed to parse profile: decoding failed due to the following error(s):"; - const line3 = "'utils.Profile' has invalid keys: db_url, gotrue_url"; expect(yield* loadError(file)).toBe( - [line1, "".padEnd(line1.length), line3.padEnd(line1.length)].join("\n"), + "failed to parse profile:\nunknown keys: db_url, gotrue_url", ); }), ); - it.effect( - "reports missing required fields with the validator's padded lines, in struct order", - () => - Effect.gen(function* () { - const file = writeProfile("incomplete.yml", "name: incomplete\n"); - const lines = [ - "invalid profile: Key: 'Profile.APIURL' Error:Field validation for 'APIURL' failed on the 'required' tag", - "Key: 'Profile.DashboardURL' Error:Field validation for 'DashboardURL' failed on the 'required' tag", - "Key: 'Profile.ProjectHost' Error:Field validation for 'ProjectHost' failed on the 'required' tag", - ]; - const width = Math.max(...lines.map((line) => line.length)); - expect(yield* loadError(file)).toBe(lines.map((line) => line.padEnd(width)).join("\n")); - }), + it.effect("reports missing required fields one per line, in field order", () => + Effect.gen(function* () { + const file = writeProfile("incomplete.yml", "name: incomplete\n"); + expect(yield* loadError(file)).toBe( + [ + "invalid profile:", + "api_url is required", + "dashboard_url is required", + "project_host is required", + ].join("\n"), + ); + }), ); it.effect("reports a missing name (only) — required covers empty strings", () => @@ -222,29 +215,23 @@ describe("loadProfile", () => { "project_host: supabase.co", ].join("\n"), ); - expect(yield* loadError(file)).toBe( - "invalid profile: Key: 'Profile.Name' Error:Field validation for 'Name' failed on the 'required' tag", - ); + expect(yield* loadError(file)).toBe("invalid profile:\nname is required"); }), ); - it.effect( - "weakly stringifies scalars like viper, so `api_url: 123` fails http_url, not decoding", - () => - Effect.gen(function* () { - const file = writeProfile( - "typebad.yml", - [ - "name: t", - "api_url: 123", - "dashboard_url: http://127.0.0.1:44444/dashboard", - "project_host: supabase.co", - ].join("\n"), - ); - expect(yield* loadError(file)).toBe( - "invalid profile: Key: 'Profile.APIURL' Error:Field validation for 'APIURL' failed on the 'http_url' tag", - ); - }), + it.effect("weakly stringifies scalars, so `api_url: 123` fails http_url, not decoding", () => + Effect.gen(function* () { + const file = writeProfile( + "typebad.yml", + [ + "name: t", + "api_url: 123", + "dashboard_url: http://127.0.0.1:44444/dashboard", + "project_host: supabase.co", + ].join("\n"), + ); + expect(yield* loadError(file)).toBe("invalid profile:\napi_url must be an http(s) URL"); + }), ); it.effect("validates the hostname_rfc1123 and http_url format tags", () => @@ -258,24 +245,25 @@ describe("loadProfile", () => { "project_host: 'bad host!'", ].join("\n"), ); - const lines = [ - "invalid profile: Key: 'Profile.APIURL' Error:Field validation for 'APIURL' failed on the 'http_url' tag", - "Key: 'Profile.ProjectHost' Error:Field validation for 'ProjectHost' failed on the 'hostname_rfc1123' tag", - ]; - const width = Math.max(...lines.map((line) => line.length)); - expect(yield* loadError(file)).toBe(lines.map((line) => line.padEnd(width)).join("\n")); + expect(yield* loadError(file)).toBe( + [ + "invalid profile:", + "api_url must be an http(s) URL", + "project_host must be a valid hostname", + ].join("\n"), + ); }), ); - it.effect("fails a malformed YAML file closed with viper's parse prefix", () => + it.effect("fails a malformed YAML file closed with the parse-error prefix", () => Effect.gen(function* () { const file = writeProfile("malformed.yml", "name: [broken\n api_url"); const message = yield* loadError(file); - expect(message).toMatch(/^failed to read profile: While parsing config: /); + expect(message).toMatch(/^failed to read profile: invalid config file: /); }), ); - it.effect("fails closed on unconvertible values (array on a string field)", () => + it.effect("fails closed on non-scalar values (array on a string field)", () => Effect.gen(function* () { const file = writeProfile( "arrayval.yml", @@ -287,22 +275,9 @@ describe("loadProfile", () => { ].join("\n"), ); const message = yield* loadError(file); - expect(message).toContain( - "failed to parse profile: decoding failed due to the following error(s):", - ); - expect(message).toContain( - "'APIURL' expected type 'string', got unconvertible type '[]interface {}'", + expect(message).toBe( + 'failed to parse profile:\napi_url: expected a string, got array: ["http://a","http://b"]', ); }), ); }); - -describe("padGoErrorBlock", () => { - it("pads every line — including blank ones — to the longest line's width", () => { - expect(padGoErrorBlock("abc\n\nlonger line")).toBe("abc \n \nlonger line"); - }); - - it("leaves single-line messages untouched", () => { - expect(padGoErrorBlock("only line")).toBe("only line"); - }); -}); diff --git a/apps/cli/src/command-internal/profile.unit.test.ts b/apps/cli/src/command-internal/profile.unit.test.ts index a83ae15b7f..7bf618d3be 100644 --- a/apps/cli/src/command-internal/profile.unit.test.ts +++ b/apps/cli/src/command-internal/profile.unit.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from "vitest"; import { billingUrl, dashboardUrl, poolerHost, projectHost } from "./profile.ts"; describe("projectHost", () => { - it("maps built-in profile names to the Go project_host", () => { + it("maps built-in profile names to the project_host", () => { expect(projectHost("supabase")).toBe("supabase.co"); expect(projectHost("supabase-staging")).toBe("supabase.red"); expect(projectHost("supabase-local")).toBe("supabase.red"); @@ -16,7 +16,7 @@ describe("projectHost", () => { }); describe("poolerHost", () => { - it("maps built-in profile names to the Go pooler_host", () => { + it("maps built-in profile names to the pooler_host", () => { expect(poolerHost("supabase")).toBe("supabase.com"); expect(poolerHost("supabase-staging")).toBe("supabase.green"); expect(poolerHost("snap")).toBe("snapcloud.co"); @@ -32,7 +32,7 @@ describe("poolerHost", () => { }); describe("dashboardUrl", () => { - it("maps built-in profile names to the Go dashboard_url", () => { + it("maps built-in profile names to the dashboard_url", () => { expect(dashboardUrl("supabase")).toBe("https://supabase.com/dashboard"); expect(dashboardUrl("supabase-staging")).toBe("https://supabase.green/dashboard"); expect(dashboardUrl("supabase-local")).toBe("http://localhost:8082"); diff --git a/apps/cli/src/command-internal/project-create-core.ts b/apps/cli/src/command-internal/project-create-core.ts index 55238d6a59..1dd095f5f1 100644 --- a/apps/cli/src/command-internal/project-create-core.ts +++ b/apps/cli/src/command-internal/project-create-core.ts @@ -5,8 +5,13 @@ import { CommandPlatformApi } from "../auth/command-platform-api.service.ts"; import { CommandSettings } from "../config/command-settings.service.ts"; import { OutputFlag } from "./global-flags.ts"; import { Output } from "../shared/output/output.service.ts"; -import { encodeEnv, encodeGoJson } from "./go-output.encoders.ts"; -import { encodeGoToml, encodeGoYaml, goString, goStruct } from "./go-struct-output.encoders.ts"; +import { encodeEnv, encodeSortedJson } from "./output.encoders.ts"; +import { + encodeStructToml, + encodeStructYaml, + shapeString, + shapeStruct, +} from "./struct-output.encoders.ts"; import { sanitizeErrorBody } from "./http-errors.ts"; import { ProjectsCreateNetworkError, @@ -27,15 +32,15 @@ import { type CreateInput = typeof V1CreateAProjectInput.Type; /** Struct spec driving `-o yaml|toml` key casing for `projects create`'s raw response. */ -const GO_PROJECT_RESPONSE = goStruct([ - ["created_at", goString], - ["id", goString], - ["name", goString], - ["organization_id", goString], - ["organization_slug", goString], - ["ref", goString], - ["region", goString], - ["status", goString], +const PROJECT_RESPONSE_SHAPE = shapeStruct([ + ["created_at", shapeString], + ["id", shapeString], + ["name", shapeString], + ["organization_id", shapeString], + ["organization_slug", shapeString], + ["ref", shapeString], + ["region", shapeString], + ["status", shapeString], ]); export interface ProjectCreateInput { @@ -72,7 +77,7 @@ export const projectCreateCore = Effect.fn("ProjectCreate.run")(function* ( input: ProjectCreateInput, ) { const output = yield* Output; - const goOutputFlag = yield* OutputFlag; + const outputFlag = yield* OutputFlag; const api = yield* CommandPlatformApi; const cliSettings = yield* CommandSettings; @@ -151,20 +156,20 @@ export const projectCreateCore = Effect.fn("ProjectCreate.run")(function* ( const projectUrl = `${dashboardUrlForProfile(cliSettings.profile)}/project/${id}`; yield* output.raw(`Created a new project at ${projectUrl}\n`, "stderr"); - const goFmt = Option.getOrUndefined(goOutputFlag); - if (goFmt === "json") { - yield* output.raw(encodeGoJson(created)); + const outputFlagFormat = Option.getOrUndefined(outputFlag); + if (outputFlagFormat === "json") { + yield* output.raw(encodeSortedJson(created)); return { ref: id, dbPassword }; } - if (goFmt === "yaml") { - yield* output.raw(encodeGoYaml(created, GO_PROJECT_RESPONSE)); + if (outputFlagFormat === "yaml") { + yield* output.raw(encodeStructYaml(created, PROJECT_RESPONSE_SHAPE)); return { ref: id, dbPassword }; } - if (goFmt === "toml") { - yield* output.raw(encodeGoToml(created, GO_PROJECT_RESPONSE)); + if (outputFlagFormat === "toml") { + yield* output.raw(encodeStructToml(created, PROJECT_RESPONSE_SHAPE)); return { ref: id, dbPassword }; } - if (goFmt === "env") { + if (outputFlagFormat === "env") { yield* output.raw(encodeEnv(created) + "\n"); return { ref: id, dbPassword }; } diff --git a/apps/cli/src/command-internal/project-environment.unit.test.ts b/apps/cli/src/command-internal/project-environment.unit.test.ts index 3e6bdab5e7..ce43ce310b 100644 --- a/apps/cli/src/command-internal/project-environment.unit.test.ts +++ b/apps/cli/src/command-internal/project-environment.unit.test.ts @@ -49,7 +49,7 @@ describe("resolveProjectEnvironmentValues", () => { }); }); - it("fills in a value from a project-root .env file Go's loadNestedEnv would load", () => { + it("fills in a value from a project-root .env file", () => { writeFileSync(join(root, ".env"), "SUPABASE_PROJECT_ID=root-env-project\n"); const merged = resolveProjectEnvironmentValues(fakeProjectEnv(), root); expect(merged["SUPABASE_PROJECT_ID"]).toBe("root-env-project"); @@ -91,7 +91,7 @@ describe("resolveProjectEnvironmentValues", () => { expect(merged["SUPABASE_PROJECT_ID"]).toBe("prod-local-project"); }); - it("skips .env.local when SUPABASE_ENV=test, matching Go's loadDefaultEnv", () => { + it("skips .env.local when SUPABASE_ENV=test", () => { process.env["SUPABASE_ENV"] = "test"; writeFileSync(join(root, ".env.local"), "SUPABASE_PROJECT_ID=local-project\n"); writeFileSync(join(root, ".env.test"), "SUPABASE_PROJECT_ID=test-project\n"); @@ -127,7 +127,7 @@ describe("resolveProjectEnvironmentValues", () => { expect(merged["SUPABASE_PROJECT_ID"]).toBe("commented-project"); }); - it("preserves a literal # in an unquoted value with no leading whitespace, matching godotenv", () => { + it("preserves a literal # in an unquoted value with no leading whitespace", () => { writeFileSync(root + "/.env", "SUPABASE_AUTH_JWT_SECRET=long#secret\n"); const merged = resolveProjectEnvironmentValues(fakeProjectEnv(), root); expect(merged["SUPABASE_AUTH_JWT_SECRET"]).toBe("long#secret"); @@ -139,13 +139,13 @@ describe("resolveProjectEnvironmentValues", () => { expect(merged["SUPABASE_PROJECT_ID"]).toBe("54323"); }); - it("strips a trailing comment after a quoted value, matching godotenv", () => { + it("strips a trailing comment after a quoted value", () => { writeFileSync(root + "/.env", 'SUPABASE_PROJECT_ID="demo" # local\n'); const merged = resolveProjectEnvironmentValues(fakeProjectEnv(), root); expect(merged["SUPABASE_PROJECT_ID"]).toBe("demo"); }); - it("accepts a colon-separated assignment, matching godotenv's YAML-style key/value form", () => { + it("accepts a colon-separated assignment (YAML-style key/value form)", () => { writeFileSync(root + "/.env", "SUPABASE_PROJECT_ID: colon-project\n"); const merged = resolveProjectEnvironmentValues(fakeProjectEnv(), root); expect(merged["SUPABASE_PROJECT_ID"]).toBe("colon-project"); @@ -180,7 +180,7 @@ describe("resolveProjectEnvironmentValues", () => { expect(merged["SUPABASE_PROJECT_ID"]).toBe("ambient-project"); }); - it("throws on a malformed line, matching Go's loadEnvIfExists propagating godotenv's parse error", () => { + it("throws on a malformed line", () => { writeFileSync(join(root, ".env"), "not a valid line\n"); expect(() => resolveProjectEnvironmentValues(fakeProjectEnv(), root)).toThrow( /failed to parse environment file/, @@ -205,19 +205,19 @@ describe("resolveProjectEnvironmentValues", () => { expect(merged["SUPABASE_PROJECT_ID"]).toBe("$BASE"); }); - it("expands an unresolved bare reference to an empty string, matching Go's map zero-value", () => { + it("expands an unresolved bare reference to an empty string", () => { writeFileSync(join(root, ".env"), "SUPABASE_PROJECT_ID=$NOPE\n"); const merged = resolveProjectEnvironmentValues(fakeProjectEnv(), root); expect(merged["SUPABASE_PROJECT_ID"]).toBe(""); }); - it("expands an unresolved braced reference to an empty string, matching Go's map zero-value", () => { + it("expands an unresolved braced reference to an empty string", () => { writeFileSync(join(root, ".env"), 'SUPABASE_AUTH_JWT_SECRET="${NOPE}"\n'); const merged = resolveProjectEnvironmentValues(fakeProjectEnv(), root); expect(merged["SUPABASE_AUTH_JWT_SECRET"]).toBe(""); }); - it("preserves a backslash-escaped $VAR reference as a literal, matching godotenv's escape rule", () => { + it("preserves a backslash-escaped $VAR reference as a literal", () => { writeFileSync(join(root, ".env"), "BASE=demo\nSUPABASE_PROJECT_ID=demo\\$BASE\n"); const merged = resolveProjectEnvironmentValues(fakeProjectEnv(), root); expect(merged["SUPABASE_PROJECT_ID"]).toBe("demo$BASE"); @@ -235,7 +235,7 @@ describe("resolveProjectEnvironmentValues", () => { expect(merged["SUPABASE_PROJECT_ID"]).toBe("demo$"); }); - it("preserves a multiline quoted value alongside an unrelated SUPABASE_* key (godotenv parity)", () => { + it("preserves a multiline quoted value alongside an unrelated SUPABASE_* key", () => { // A quoted value spanning physical lines (e.g. a pasted PEM key) must not break // parsing of the rest of the file. const pem = "-----BEGIN PRIVATE KEY-----\nMIIBogIBAAJ\n-----END PRIVATE KEY-----"; diff --git a/apps/cli/src/command-internal/schema-flags.ts b/apps/cli/src/command-internal/schema-flags.ts index a8599718d3..36b014a822 100644 --- a/apps/cli/src/command-internal/schema-flags.ts +++ b/apps/cli/src/command-internal/schema-flags.ts @@ -12,10 +12,10 @@ export { StringSliceFlagParseError as SchemaFlagParseError }; export const parseSchemaFlags = parseStringSliceFlag; -// Whether a CSV field must be quoted (matches Go's `encoding/csv` writer, so a printed +// Whether a CSV field must be quoted (so a printed // suggestion round-trips through the same CSV parsing rules `--schema` values use): never quote // the empty string; always quote `\.`; quote when the field contains `,`, `"`, `\r`, or `\n`; -// otherwise quote when the first rune is whitespace. +// otherwise quote when the first character is whitespace. function fieldNeedsQuotes(field: string): boolean { if (field === "") return false; if (field === "\\.") return true; diff --git a/apps/cli/src/command-internal/schema-flags.unit.test.ts b/apps/cli/src/command-internal/schema-flags.unit.test.ts index 54d19e95f4..12dd8e8309 100644 --- a/apps/cli/src/command-internal/schema-flags.unit.test.ts +++ b/apps/cli/src/command-internal/schema-flags.unit.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { parseSchemaFlags, SchemaFlagParseError, schemaToCsvField } from "./schema-flags.ts"; -describe("parseSchemaFlags (pflag StringSlice CSV parity)", () => { +describe("parseSchemaFlags", () => { it("splits unquoted comma-separated values", () => { expect(parseSchemaFlags(["public,private"])).toEqual(["public", "private"]); }); @@ -34,7 +34,7 @@ describe("parseSchemaFlags (pflag StringSlice CSV parity)", () => { expect(parseSchemaFlags([])).toEqual([]); }); - it("preserves whitespace (Go does not trim)", () => { + it("preserves whitespace (no trimming)", () => { expect(parseSchemaFlags([" public , private "])).toEqual([" public ", " private "]); }); @@ -63,7 +63,7 @@ describe("schemaToCsvField (inverse — re-encode one value as a CSV field)", () expect(schemaToCsvField("public")).toBe("public"); }); - it("leaves the empty string unquoted (Go csv.Writer)", () => { + it("leaves the empty string unquoted", () => { expect(schemaToCsvField("")).toBe(""); }); diff --git a/apps/cli/src/command-internal/seed-buckets.ts b/apps/cli/src/command-internal/seed-buckets.ts index 0fda1b5e35..23c8228b57 100644 --- a/apps/cli/src/command-internal/seed-buckets.ts +++ b/apps/cli/src/command-internal/seed-buckets.ts @@ -1,5 +1,5 @@ import { type CliConfig, CliConfigSchema } from "@supabase/config/effect"; -import { loadCliConfig, type InternalLoadCliConfigOptions } from "@supabase/config/internal"; +import { loadCliConfig, type CliConfigLoadOptions } from "./cli-config-load.ts"; import { BunPath } from "@effect/platform-bun"; import { Effect, FileSystem, Path, Schema } from "effect"; import { FetchHttpClient } from "effect/unstable/http"; @@ -171,10 +171,7 @@ export const seedBucketsRun = Effect.fnUntraced(function* (opts: { // An explicit `opts.workdir` is the exact project root; only the caller's own workdir // (`cliSettings.workdir`) may still search ancestors for `config.toml`. const search = opts.workdir === undefined && shouldSearchAncestors(cliSettings); - const loadOptions: InternalLoadCliConfigOptions = - projectRef !== "" - ? { projectRef, goViperCompat: true, search } - : { goViperCompat: true, search }; + const loadOptions: CliConfigLoadOptions = projectRef !== "" ? { projectRef, search } : { search }; const loaded = opts.resolvedConfig !== undefined ? null diff --git a/apps/cli/src/command-internal/seed-ops.unit.test.ts b/apps/cli/src/command-internal/seed-ops.unit.test.ts index 85c91b5e5e..93979e1795 100644 --- a/apps/cli/src/command-internal/seed-ops.unit.test.ts +++ b/apps/cli/src/command-internal/seed-ops.unit.test.ts @@ -38,29 +38,26 @@ function fakeSeedSession(opts: { restoreRoleSql?: string } = {}) { // (`../../../shared/path-match.ts`) end to end; pattern-matching semantics are covered by // `path-match.unit.test.ts`. describe("getPendingSeeds (glob character classes)", () => { - it.effect( - "treats a leading `!` in a bracket class as literal, not negation (Go path.Match parity)", - () => { - const dir = mkdtempSync(join(tmpdir(), "seed-glob-")); - writeFileSync(join(dir, "a.sql"), "select 1;"); - writeFileSync(join(dir, "b.sql"), "select 2;"); - const { session } = fakeSeedSession(); - return Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem; - const path = yield* Path.Path; - // `[!a]` is a positive class of `!` and `a`; only a leading `^` negates. - const pending = yield* getPendingSeeds(session, fs, path, ["[!a].sql"], dir); - expect(pending.map((seed) => seed.path)).toEqual(["a.sql"]); - rmSync(dir, { recursive: true, force: true }); - }).pipe( - Effect.provide(mockOutput({ format: "text" }).layer), - Effect.provide(BunServices.layer), - ); - }, - ); + it.effect("treats a leading `!` in a bracket class as literal, not negation", () => { + const dir = mkdtempSync(join(tmpdir(), "seed-glob-")); + writeFileSync(join(dir, "a.sql"), "select 1;"); + writeFileSync(join(dir, "b.sql"), "select 2;"); + const { session } = fakeSeedSession(); + return Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + // `[!a]` is a positive class of `!` and `a`; only a leading `^` negates. + const pending = yield* getPendingSeeds(session, fs, path, ["[!a].sql"], dir); + expect(pending.map((seed) => seed.path)).toEqual(["a.sql"]); + rmSync(dir, { recursive: true, force: true }); + }).pipe( + Effect.provide(mockOutput({ format: "text" }).layer), + Effect.provide(BunServices.layer), + ); + }); it.effect( - "warns Go's bad-pattern message for an unterminated bracket class, not a bogus no-match", + "warns the bad-pattern message for an unterminated bracket class, not a bogus no-match", () => { // An unclosed `[` is malformed, so `fs.Glob` reports a syntax error, not a bogus // "no files matched" for a well-formed-but-empty pattern. @@ -116,7 +113,7 @@ describe("seedData (dirty parse)", () => { }); it.effect( - "rejects an oversized seed statement when SUPABASE_SCANNER_BUFFER_SIZE is configured (Go SeedFile.ExecBatchWithCache parity)", + "rejects an oversized seed statement when SUPABASE_SCANNER_BUFFER_SIZE is configured", () => { const dir = mkdtempSync(join(tmpdir(), "seed-scanner-")); // The seed text must exceed the 4096-byte scanner floor regardless of the configured diff --git a/apps/cli/src/command-internal/seed.unit.test.ts b/apps/cli/src/command-internal/seed.unit.test.ts index 1863e6919f..c3c15d3cd9 100644 --- a/apps/cli/src/command-internal/seed.unit.test.ts +++ b/apps/cli/src/command-internal/seed.unit.test.ts @@ -78,38 +78,35 @@ describe("applySeedFiles seed glob", () => { ); }); - it.effect( - "expands a matched directory to its sorted, regular .sql files (Go's Glob.SQLFiles)", - () => { - const dir = mkdtempSync(join(tmpdir(), "seed-")); - mkdirSync(join(dir, "seeds")); - writeFileSync(join(dir, "seeds", "b.sql"), "insert into t values (2);"); - writeFileSync(join(dir, "seeds", "a.sql"), "insert into t values (1);"); - writeFileSync(join(dir, "seeds", "README.md"), "not a seed file"); - const { session, queries } = fakeSession(); - const out = mockOutput(); - return run(session, dir, ["seeds"], out).pipe( - Effect.tap(() => - Effect.sync(() => { - const upserts = queries.filter((q) => - q.sql.includes("INSERT INTO supabase_migrations.seed_files"), - ); - expect(upserts.map((q) => q.params?.[0])).toEqual(["seeds/a.sql", "seeds/b.sql"]); - expect(out.rawChunks.map((c) => c.text)).toEqual([ - "Seeding data from seeds/a.sql...\n", - "Seeding data from seeds/b.sql...\n", - ]); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("expands a matched directory to its sorted, regular .sql files", () => { + const dir = mkdtempSync(join(tmpdir(), "seed-")); + mkdirSync(join(dir, "seeds")); + writeFileSync(join(dir, "seeds", "b.sql"), "insert into t values (2);"); + writeFileSync(join(dir, "seeds", "a.sql"), "insert into t values (1);"); + writeFileSync(join(dir, "seeds", "README.md"), "not a seed file"); + const { session, queries } = fakeSession(); + const out = mockOutput(); + return run(session, dir, ["seeds"], out).pipe( + Effect.tap(() => + Effect.sync(() => { + const upserts = queries.filter((q) => + q.sql.includes("INSERT INTO supabase_migrations.seed_files"), + ); + expect(upserts.map((q) => q.params?.[0])).toEqual(["seeds/a.sql", "seeds/b.sql"]); + expect(out.rawChunks.map((c) => c.text)).toEqual([ + "Seeding data from seeds/a.sql...\n", + "Seeding data from seeds/b.sql...\n", + ]); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); }); describe("applySeedFiles scanner buffer size", () => { it.effect( - "rejects an oversized seed statement when SUPABASE_SCANNER_BUFFER_SIZE is configured (Go SeedFile.ExecBatchWithCache parity)", + "rejects an oversized seed statement when SUPABASE_SCANNER_BUFFER_SIZE is configured", () => { const dir = mkdtempSync(join(tmpdir(), "seed-scanner-")); // The seed text must exceed the 4096-byte scanner floor regardless of the configured diff --git a/apps/cli/src/command-internal/size-units.ts b/apps/cli/src/command-internal/size-units.ts index e8af1e3feb..17317229b4 100644 --- a/apps/cli/src/command-internal/size-units.ts +++ b/apps/cli/src/command-internal/size-units.ts @@ -1,9 +1,7 @@ /** - * Parses `file_size_limit` config values using `docker/go-units`' `RAMInBytes` grammar - * (1024-based, case-insensitive, optional trailing `b`) before sending them to service APIs. + * Parses `file_size_limit` config values (1024-based, case-insensitive, optional trailing `b`) + * before sending them to service APIs. * Shared by `config push` (storage/auth/api/db diffing) and `seed buckets`. - * - * @see github.com/docker/go-units@v0.5.0/size.go */ const BINARY_MAP: Readonly> = { @@ -48,7 +46,7 @@ export function ramInBytes(sizeStr: string): number { throw new Error(`invalid size: '${sizeStr}'`); } // Strip the (already-validated, between-digits) underscores before parsing: - // JS `Number.parseFloat("1_000")` stops at the underscore (→1), unlike Go. + // JS `Number.parseFloat("1_000")` stops at the underscore (→1). const size = Number.parseFloat(num.replace(/_/g, "")); // Reject NaN and ±Infinity: an overflowing numeral like `1e309` parses to Infinity in JS, // but must fail here rather than flow through as `null` in the request body. diff --git a/apps/cli/src/command-internal/sql-files-glob.unit.test.ts b/apps/cli/src/command-internal/sql-files-glob.unit.test.ts index 6d6e904185..9cefc995a1 100644 --- a/apps/cli/src/command-internal/sql-files-glob.unit.test.ts +++ b/apps/cli/src/command-internal/sql-files-glob.unit.test.ts @@ -15,70 +15,61 @@ const run = (patterns: ReadonlyArray, workdir: string) => }).pipe(Effect.provide(BunServices.layer)); describe("sqlFilesGlob", () => { - it.effect( - "treats an empty pattern as no match, not the workdir itself (Go fs.Glob parity)", - () => { - const dir = mkdtempSync(join(tmpdir(), "sql-glob-empty-")); - return run([""], dir).pipe( - Effect.tap((result) => - Effect.sync(() => { - expect(result.files).toEqual([]); - expect(result.warnings).toEqual(["no files matched pattern: "]); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("treats an empty pattern as no match, not the workdir itself", () => { + const dir = mkdtempSync(join(tmpdir(), "sql-glob-empty-")); + return run([""], dir).pipe( + Effect.tap((result) => + Effect.sync(() => { + expect(result.files).toEqual([]); + expect(result.warnings).toEqual(["no files matched pattern: "]); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); - it.effect( - "does not follow a symlinked .sql file below a matched directory (Go WalkDir parity)", - () => { - const dir = mkdtempSync(join(tmpdir(), "sql-glob-symlink-file-")); - const schemasDir = join(dir, "schemas"); - mkdirSync(schemasDir); - writeFileSync(join(schemasDir, "real.sql"), "select 1;"); - const outsideDir = join(dir, "outside"); - mkdirSync(outsideDir); - writeFileSync(join(outsideDir, "evil.sql"), "select 2;"); - symlinkSync(join(outsideDir, "evil.sql"), join(schemasDir, "linked.sql")); - return run(["schemas"], dir).pipe( - Effect.tap((result) => - Effect.sync(() => { - expect(result.files).toEqual(["schemas/real.sql"]); - expect(result.warnings).toEqual([]); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("does not follow a symlinked .sql file below a matched directory", () => { + const dir = mkdtempSync(join(tmpdir(), "sql-glob-symlink-file-")); + const schemasDir = join(dir, "schemas"); + mkdirSync(schemasDir); + writeFileSync(join(schemasDir, "real.sql"), "select 1;"); + const outsideDir = join(dir, "outside"); + mkdirSync(outsideDir); + writeFileSync(join(outsideDir, "evil.sql"), "select 2;"); + symlinkSync(join(outsideDir, "evil.sql"), join(schemasDir, "linked.sql")); + return run(["schemas"], dir).pipe( + Effect.tap((result) => + Effect.sync(() => { + expect(result.files).toEqual(["schemas/real.sql"]); + expect(result.warnings).toEqual([]); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); - it.effect( - "does not recurse into a symlinked subdirectory below a matched directory (Go WalkDir parity)", - () => { - const dir = mkdtempSync(join(tmpdir(), "sql-glob-symlink-dir-")); - const schemasDir = join(dir, "schemas"); - mkdirSync(schemasDir); - writeFileSync(join(schemasDir, "real.sql"), "select 1;"); - const outsideSubdir = join(dir, "outside-subdir"); - mkdirSync(outsideSubdir); - writeFileSync(join(outsideSubdir, "nested.sql"), "select 3;"); - symlinkSync(outsideSubdir, join(schemasDir, "linked-dir")); - return run(["schemas"], dir).pipe( - Effect.tap((result) => - Effect.sync(() => { - expect(result.files).toEqual(["schemas/real.sql"]); - expect(result.warnings).toEqual([]); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("does not recurse into a symlinked subdirectory below a matched directory", () => { + const dir = mkdtempSync(join(tmpdir(), "sql-glob-symlink-dir-")); + const schemasDir = join(dir, "schemas"); + mkdirSync(schemasDir); + writeFileSync(join(schemasDir, "real.sql"), "select 1;"); + const outsideSubdir = join(dir, "outside-subdir"); + mkdirSync(outsideSubdir); + writeFileSync(join(outsideSubdir, "nested.sql"), "select 3;"); + symlinkSync(outsideSubdir, join(schemasDir, "linked-dir")); + return run(["schemas"], dir).pipe( + Effect.tap((result) => + Effect.sync(() => { + expect(result.files).toEqual(["schemas/real.sql"]); + expect(result.warnings).toEqual([]); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); it.effect( - "surfaces a stat failure on a matched file as a warning instead of treating it as a regular file (Go parity)", + "surfaces a stat failure on a matched file as a warning instead of treating it as a regular file", () => { const dir = mkdtempSync(join(tmpdir(), "sql-glob-stat-fail-")); const schemasDir = join(dir, "schemas"); @@ -101,7 +92,7 @@ describe("sqlFilesGlob", () => { ); it.effect( - "surfaces a stat failure for a LITERAL (no-metacharacter) pattern naming a broken symlink, instead of reporting no match (Go afero.Glob Lstat parity)", + "surfaces a stat failure for a LITERAL (no-metacharacter) pattern naming a broken symlink, instead of reporting no match", () => { const dir = mkdtempSync(join(tmpdir(), "sql-glob-literal-symlink-")); const schemasDir = join(dir, "schemas"); @@ -122,7 +113,7 @@ describe("sqlFilesGlob", () => { ); it.effect( - "keeps a bare root ('/') as the directory when a glob pattern's meta character is in the first path component (Go afero.Glob parity)", + "keeps a bare root ('/') as the directory when a glob pattern's meta character is in the first path component", () => { // The canary file lives in the workdir (never the real "/"), proving this doesn't // fall back to treating the root component as workdir-relative. @@ -143,7 +134,7 @@ describe("sqlFilesGlob", () => { ); it.effect( - "recurses through a root-anchored directory component without falling back to the workdir (Go afero.Glob parity)", + "recurses through a root-anchored directory component without falling back to the workdir", () => { // The workdir here contains a subdirectory that would match "foo*" only if the // recursive call incorrectly fell back to reading the workdir instead of "/". @@ -166,7 +157,7 @@ describe("sqlFilesGlob", () => { ); it.effect( - "preserves a Windows drive root ('C:/') as the directory when splitting a glob pattern (Go filepath.Split parity)", + "preserves a Windows drive root ('C:/') as the directory when splitting a glob pattern", () => { // Forces win32 path semantics (`BunPath.layerWin32` + `process.platform`) so the test // exercises the same branch a real Windows install takes. @@ -206,7 +197,7 @@ describe("sqlFilesGlob", () => { ); it.effect( - "reports the raw backslash pattern in a 'no files matched' warning on Windows, not the slashed form used for matching (Go filepath.ToSlash parity)", + "reports the raw backslash pattern in a 'no files matched' warning on Windows, not the slashed form used for matching", () => { // Forces win32 path semantics, same as the drive-root test above. const originalPlatform = process.platform; @@ -234,56 +225,53 @@ describe("sqlFilesGlob", () => { }, ); - it.effect( - "sorts raw backslash-joined Windows matches BEFORE slashing, not after (Go afero.Glob/filepath.ToSlash ordering parity)", - () => { - // For `a\x.sql` vs `a0\x.sql`, sorting the raw backslash bytes puts `a0\x.sql` first - // (`\` is `0x5C` > `0`'s `0x30`), while sorting the slashed form would put `a/x.sql` - // first instead — a fully faked filesystem supplies canned results keyed by the exact - // backslash-joined paths `BunPath.layerWin32`'s `path.join` computes. - const originalPlatform = process.platform; - Object.defineProperty(process, "platform", { value: "win32" }); - const scratchDir = mkdtempSync(join(tmpdir(), "sql-glob-win-sort-")); - const canaryFile = join(scratchDir, "canary.sql"); - writeFileSync(canaryFile, "select 1;"); - return Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem; - const path = yield* Path.Path; - const fileInfo = yield* fs.stat(canaryFile); - const workdir = "/workdir"; - const winFs: FileSystem.FileSystem = { - ...fs, - readDirectory: (p: string) => { - if (p === workdir) return Effect.succeed(["a", "a0"]); - if (p === "\\workdir\\a" || p === "\\workdir\\a0") return Effect.succeed(["x.sql"]); - return fs.readDirectory(p); - }, - stat: (p: string) => - p === "\\workdir\\a\\x.sql" || p === "\\workdir\\a0\\x.sql" - ? Effect.succeed(fileInfo) - : fs.stat(p), - }; - return yield* sqlFilesGlob(winFs, path, ["a*/x.sql"], workdir); - }).pipe( - Effect.provide(Layer.mergeAll(BunFileSystem.layer, BunPath.layerWin32)), - Effect.tap((result) => - Effect.sync(() => { - expect(result.files).toEqual(["a0/x.sql", "a/x.sql"]); - expect(result.warnings).toEqual([]); - }), - ), - Effect.ensuring( - Effect.sync(() => { - Object.defineProperty(process, "platform", { value: originalPlatform }); - rmSync(scratchDir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("sorts raw backslash-joined Windows matches BEFORE slashing, not after", () => { + // For `a\x.sql` vs `a0\x.sql`, sorting the raw backslash bytes puts `a0\x.sql` first + // (`\` is `0x5C` > `0`'s `0x30`), while sorting the slashed form would put `a/x.sql` + // first instead — a fully faked filesystem supplies canned results keyed by the exact + // backslash-joined paths `BunPath.layerWin32`'s `path.join` computes. + const originalPlatform = process.platform; + Object.defineProperty(process, "platform", { value: "win32" }); + const scratchDir = mkdtempSync(join(tmpdir(), "sql-glob-win-sort-")); + const canaryFile = join(scratchDir, "canary.sql"); + writeFileSync(canaryFile, "select 1;"); + return Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const fileInfo = yield* fs.stat(canaryFile); + const workdir = "/workdir"; + const winFs: FileSystem.FileSystem = { + ...fs, + readDirectory: (p: string) => { + if (p === workdir) return Effect.succeed(["a", "a0"]); + if (p === "\\workdir\\a" || p === "\\workdir\\a0") return Effect.succeed(["x.sql"]); + return fs.readDirectory(p); + }, + stat: (p: string) => + p === "\\workdir\\a\\x.sql" || p === "\\workdir\\a0\\x.sql" + ? Effect.succeed(fileInfo) + : fs.stat(p), + }; + return yield* sqlFilesGlob(winFs, path, ["a*/x.sql"], workdir); + }).pipe( + Effect.provide(Layer.mergeAll(BunFileSystem.layer, BunPath.layerWin32)), + Effect.tap((result) => + Effect.sync(() => { + expect(result.files).toEqual(["a0/x.sql", "a/x.sql"]); + expect(result.warnings).toEqual([]); + }), + ), + Effect.ensuring( + Effect.sync(() => { + Object.defineProperty(process, "platform", { value: originalPlatform }); + rmSync(scratchDir, { recursive: true, force: true }); + }), + ), + ); + }); it.effect( - "normalizes a doubled slash when the matched directory itself has a trailing slash (Go path.Join parity)", + "normalizes a doubled slash when the matched directory itself has a trailing slash", () => { const dir = mkdtempSync(join(tmpdir(), "sql-glob-trailing-slash-")); const schemasDir = join(dir, "schemas"); @@ -301,89 +289,77 @@ describe("sqlFilesGlob", () => { }, ); - it.effect( - "drops the './' prefix when the matched directory cleans to '.' (Go path.Join parity)", - () => { - const dir = mkdtempSync(join(tmpdir(), "sql-glob-dot-root-")); - writeFileSync(join(dir, "a.sql"), "select 1;"); - const nestedDir = join(dir, "nested"); - mkdirSync(nestedDir); - writeFileSync(join(nestedDir, "b.sql"), "select 2;"); - return run(["."], dir).pipe( - Effect.tap((result) => - Effect.sync(() => { - expect(result.files).toEqual(["a.sql", "nested/b.sql"]); - expect(result.warnings).toEqual([]); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("drops the './' prefix when the matched directory cleans to '.'", () => { + const dir = mkdtempSync(join(tmpdir(), "sql-glob-dot-root-")); + writeFileSync(join(dir, "a.sql"), "select 1;"); + const nestedDir = join(dir, "nested"); + mkdirSync(nestedDir); + writeFileSync(join(nestedDir, "b.sql"), "select 2;"); + return run(["."], dir).pipe( + Effect.tap((result) => + Effect.sync(() => { + expect(result.files).toEqual(["a.sql", "nested/b.sql"]); + expect(result.warnings).toEqual([]); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); - it.effect( - "cleans a '..'-segment matched directory when walking its children (Go path.Join parity)", - () => { - const dir = mkdtempSync(join(tmpdir(), "sql-glob-dotdot-segment-")); - const schemasDir = join(dir, "schemas"); - mkdirSync(schemasDir); - writeFileSync(join(schemasDir, "a.sql"), "select 1;"); - return run(["nested/../schemas"], dir).pipe( - Effect.tap((result) => - Effect.sync(() => { - expect(result.files).toEqual(["schemas/a.sql"]); - expect(result.warnings).toEqual([]); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("cleans a '..'-segment matched directory when walking its children", () => { + const dir = mkdtempSync(join(tmpdir(), "sql-glob-dotdot-segment-")); + const schemasDir = join(dir, "schemas"); + mkdirSync(schemasDir); + writeFileSync(join(schemasDir, "a.sql"), "select 1;"); + return run(["nested/../schemas"], dir).pipe( + Effect.tap((result) => + Effect.sync(() => { + expect(result.files).toEqual(["schemas/a.sql"]); + expect(result.warnings).toEqual([]); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); - it.effect( - "cleans a direct glob match whose directory portion has a '.' segment (Go afero.Glob parity)", - () => { - // Distinct from the walked-child cleaning tests above: the glob metacharacter is in - // the final component, so this matches directly via `globOne`, never `walkSqlFiles`. - const dir = mkdtempSync(join(tmpdir(), "sql-glob-direct-dot-segment-")); - const schemasDir = join(dir, "schemas"); - mkdirSync(schemasDir); - writeFileSync(join(schemasDir, "a.sql"), "select 1;"); - return run(["schemas/./*.sql"], dir).pipe( - Effect.tap((result) => - Effect.sync(() => { - expect(result.files).toEqual(["schemas/a.sql"]); - expect(result.warnings).toEqual([]); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("cleans a direct glob match whose directory portion has a '.' segment", () => { + // Distinct from the walked-child cleaning tests above: the glob metacharacter is in + // the final component, so this matches directly via `globOne`, never `walkSqlFiles`. + const dir = mkdtempSync(join(tmpdir(), "sql-glob-direct-dot-segment-")); + const schemasDir = join(dir, "schemas"); + mkdirSync(schemasDir); + writeFileSync(join(schemasDir, "a.sql"), "select 1;"); + return run(["schemas/./*.sql"], dir).pipe( + Effect.tap((result) => + Effect.sync(() => { + expect(result.files).toEqual(["schemas/a.sql"]); + expect(result.warnings).toEqual([]); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); - it.effect( - "cleans a direct glob match whose directory portion has a '..' segment (Go afero.Glob parity)", - () => { - // Same distinction as above (a direct match via `globOne`), but for an embedded `..` - // segment instead of `.`. - const dir = mkdtempSync(join(tmpdir(), "sql-glob-direct-dotdot-segment-")); - const schemasDir = join(dir, "schemas"); - mkdirSync(schemasDir); - writeFileSync(join(schemasDir, "a.sql"), "select 1;"); - return run(["nested/../schemas/*.sql"], dir).pipe( - Effect.tap((result) => - Effect.sync(() => { - expect(result.files).toEqual(["schemas/a.sql"]); - expect(result.warnings).toEqual([]); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("cleans a direct glob match whose directory portion has a '..' segment", () => { + // Same distinction as above (a direct match via `globOne`), but for an embedded `..` + // segment instead of `.`. + const dir = mkdtempSync(join(tmpdir(), "sql-glob-direct-dotdot-segment-")); + const schemasDir = join(dir, "schemas"); + mkdirSync(schemasDir); + writeFileSync(join(schemasDir, "a.sql"), "select 1;"); + return run(["nested/../schemas/*.sql"], dir).pipe( + Effect.tap((result) => + Effect.sync(() => { + expect(result.files).toEqual(["schemas/a.sql"]); + expect(result.warnings).toEqual([]); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); it.effect( - "normalizes a doubled slash for a direct glob match under a trailing-slash directory component (Go afero.Glob parity)", + "normalizes a doubled slash for a direct glob match under a trailing-slash directory component", () => { // `splitPath` on `"schemas//*.sql"` yields `dir: "schemas/"` (the trailing slash // survives the split), so the join must still collapse the doubled slash. @@ -403,42 +379,39 @@ describe("sqlFilesGlob", () => { }, ); - it.effect( - "still includes a '.sql' child whose stat fails after it's already listed (Go WalkDir parity)", - () => { - // Simulates the race by deleting the file the instant after `readDirectory` returns - // it, via a `FileSystem` layer that removes on the first `stat` call for that path. - const dir = mkdtempSync(join(tmpdir(), "sql-glob-stat-race-")); - const schemasDir = join(dir, "schemas"); - mkdirSync(schemasDir); - const racyFile = join(schemasDir, "racy.sql"); - writeFileSync(racyFile, "select 1;"); - return Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem; - const path = yield* Path.Path; - const racyFs: FileSystem.FileSystem = { - ...fs, - stat: (p: string) => - p === racyFile - ? Effect.sync(() => rmSync(racyFile)).pipe(Effect.andThen(fs.stat(p))) - : fs.stat(p), - }; - return yield* sqlFilesGlob(racyFs, path, ["schemas"], dir); - }).pipe( - Effect.provide(BunServices.layer), - Effect.tap((result) => - Effect.sync(() => { - expect(result.files).toEqual(["schemas/racy.sql"]); - expect(result.warnings).toEqual([]); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("still includes a '.sql' child whose stat fails after it's already listed", () => { + // Simulates the race by deleting the file the instant after `readDirectory` returns + // it, via a `FileSystem` layer that removes on the first `stat` call for that path. + const dir = mkdtempSync(join(tmpdir(), "sql-glob-stat-race-")); + const schemasDir = join(dir, "schemas"); + mkdirSync(schemasDir); + const racyFile = join(schemasDir, "racy.sql"); + writeFileSync(racyFile, "select 1;"); + return Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const racyFs: FileSystem.FileSystem = { + ...fs, + stat: (p: string) => + p === racyFile + ? Effect.sync(() => rmSync(racyFile)).pipe(Effect.andThen(fs.stat(p))) + : fs.stat(p), + }; + return yield* sqlFilesGlob(racyFs, path, ["schemas"], dir); + }).pipe( + Effect.provide(BunServices.layer), + Effect.tap((result) => + Effect.sync(() => { + expect(result.files).toEqual(["schemas/racy.sql"]); + expect(result.warnings).toEqual([]); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); it.effect( - "fails the whole walk when a non-'.sql' child whose stat fails could have been a subdirectory (Go WalkDir parity)", + "fails the whole walk when a non-'.sql' child whose stat fails could have been a subdirectory", () => { const dir = mkdtempSync(join(tmpdir(), "sql-glob-dir-race-")); const schemasDir = join(dir, "schemas"); @@ -497,7 +470,7 @@ describe("sqlFilesGlob", () => { const isRoot = typeof process.getuid === "function" && process.getuid() === 0; it.effect.skipIf(isRoot)( - "surfaces a directory-read failure during walk as a warning instead of an empty match (Go WalkDir parity)", + "surfaces a directory-read failure during walk as a warning instead of an empty match", () => { const dir = mkdtempSync(join(tmpdir(), "sql-glob-walk-fail-")); const schemasDir = join(dir, "schemas"); @@ -525,7 +498,7 @@ describe("sqlFilesGlob", () => { ); it.effect.skipIf(isRoot)( - "surfaces a NESTED directory-read failure during walk with a workdir-relative path, not the matched root's (Go WalkDir parity)", + "surfaces a NESTED directory-read failure during walk with a workdir-relative path, not the matched root's", () => { const dir = mkdtempSync(join(tmpdir(), "sql-glob-walk-fail-nested-")); const schemasDir = join(dir, "schemas"); @@ -584,7 +557,7 @@ describe("sqlFilesGlob", () => { ); it.effect.skipIf(isRoot)( - "picks the lexically-first failing subdirectory as the fatal error, matching Go's fs.WalkDir sorted-visit order (review CLI-1958)", + "picks the lexically-first failing subdirectory as the fatal error", () => { // The fake `readDirectory` returns "schemas"'s children in reverse // order, to prove the walk sorts them back (`utf8Compare`) before iterating. @@ -626,26 +599,23 @@ describe("sqlFilesGlob", () => { }, ); - it.effect( - "sorts direct wildcard matches by UTF-8 byte order, not UTF-16 code units (Go sort.Strings parity)", - () => { - // The emoji (4-byte UTF-8, lead byte 0xF0) sorts after the fullwidth exclamation mark - // (3-byte UTF-8, lead byte 0xEF) in byte order, but before it in UTF-16 code-unit - // order (0xD83D surrogate lead vs 0xFF01) — these two characters expose the difference. - const dir = mkdtempSync(join(tmpdir(), "sql-glob-utf8-sort-")); - const schemasDir = join(dir, "schemas"); - mkdirSync(schemasDir); - writeFileSync(join(schemasDir, "\u{1F600}.sql"), "select 1;"); // 😀 - writeFileSync(join(schemasDir, "!.sql"), "select 2;"); // ! - return run(["schemas/*.sql"], dir).pipe( - Effect.tap((result) => - Effect.sync(() => { - expect(result.files).toEqual(["schemas/!.sql", "schemas/\u{1F600}.sql"]); - expect(result.warnings).toEqual([]); - rmSync(dir, { recursive: true, force: true }); - }), - ), - ); - }, - ); + it.effect("sorts direct wildcard matches by UTF-8 byte order, not UTF-16 code units", () => { + // The emoji (4-byte UTF-8, lead byte 0xF0) sorts after the fullwidth exclamation mark + // (3-byte UTF-8, lead byte 0xEF) in byte order, but before it in UTF-16 code-unit + // order (0xD83D surrogate lead vs 0xFF01) — these two characters expose the difference. + const dir = mkdtempSync(join(tmpdir(), "sql-glob-utf8-sort-")); + const schemasDir = join(dir, "schemas"); + mkdirSync(schemasDir); + writeFileSync(join(schemasDir, "\u{1F600}.sql"), "select 1;"); // 😀 + writeFileSync(join(schemasDir, "!.sql"), "select 2;"); // ! + return run(["schemas/*.sql"], dir).pipe( + Effect.tap((result) => + Effect.sync(() => { + expect(result.files).toEqual(["schemas/!.sql", "schemas/\u{1F600}.sql"]); + expect(result.warnings).toEqual([]); + rmSync(dir, { recursive: true, force: true }); + }), + ), + ); + }); }); diff --git a/apps/cli/src/command-internal/sql-split.ts b/apps/cli/src/command-internal/sql-split.ts index 66c7a71742..85b1b799db 100644 --- a/apps/cli/src/command-internal/sql-split.ts +++ b/apps/cli/src/command-internal/sql-split.ts @@ -9,7 +9,7 @@ interface State { /** Returns the next state, or `null` to emit a token (statement boundary). */ - next(rune: string, data: string): State | null; + next(char: string, data: string): State | null; } const BEGIN_ATOMIC = "ATOMIC"; @@ -17,17 +17,17 @@ const END_ATOMIC = "END"; // PostgreSQL's scan.l treats every code point at or above 0x80 as an identifier/dollar-tag // character (`ident_cont`/`dolq_cont`), whatever its Unicode category. -const isIdentifierRune = (rune: string): boolean => { - const codePoint = rune.codePointAt(0); - return codePoint !== undefined && (codePoint >= 0x80 || /[A-Za-z0-9_$]/u.test(rune)); +const isIdentifierChar = (char: string): boolean => { + const codePoint = char.codePointAt(0); + return codePoint !== undefined && (codePoint >= 0x80 || /[A-Za-z0-9_$]/u.test(char)); }; // A code point spans at most two UTF-16 units, so the last one before `offset` lies within // the preceding two. -const hasIdentifierRuneBefore = (data: string, offset: number): boolean => { +const hasIdentifierCharBefore = (data: string, offset: number): boolean => { if (offset <= 0) return false; - const rune = Array.from(data.slice(Math.max(0, offset - 2), offset)).at(-1); - return rune !== undefined && isIdentifierRune(rune); + const char = Array.from(data.slice(Math.max(0, offset - 2), offset)).at(-1); + return char !== undefined && isIdentifierChar(char); }; const asciiUpper = (text: string): string => text.replace(/[a-z]/g, (c) => c.toUpperCase()); @@ -35,13 +35,13 @@ const asciiUpper = (text: string): string => text.replace(/[a-z]/g, (c) => c.toU function endsWithKeyword(data: string, keyword: string): boolean { const offset = data.length - keyword.length; if (offset < 0 || asciiUpper(data.slice(offset)) !== keyword) return false; - return !hasIdentifierRuneBefore(data, offset); + return !hasIdentifierCharBefore(data, offset); } -const isSqlWhitespace = (rune: string): boolean => " \t\n\r\f\v".includes(rune); +const isSqlWhitespace = (char: string): boolean => " \t\n\r\f\v".includes(char); // scan.l `newline`: a `--` comment ends at either. -const isNewline = (rune: string): boolean => rune === "\n" || rune === "\r"; +const isNewline = (char: string): boolean => char === "\n" || char === "\r"; function isBeginAtomic(data: string): boolean { if (!endsWithKeyword(data, BEGIN_ATOMIC)) return false; @@ -78,22 +78,22 @@ function isCommentsAndWhitespace(text: string): boolean { } class ReadyState implements State { - next(rune: string, data: string): State | null { - switch (rune) { + next(char: string, data: string): State | null { + switch (char) { case "$": { - // A `$` after an identifier rune continues the identifier (`pending$$foo$`), not a + // A `$` after an identifier char continues the identifier (`pending$$foo$`), not a // dollar quote. A digit counts too (`1$$`), unlike PostgreSQL; valid SQL never has that. - const offset = data.length - rune.length; - if (hasIdentifierRuneBefore(data, offset)) return this; + const offset = data.length - char.length; + if (hasIdentifierCharBefore(data, offset)) return this; return new TagState(offset); } case "'": // `E'…'` is an escape string constant only when the `E` starts a token (scan.l // `xestart`); in `type'…'` it ends an identifier. A digit or `$` before the `E` // counts as one too, unlike PostgreSQL; valid SQL never has that. - return new QuoteState(rune, endsWithKeyword(data.slice(0, -1), "E")); + return new QuoteState(char, endsWithKeyword(data.slice(0, -1), "E")); case '"': - return new QuoteState(rune, false); + return new QuoteState(char, false); case "-": return new CommentState(); case "/": @@ -115,27 +115,27 @@ class ReadyState implements State { } class CommentState implements State { - next(rune: string, data: string): State | null { - if (rune === "-") return new LineCommentState(); - return new ReadyState().next(rune, data); + next(char: string, data: string): State | null { + if (char === "-") return new LineCommentState(); + return new ReadyState().next(char, data); } } class LineCommentState implements State { - next(rune: string): State { - return isNewline(rune) ? new ReadyState() : this; + next(char: string): State { + return isNewline(char) ? new ReadyState() : this; } } class BlockState implements State { private depth = 0; - next(rune: string, data: string): State | null { + next(char: string, data: string): State | null { const window = data.slice(-2); if (window === "/*") { this.depth += 1; return this; } - if (this.depth === 0) return new ReadyState().next(rune, data); + if (this.depth === 0) return new ReadyState().next(char, data); if (window === "*/") { this.depth -= 1; if (this.depth === 0) return new ReadyState(); @@ -151,26 +151,26 @@ class QuoteState implements State { private readonly delimiter: string, private readonly backslashEscapes: boolean, ) {} - next(rune: string, data: string): State | null { + next(char: string, data: string): State | null { if (this.escape) { // Preserve a doubled quote ('' or ""). - if (rune === this.delimiter) { + if (char === this.delimiter) { this.escape = false; return this; } - if (this.backslashEscapes) return new QuoteContinueState().next(rune, data); - return new ReadyState().next(rune, data); + if (this.backslashEscapes) return new QuoteContinueState().next(char, data); + return new ReadyState().next(char, data); } if (this.backslash) { - // Preserve the rune after a backslash (\' or \\). + // Preserve the char after a backslash (\' or \\). this.backslash = false; return this; } - if (this.backslashEscapes && rune === "\\") { + if (this.backslashEscapes && char === "\\") { this.backslash = true; return this; } - if (rune === this.delimiter) this.escape = true; + if (char === this.delimiter) this.escape = true; return this; } } @@ -180,32 +180,32 @@ class QuoteState implements State { class QuoteContinueState implements State { private newline = false; private dashes = 0; - next(rune: string, data: string): State | null { + next(char: string, data: string): State | null { if (this.dashes === 2) { - if (isNewline(rune)) { + if (isNewline(char)) { this.dashes = 0; this.newline = true; } return this; } - if (rune === "-") { + if (char === "-") { this.dashes += 1; return this; } if (this.dashes === 0) { - if (isSqlWhitespace(rune)) { - this.newline ||= isNewline(rune); + if (isSqlWhitespace(char)) { + this.newline ||= isNewline(char); return this; } - if (this.newline && rune === "'") return new QuoteState(rune, true); + if (this.newline && char === "'") return new QuoteState(char, true); } - return new ReadyState().next(rune, data); + return new ReadyState().next(char, data); } } class DollarState implements State { constructor(private readonly delimiter: string) {} - next(_rune: string, data: string): State | null { + next(_char: string, data: string): State | null { if (data.slice(-this.delimiter.length) === this.delimiter) return new ReadyState(); return this; } @@ -213,10 +213,10 @@ class DollarState implements State { class TagState implements State { constructor(private readonly offset: number) {} - next(rune: string, data: string): State | null { - if (rune === "$") return new DollarState(data.slice(this.offset)); - if (isIdentifierRune(rune)) return this; - return new ReadyState().next(rune, data); + next(char: string, data: string): State | null { + if (char === "$") return new DollarState(data.slice(this.offset)); + if (isIdentifierChar(char)) return this; + return new ReadyState().next(char, data); } } @@ -228,15 +228,15 @@ class EscapeState implements State { class ParenState implements State { constructor(private prev: State) {} - next(rune: string, data: string): State | null { - const curr = this.prev.next(rune, data); + next(char: string, data: string): State | null { + const curr = this.prev.next(char, data); if (curr === null) { this.prev = new ReadyState(); return this; } this.prev = curr; if (!(this.prev instanceof ReadyState)) return this; - return rune === ")" ? new ReadyState() : this; + return char === ")" ? new ReadyState() : this; } } @@ -250,12 +250,12 @@ class AtomicState implements State { ) { this.statementStart = start; } - next(rune: string, data: string): State | null { + next(char: string, data: string): State | null { const pendingEnd = this.pendingEnd; this.pendingEnd = false; - if (pendingEnd && !isIdentifierRune(rune)) return new ReadyState().next(rune, data); + if (pendingEnd && !isIdentifierChar(char)) return new ReadyState().next(char, data); // An `END` inside a nested quote/comment doesn't count. - const curr = this.prev.next(rune, data); + const curr = this.prev.next(char, data); if (curr === null) { this.prev = new ReadyState(); this.statementStart = data.length; @@ -294,14 +294,14 @@ function splitRaw(sql: string): RawToken[] { let state: State = new ReadyState(); const tokens: RawToken[] = []; // Slice each token from `sql` instead of growing it with `+=`: states read the token's tail every - // rune, which would rebuild the whole string each time and go quadratic on large tokens. `data` + // char, which would rebuild the whole string each time and go quadratic on large tokens. `data` // starts at the token, so offsets held by states are token-relative. let start = 0; let end = 0; - for (const rune of sql) { - end += rune.length; + for (const char of sql) { + end += char.length; const data = sql.slice(start, end); - const next = state.next(rune, data); + const next = state.next(char, data); if (next === null) { tokens.push({ text: data, terminated: true }); start = end; diff --git a/apps/cli/src/command-internal/sql-split.unit.test.ts b/apps/cli/src/command-internal/sql-split.unit.test.ts index b904d19e85..374275125c 100644 --- a/apps/cli/src/command-internal/sql-split.unit.test.ts +++ b/apps/cli/src/command-internal/sql-split.unit.test.ts @@ -234,7 +234,7 @@ describe("splitAndTrim", () => { it.each(["\u00A0", "\uFEFF", "\u0085"])( "does not treat BEGIN %s ATOMIC as the keyword pair", (gap) => { - // Only PostgreSQL's own whitespace separates the keywords; these are identifier runes. + // Only PostgreSQL's own whitespace separates the keywords; these are identifier characters. const sql = `BEGIN ${gap} ATOMIC; SELECT 1; end; SELECT 2;`; expect(splitAndTrim(sql)).toEqual([`BEGIN ${gap} ATOMIC`, "SELECT 1", "end", "SELECT 2"]); }, diff --git a/apps/cli/src/command-internal/stack-config.ts b/apps/cli/src/command-internal/stack-config.ts index b8094c3d99..830c100d7a 100644 --- a/apps/cli/src/command-internal/stack-config.ts +++ b/apps/cli/src/command-internal/stack-config.ts @@ -1,5 +1,5 @@ import { getDefaultCliConfig, type CliConfig } from "@supabase/config"; -import { resolveCliConfigSubtree } from "@supabase/config/internal"; +import { resolveCliConfigSubtree } from "./cli-config-load.ts"; import { validateCliConfig } from "@supabase/config/effect"; import { DEFAULT_LOCAL_S3_ACCESS_KEY_ID, @@ -15,7 +15,7 @@ import { loadLocalProjectContext, type LocalProjectContext } from "./local-proje import { RuntimeInfo } from "../shared/runtime/runtime-info.service.ts"; import { CLI_VERSION } from "../shared/cli/version.ts"; import { resolveAuthConfig } from "./stack-auth-config.ts"; -import { parseGoDuration } from "./go-duration.ts"; +import { parseDuration } from "./duration.ts"; import { parseFileSizeLimit } from "./storage-bucket-config.ts"; import { @@ -53,7 +53,7 @@ import { resolveGotrueWeb3, strToArr, } from "./local-config-values.ts"; -import { generateAsymmetricGoJwt } from "./go-jwt.ts"; +import { generateAsymmetricLocalJwt } from "./local-jwt.ts"; import { recordOrioleDbTelemetry } from "./db-image.ts"; import { resolveRemoteJwks, @@ -982,12 +982,12 @@ export const loadStackConfig = Effect.fn("StackConfig.load")( ...(configuredAnonKey === undefined ? signingKey === undefined ? {} - : { anonKey: generateAsymmetricGoJwt(signingKey, "anon") } + : { anonKey: generateAsymmetricLocalJwt(signingKey, "anon") } : { anonKey: configuredAnonKey }), ...(configuredServiceRoleKey === undefined ? signingKey === undefined ? {} - : { serviceRoleKey: generateAsymmetricGoJwt(signingKey, "service_role") } + : { serviceRoleKey: generateAsymmetricLocalJwt(signingKey, "service_role") } : { serviceRoleKey: configuredServiceRoleKey }), anonKeyIsOverride: configuredAnonKey !== undefined, serviceRoleKeyIsOverride: configuredServiceRoleKey !== undefined, @@ -1029,7 +1029,6 @@ export const loadStackConfig = Effect.fn("StackConfig.load")( config.env, { values: context.projectEnvValues }, `functions.${name}.env`, - { goViperCompat: true }, ).pipe( Effect.map( (env) => @@ -1098,7 +1097,7 @@ export const loadStackConfig = Effect.fn("StackConfig.load")( new StackConfigError({ message: `Invalid storage.file_size_limit: ${String(cause)}` }), }); const healthTimeoutMs = yield* Effect.try({ - try: () => parseGoDuration(validatedConfig.db.health_timeout) / 1_000_000, + try: () => parseDuration(validatedConfig.db.health_timeout) / 1_000_000, catch: (cause) => new StackConfigError({ message: `Invalid db.health_timeout: ${String(cause)}` }), }); diff --git a/apps/cli/src/command-internal/stack-shadow-cache.ts b/apps/cli/src/command-internal/stack-shadow-cache.ts index d53aec24de..340f7ba26f 100644 --- a/apps/cli/src/command-internal/stack-shadow-cache.ts +++ b/apps/cli/src/command-internal/stack-shadow-cache.ts @@ -7,7 +7,7 @@ import { type ShadowCacheKeyInputs, } from "./db-bootstrap/shadow-cache.ts"; import { resolveSetupWebhooksEnabled, type SetupDatabaseOptions } from "./db-bootstrap/db-setup.ts"; -import { viperEnvBoolWithProjectFallback } from "./viper-env.ts"; +import { supabaseEnvBoolWithProjectFallback } from "./supabase-env.ts"; interface StackShadowCacheEntry { readonly key: string; @@ -31,7 +31,7 @@ export const stackShadowCacheEntry = Effect.fn("StackShadowCache.entry")(functio ) { if ( bypassCache || - !viperEnvBoolWithProjectFallback(SHADOW_CACHE_ENV, input.setup.projectEnvValues ?? {}, { + !supabaseEnvBoolWithProjectFallback(SHADOW_CACHE_ENV, input.setup.projectEnvValues ?? {}, { whenUnset: true, }) ) diff --git a/apps/cli/src/command-internal/status-pretty.ts b/apps/cli/src/command-internal/status-pretty.ts index 62ab0232d5..ec400a0d9a 100644 --- a/apps/cli/src/command-internal/status-pretty.ts +++ b/apps/cli/src/command-internal/status-pretty.ts @@ -78,7 +78,7 @@ export function statusGroups( /** * Display width for this command's inputs: URLs/keys/labels are always plain ASCII, so every - * rune is width 1. The fixed group-title emoji are the only non-ASCII runes ever rendered, + * character is width 1. The fixed group-title emoji are the only non-ASCII characters ever rendered, * and their widths are hardcoded in {@link HEADER_DISPLAY_WIDTH} instead of computed * generically. */ diff --git a/apps/cli/src/command-internal/status-pretty.unit.test.ts b/apps/cli/src/command-internal/status-pretty.unit.test.ts index c98a8cb76b..f410a98633 100644 --- a/apps/cli/src/command-internal/status-pretty.unit.test.ts +++ b/apps/cli/src/command-internal/status-pretty.unit.test.ts @@ -45,7 +45,7 @@ const FULL_VALUES: Record = { }; describe("renderStatusPretty", () => { - it("matches the Go rounded-table fixture for a fully running stack", () => { + it("matches the rounded-table fixture for a fully running stack", () => { const out = stripAnsi(renderStatusPretty(FULL_VALUES, NAMES)); const expected = [ @@ -94,7 +94,7 @@ describe("renderStatusPretty", () => { }); // All other groups are empty in this fixture, so only the Database box should appear. - it("matches the Go rounded-table fixture for a single-row group", () => { + it("matches the rounded-table fixture for a single-row group", () => { const out = stripAnsi( renderStatusPretty({ DB_URL: FULL_VALUES.DB_URL ?? "" }, { ...NAMES, dbUrl: "DB_URL" }), ); @@ -112,7 +112,7 @@ describe("renderStatusPretty", () => { }); // All other groups are empty in this fixture; only Project URL is present in APIs. - it("matches the Go rounded-table fixture for a partial APIs group", () => { + it("matches the rounded-table fixture for a partial APIs group", () => { const out = stripAnsi(renderStatusPretty({ API_URL: "http://127.0.0.1:54321" }, NAMES)); const expectedTable = [ diff --git a/apps/cli/src/command-internal/status-values.ts b/apps/cli/src/command-internal/status-values.ts index 5f5e1d6d36..7d8cf61dc9 100644 --- a/apps/cli/src/command-internal/status-values.ts +++ b/apps/cli/src/command-internal/status-values.ts @@ -240,7 +240,7 @@ export interface StatusLocalState { * @throws {InvalidBoolEnvOverrideError} when a `SUPABASE_*_ENABLED` env/dotenv override * doesn't parse as a valid bool. * @throws when `auth.signing_keys_path` is set but the file is missing, malformed, or its - * first key is unsupported — see {@link generateAsymmetricGoJwt}. + * first key is unsupported — see {@link generateAsymmetricLocalJwt}. */ export function resolveStatusLocalState( config: CliConfig, @@ -253,7 +253,7 @@ export function resolveStatusLocalState( * An already-resolved {@link resolveLocalConfigValues} result to reuse instead of * re-deriving one. Callers that resolved `local` earlier in the same process (e.g. * `start`'s success-path status print) must pass it here: a second call re-mints a - * time-dependent asymmetric JWT (`auth.signing_keys_path` + {@link generateAsymmetricGoJwt}'s + * time-dependent asymmetric JWT (`auth.signing_keys_path` + {@link generateAsymmetricLocalJwt}'s * `exp` claim) with a different signature than the one baked into the already-running * containers. */ diff --git a/apps/cli/src/command-internal/storage-bucket-config.unit.test.ts b/apps/cli/src/command-internal/storage-bucket-config.unit.test.ts index 042e29ec97..c33d935f48 100644 --- a/apps/cli/src/command-internal/storage-bucket-config.unit.test.ts +++ b/apps/cli/src/command-internal/storage-bucket-config.unit.test.ts @@ -15,7 +15,7 @@ describe("parseFileSizeLimit", () => { expect(() => parseFileSizeLimit("not-a-size")).toThrow(); }); - it("accepts Go-valid numeral forms (strconv.ParseFloat parity)", () => { + it("accepts valid numeral forms", () => { expect(parseFileSizeLimit(".5MiB")).toBe(Math.trunc(0.5 * 1024 * 1024)); expect(parseFileSizeLimit("1.MiB")).toBe(1024 * 1024); expect(parseFileSizeLimit("1e6")).toBe(1_000_000); @@ -23,7 +23,7 @@ describe("parseFileSizeLimit", () => { expect(parseFileSizeLimit("1_0MiB")).toBe(10 * 1024 * 1024); }); - it("rejects badly-placed underscores (Go literal rule)", () => { + it("rejects badly-placed underscores", () => { expect(() => parseFileSizeLimit("_1000MiB")).toThrow("invalid size"); expect(() => parseFileSizeLimit("1__0MiB")).toThrow("invalid size"); }); @@ -34,7 +34,7 @@ describe("parseFileSizeLimit", () => { expect(() => parseFileSizeLimit("-5MiB")).toThrow("invalid size"); }); - it("rejects an overflowing numeral (Go ParseFloat range error)", () => { + it("rejects an overflowing numeral", () => { expect(() => parseFileSizeLimit("1e309")).toThrow("invalid size"); }); }); diff --git a/apps/cli/src/command-internal/storage-content-type.unit.test.ts b/apps/cli/src/command-internal/storage-content-type.unit.test.ts index 3c8c023247..16a0b55ffc 100644 --- a/apps/cli/src/command-internal/storage-content-type.unit.test.ts +++ b/apps/cli/src/command-internal/storage-content-type.unit.test.ts @@ -46,7 +46,7 @@ describe("contentTypeForUpload", () => { }); describe("refineUploadContentType", () => { - it("refines an explicit text/plain content-type by extension (Go refines the flag too)", () => { + it("refines an explicit text/plain content-type by extension", () => { expect(refineUploadContentType("text/plain", "/x/a.json")).toBe("application/json"); }); diff --git a/apps/cli/src/command-internal/storage-credentials.ts b/apps/cli/src/command-internal/storage-credentials.ts index 90303794ac..1fa23f56aa 100644 --- a/apps/cli/src/command-internal/storage-credentials.ts +++ b/apps/cli/src/command-internal/storage-credentials.ts @@ -6,7 +6,7 @@ import { resolveApiExternalUrl } from "./api-url.ts"; import { validateApiPort, validateApiTlsPresence } from "./config-validate.ts"; import { loadProjectEnv } from "./db-config.toml-read.ts"; import { mapTenantApiKeysError } from "./get-tenant-api-keys.ts"; -import { generateGoJwt } from "./go-jwt.ts"; +import { generateLocalJwt } from "./local-jwt.ts"; import { getHostname } from "./hostname.ts"; import { decryptAuthSecret, @@ -224,7 +224,7 @@ const resolveLocalServiceRoleKey = Effect.fnUntraced(function* ( }); return configuredKey !== undefined && configuredKey.length > 0 ? configuredKey - : generateGoJwt(jwtSecret, "service_role"); + : generateLocalJwt(jwtSecret, "service_role"); }); /** diff --git a/apps/cli/src/command-internal/storage-gateway.ts b/apps/cli/src/command-internal/storage-gateway.ts index 3fd73f6f1a..e35048188c 100644 --- a/apps/cli/src/command-internal/storage-gateway.ts +++ b/apps/cli/src/command-internal/storage-gateway.ts @@ -6,7 +6,7 @@ import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"; import { kongAuthHeaders } from "./kong-auth.ts"; import { StorageGatewayNetworkError, StorageGatewayStatusError } from "./storage-gateway.errors.ts"; -import { goPathSplit } from "./storage-url.ts"; +import { splitDirAndFile } from "./storage-url.ts"; /** * Client for the Supabase Storage service gateway (Kong). See @@ -424,7 +424,7 @@ export const makeStorageGateway = Effect.fnUntraced(function* (opts: { Effect.flatMap((body) => decodeFieldResponse(body, "message")), ), listObjects: (bucket, prefix, page) => { - const [dir, name] = goPathSplit(prefix); + const [dir, name] = splitDirAndFile(prefix); const query: Record = { prefix: dir }; if (name.length > 0) query["search"] = name; query["limit"] = PAGE_LIMIT; diff --git a/apps/cli/src/command-internal/storage-gateway.unit.test.ts b/apps/cli/src/command-internal/storage-gateway.unit.test.ts index 59b38ed5b7..96e5b63831 100644 --- a/apps/cli/src/command-internal/storage-gateway.unit.test.ts +++ b/apps/cli/src/command-internal/storage-gateway.unit.test.ts @@ -10,7 +10,7 @@ import { StorageGatewayStatusError } from "./storage-gateway.errors.ts"; import { bucketBody, makeStorageGateway } from "./storage-gateway.ts"; describe("bucketBody", () => { - it("omits public when undefined (Go *bool nil / omitempty)", () => { + it("omits public when undefined", () => { expect(bucketBody({ public: undefined, fileSizeLimit: 0, allowedMimeTypes: [] })).toEqual({}); }); diff --git a/apps/cli/src/command-internal/storage-url.ts b/apps/cli/src/command-internal/storage-url.ts index 6bcc9c9447..22d22fbf28 100644 --- a/apps/cli/src/command-internal/storage-url.ts +++ b/apps/cli/src/command-internal/storage-url.ts @@ -6,8 +6,8 @@ import { } from "../shared/telemetry/error-actionability.ts"; /** - * Storage URL parsing, matching Go's `net/url.Parse` semantics for the `ss://` - * scheme. `goUrlParse` implements `net/url.Parse` for the fields storage + * Storage URL parsing for the `ss://` + * scheme. `parseUrl` parses the fields storage * commands need (scheme, host, path); `parseStorageUrl` additionally requires * scheme `ss` (case-insensitive), a non-empty path, and no host. * @@ -20,10 +20,11 @@ export const STORAGE_SCHEME = "ss"; const STORAGE_INVALID_URL_MESSAGE = "URL must match pattern ss:///bucket/[prefix]"; /** - * Thrown when `goUrlParse` fails: `parse "": `. Callers wrap + * Thrown when `parseUrl` fails: `parse "": `. Callers wrap * `.message` in their own `failed to parse … url: ` text. */ -export class GoUrlParseError extends Error { +export class UrlParseError extends Error { + // The fingerprint and `name` are the stable telemetry/output identity of this error. static readonly [ErrorActionabilityFingerprintId] = "GoUrlParseError"; constructor(rawURL: string, inner: string) { super(`parse "${rawURL}": ${inner}`); @@ -37,7 +38,7 @@ export class GoUrlParseError extends Error { /** * Thrown when a URL parses but does not match the `ss:///bucket/[prefix]` - * pattern. Distinct from `GoUrlParseError` so handlers can map it to a + * pattern. Distinct from `UrlParseError` so handlers can map it to a * separate tagged error. */ export class StorageUrlPatternError extends Error { @@ -52,7 +53,7 @@ export class StorageUrlPatternError extends Error { } } -export interface GoUrl { +export interface ParsedUrl { /** Lowercased scheme; `""` when none. */ readonly scheme: string; /** Authority host (after `//`); `""` when absent. */ @@ -166,12 +167,12 @@ function isValidOptionalPort(port: string): boolean { /** * Validates IP-literal (`[...]`) bracket/port syntax and `host[:port]` - * port-digit checks, matching Go's host validation for realistic inputs but + * port-digit checks, for realistic inputs but * skipping bracketed-literal IP-address checks and IPv6 zone-id decoding. * http/https treat the first colon as the port separator; other schemes use * the last. */ -function validateGoUrlHost(scheme: string, host: string): void { +function validateUrlHost(scheme: string, host: string): void { if (host.length === 0) return; const openBracketIdx = host.indexOf("["); if (openBracketIdx > 0) { @@ -199,16 +200,16 @@ function validateGoUrlHost(scheme: string, host: string): void { } /** - * Parses a URL for `scheme`/`host`/`path`, matching Go's `net/url.Parse`. - * Throws `GoUrlParseError` on parse failure. Query and fragment are stripped, + * Parses a URL for `scheme`/`host`/`path`. + * Throws `UrlParseError` on parse failure. Query and fragment are stripped, * though storage URLs never use them. */ -export function goUrlParse(rawURL: string): GoUrl { +export function parseUrl(rawURL: string): ParsedUrl { const hashIdx = rawURL.indexOf("#"); const u = hashIdx === -1 ? rawURL : rawURL.slice(0, hashIdx); if (containsCtlByte(u)) { - throw new GoUrlParseError(u, "net/url: invalid control character in URL"); + throw new UrlParseError(u, "net/url: invalid control character in URL"); } if (u === "*") { return { scheme: "", host: "", path: "*" }; @@ -221,7 +222,7 @@ export function goUrlParse(rawURL: string): GoUrl { scheme = parsed.scheme.toLowerCase(); rest = parsed.rest; } catch (cause) { - throw new GoUrlParseError(u, cause instanceof Error ? cause.message : String(cause)); + throw new UrlParseError(u, cause instanceof Error ? cause.message : String(cause)); } // Strip the query string. @@ -237,7 +238,7 @@ export function goUrlParse(rawURL: string): GoUrl { const slash = rest.indexOf("/"); const segment = slash === -1 ? rest : rest.slice(0, slash); if (segment.includes(":")) { - throw new GoUrlParseError(u, "first path segment in URL cannot contain colon"); + throw new UrlParseError(u, "first path segment in URL cannot contain colon"); } } @@ -251,9 +252,9 @@ export function goUrlParse(rawURL: string): GoUrl { rest = slash === -1 ? "" : afterSlashes.slice(slash); host = hostFromAuthority(authority); try { - validateGoUrlHost(scheme, host); + validateUrlHost(scheme, host); } catch (cause) { - throw new GoUrlParseError(u, cause instanceof Error ? cause.message : String(cause)); + throw new UrlParseError(u, cause instanceof Error ? cause.message : String(cause)); } } @@ -261,7 +262,7 @@ export function goUrlParse(rawURL: string): GoUrl { try { path = unescapePath(rest); } catch (cause) { - throw new GoUrlParseError(u, cause instanceof Error ? cause.message : String(cause)); + throw new UrlParseError(u, cause instanceof Error ? cause.message : String(cause)); } return { scheme, host, path }; } @@ -269,11 +270,11 @@ export function goUrlParse(rawURL: string): GoUrl { /** * Parses a storage URL, requiring scheme `ss` (case-insensitive), a * non-empty path, and no host, and returns the path. Throws - * `GoUrlParseError` on a parse failure, or `StorageUrlPatternError` when it + * `UrlParseError` on a parse failure, or `StorageUrlPatternError` when it * doesn't match `ss:///bucket/[prefix]`. */ export function parseStorageUrl(objectURL: string): string { - const parsed = goUrlParse(objectURL); + const parsed = parseUrl(objectURL); if (parsed.scheme !== STORAGE_SCHEME || parsed.path.length === 0 || parsed.host.length > 0) { throw new StorageUrlPatternError(); } @@ -299,10 +300,10 @@ export function splitBucketPrefix(objectPath: string): readonly [string, string] /** * Lowercased scheme for a raw URL; `""` when the URL has no scheme (a local - * path). Throws `GoUrlParseError` on a malformed URL (e.g. `:`). + * path). Throws `UrlParseError` on a malformed URL (e.g. `:`). */ export function detectScheme(rawURL: string): string { - return goUrlParse(rawURL).scheme; + return parseUrl(rawURL).scheme; } /** An object prefix is a directory when it is empty or ends with `/`. */ @@ -315,7 +316,7 @@ export function storageIsDir(objectPrefix: string): boolean { * trailing slash: `folder/name.png` → `["folder/", "name.png"]`; `dir` → * `["", "dir"]`; `tmp/` → `["tmp/", ""]`; `""` → `["", ""]`. */ -export function goPathSplit(p: string): readonly [string, string] { +export function splitDirAndFile(p: string): readonly [string, string] { const i = p.lastIndexOf("/"); return [p.slice(0, i + 1), p.slice(i + 1)]; } diff --git a/apps/cli/src/command-internal/storage-url.unit.test.ts b/apps/cli/src/command-internal/storage-url.unit.test.ts index 284b0ab346..c2f4d72719 100644 --- a/apps/cli/src/command-internal/storage-url.unit.test.ts +++ b/apps/cli/src/command-internal/storage-url.unit.test.ts @@ -1,10 +1,10 @@ import { describe, expect, it } from "vitest"; import { - GoUrlParseError, + UrlParseError, StorageUrlPatternError, detectScheme, - goUrlParse, + parseUrl, parseStorageUrl, splitBucketPrefix, storageIsDir, @@ -31,7 +31,7 @@ describe("parseStorageUrl", () => { }); it("surfaces the url-parse error on a missing protocol scheme (:)", () => { - expect(() => parseStorageUrl(":")).toThrow(GoUrlParseError); + expect(() => parseStorageUrl(":")).toThrow(UrlParseError); expect(() => parseStorageUrl(":")).toThrow("missing protocol scheme"); }); @@ -40,7 +40,7 @@ describe("parseStorageUrl", () => { }); it("rejects a malformed percent-escape in the path", () => { - expect(() => parseStorageUrl("ss:///bucket/a%2")).toThrow(GoUrlParseError); + expect(() => parseStorageUrl("ss:///bucket/a%2")).toThrow(UrlParseError); expect(() => parseStorageUrl("ss:///bucket/a%2")).toThrow('invalid URL escape "%2"'); }); @@ -48,8 +48,8 @@ describe("parseStorageUrl", () => { expect(parseStorageUrl("ss:///bucket/a%20b")).toBe("/bucket/a b"); }); - it("decodes a multi-byte UTF-8 percent-escape as one rune (Go raw-byte path)", () => { - // %E4%B8%AD is the UTF-8 encoding of 中 — decoded as one rune, not three. + it("decodes a multi-byte UTF-8 percent-escape as one code point", () => { + // %E4%B8%AD is the UTF-8 encoding of 中 — decoded as one code point, not three. expect(parseStorageUrl("ss:///bucket/%E4%B8%AD.txt")).toBe("/bucket/中.txt"); }); }); @@ -85,44 +85,44 @@ describe("detectScheme", () => { }); // Host validation here also backs `studio.api_url` validation. -describe("goUrlParse (host validation)", () => { +describe("parseUrl (host validation)", () => { it("rejects an unterminated IPv6 literal", () => { - expect(() => goUrlParse("http://[::1")).toThrow(GoUrlParseError); - expect(() => goUrlParse("http://[::1")).toThrow(`parse "http://[::1": missing ']' in host`); + expect(() => parseUrl("http://[::1")).toThrow(UrlParseError); + expect(() => parseUrl("http://[::1")).toThrow(`parse "http://[::1": missing ']' in host`); }); it("accepts a bracketed IPv6 literal with no port", () => { - expect(goUrlParse("http://[::1]").host).toBe("[::1]"); + expect(parseUrl("http://[::1]").host).toBe("[::1]"); }); it("accepts a bracketed IPv6 literal with a valid port", () => { - expect(goUrlParse("http://[::1]:8080").host).toBe("[::1]:8080"); + expect(parseUrl("http://[::1]:8080").host).toBe("[::1]:8080"); }); it("rejects a bracketed IPv6 literal with a non-numeric port", () => { - expect(() => goUrlParse("http://[::1]:abc")).toThrow( + expect(() => parseUrl("http://[::1]:abc")).toThrow( `parse "http://[::1]:abc": invalid port ":abc" after host`, ); }); it("rejects a bracket that isn't the first character of the host", () => { - expect(() => goUrlParse("http://host[::1]")).toThrow( + expect(() => parseUrl("http://host[::1]")).toThrow( `parse "http://host[::1]": invalid IP-literal`, ); }); it("accepts a plain hostname with a numeric port", () => { - expect(goUrlParse("http://example.com:99999").host).toBe("example.com:99999"); + expect(parseUrl("http://example.com:99999").host).toBe("example.com:99999"); }); it("rejects more than one colon in an http(s) host (strict-colon default)", () => { - expect(() => goUrlParse("http://host:1:2")).toThrow( + expect(() => parseUrl("http://host:1:2")).toThrow( `parse "http://host:1:2": invalid port ":1:2" after host`, ); }); it("does not validate a host for a scheme with no authority (ss:///bucket)", () => { - expect(() => goUrlParse("ss:///bucket/x")).not.toThrow(); + expect(() => parseUrl("ss:///bucket/x")).not.toThrow(); }); }); diff --git a/apps/cli/src/command-internal/string-slice-flag.ts b/apps/cli/src/command-internal/string-slice-flag.ts index d754c79305..bebe3449b0 100644 --- a/apps/cli/src/command-internal/string-slice-flag.ts +++ b/apps/cli/src/command-internal/string-slice-flag.ts @@ -6,8 +6,8 @@ import { } from "../shared/telemetry/error-actionability.ts"; /** - * Parses a pflag `StringSliceVar` flag: CSV-splits each occurrence, matching - * `encoding/csv`, and accumulates across repeats. A naive `.split(",")` + * Parses a repeatable string-slice flag: CSV-splits each occurrence + * and accumulates across repeats. A naive `.split(",")` * diverges on quoted/embedded commas (e.g. `'"a,b",c'`). * * Whitespace is not trimmed and empty fields are not dropped. @@ -64,8 +64,7 @@ export class StringSliceFlagParseError extends Error { } /** - * Parses one CSV record from `val`, matching Go's `encoding/csv` reader as - * used by pflag's `readAsCSV`. Only the first record is read — an unquoted + * Parses one CSV record from `val`. Only the first record is read — an unquoted * newline ends it and the rest is dropped. Blank lines before the record are * skipped; blank-only input throws `EOF`. `\r\n` normalizes to `\n`, and * parse errors report 1-based line/byte-column positions. @@ -242,10 +241,10 @@ export function parseStringSliceFlag(rawValues: ReadonlyArray): Readonly } /** - * Builds a repeatable CSV-split flag matching pflag's `StringSliceVar` - * behavior, including its `invalid argument %q for %q flag: %v` diagnostic. + * Builds a repeatable CSV-split flag, including its `invalid argument "" for "" flag: ` + * diagnostic. * - * `options.alias` must be registered here, not piped on afterwards: pflag's + * `options.alias` must be registered here, not piped on afterwards: the * diagnostic frames both spellings (`-x, --exclude`), so the alias must be * present when the error message is built. */ @@ -255,14 +254,14 @@ export function stringSliceFlag( options?: { readonly alias?: string }, ) { const alias = options?.alias; - const pflagName = alias === undefined ? `--${name}` : `-${alias}, --${name}`; + const flagLabel = alias === undefined ? `--${name}` : `-${alias}, --${name}`; const base = Flag.string(name).pipe(Flag.withDescription(description), Flag.atLeast(0)); return (alias === undefined ? base : base.pipe(Flag.withAlias(alias))).pipe( Flag.mapTryCatch( (rawValues) => parseStringSliceFlag(rawValues), (err) => err instanceof StringSliceFlagParseError - ? `invalid argument ${JSON.stringify(err.value)} for "${pflagName}" flag: ${err.message}` + ? `invalid argument ${JSON.stringify(err.value)} for "${flagLabel}" flag: ${err.message}` : err instanceof Error ? err.message : String(err), diff --git a/apps/cli/src/command-internal/string-slice-flag.unit.test.ts b/apps/cli/src/command-internal/string-slice-flag.unit.test.ts index 7d70f88533..438e1bf775 100644 --- a/apps/cli/src/command-internal/string-slice-flag.unit.test.ts +++ b/apps/cli/src/command-internal/string-slice-flag.unit.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { parseStringSliceFlag, StringSliceFlagParseError } from "./string-slice-flag.ts"; -describe("parseStringSliceFlag (pflag StringSlice CSV parity)", () => { +describe("parseStringSliceFlag", () => { it("splits unquoted comma-separated values", () => { expect(parseStringSliceFlag(["public,private"])).toEqual(["public", "private"]); }); @@ -34,13 +34,13 @@ describe("parseStringSliceFlag (pflag StringSlice CSV parity)", () => { expect(parseStringSliceFlag([])).toEqual([]); }); - it("preserves whitespace (Go does not trim)", () => { + it("preserves whitespace (no trimming)", () => { expect(parseStringSliceFlag([" public , private "])).toEqual([" public ", " private "]); }); // Columns are 1-based byte offsets. - it("throws on an unterminated quoted field (column = byte length + 1, Go hits EOF)", () => { + it("throws on an unterminated quoted field (column = byte length + 1, reached EOF)", () => { expect(() => parseStringSliceFlag(['"tenant'])).toThrow(StringSliceFlagParseError); expect(() => parseStringSliceFlag(['"tenant'])).toThrow( 'parse error on line 1, column 8: extraneous or missing " in quoted-field', @@ -74,7 +74,7 @@ describe("parseStringSliceFlag (pflag StringSlice CSV parity)", () => { ); }); - it("counts columns in bytes, not code points (Go csv tracks byte offsets)", () => { + it("counts columns in bytes, not code points", () => { // é is 2 UTF-8 bytes, so the bare quote is at byte 3. expect(() => parseStringSliceFlag(['é"x'])).toThrow( 'parse error on line 1, column 3: bare " in non-quoted-field', @@ -85,7 +85,7 @@ describe("parseStringSliceFlag (pflag StringSlice CSV parity)", () => { ); }); - it("carries the offending occurrence and Go's exact message on the error for pflag framing", () => { + it("carries the offending occurrence and the exact message on the error for flag framing", () => { try { parseStringSliceFlag(["public", '"broken']); expect.unreachable("expected parseStringSliceFlag to throw"); @@ -106,7 +106,7 @@ describe("parseStringSliceFlag (pflag StringSlice CSV parity)", () => { // Only the first CSV record survives; blank lines before it are skipped, // `\r\n` normalizes to `\n`, and parse errors report per-line byte columns. - it("keeps only the first record when an unquoted newline ends it (pflag reads ONE record)", () => { + it("keeps only the first record when an unquoted newline ends it (only ONE record is read)", () => { expect(parseStringSliceFlag(["1.2.3.4\n5.6.7.8"])).toEqual(["1.2.3.4"]); // The dropped remainder is itself malformed CSV, but only the first record is parsed. expect(parseStringSliceFlag(['1.2.3.4\na"b'])).toEqual(["1.2.3.4"]); @@ -116,7 +116,7 @@ describe("parseStringSliceFlag (pflag StringSlice CSV parity)", () => { expect(parseStringSliceFlag(["a\r\nb"])).toEqual(["a"]); }); - it("skips blank lines before the record and errors with pflag's EOF for blank-only values", () => { + it("skips blank lines before the record and errors with EOF for blank-only values", () => { expect(parseStringSliceFlag(["\n\na"])).toEqual(["a"]); expect(parseStringSliceFlag(["\r\na"])).toEqual(["a"]); // A value of only blank lines throws the bare `EOF` message. diff --git a/apps/cli/src/command-internal/rune-width.ts b/apps/cli/src/command-internal/string-width.ts similarity index 98% rename from apps/cli/src/command-internal/rune-width.ts rename to apps/cli/src/command-internal/string-width.ts index 110dbb7fb8..ac73acf2c2 100644 --- a/apps/cli/src/command-internal/rune-width.ts +++ b/apps/cli/src/command-internal/string-width.ts @@ -376,7 +376,7 @@ function inRanges(cp: number, ranges: ReadonlyArray): boolean { } /** Display width of a single code point (0, 1, or 2). */ -function runeWidth(cp: number): number { +function codePointWidth(cp: number): number { // C0/C1 controls (except those handled by the caller) have no print width. if (cp === 0) return 0; if (cp < 0x20 || (cp >= 0x7f && cp < 0xa0)) return 0; @@ -388,6 +388,6 @@ function runeWidth(cp: number): number { /** Display width of a string, summing per-code-point widths. */ export function stringWidth(text: string): number { let width = 0; - for (const ch of text) width += runeWidth(ch.codePointAt(0)!); + for (const ch of text) width += codePointWidth(ch.codePointAt(0)!); return width; } diff --git a/apps/cli/src/command-internal/rune-width.unit.test.ts b/apps/cli/src/command-internal/string-width.unit.test.ts similarity index 88% rename from apps/cli/src/command-internal/rune-width.unit.test.ts rename to apps/cli/src/command-internal/string-width.unit.test.ts index 92d4cb21f0..44ea607d25 100644 --- a/apps/cli/src/command-internal/rune-width.unit.test.ts +++ b/apps/cli/src/command-internal/string-width.unit.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { stringWidth } from "./rune-width.ts"; +import { stringWidth } from "./string-width.ts"; describe("stringWidth", () => { it("counts ASCII as 1 each", () => { @@ -25,7 +25,7 @@ describe("stringWidth", () => { }); it("treats East Asian Ambiguous as width 1 (modern-terminal default)", () => { - // U+00A1 (¡) is Ambiguous; Go's runewidth with EastAsianWidth=false counts it as 1. + // U+00A1 (¡) is Ambiguous and counts as 1. expect(stringWidth("¡")).toBe(1); }); }); diff --git a/apps/cli/src/command-internal/go-struct-output.encoders.ts b/apps/cli/src/command-internal/struct-output.encoders.ts similarity index 74% rename from apps/cli/src/command-internal/go-struct-output.encoders.ts rename to apps/cli/src/command-internal/struct-output.encoders.ts index e8d07e326f..01713fe098 100644 --- a/apps/cli/src/command-internal/go-struct-output.encoders.ts +++ b/apps/cli/src/command-internal/struct-output.encoders.ts @@ -6,76 +6,75 @@ import { } from "../shared/telemetry/error-actionability.ts"; /** - * Byte-faithful `-o yaml`/`-o toml` output for struct payloads, matching `gopkg.in/yaml.v3` and - * `github.com/BurntSushi/toml`. Neither library reads `json:` tags, so emitted keys are the - * original Go field names, not the snake_case JSON the API returns. + * Byte-stable `-o yaml`/`-o toml` output for struct payloads. Emitted keys are the + * PascalCase field names declared by the shape, not the snake_case JSON the API returns. * - * Each payload family declares a {@link GoType} spec mirroring the original struct so the decoded - * JSON can be re-expressed with each library's own casing, nil handling, and quoting rules. + * Each payload family declares a {@link OutputShape} spec describing the struct so the decoded + * JSON can be re-expressed with the established casing, null handling, and quoting rules. */ -export type GoType = +export type OutputShape = | { readonly kind: "string" } | { readonly kind: "uuid" } | { readonly kind: "bool" } | { readonly kind: "int" } | { readonly kind: "float"; readonly bits: 32 | 64 } - /** Go `time.Time` — native TOML datetime, unquoted yaml timestamp. */ + /** RFC3339 timestamp — native TOML datetime, unquoted yaml timestamp. */ | { readonly kind: "time" } - /** Go `interface{}` — shape inferred from the JSON value like `encoding/json` decoding. */ + /** Untyped value — shape inferred from the JSON value. */ | { readonly kind: "any" } - | { readonly kind: "ptr"; readonly elem: GoType } - /** oapi-codegen `nullable.Nullable[T]` — a `map[bool]T` under the hood. */ - | { readonly kind: "nullable"; readonly elem: GoType } - | { readonly kind: "slice"; readonly elem: GoType } - | { readonly kind: "map"; readonly value: GoType } - | { readonly kind: "struct"; readonly fields: ReadonlyArray }; - -interface GoStructField { + | { readonly kind: "ptr"; readonly elem: OutputShape } + /** A nullable API field: absent, explicit null, or a value. */ + | { readonly kind: "nullable"; readonly elem: OutputShape } + | { readonly kind: "slice"; readonly elem: OutputShape } + | { readonly kind: "map"; readonly value: OutputShape } + | { readonly kind: "struct"; readonly fields: ReadonlyArray }; + +interface OutputShapeField { /** JSON tag name — the key present in the decoded payload. */ readonly json: string; - /** Go field name (PascalCase). */ - readonly go: string; - readonly type: GoType; + /** Struct field name (PascalCase). */ + readonly name: string; + readonly type: OutputShape; } -export const goString: GoType = { kind: "string" }; -export const goUuid: GoType = { kind: "uuid" }; -export const goBool: GoType = { kind: "bool" }; -export const goInt: GoType = { kind: "int" }; -export const goFloat32: GoType = { kind: "float", bits: 32 }; -export const goFloat64: GoType = { kind: "float", bits: 64 }; -export const goTime: GoType = { kind: "time" }; -export const goAny: GoType = { kind: "any" }; +export const shapeString: OutputShape = { kind: "string" }; +export const shapeUuid: OutputShape = { kind: "uuid" }; +export const shapeBool: OutputShape = { kind: "bool" }; +export const shapeInt: OutputShape = { kind: "int" }; +export const shapeFloat32: OutputShape = { kind: "float", bits: 32 }; +export const shapeFloat64: OutputShape = { kind: "float", bits: 64 }; +export const shapeTime: OutputShape = { kind: "time" }; +export const shapeAny: OutputShape = { kind: "any" }; -export function goPtr(elem: GoType): GoType { +export function shapePtr(elem: OutputShape): OutputShape { return { kind: "ptr", elem }; } -export function goNullable(elem: GoType): GoType { +export function shapeNullable(elem: OutputShape): OutputShape { return { kind: "nullable", elem }; } -export function goSlice(elem: GoType): GoType { +export function shapeSlice(elem: OutputShape): OutputShape { return { kind: "slice", elem }; } -export function goMap(value: GoType): GoType { +export function shapeMap(value: OutputShape): OutputShape { return { kind: "map", value }; } /** - * A struct field spec entry: `[jsonName, type]` derives the Go field name mechanically (each - * snake_case token capitalized: `api_key` → `ApiKey`), or `[jsonName, type, goName]` for explicit + * A struct field spec entry: `[jsonName, type]` derives the field name mechanically (each + * snake_case token capitalized: `api_key` → `ApiKey`), or `[jsonName, type, fieldName]` for explicit * names. */ -export type GoFieldSpec = - | readonly [json: string, type: GoType] - | readonly [json: string, type: GoType, goName: string]; +export type OutputShapeFieldSpec = + | readonly [json: string, type: OutputShape] + | readonly [json: string, type: OutputShape, fieldName: string]; -export function goStruct(fields: ReadonlyArray): GoType { +export function shapeStruct(fields: ReadonlyArray): OutputShape { return { kind: "struct", - fields: fields.map(([json, type, goName]) => ({ + fields: fields.map(([json, type, fieldName]) => ({ json, - go: goName ?? goFieldName(json), + name: fieldName ?? pascalCaseFieldName(json), type, })), }; @@ -83,57 +82,61 @@ export function goStruct(fields: ReadonlyArray): GoType { /** * The anonymous wrapper struct list commands use for TOML output (a `toml:` tag keeps the wrapper - * key lowercase while the elements keep Go field names), and also models a single-key map wrapper + * key lowercase while the elements keep PascalCase field names), and also models a single-key map wrapper * (`sso list`), since a one-field lowercase-keyed struct renders identically to a one-key map in * both encoders. */ -export function goTomlListWrapper(key: string, elem: GoType): GoType { - return { kind: "struct", fields: [{ json: key, go: key, type: goSlice(elem) }] }; +export function shapeTomlListWrapper(key: string, elem: OutputShape): OutputShape { + return { kind: "struct", fields: [{ json: key, name: key, type: shapeSlice(elem) }] }; } /** `api_key` → `ApiKey`, `dbAllowedCidrs` → `DbAllowedCidrs`. */ -export function goFieldName(jsonName: string): string { +export function pascalCaseFieldName(jsonName: string): string { return jsonName .split("_") .map((part) => (part.length === 0 ? part : part[0]?.toUpperCase() + part.slice(1))) .join(""); } -type GoValue = +type NormalizedValue = | { readonly k: "nil" } | { readonly k: "str"; readonly v: string } | { readonly k: "bool"; readonly v: boolean } | { readonly k: "int"; readonly v: number } | { readonly k: "float"; readonly v: number; readonly bits: 32 | 64 } | { readonly k: "time"; readonly v: string } - | { readonly k: "struct"; readonly entries: ReadonlyArray } + | { readonly k: "struct"; readonly entries: ReadonlyArray } | { readonly k: "map"; readonly nil: boolean; - readonly entries: ReadonlyArray; + readonly entries: ReadonlyArray; + } + /** A nullable field: absent (nil), `{false: zero}` (explicit null) or `{true: value}`. */ + | { + readonly k: "nullable"; + readonly present: boolean | undefined; + readonly value?: NormalizedValue; } - /** `nullable.Nullable[T]`: nil map, `{false: zero}` (explicit null) or `{true: value}`. */ - | { readonly k: "nullable"; readonly present: boolean | undefined; readonly value?: GoValue } | { readonly k: "slice"; readonly nil: boolean; - readonly items: ReadonlyArray; + readonly items: ReadonlyArray; readonly tables: boolean; }; -const GO_ZERO_TIME = "0001-01-01T00:00:00Z"; -const GO_ZERO_UUID = "00000000-0000-0000-0000-000000000000"; +const ZERO_TIME = "0001-01-01T00:00:00Z"; +const ZERO_UUID = "00000000-0000-0000-0000-000000000000"; function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } -function zeroValue(type: GoType): GoValue { +function zeroValue(type: OutputShape): NormalizedValue { switch (type.kind) { case "string": return { k: "str", v: "" }; case "uuid": - return { k: "str", v: GO_ZERO_UUID }; + return { k: "str", v: ZERO_UUID }; case "bool": return { k: "bool", v: false }; case "int": @@ -141,7 +144,7 @@ function zeroValue(type: GoType): GoValue { case "float": return { k: "float", v: 0, bits: type.bits }; case "time": - return { k: "time", v: GO_ZERO_TIME }; + return { k: "time", v: ZERO_TIME }; case "struct": return normalize(undefined, type); case "ptr": @@ -156,7 +159,7 @@ function zeroValue(type: GoType): GoValue { } } -function elementsAreTables(elem: GoType, items: ReadonlyArray): boolean { +function elementsAreTables(elem: OutputShape, items: ReadonlyArray): boolean { switch (elem.kind) { case "struct": case "map": @@ -166,15 +169,14 @@ function elementsAreTables(elem: GoType, items: ReadonlyArray): boolean case "slice": return elem.kind === "ptr" ? elementsAreTables(elem.elem, items) : false; case "any": - // Like Go's runtime type inspection: JSON objects decode to - // map[string]interface{} which BurntSushi treats as tables. + // JSON objects decode to maps, which are rendered as tables. return items.length > 0 && items.every(isRecord); default: return false; } } -function normalize(value: unknown, type: GoType): GoValue { +function normalize(value: unknown, type: OutputShape): NormalizedValue { switch (type.kind) { case "string": return typeof value === "string" ? { k: "str", v: value } : zeroValue(type); @@ -194,13 +196,12 @@ function normalize(value: unknown, type: GoType): GoValue { : zeroValue(type); case "time": return typeof value === "string" && value.length > 0 - ? { k: "time", v: normalizeGoTime(value) } + ? { k: "time", v: normalizeTime(value) } : zeroValue(type); case "ptr": return value === undefined || value === null ? { k: "nil" } : normalize(value, type.elem); case "nullable": - // oapi-codegen: absent key → nil map; explicit JSON null → {false: zero}; - // value → {true: value}. + // Absent key → nil; explicit JSON null → {false: zero}; value → {true: value}. if (value === undefined) return { k: "nullable", present: undefined }; if (value === null) return { k: "nullable", present: false, value: zeroValue(type.elem) }; return { k: "nullable", present: true, value: normalize(value, type.elem) }; @@ -228,7 +229,7 @@ function normalize(value: unknown, type: GoType): GoValue { return { k: "struct", entries: type.fields.map( - (field) => [field.go, normalize(record[field.json], field.type)] as const, + (field) => [field.name, normalize(record[field.json], field.type)] as const, ), }; } @@ -237,13 +238,13 @@ function normalize(value: unknown, type: GoType): GoValue { } } -/** Mirror `encoding/json` decoding into `interface{}`. */ -function normalizeAny(value: unknown): GoValue { +/** Normalize a decoded JSON value into an untyped value. */ +function normalizeAny(value: unknown): NormalizedValue { if (value === undefined || value === null) return { k: "nil" }; if (typeof value === "string") return { k: "str", v: value }; if (typeof value === "boolean") return { k: "bool", v: value }; if (typeof value === "number") { - // JSON numbers decode to float64 in Go's interface{} world. + // JSON numbers decode to float64. return { k: "float", v: value, bits: 64 }; } if (Array.isArray(value)) { @@ -265,13 +266,12 @@ function normalizeAny(value: unknown): GoValue { } /** - * Formats an RFC3339 input the way Go renders a decoded `time.Time` with `time.RFC3339Nano`: the + * Formats an RFC3339 input in `RFC3339Nano` form: the * fraction truncated (not rounded) to at most 9 digits, then trailing zeros trimmed and a zero * offset rendered as `Z`. */ -function normalizeGoTime(value: string): string { - // Both `.` and `,` are accepted as the fractional separator on decode; normalize to the dot Go - // emits. +function normalizeTime(value: string): string { + // Both `.` and `,` are accepted as the fractional separator on decode; normalize to the dot. const match = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})([.,]\d+)?(Z|[+-]\d{2}:\d{2})$/.exec(value); if (match === null) return value; const [, base, fraction, offset] = match; @@ -285,10 +285,9 @@ function normalizeGoTime(value: string): string { } /** - * Shortest round-trip digits for a float32 value, matching `strconv.FormatFloat(f, 'g', -1, 32)`'s - * Ryu algorithm: the fewest significant digits that parse back to the same float32, with an exact + * Shortest round-trip digits for a float32 value (Ryu algorithm): the fewest significant digits that parse back to the same float32, with an exact * decimal tie rounded to the even final digit (unlike JS `toPrecision`, which rounds half up). - * Returns `e<±exp>` for {@link goFormatFloat}. + * Returns `e<±exp>` for {@link formatStructFloat}. */ function shortestFloat32(value: number): string { const rounded = Math.fround(value); @@ -357,16 +356,15 @@ function roundDecimalDigits( } /** - * `strconv.FormatFloat(f, 'g', -1, bits)`: shortest digits, switching to - * scientific notation when the decimal exponent is < -4 or >= 6 (Go uses - * `eprec = 6` for shortest formatting), with a sign and >= 2 exponent digits. + * Shortest round-trip digits, switching to scientific notation when the decimal exponent is + * < -4 or >= 6, with a sign and >= 2 exponent digits. */ -export function goFormatFloat(value: number, bits: 32 | 64): string { +export function formatStructFloat(value: number, bits: 32 | 64): string { if (Number.isNaN(value)) return "NaN"; if (value === Infinity) return "+Inf"; if (value === -Infinity) return "-Inf"; const repr = bits === 32 ? shortestFloat32(value) : String(value); - // JS String(-0) drops the sign; Go's FormatFloat keeps it ("-0"). + // JS String(-0) drops the sign; the output keeps it ("-0"). const negative = repr.startsWith("-") || Object.is(value, -0); const unsigned = negative ? repr.slice(1) : repr; // Decompose into digits + decimal exponent. @@ -403,14 +401,13 @@ export function goFormatFloat(value: number, bits: 32 | 64): string { } /** - * Encode a decoded payload as the Go CLI's `-o yaml` output for the given Go - * struct spec. Returns the full document bytes (trailing newline included). + * Encode a decoded payload as `-o yaml` output for the given struct spec. Returns the full document bytes (trailing newline included). */ -export function encodeGoYaml(value: unknown, type: GoType): string { +export function encodeStructYaml(value: unknown, type: OutputShape): string { return yamlDocument(normalize(value, type)); } -function yamlDocument(root: GoValue): string { +function yamlDocument(root: NormalizedValue): string { switch (root.k) { case "slice": if (root.items.length === 0) return "[]\n"; @@ -431,43 +428,40 @@ function yamlDocument(root: GoValue): string { } /** - * A populated `nullable.Nullable[T]` is a `map[bool]T`; yaml.v3 renders the - * bool key plain (`true:` / `false:`), unlike the string keys `"true"` would - * produce. + * A populated nullable renders as a mapping with a plain bool key (`true:` / `false:`), + * unlike the quoted string keys `"true"` would produce. */ -function yamlNullableBlock(present: boolean, value: GoValue, indent: number): string { +function yamlNullableBlock(present: boolean, value: NormalizedValue, indent: number): string { const pad = " ".repeat(indent); return `${pad}${present ? "true" : "false"}:${yamlValueSuffix(value, indent)}`; } -/** yaml.v3's indent algorithm: children of a mapping align to the next 4-column stop. */ +/** Children of a mapping align to the next 4-column stop. */ function yamlNextIndent(indent: number): number { return 4 * Math.floor((indent + 4) / 4); } function yamlStructEntries( - entries: ReadonlyArray, -): ReadonlyArray { - // yaml.v3 lowercases Go field names wholesale (no yaml tags on these structs). - return entries.map(([go, value]) => [go.toLowerCase(), value] as const); + entries: ReadonlyArray, +): ReadonlyArray { + // Field names are lowercased wholesale in YAML output. + return entries.map(([name, value]) => [name.toLowerCase(), value] as const); } function yamlMapEntries( - entries: ReadonlyArray, -): ReadonlyArray { + entries: ReadonlyArray, +): ReadonlyArray { return [...entries].sort(([a], [b]) => (yamlKeyLess(a, b) ? -1 : yamlKeyLess(b, a) ? 1 : 0)); } /** - * Unicode code-point string ordering, matching both `sort.Strings` (UTF-8 byte order) and - * yaml.v3's `keyList.Less` (rune order) — the two are equivalent, and both differ from JS `<` - * (UTF-16 code-unit order) when an astral character meets a high-BMP one (Go sorts U+E000 before - * U+1F600, UTF-16 the reverse). + * Unicode code-point string ordering (equivalent to UTF-8 byte order), which differs from JS `<` + * (UTF-16 code-unit order) when an astral character meets a high-BMP one (code-point order sorts + * U+E000 before U+1F600, UTF-16 the reverse). * - * Also used by `go-output.encoders.ts`'s `sortKeysDeep`, since `encoding/json`'s map-key sort is - * the same order. + * Also used by `output.encoders.ts`'s `sortKeysDeep`. */ -export function goStringCompare(a: string, b: string): number { +export function compareByCodepoint(a: string, b: string): number { let i = 0; while (i < a.length && i < b.length) { const ac = a.codePointAt(i) as number; @@ -479,8 +473,8 @@ export function goStringCompare(a: string, b: string): number { } /** - * yaml.v3's `keyList.Less` natural string ordering. Digit runs use `unicode.IsDigit` (any Unicode - * `Nd` digit), so a non-ASCII digit like Arabic-Indic sorts by its raw code point and ends up + * Natural string ordering: runs of digits compare numerically. Digit runs match any Unicode + * `Nd` digit, so a non-ASCII digit like Arabic-Indic sorts by its raw code point and ends up * after ASCII digit runs. The ASCII-only {@link isDigit} stays for the scalar parser. */ function yamlKeyLess(a: string, b: string): boolean { @@ -496,7 +490,7 @@ function yamlKeyLess(a: string, b: string): boolean { } const al = isLetter(ac); const bl = isLetter(bc); - // Go compares runes (`ar[i] < br[i]`), i.e. code points, not UTF-16 units. + // Compare code points, not UTF-16 units. if (al && bl) return (ac.codePointAt(0) as number) < (bc.codePointAt(0) as number); if (al || bl) return digits ? al : bl; let an = 0n; @@ -512,7 +506,7 @@ function yamlKeyLess(a: string, b: string): boolean { } let ai = i; let bi = i; - // Go accumulates into `int64` without overflow checks, so a 19+-digit run wraps negative and + // Digit runs accumulate into `int64` without overflow checks, so a 19+-digit run wraps negative and // sorts before a shorter positive run. `BigInt.asIntN(64, …)` reproduces the wrap. for (; ai < ar.length && isSortDigit(ar[ai] as string); ai++) { an = BigInt.asIntN(64, an * 10n + BigInt(((ar[ai] as string).codePointAt(0) as number) - 48)); @@ -527,7 +521,7 @@ function yamlKeyLess(a: string, b: string): boolean { return ar.length < br.length; } -/** yaml.v3 sorter's `unicode.IsDigit` — any Unicode decimal digit (`Nd`). */ +/** Any Unicode decimal digit (`Nd`), as used by the key sorter. */ function isSortDigit(c: string): boolean { return /\p{Nd}/u.test(c); } @@ -540,7 +534,10 @@ function isLetter(c: string): boolean { return /\p{L}/u.test(c); } -function yamlMapping(entries: ReadonlyArray, indent: number): string { +function yamlMapping( + entries: ReadonlyArray, + indent: number, +): string { const pad = " ".repeat(indent); let out = ""; for (const [key, value] of entries) { @@ -554,7 +551,7 @@ function yamlMapping(entries: ReadonlyArray, indent: * Everything after `key:` — either ` \n`, a block-literal header plus * content lines, or `\n` plus an indented child block. */ -function yamlValueSuffix(value: GoValue, indent: number): string { +function yamlValueSuffix(value: NormalizedValue, indent: number): string { switch (value.k) { case "nil": return " null\n"; @@ -589,7 +586,7 @@ function yamlValueSuffix(value: GoValue, indent: number): string { } } -function yamlSequence(items: ReadonlyArray, indent: number): string { +function yamlSequence(items: ReadonlyArray, indent: number): string { const pad = " ".repeat(indent); let out = ""; for (const item of items) { @@ -603,7 +600,7 @@ function yamlSequence(items: ReadonlyArray, indent: number): string { break; } // Compact form: the first key rides on the `- ` line; the block keeps - // a +2 indent (yaml.v3 special-cases indent inside sequence items). + // a +2 indent (indent inside sequence items is special-cased). const block = yamlMapping(entries, indent + 2); out += `${pad}- ${block.slice(indent + 2)}`; break; @@ -642,7 +639,7 @@ function yamlSequence(items: ReadonlyArray, indent: number): string { return out; } -function yamlScalar(value: GoValue): string { +function yamlScalar(value: NormalizedValue): string { switch (value.k) { case "nil": return "null"; @@ -651,7 +648,7 @@ function yamlScalar(value: GoValue): string { case "int": return String(value.v); case "float": - return goFormatFloat(value.v, value.bits); + return formatStructFloat(value.v, value.bits); case "time": return value.v; case "str": { @@ -673,7 +670,7 @@ function yamlKeyScalar(key: string): string { type YamlStringStyle = "plain" | "single" | "double" | "literal"; /** - * yaml.v3's style selection: literal for multi-line strings, double quotes for a string that + * Style selection: literal for multi-line strings, double quotes for a string that * resolves to a non-string tag, otherwise downgraded from plain to single (or double) based on * scalar analysis. */ @@ -693,7 +690,7 @@ function yamlStringStyle(s: string): YamlStringStyle { } /** - * Characters yaml.v3 treats as "special" (not printable) or line breaks other + * Characters treated as "special" (not printable) or line breaks other * than `\n` — all of these force double-quoted style with escapes. U+2028 and * U+2029 are technically YAML line breaks, but every realistic payload * containing them round-trips through the double-quoted `\L` / `\P` escapes. @@ -709,8 +706,7 @@ function yamlHasSpecialChars(s: string): boolean { } /** - * libyaml's `is_printable` in code-point terms. The byte-oriented original never accepts a 4-byte - * UTF-8 lead, so every astral character — along with C0/C1 controls, DEL, surrogates, the U+FEFF + * YAML printability in code-point terms: every astral character — along with C0/C1 controls, DEL, surrogates, the U+FEFF * BOM, and U+FFFE/U+FFFF — is "not printable" and gets double-quoted escapes. */ function yamlIsPrintable(code: number): boolean { @@ -736,7 +732,7 @@ function yamlPlainDisallowed(s: string): boolean { } /** - * Would yaml.v3's `resolve("", s)` produce a non-string tag? Also covers the + * Would the plain scalar `s` resolve to a non-string YAML tag? Also covers the * YAML 1.1 "old bool" and base-60 spellings the encoder force-quotes. */ function yamlResolvesToString(s: string): boolean { @@ -745,15 +741,15 @@ function yamlResolvesToString(s: string): boolean { if (YAML_BASE60.test(s)) return false; const first = s[0] as string; if (first === ".") { - // strconv.ParseFloat errors on overflow (±Inf), so an overflowing spelling like `1e999` stays + // Float parsing errors on overflow (±Inf), so an overflowing spelling like `1e999` stays // a string and needs no quoting. return !(/^\.\d+(?:[eE][+-]?\d+)?$/.test(s) && Number.isFinite(Number(s))); } if (first === "+" || first === "-" || isDigit(first)) { if (yamlIsTimestamp(s)) return false; const plain = s.replaceAll("_", ""); - if (goParseIntBase0(plain)) return false; - // An overflowing float (→ ±Inf) is a ParseFloat error, so it resolves as a string and stays + if (parsesAsBaseZeroInt(plain)) return false; + // An overflowing float (→ ±Inf) is a float parse error, so it resolves as a string and stays // plain; an underflowing one (1e-999 → 0) succeeds and stays float-tagged, hence quoted. // `Number` mirrors the accepted shapes since YAML_STYLE_FLOAT gates the syntax first. if (YAML_STYLE_FLOAT.test(plain) && Number.isFinite(Number(plain))) return false; @@ -810,8 +806,8 @@ const YAML_RESOLVE_MAP = new Set([ const YAML_BASE60 = /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$/; const YAML_STYLE_FLOAT = /^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$/; -/** `strconv.ParseInt(s, 0, 64)` / `ParseUint` success (underscores pre-stripped). */ -function goParseIntBase0(plain: string): boolean { +/** Whether `plain` parses as a base-0 int64/uint64 (underscores pre-stripped). */ +function parsesAsBaseZeroInt(plain: string): boolean { let body = plain; let negative = false; if (body.startsWith("+") || body.startsWith("-")) { @@ -844,20 +840,18 @@ function goParseIntBase0(plain: string): boolean { } else { return false; } - // resolve() falls back from ParseInt to ParseUint, so the accepted range is + // Resolution falls back from int64 to uint64, so the accepted range is // [-2^63, 2^64) — anything beyond either bound is not an int. if (negative) return parsed <= 9223372036854775808n; return parsed < 18446744073709551616n; } /** - * yaml.v3's `parseTimestamp` layouts, which delegate to `time.Parse` — so calendar dates and zone - * offsets are validated exactly like Go's time package (`2025-02-31` and `2100-02-29` stay plain, - * `2024-02-29` is a timestamp). + * Timestamp layouts with validated calendar dates and zone offsets (`2025-02-31` and + * `2100-02-29` stay plain, `2024-02-29` is a timestamp). */ function yamlIsTimestamp(s: string): boolean { - // The fraction separator is `.` or `,` — yaml.v3 resolves timestamps through `time.Parse`, - // which accepts either. + // The fraction separator is `.` or `,`. const match = /^(\d{4})-(\d{1,2})-(\d{1,2})(?:([Tt ])(\d{1,2}):(\d{1,2}):(\d{1,2})(?:[.,]\d+)?(Z|[+-]\d{2}:\d{2})?)?$/.exec( s, @@ -870,7 +864,7 @@ function yamlIsTimestamp(s: string): boolean { if (separator !== " " && offset === undefined) return false; if (Number(hour) > 23 || Number(minute) > 59 || Number(second) > 59) return false; } - // time.Parse's zone-offset range checks: the hour is rejected above 24 and the minute above 60 + // Zone-offset range checks: the hour is rejected above 24 and the minute above 60 // — `+24:59` and `+00:60` are accepted, `+25:00` and `+23:99` are not. if (offset !== undefined && offset !== "Z") { if (Number(offset.slice(1, 3)) > 24 || Number(offset.slice(4, 6)) > 60) return false; @@ -878,12 +872,12 @@ function yamlIsTimestamp(s: string): boolean { const month = Number(monthRaw); const day = Number(dayRaw); if (month < 1 || month > 12) return false; - if (day < 1 || day > goDaysInMonth(Number(yearRaw), month)) return false; + if (day < 1 || day > daysInMonth(Number(yearRaw), month)) return false; return true; } -/** `time.Parse`'s "day out of range" bound (`daysIn`, proleptic Gregorian). */ -function goDaysInMonth(year: number, month: number): number { +/** Days in a month of the proleptic Gregorian calendar. */ +function daysInMonth(year: number, month: number): number { if (month === 2) { const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); return leap ? 29 : 28; @@ -950,8 +944,7 @@ function yamlDoubleQuoted(s: string): string { out += "\\P"; break; default: - // Non-printables escape by rune width like yaml.v3's double-quoted - // writer: `\xXX`, `\uXXXX`, or `\U00XXXXXX` with uppercase hex. + // Non-printables escape as `\xXX`, `\uXXXX`, or `\U00XXXXXX` with uppercase hex. if (yamlIsPrintable(code)) { out += ch; } else if (code <= 0xff) { @@ -971,10 +964,8 @@ function yamlDoubleQuoted(s: string): string { * explicit `4` indentation indicator when the first line starts with a space * or is empty, and content indented to the next 4-column stop. * - * Unlike Go's streaming bufio-backed encoder (which can flush partial output - * before a later error), this builds the whole document in memory — callers - * emit all-or-nothing, which only differs observably from Go on multi-KB - * payloads that fail mid-encode. + * Builds the whole document in memory — callers emit all-or-nothing, so no partial output + * is flushed when a later field fails mid-encode. */ function yamlBlockLiteral(s: string, indent: number): string { const contentIndent = yamlNextIndent(indent); @@ -984,7 +975,7 @@ function yamlBlockLiteral(s: string, indent: number): string { const indicator = s.startsWith(" ") || s.startsWith("\n") ? "4" : ""; const lines = s.split("\n"); if (s.endsWith("\n")) lines.pop(); - // yaml.v3 merges a leading empty line's break with the header newline (e.g. "\nx" → `|4-\n + // A leading empty line's break merges with the header newline (e.g. "\nx" → `|4-\n // x\n`). if (lines[0] === "") lines.shift(); const body = lines.map((line) => (line.length === 0 ? "" : `${pad}${line}`)).join("\n"); @@ -992,13 +983,13 @@ function yamlBlockLiteral(s: string, indent: number): string { } /** - * Thrown when BurntSushi would refuse the payload: a populated - * `nullable.Nullable` field (`map[bool]T` has a non-string key type — observed - * on `snippets list -o toml`) or a `nil` element inside an inline array. + * Thrown when the payload has no TOML form: a populated nullable field (its bool key is not a + * valid TOML key — observed on `snippets list -o toml`) or a null element inside an inline array. */ -export class GoTomlEncodeError extends Error { +export class TomlEncodeError extends Error { + // The fingerprint and `name` are the stable telemetry/output identity of this error. static readonly [ErrorActionabilityFingerprintId] = "GoTomlEncodeError"; - constructor(message = "toml: cannot encode a map with non-string key type") { + constructor(message = "cannot encode a map with non-string keys") { super(message); this.name = "GoTomlEncodeError"; } @@ -1009,15 +1000,13 @@ export class GoTomlEncodeError extends Error { } /** - * Encodes a decoded payload as the Go CLI's `-o toml` output. Returns the full document, which can - * be empty (BurntSushi emits nothing for an all-nil payload). Throws {@link GoTomlEncodeError} to - * match Go's runtime failure on a populated nullable field. + * Encodes a decoded payload as `-o toml` output. Returns the full document, which can + * be empty (an all-nil payload emits nothing). Throws {@link TomlEncodeError} on + * a populated nullable field. * - * On the error path only, Go's real stdout can already contain partial buffered output that this - * in-memory port doesn't reproduce; emit nothing on error rather than the accumulated prefix, - * which would emit more than Go does. + * Emits nothing on error rather than the accumulated prefix. */ -export function encodeGoToml(value: unknown, type: GoType): string { +export function encodeStructToml(value: unknown, type: OutputShape): string { const state = { out: "", hasWritten: false }; tomlEncode(state, [], normalize(value, type)); return state.out; @@ -1042,7 +1031,7 @@ function tomlIndent(key: ReadonlyArray): string { return " ".repeat(Math.max(key.length - 1, 0)); } -function tomlIsTable(value: GoValue): boolean { +function tomlIsTable(value: NormalizedValue): boolean { switch (value.k) { case "struct": return true; @@ -1051,15 +1040,14 @@ function tomlIsTable(value: GoValue): boolean { case "nullable": return true; case "slice": - // Array-of-tables only when non-empty with table elements (BurntSushi's - // isTableArray returns false for empty slices). + // Array-of-tables only when non-empty with table elements. return value.tables && value.items.length > 0; default: return false; } } -function tomlIsNil(value: GoValue): boolean { +function tomlIsNil(value: NormalizedValue): boolean { switch (value.k) { case "nil": return true; @@ -1074,7 +1062,7 @@ function tomlIsNil(value: GoValue): boolean { } } -function tomlEncode(state: TomlState, key: ReadonlyArray, value: GoValue): void { +function tomlEncode(state: TomlState, key: ReadonlyArray, value: NormalizedValue): void { if (tomlIsNil(value)) return; switch (value.k) { case "struct": @@ -1082,8 +1070,8 @@ function tomlEncode(state: TomlState, key: ReadonlyArray, value: GoValue tomlTable(state, key, value); return; case "nullable": - // Populated nullable.Nullable[T] is a map[bool]T — BurntSushi panics. - throw new GoTomlEncodeError(); + // A populated nullable has a bool key, which TOML cannot encode. + throw new TomlEncodeError(); case "slice": if (tomlIsTable(value)) { tomlArrayOfTables(state, key, value.items); @@ -1097,19 +1085,19 @@ function tomlEncode(state: TomlState, key: ReadonlyArray, value: GoValue } /** - * Map/struct entries in BurntSushi's write order: map entries sorted by - * {@link goStringCompare} (structs keep declaration order), then both + * Map/struct entries in TOML write order: map entries sorted by + * {@link compareByCodepoint} (structs keep declaration order), then both * partitioned into non-table ("direct") and table ("sub") groups via - * {@link tomlIsTable} — `eStruct`/`eMap` always write direct fields before + * {@link tomlIsTable} — direct fields are always written before * sub-tables. */ -function tomlOrderedEntries(value: Extract): { - direct: ReadonlyArray; - sub: ReadonlyArray; +function tomlOrderedEntries(value: Extract): { + direct: ReadonlyArray; + sub: ReadonlyArray; } { const entries = value.k === "map" - ? [...value.entries].sort(([a], [b]) => goStringCompare(a, b)) + ? [...value.entries].sort(([a], [b]) => compareByCodepoint(a, b)) : value.entries; return { direct: entries.filter(([, v]) => !tomlIsTable(v)), @@ -1120,7 +1108,7 @@ function tomlOrderedEntries(value: Extract): { function tomlTable( state: TomlState, key: ReadonlyArray, - value: Extract, + value: Extract, ): void { if (key.length === 1) { // Extra newline between top-level tables. @@ -1143,7 +1131,7 @@ function tomlTable( function tomlArrayOfTables( state: TomlState, key: ReadonlyArray, - items: ReadonlyArray, + items: ReadonlyArray, ): void { for (const item of items) { if (tomlIsNil(item)) continue; @@ -1156,12 +1144,12 @@ function tomlArrayOfTables( tomlEncode(state, [...key, name], v); } } else if (item.k === "nullable") { - throw new GoTomlEncodeError(); + throw new TomlEncodeError(); } } } -function tomlKeyValue(state: TomlState, key: ReadonlyArray, value: GoValue): void { +function tomlKeyValue(state: TomlState, key: ReadonlyArray, value: NormalizedValue): void { const name = key[key.length - 1] as string; tomlWrite(state, `${tomlIndent(key)}${tomlKeyName(name)} = ${tomlElement(value)}\n`); } @@ -1172,7 +1160,7 @@ function tomlKeyName(name: string): string { return TOML_BARE_KEY.test(name) ? name : tomlQuoted(name); } -function tomlElement(value: GoValue): string { +function tomlElement(value: NormalizedValue): string { switch (value.k) { case "str": return tomlQuoted(value.v); @@ -1181,7 +1169,7 @@ function tomlElement(value: GoValue): string { case "int": return String(value.v); case "float": { - const repr = goFormatFloat(value.v, value.bits); + const repr = formatStructFloat(value.v, value.bits); // TOML floats must carry a decimal point unless in exponent form. return repr.includes(".") || repr.includes("e") ? repr : `${repr}.0`; } @@ -1193,16 +1181,16 @@ function tomlElement(value: GoValue): string { case "map": return tomlInlineTable(value); case "nullable": - throw new GoTomlEncodeError(); + throw new TomlEncodeError(); case "nil": - // BurntSushi's `eElement` rejects nil inline-array elements (`[null, "x"]` fails), while nil + // Null inline-array elements are rejected (`[null, "x"]` fails), while null // map values are silently skipped. - throw new GoTomlEncodeError("toml: cannot encode array with nil element"); + throw new TomlEncodeError("cannot encode an array with a null element"); } } /** - * BurntSushi's inline-table form, used for map/struct elements of arrays that aren't + * The inline-table form, used for map/struct elements of arrays that aren't * arrays-of-tables: `{k = v, ...}` with nil entries skipped and non-table values before table * values, map keys byte-sorted within each group. * @@ -1210,14 +1198,14 @@ function tomlElement(value: GoValue): string { * written, so a skipped nil entry in the final position leaves a dangling `", "` (e.g. * `[{"10":1,"b":null},false]` → `[{10 = 1.0, }, false]`). */ -function tomlInlineTable(value: Extract): string { +function tomlInlineTable(value: Extract): string { let out = "{"; if (value.k === "map") { - const sorted = [...value.entries].sort(([a], [b]) => goStringCompare(a, b)); + const sorted = [...value.entries].sort(([a], [b]) => compareByCodepoint(a, b)); const direct = sorted.filter(([, v]) => !tomlIsTable(v)); const sub = sorted.filter(([, v]) => tomlIsTable(v)); const writeGroup = ( - group: ReadonlyArray, + group: ReadonlyArray, trailingComma: boolean, ): void => { for (const [index, [name, v]] of group.entries()) { @@ -1241,7 +1229,7 @@ function tomlInlineTable(value: Extract): stri return `${out}}`; } -/** BurntSushi's `dblQuotedReplacer` escape set. */ +/** The double-quoted TOML string escape set. */ function tomlQuoted(s: string): string { let out = '"'; for (const ch of s) { diff --git a/apps/cli/src/command-internal/go-struct-output.encoders.unit.test.ts b/apps/cli/src/command-internal/struct-output.encoders.unit.test.ts similarity index 50% rename from apps/cli/src/command-internal/go-struct-output.encoders.unit.test.ts rename to apps/cli/src/command-internal/struct-output.encoders.unit.test.ts index 0c8900aac7..93af2144df 100644 --- a/apps/cli/src/command-internal/go-struct-output.encoders.unit.test.ts +++ b/apps/cli/src/command-internal/struct-output.encoders.unit.test.ts @@ -1,52 +1,46 @@ import { describe, expect, it } from "vitest"; import { - GoTomlEncodeError, - encodeGoToml, - encodeGoYaml, - goAny, - goBool, - goFieldName, - goFloat32, - goFloat64, - goFormatFloat, - goInt, - goMap, - goNullable, - goPtr, - goSlice, - goString, - goStruct, - goTime, - goTomlListWrapper, - goUuid, -} from "./go-struct-output.encoders.ts"; - -/** - * Golden byte strings in this file were captured from a scratch Go program calling - * `utils.EncodeOutput`, using the same BurntSushi toml and yaml.v3 versions pinned in the - * reference `go.mod`. - */ + TomlEncodeError, + encodeStructToml, + encodeStructYaml, + shapeAny, + shapeBool, + pascalCaseFieldName, + shapeFloat32, + shapeFloat64, + formatStructFloat, + shapeInt, + shapeMap, + shapeNullable, + shapePtr, + shapeSlice, + shapeString, + shapeStruct, + shapeTime, + shapeTomlListWrapper, + shapeUuid, +} from "./struct-output.encoders.ts"; // Mirrors the branch response struct. -const BRANCH_RESPONSE = goStruct([ - ["created_at", goTime], - ["deletion_scheduled_at", goPtr(goTime)], - ["git_branch", goPtr(goString)], - ["id", goUuid], - ["is_default", goBool], - ["latest_check_run_id", goPtr(goFloat32)], - ["name", goString], - ["notify_url", goPtr(goString)], - ["parent_project_ref", goString], - ["persistent", goBool], - ["pr_number", goPtr(goInt)], - ["preview_project_status", goPtr(goString)], - ["project_ref", goString], - ["review_requested_at", goPtr(goTime)], - ["status", goString], - ["updated_at", goTime], - ["with_data", goBool], +const BRANCH_RESPONSE = shapeStruct([ + ["created_at", shapeTime], + ["deletion_scheduled_at", shapePtr(shapeTime)], + ["git_branch", shapePtr(shapeString)], + ["id", shapeUuid], + ["is_default", shapeBool], + ["latest_check_run_id", shapePtr(shapeFloat32)], + ["name", shapeString], + ["notify_url", shapePtr(shapeString)], + ["parent_project_ref", shapeString], + ["persistent", shapeBool], + ["pr_number", shapePtr(shapeInt)], + ["preview_project_status", shapePtr(shapeString)], + ["project_ref", shapeString], + ["review_requested_at", shapePtr(shapeTime)], + ["status", shapeString], + ["updated_at", shapeTime], + ["with_data", shapeBool], ]); const SAMPLE_BRANCH = { @@ -63,7 +57,7 @@ const SAMPLE_BRANCH = { with_data: true, }; -// All pointer fields absent — Go zero-fills the value fields. +// All pointer fields absent — the value fields are zero-filled. const ZERO_BRANCH = { name: "Production", is_default: true, @@ -72,10 +66,10 @@ const ZERO_BRANCH = { status: "FUNCTIONS_DEPLOYED", }; -describe("encodeGoToml", () => { - it("matches Go byte-for-byte for a branches list wrapper (PascalCase, nil pointers omitted, native datetimes)", () => { - const wrapper = goTomlListWrapper("branches", BRANCH_RESPONSE); - expect(encodeGoToml({ branches: [SAMPLE_BRANCH, ZERO_BRANCH] }, wrapper)).toBe( +describe("encodeStructToml", () => { + it("matches the golden output for a branches list wrapper (PascalCase, nil pointers omitted, native datetimes)", () => { + const wrapper = shapeTomlListWrapper("branches", BRANCH_RESPONSE); + expect(encodeStructToml({ branches: [SAMPLE_BRANCH, ZERO_BRANCH] }, wrapper)).toBe( `[[branches]] CreatedAt = 2026-05-27T01:02:03Z GitBranch = "feat-1" @@ -105,7 +99,7 @@ describe("encodeGoToml", () => { }); it("emits a top-level struct without a table header (branches create)", () => { - expect(encodeGoToml(SAMPLE_BRANCH, BRANCH_RESPONSE)).toBe( + expect(encodeStructToml(SAMPLE_BRANCH, BRANCH_RESPONSE)).toBe( `CreatedAt = 2026-05-27T01:02:03Z GitBranch = "feat-1" Id = "11111111-2222-3333-4444-555555555555" @@ -122,60 +116,60 @@ WithData = true }); it("emits nothing for a nil list and `key = []` for a decoded empty list", () => { - const wrapper = goTomlListWrapper("branches", BRANCH_RESPONSE); - // Go: `var result []api.BranchResponse` stays nil when empty → no output. - expect(encodeGoToml({ branches: undefined }, wrapper)).toBe(""); - // Go: a decoded `[]` is a non-nil empty slice → `branches = []`. - expect(encodeGoToml({ branches: [] }, wrapper)).toBe("branches = []\n"); + const wrapper = shapeTomlListWrapper("branches", BRANCH_RESPONSE); + // An absent list → no output. + expect(encodeStructToml({ branches: undefined }, wrapper)).toBe(""); + // A decoded `[]` is an empty list → `branches = []`. + expect(encodeStructToml({ branches: [] }, wrapper)).toBe("branches = []\n"); }); it("nests sub-tables after primitives with 2-space indentation (hostnames shape)", () => { // Mirrors the custom-hostname update response struct. - const spec = goStruct([ - ["custom_hostname", goString], + const spec = shapeStruct([ + ["custom_hostname", shapeString], [ "data", - goStruct([ - ["errors", goSlice(goAny)], - ["messages", goSlice(goAny)], + shapeStruct([ + ["errors", shapeSlice(shapeAny)], + ["messages", shapeSlice(shapeAny)], [ "result", - goStruct([ - ["custom_origin_server", goString], - ["hostname", goString], - ["id", goString], + shapeStruct([ + ["custom_origin_server", shapeString], + ["hostname", shapeString], + ["id", shapeString], [ "ownership_verification", - goStruct([ - ["name", goString], - ["type", goString], - ["value", goString], + shapeStruct([ + ["name", shapeString], + ["type", shapeString], + ["value", shapeString], ]), ], [ "ssl", - goStruct([ - ["status", goString], - ["validation_errors", goPtr(goSlice(goAny))], + shapeStruct([ + ["status", shapeString], + ["validation_errors", shapePtr(shapeSlice(shapeAny))], [ "validation_records", - goSlice( - goStruct([ - ["txt_name", goString], - ["txt_value", goString], + shapeSlice( + shapeStruct([ + ["txt_name", shapeString], + ["txt_value", shapeString], ]), ), ], ]), ], - ["status", goString], - ["verification_errors", goPtr(goSlice(goString))], + ["status", shapeString], + ["verification_errors", shapePtr(shapeSlice(shapeString))], ]), ], - ["success", goBool], + ["success", shapeBool], ]), ], - ["status", goString], + ["status", shapeString], ]); const payload = { custom_hostname: "custom.example.com", @@ -198,7 +192,7 @@ WithData = true }, }, }; - expect(encodeGoToml(payload, spec)).toBe( + expect(encodeStructToml(payload, spec)).toBe( `CustomHostname = "custom.example.com" Status = "2_initiated" @@ -223,35 +217,35 @@ Status = "2_initiated" ); }); - it("escapes strings like BurntSushi and quotes string-typed timestamps (sso provider)", () => { - const spec = goStruct([ - ["created_at", goPtr(goString)], + it("escapes TOML strings and quotes string-typed timestamps (sso provider)", () => { + const spec = shapeStruct([ + ["created_at", shapePtr(shapeString)], [ "domains", - goPtr( - goSlice( - goStruct([ - ["created_at", goPtr(goString)], - ["domain", goPtr(goString)], - ["updated_at", goPtr(goString)], + shapePtr( + shapeSlice( + shapeStruct([ + ["created_at", shapePtr(shapeString)], + ["domain", shapePtr(shapeString)], + ["updated_at", shapePtr(shapeString)], ]), ), ), ], - ["id", goString], + ["id", shapeString], [ "saml", - goPtr( - goStruct([ - ["attribute_mapping", goPtr(goStruct([["keys", goMap(goAny)]]))], - ["entity_id", goString], - ["metadata_url", goPtr(goString)], - ["metadata_xml", goPtr(goString)], - ["name_id_format", goPtr(goString)], + shapePtr( + shapeStruct([ + ["attribute_mapping", shapePtr(shapeStruct([["keys", shapeMap(shapeAny)]]))], + ["entity_id", shapeString], + ["metadata_url", shapePtr(shapeString)], + ["metadata_xml", shapePtr(shapeString)], + ["name_id_format", shapePtr(shapeString)], ]), ), ], - ["updated_at", goPtr(goString)], + ["updated_at", shapePtr(shapeString)], ]); const payload = { id: "8b64a95d-6e29-4c58-8f04-1d0ac6bcda31", @@ -264,7 +258,7 @@ Status = "2_initiated" '\n&', }, }; - expect(encodeGoToml(payload, spec)).toBe( + expect(encodeStructToml(payload, spec)).toBe( `CreatedAt = "2026-05-27T01:02:03.123456Z" Id = "8b64a95d-6e29-4c58-8f04-1d0ac6bcda31" UpdatedAt = "2026-05-27T01:02:03.123456Z" @@ -280,17 +274,17 @@ UpdatedAt = "2026-05-27T01:02:03.123456Z" ); }); - it("keeps hand-written Go struct declaration order (services imageVersion)", () => { - const spec = goTomlListWrapper( + it("keeps hand-written struct declaration order (services imageVersion)", () => { + const spec = shapeTomlListWrapper( "services", - goStruct([ - ["name", goString], - ["local", goString], - ["remote", goString], + shapeStruct([ + ["name", shapeString], + ["local", shapeString], + ["remote", shapeString], ]), ); expect( - encodeGoToml( + encodeStructToml( { services: [{ name: "supabase/postgres", local: "17.4.1.037", remote: "" }] }, spec, ), @@ -304,33 +298,33 @@ UpdatedAt = "2026-05-27T01:02:03.123456Z" }); it("renders inline primitive arrays (network bans wrapper)", () => { - const spec = goStruct([["banned_ips", goSlice(goString), "banned_ips"]]); - expect(encodeGoToml({ banned_ips: ["1.2.3.4", "5.6.7.8"] }, spec)).toBe( + const spec = shapeStruct([["banned_ips", shapeSlice(shapeString), "banned_ips"]]); + expect(encodeStructToml({ banned_ips: ["1.2.3.4", "5.6.7.8"] }, spec)).toBe( 'banned_ips = ["1.2.3.4", "5.6.7.8"]\n', ); }); - it("skips nil nullable fields and fails like Go on populated ones", () => { - const spec = goStruct([ - ["desc", goNullable(goString)], - ["name", goString], + it("skips nil nullable fields and fails on populated ones", () => { + const spec = shapeStruct([ + ["desc", shapeNullable(shapeString)], + ["name", shapeString], ]); - expect(encodeGoToml({ name: "x" }, spec)).toBe('Name = "x"\n'); - expect(() => encodeGoToml({ name: "x", desc: null }, spec)).toThrow( - new GoTomlEncodeError().message, + expect(encodeStructToml({ name: "x" }, spec)).toBe('Name = "x"\n'); + expect(() => encodeStructToml({ name: "x", desc: null }, spec)).toThrow( + new TomlEncodeError().message, ); - expect(() => encodeGoToml({ name: "x", desc: "d" }, spec)).toThrow( - "toml: cannot encode a map with non-string key type", + expect(() => encodeStructToml({ name: "x", desc: "d" }, spec)).toThrow( + "cannot encode a map with non-string keys", ); }); - it("renders floats with a decimal point and Go's exponent form", () => { - const spec = goStruct([ - ["f1", goFloat32], - ["f2", goFloat64], - ["f6", goFloat64], + it("renders floats with a decimal point and the exponent form", () => { + const spec = shapeStruct([ + ["f1", shapeFloat32], + ["f2", shapeFloat64], + ["f6", shapeFloat64], ]); - expect(encodeGoToml({ f1: 1, f2: 1000000, f6: 1234567 }, spec)).toBe( + expect(encodeStructToml({ f1: 1, f2: 1000000, f6: 1234567 }, spec)).toBe( `F1 = 1.0 F2 = 1e+06 F6 = 1.234567e+06 @@ -339,9 +333,9 @@ F6 = 1.234567e+06 }); it("sorts map keys and quotes non-bare keys (branches get envs)", () => { - const spec = goMap(goString); + const spec = shapeMap(shapeString); expect( - encodeGoToml( + encodeStructToml( { SUPABASE_ANON_KEY: "anon", POSTGRES_URL: "postgres://u:p@h:6543/postgres" }, spec, ), @@ -352,95 +346,95 @@ SUPABASE_ANON_KEY = "anon" ); }); - it("renders map elements of mixed interface{} arrays as inline tables like BurntSushi", () => { - const spec = goStruct([["default", goAny, "Default"]]); + it("renders map elements of mixed untyped arrays as inline tables", () => { + const spec = shapeStruct([["default", shapeAny, "Default"]]); // Sorted byte order, non-table values before table values. - expect(encodeGoToml({ default: [{ b: 2, a: 1, C: 3 }, "x"] }, spec)).toBe( + expect(encodeStructToml({ default: [{ b: 2, a: 1, C: 3 }, "x"] }, spec)).toBe( 'Default = [{C = 3.0, a = 1.0, b = 2.0}, "x"]\n', ); - expect(encodeGoToml({ default: [{ a: { b: 1 }, z: 2 }, "x"] }, spec)).toBe( + expect(encodeStructToml({ default: [{ a: { b: 1 }, z: 2 }, "x"] }, spec)).toBe( 'Default = [{z = 2.0, a = {b = 1.0}}, "x"]\n', ); - expect(encodeGoToml({ default: [{ a: [{ b: 1 }], z: 2 }, "x"] }, spec)).toBe( + expect(encodeStructToml({ default: [{ a: [{ b: 1 }], z: 2 }, "x"] }, spec)).toBe( 'Default = [{z = 2.0, a = [{b = 1.0}]}, "x"]\n', ); // Non-bare keys are quoted; empty and all-nil tables collapse to {}. - expect(encodeGoToml({ default: [{ "a b": 1 }, "x"] }, spec)).toBe( + expect(encodeStructToml({ default: [{ "a b": 1 }, "x"] }, spec)).toBe( 'Default = [{"a b" = 1.0}, "x"]\n', ); - expect(encodeGoToml({ default: [{}, "x"] }, spec)).toBe('Default = [{}, "x"]\n'); - expect(encodeGoToml({ default: [{ a: null }, "x"] }, spec)).toBe('Default = [{}, "x"]\n'); + expect(encodeStructToml({ default: [{}, "x"] }, spec)).toBe('Default = [{}, "x"]\n'); + expect(encodeStructToml({ default: [{ a: null }, "x"] }, spec)).toBe('Default = [{}, "x"]\n'); // eMap decides the ", " separator by group position before skipping nil // entries, so a nil in the final position leaves a dangling separator. - expect(encodeGoToml({ default: [{ "10": 78797, b: null }, false] }, spec)).toBe( + expect(encodeStructToml({ default: [{ "10": 78797, b: null }, false] }, spec)).toBe( "Default = [{10 = 78797.0, }, false]\n", ); }); - it("fails like Go on nil elements inside interface{} arrays", () => { - const spec = goStruct([["default", goAny, "Default"]]); - const message = "toml: cannot encode array with nil element"; - expect(() => encodeGoToml({ default: [null, "x"] }, spec)).toThrow(message); - expect(() => encodeGoToml({ default: [null] }, spec)).toThrow(message); - expect(() => encodeGoToml({ default: [[null], "x"] }, spec)).toThrow(message); + it("fails on null elements inside untyped arrays", () => { + const spec = shapeStruct([["default", shapeAny, "Default"]]); + const message = "cannot encode an array with a null element"; + expect(() => encodeStructToml({ default: [null, "x"] }, spec)).toThrow(message); + expect(() => encodeStructToml({ default: [null] }, spec)).toThrow(message); + expect(() => encodeStructToml({ default: [[null], "x"] }, spec)).toThrow(message); }); - it("truncates time fractions to nanoseconds like time.Time's decoder", () => { - const spec = goStruct([["t", goTime, "T"]]); - expect(encodeGoToml({ t: "2026-01-01T00:00:00.1234567895Z" }, spec)).toBe( + it("truncates time fractions to nanoseconds", () => { + const spec = shapeStruct([["t", shapeTime, "T"]]); + expect(encodeStructToml({ t: "2026-01-01T00:00:00.1234567895Z" }, spec)).toBe( "T = 2026-01-01T00:00:00.123456789Z\n", ); - expect(encodeGoToml({ t: "2026-01-01T00:00:00.1000000005Z" }, spec)).toBe( + expect(encodeStructToml({ t: "2026-01-01T00:00:00.1000000005Z" }, spec)).toBe( "T = 2026-01-01T00:00:00.1Z\n", ); }); - it("quotes comma-fraction timestamp-shaped STRINGS like yaml.v3's resolver", () => { - const spec = goStruct([["s", goString, "S"]]); - expect(encodeGoYaml({ s: "2026-01-01T00:00:00,123Z" }, spec)).toBe( + it("quotes comma-fraction timestamp-shaped strings", () => { + const spec = shapeStruct([["s", shapeString, "S"]]); + expect(encodeStructYaml({ s: "2026-01-01T00:00:00,123Z" }, spec)).toBe( 's: "2026-01-01T00:00:00,123Z"\n', ); }); - it("leaves overflowing float-shaped strings plain like yaml.v3's ParseFloat gate", () => { - const spec = goStruct([["s", goString, "S"]]); - expect(encodeGoYaml({ s: "1e999" }, spec)).toBe("s: 1e999\n"); - expect(encodeGoYaml({ s: "-1e999" }, spec)).toBe("s: -1e999\n"); - expect(encodeGoYaml({ s: ".5e999" }, spec)).toBe("s: .5e999\n"); - expect(encodeGoYaml({ s: "1e-999" }, spec)).toBe('s: "1e-999"\n'); - expect(encodeGoYaml({ s: "1e10" }, spec)).toBe('s: "1e10"\n'); + it("leaves overflowing float-shaped strings plain", () => { + const spec = shapeStruct([["s", shapeString, "S"]]); + expect(encodeStructYaml({ s: "1e999" }, spec)).toBe("s: 1e999\n"); + expect(encodeStructYaml({ s: "-1e999" }, spec)).toBe("s: -1e999\n"); + expect(encodeStructYaml({ s: ".5e999" }, spec)).toBe("s: .5e999\n"); + expect(encodeStructYaml({ s: "1e-999" }, spec)).toBe('s: "1e-999"\n'); + expect(encodeStructYaml({ s: "1e10" }, spec)).toBe('s: "1e10"\n'); }); - it("wraps 19+-digit numeric key runs like Go's unchecked int64 accumulation", () => { - const spec = goStruct([["default", goAny, "Default"]]); + it("wraps 19+-digit numeric key runs via unchecked int64 accumulation", () => { + const spec = shapeStruct([["default", shapeAny, "Default"]]); expect( - encodeGoYaml({ default: { a9000000000000000000: 1, a10000000000000000000: 2 } }, spec), + encodeStructYaml({ default: { a9000000000000000000: 1, a10000000000000000000: 2 } }, spec), ).toBe("default:\n a10000000000000000000: 2\n a9000000000000000000: 1\n"); }); - it("orders Unicode-digit map keys with yaml.v3's naive rune arithmetic", () => { - const spec = goStruct([["default", goAny, "Default"]]); - expect(encodeGoYaml({ default: { a٢: 1, a3: 2, a10: 3, a9: 4 } }, spec)).toBe( + it("orders Unicode-digit map keys by code point, with numeric runs compared by ASCII digits only", () => { + const spec = shapeStruct([["default", shapeAny, "Default"]]); + expect(encodeStructYaml({ default: { a٢: 1, a3: 2, a10: 3, a9: 4 } }, spec)).toBe( "default:\n a3: 2\n a9: 4\n a10: 3\n a٢: 1\n", ); }); - it("normalizes Go's accepted comma fractional separator to the dot Go re-emits", () => { - const spec = goStruct([["t", goTime, "T"]]); - expect(encodeGoToml({ t: "2026-01-01T00:00:00,123Z" }, spec)).toBe( + it("normalizes a comma fractional separator to a dot", () => { + const spec = shapeStruct([["t", shapeTime, "T"]]); + expect(encodeStructToml({ t: "2026-01-01T00:00:00,123Z" }, spec)).toBe( "T = 2026-01-01T00:00:00.123Z\n", ); - expect(encodeGoYaml({ t: "2026-01-01T00:00:00,1234567895Z" }, spec)).toBe( + expect(encodeStructYaml({ t: "2026-01-01T00:00:00,1234567895Z" }, spec)).toBe( "t: 2026-01-01T00:00:00.123456789Z\n", ); }); - it("sorts map keys by UTF-8 byte order like Go's sort.Strings", () => { + it("sorts map keys by UTF-8 byte order", () => { // U+E000/U+FF21 sort before the astral U+1D400/U+1F600 in UTF-8 byte order; JS `<` on UTF-16 // units would sort both astral keys first. - const spec = goMap(goString); + const spec = shapeMap(shapeString); expect( - encodeGoToml( + encodeStructToml( { "\u{1F600}": "emoji", "\uE000": "private-use", @@ -463,9 +457,9 @@ SUPABASE_ANON_KEY = "anon" }); }); -describe("encodeGoYaml", () => { - it("matches Go byte-for-byte for a branches list (lowercased keys, explicit nulls)", () => { - expect(encodeGoYaml([SAMPLE_BRANCH, ZERO_BRANCH], goSlice(BRANCH_RESPONSE))).toBe( +describe("encodeStructYaml", () => { + it("matches the golden output for a branches list (lowercased keys, explicit nulls)", () => { + expect(encodeStructYaml([SAMPLE_BRANCH, ZERO_BRANCH], shapeSlice(BRANCH_RESPONSE))).toBe( `- createdat: 2026-05-27T01:02:03Z deletionscheduledat: null gitbranch: feat-1 @@ -505,39 +499,39 @@ describe("encodeGoYaml", () => { }); it("renders an empty list as [] regardless of nil-ness", () => { - expect(encodeGoYaml([], goSlice(BRANCH_RESPONSE))).toBe("[]\n"); - expect(encodeGoYaml(undefined, goSlice(BRANCH_RESPONSE))).toBe("[]\n"); + expect(encodeStructYaml([], shapeSlice(BRANCH_RESPONSE))).toBe("[]\n"); + expect(encodeStructYaml(undefined, shapeSlice(BRANCH_RESPONSE))).toBe("[]\n"); }); it("uses 4-column indentation, block literals, and quoted string timestamps (sso show)", () => { - const spec = goStruct([ - ["created_at", goPtr(goString)], + const spec = shapeStruct([ + ["created_at", shapePtr(shapeString)], [ "domains", - goPtr( - goSlice( - goStruct([ - ["created_at", goPtr(goString)], - ["domain", goPtr(goString)], - ["updated_at", goPtr(goString)], + shapePtr( + shapeSlice( + shapeStruct([ + ["created_at", shapePtr(shapeString)], + ["domain", shapePtr(shapeString)], + ["updated_at", shapePtr(shapeString)], ]), ), ), ], - ["id", goString], + ["id", shapeString], [ "saml", - goPtr( - goStruct([ - ["attribute_mapping", goPtr(goStruct([["keys", goMap(goAny)]]))], - ["entity_id", goString], - ["metadata_url", goPtr(goString)], - ["metadata_xml", goPtr(goString)], - ["name_id_format", goPtr(goString)], + shapePtr( + shapeStruct([ + ["attribute_mapping", shapePtr(shapeStruct([["keys", shapeMap(shapeAny)]]))], + ["entity_id", shapeString], + ["metadata_url", shapePtr(shapeString)], + ["metadata_xml", shapePtr(shapeString)], + ["name_id_format", shapePtr(shapeString)], ]), ), ], - ["updated_at", goPtr(goString)], + ["updated_at", shapePtr(shapeString)], ]); const payload = { id: "8b64a95d-6e29-4c58-8f04-1d0ac6bcda31", @@ -550,7 +544,7 @@ describe("encodeGoYaml", () => { '\n&', }, }; - expect(encodeGoYaml(payload, spec)).toBe( + expect(encodeStructYaml(payload, spec)).toBe( `createdat: "2026-05-27T01:02:03.123456Z" domains: - createdat: "2026-05-27T01:02:03Z" @@ -570,27 +564,27 @@ updatedat: "2026-05-27T01:02:03.123456Z" ); }); - it("renders nullable fields the way yaml.v3 renders map[bool]T (api keys)", () => { + it("renders nullable fields as a mapping with a plain bool key (api keys)", () => { // Mirror of api.ApiKeyResponse. - const spec = goSlice( - goStruct([ - ["api_key", goNullable(goString)], - ["description", goNullable(goString)], - ["hash", goNullable(goString)], - ["id", goNullable(goString)], - ["inserted_at", goNullable(goTime)], - ["name", goString], - ["prefix", goNullable(goString)], - ["secret_jwt_template", goNullable(goMap(goAny))], - ["type", goNullable(goString)], - ["updated_at", goNullable(goTime)], + const spec = shapeSlice( + shapeStruct([ + ["api_key", shapeNullable(shapeString)], + ["description", shapeNullable(shapeString)], + ["hash", shapeNullable(shapeString)], + ["id", shapeNullable(shapeString)], + ["inserted_at", shapeNullable(shapeTime)], + ["name", shapeString], + ["prefix", shapeNullable(shapeString)], + ["secret_jwt_template", shapeNullable(shapeMap(shapeAny))], + ["type", shapeNullable(shapeString)], + ["updated_at", shapeNullable(shapeTime)], ]), ); const payload = [ { name: "anon", api_key: "anon-key-value", id: "key-id-1", type: "legacy" }, { name: "service_role" }, ]; - expect(encodeGoYaml(payload, spec)).toBe( + expect(encodeStructYaml(payload, spec)).toBe( `- apikey: true: anon-key-value description: {} @@ -619,11 +613,11 @@ updatedat: "2026-05-27T01:02:03.123456Z" }); it("renders an explicit JSON null nullable as a false-keyed zero (snippets description)", () => { - const spec = goStruct([ - ["desc", goNullable(goString)], - ["name", goString], + const spec = shapeStruct([ + ["desc", shapeNullable(shapeString)], + ["name", shapeString], ]); - expect(encodeGoYaml({ desc: null, name: "x" }, spec)).toBe( + expect(encodeStructYaml({ desc: null, name: "x" }, spec)).toBe( `desc: false: "" name: x @@ -632,28 +626,28 @@ name: x }); it("renders nil and empty slices as [] and nested maps at +4 (backups list)", () => { - const spec = goStruct([ + const spec = shapeStruct([ [ "backups", - goSlice( - goStruct([ - ["id", goInt], - ["inserted_at", goString], - ["is_physical_backup", goBool], - ["status", goString], + shapeSlice( + shapeStruct([ + ["id", shapeInt], + ["inserted_at", shapeString], + ["is_physical_backup", shapeBool], + ["status", shapeString], ]), ), ], [ "physical_backup_data", - goStruct([ - ["earliest_physical_backup_date_unix", goPtr(goInt)], - ["latest_physical_backup_date_unix", goPtr(goInt)], + shapeStruct([ + ["earliest_physical_backup_date_unix", shapePtr(shapeInt)], + ["latest_physical_backup_date_unix", shapePtr(shapeInt)], ]), ], - ["pitr_enabled", goBool], - ["region", goString], - ["walg_enabled", goBool], + ["pitr_enabled", shapeBool], + ["region", shapeString], + ["walg_enabled", shapeBool], ]); const payload = { backups: [], @@ -662,7 +656,7 @@ name: x region: "us-east-1", walg_enabled: true, }; - expect(encodeGoYaml(payload, spec)).toBe( + expect(encodeStructYaml(payload, spec)).toBe( `backups: [] physicalbackupdata: earliestphysicalbackupdateunix: 1687279254 @@ -674,8 +668,8 @@ walgenabled: true ); }); - it("quotes strings exactly like yaml.v3's resolver and emitter", () => { - const spec = goMap(goString); + it("quotes strings that would resolve to a non-string YAML tag", () => { + const spec = shapeMap(shapeString); const payload = { k01: "yes", k04: "~", @@ -708,7 +702,7 @@ walgenabled: true k52: "2001-12-14 21:59:43.10 -5", k53: "2001-12-15 2:59:43.10", }; - expect(encodeGoYaml(payload, spec)).toBe( + expect(encodeStructYaml(payload, spec)).toBe( `k01: "yes" k04: "~" k07: ' leading-space' @@ -743,10 +737,13 @@ k53: "2001-12-15 2:59:43.10" ); }); - it("renders block literal chomping indicators like yaml.v3", () => { - const spec = goMap(goString); + it("renders block literal chomping indicators", () => { + const spec = shapeMap(shapeString); expect( - encodeGoYaml({ k27: "line1\nline2\n", k28: "line1\nline2\n\n", k29: "with\rcarriage" }, spec), + encodeStructYaml( + { k27: "line1\nline2\n", k28: "line1\nline2\n\n", k29: "with\rcarriage" }, + spec, + ), ).toBe( `k27: | line1 @@ -760,9 +757,11 @@ k29: "with\\rcarriage" ); }); - it("renders floats with Go's g-format exponent switch", () => { - const spec = goMap(goAny); - expect(encodeGoYaml({ f2: 1000000, f3: 78125, f4: 0.5, f5: 0.000001, f6: 1234567 }, spec)).toBe( + it("renders floats with the g-format exponent switch", () => { + const spec = shapeMap(shapeAny); + expect( + encodeStructYaml({ f2: 1000000, f3: 78125, f4: 0.5, f5: 0.000001, f6: 1234567 }, spec), + ).toBe( `f2: 1e+06 f3: 78125 f4: 0.5 @@ -772,9 +771,9 @@ f6: 1.234567e+06 ); }); - it("sorts plain map keys with yaml.v3's natural ordering", () => { - const spec = goMap(goString); - expect(encodeGoYaml({ z: "1", a: "2", "10": "3", "2": "4", B: "5", b: "6" }, spec)).toBe( + it("sorts plain map keys in natural order", () => { + const spec = shapeMap(shapeString); + expect(encodeStructYaml({ z: "1", a: "2", "10": "3", "2": "4", B: "5", b: "6" }, spec)).toBe( `"2": "4" "10": "3" B: "5" @@ -785,12 +784,12 @@ z: "1" ); }); - it("sorts unicode map keys by rune and escapes astral keys like yaml.v3", () => { - // keyList.Less compares runes, so the astral U+1F600/U+1D400 sort after U+E000/U+FF21; the - // emitter double-quotes astral characters as \U-escapes (not printable to libyaml). - const spec = goMap(goString); + it("sorts unicode map keys by code point and double-quotes astral keys with \\U escapes", () => { + // Keys compare by code point, so the astral U+1F600/U+1D400 sort after U+E000/U+FF21; the + // emitter double-quotes astral characters as \U-escapes (not printable in YAML). + const spec = shapeMap(shapeString); expect( - encodeGoYaml( + encodeStructYaml( { "\u{1F600}": "emoji", "\uE000": "private-use", @@ -813,9 +812,9 @@ z: ascii }); it("validates calendar dates and zone offsets like time.Parse before quoting timestamps", () => { - const spec = goMap(goString); + const spec = shapeMap(shapeString); expect( - encodeGoYaml( + encodeStructYaml( { t01: "2025-02-31", t02: "2024-02-29", @@ -849,29 +848,29 @@ t12: 1900-02-29 ); }); - it("truncates time fractions to nanoseconds like time.Time's decoder", () => { - const spec = goStruct([["t", goTime, "T"]]); - expect(encodeGoYaml({ t: "2026-01-01T00:00:00.1234567895Z" }, spec)).toBe( + it("truncates time fractions to nanoseconds", () => { + const spec = shapeStruct([["t", shapeTime, "T"]]); + expect(encodeStructYaml({ t: "2026-01-01T00:00:00.1234567895Z" }, spec)).toBe( "t: 2026-01-01T00:00:00.123456789Z\n", ); - expect(encodeGoYaml({ t: "2026-01-01T00:00:00.12345678901234Z" }, spec)).toBe( + expect(encodeStructYaml({ t: "2026-01-01T00:00:00.12345678901234Z" }, spec)).toBe( "t: 2026-01-01T00:00:00.123456789Z\n", ); - expect(encodeGoYaml({ t: "2026-01-01T00:00:00.9999999999Z" }, spec)).toBe( + expect(encodeStructYaml({ t: "2026-01-01T00:00:00.9999999999Z" }, spec)).toBe( "t: 2026-01-01T00:00:00.999999999Z\n", ); - expect(encodeGoYaml({ t: "2026-01-01T00:00:00.1000000005Z" }, spec)).toBe( + expect(encodeStructYaml({ t: "2026-01-01T00:00:00.1000000005Z" }, spec)).toBe( "t: 2026-01-01T00:00:00.1Z\n", ); - expect(encodeGoYaml({ t: "2026-01-01T00:00:00.1234567895+07:00" }, spec)).toBe( + expect(encodeStructYaml({ t: "2026-01-01T00:00:00.1234567895+07:00" }, spec)).toBe( "t: 2026-01-01T00:00:00.123456789+07:00\n", ); }); - it("escapes non-printable scalars with \\x/\\u/\\U like yaml.v3's emitter", () => { - const spec = goMap(goString); + it("escapes non-printable scalars with \\x/\\u/\\U", () => { + const spec = shapeMap(shapeString); expect( - encodeGoYaml( + encodeStructYaml( { e1: "a\uFEFFb", e2: "a\uFFFEb", @@ -892,50 +891,50 @@ e5: "nel\\Nbreak" }); }); -describe("goFieldName", () => { +describe("pascalCaseFieldName", () => { it("capitalizes snake_case tokens like oapi-codegen", () => { - expect(goFieldName("api_key")).toBe("ApiKey"); - expect(goFieldName("metadata_xml")).toBe("MetadataXml"); - expect(goFieldName("parent_project_ref")).toBe("ParentProjectRef"); - expect(goFieldName("ezbr_sha256")).toBe("EzbrSha256"); + expect(pascalCaseFieldName("api_key")).toBe("ApiKey"); + expect(pascalCaseFieldName("metadata_xml")).toBe("MetadataXml"); + expect(pascalCaseFieldName("parent_project_ref")).toBe("ParentProjectRef"); + expect(pascalCaseFieldName("ezbr_sha256")).toBe("EzbrSha256"); }); it("capitalizes the first letter of camelCase tags", () => { - expect(goFieldName("appliedSuccessfully")).toBe("AppliedSuccessfully"); - expect(goFieldName("currentConfig")).toBe("CurrentConfig"); + expect(pascalCaseFieldName("appliedSuccessfully")).toBe("AppliedSuccessfully"); + expect(pascalCaseFieldName("currentConfig")).toBe("CurrentConfig"); }); }); -describe("goFormatFloat", () => { - it("matches strconv.FormatFloat(f, 'g', -1, 64)", () => { - expect(goFormatFloat(1, 64)).toBe("1"); - expect(goFormatFloat(123456, 64)).toBe("123456"); - expect(goFormatFloat(1000000, 64)).toBe("1e+06"); - expect(goFormatFloat(1234567, 64)).toBe("1.234567e+06"); - expect(goFormatFloat(0.5, 64)).toBe("0.5"); - expect(goFormatFloat(0.000001, 64)).toBe("1e-06"); - expect(goFormatFloat(0, 64)).toBe("0"); - expect(goFormatFloat(-2.5, 64)).toBe("-2.5"); +describe("formatStructFloat", () => { + it("formats shortest round-trip digits for 64-bit floats", () => { + expect(formatStructFloat(1, 64)).toBe("1"); + expect(formatStructFloat(123456, 64)).toBe("123456"); + expect(formatStructFloat(1000000, 64)).toBe("1e+06"); + expect(formatStructFloat(1234567, 64)).toBe("1.234567e+06"); + expect(formatStructFloat(0.5, 64)).toBe("0.5"); + expect(formatStructFloat(0.000001, 64)).toBe("1e-06"); + expect(formatStructFloat(0, 64)).toBe("0"); + expect(formatStructFloat(-2.5, 64)).toBe("-2.5"); }); - it("rounds through float32 like Go's typed fields", () => { - expect(goFormatFloat(16777217, 32)).toBe("1.6777216e+07"); - expect(goFormatFloat(78125, 32)).toBe("78125"); - expect(goFormatFloat(0.5, 32)).toBe("0.5"); + it("rounds through float32 for typed fields", () => { + expect(formatStructFloat(16777217, 32)).toBe("1.6777216e+07"); + expect(formatStructFloat(78125, 32)).toBe("78125"); + expect(formatStructFloat(0.5, 32)).toBe("0.5"); }); it("breaks exact shortest-digit ties to even like Ryu, not half-up", () => { // 4249.03125 sits exactly between the two shortest 8-digit candidates, rounding to the even // final digit both ways. - expect(goFormatFloat(4249.03125, 32)).toBe("4249.0312"); - expect(goFormatFloat(4249.09375, 32)).toBe("4249.0938"); - expect(goFormatFloat(123456789, 32)).toBe("1.2345679e+08"); - expect(goFormatFloat(1048575.5, 32)).toBe("1.0485755e+06"); - expect(goFormatFloat(8388607.5, 32)).toBe("8.3886075e+06"); + expect(formatStructFloat(4249.03125, 32)).toBe("4249.0312"); + expect(formatStructFloat(4249.09375, 32)).toBe("4249.0938"); + expect(formatStructFloat(123456789, 32)).toBe("1.2345679e+08"); + expect(formatStructFloat(1048575.5, 32)).toBe("1.0485755e+06"); + expect(formatStructFloat(8388607.5, 32)).toBe("8.3886075e+06"); // Boundaries: smallest subnormal, subnormal→normal edge, and max finite. - expect(goFormatFloat(1.401298464324817e-45, 32)).toBe("1e-45"); - expect(goFormatFloat(1.1754943508222875e-38, 32)).toBe("1.1754944e-38"); - expect(goFormatFloat(3.4028234663852886e38, 32)).toBe("3.4028235e+38"); - expect(goFormatFloat(-4249.03125, 32)).toBe("-4249.0312"); + expect(formatStructFloat(1.401298464324817e-45, 32)).toBe("1e-45"); + expect(formatStructFloat(1.1754943508222875e-38, 32)).toBe("1.1754944e-38"); + expect(formatStructFloat(3.4028234663852886e38, 32)).toBe("3.4028235e+38"); + expect(formatStructFloat(-4249.03125, 32)).toBe("-4249.0312"); }); }); diff --git a/apps/cli/src/command-internal/go-struct-output.openapi-drift.unit.test.ts b/apps/cli/src/command-internal/struct-output.openapi-drift.unit.test.ts similarity index 77% rename from apps/cli/src/command-internal/go-struct-output.openapi-drift.unit.test.ts rename to apps/cli/src/command-internal/struct-output.openapi-drift.unit.test.ts index 23ec1da4e8..78bccd0c81 100644 --- a/apps/cli/src/command-internal/go-struct-output.openapi-drift.unit.test.ts +++ b/apps/cli/src/command-internal/struct-output.openapi-drift.unit.test.ts @@ -1,23 +1,23 @@ import { describe, expect, it } from "vitest"; import openApiSpec from "@supabase/api/openapi.json"; -import { GO_BRANCH_RESPONSE } from "../commands/branches/branches.go-payload.ts"; -import { GO_ORGANIZATION_RESPONSE } from "../commands/orgs/orgs.go-payload.ts"; -import { GO_SSL_ENFORCEMENT_RESPONSE } from "../commands/ssl-enforcement/ssl-enforcement.go-payload.ts"; -import { GO_SSO_PROVIDER_RESPONSE } from "../commands/sso/sso.go-payload.ts"; -import type { GoType } from "./go-struct-output.encoders.ts"; +import { BRANCH_RESPONSE_SHAPE } from "../commands/branches/branches.response-shape.ts"; +import { ORGANIZATION_RESPONSE_SHAPE } from "../commands/orgs/orgs.response-shape.ts"; +import { SSL_ENFORCEMENT_RESPONSE_SHAPE } from "../commands/ssl-enforcement/ssl-enforcement.response-shape.ts"; +import { SSO_PROVIDER_RESPONSE_SHAPE } from "../commands/sso/sso.response-shape.ts"; +import type { OutputShape } from "./struct-output.encoders.ts"; /** - * Mechanical drift check for the `*.go-payload.ts` specs against the live OpenAPI schemas they - * mirror — field-name set, `goPtr` vs `required`, field order, and value type/kind must all + * Mechanical drift check for the `*.response-shape.ts` specs against the live OpenAPI schemas they + * mirror — field-name set, `shapePtr` vs `required`, field order, and value type/kind must all * match, or a future spec edit silently changes `-o yaml`/`-o toml` bytes instead of failing a * test. * - * oapi-codegen names its Go struct fields from the schema's JSON keys and declares them in - * alphabetical order, not the schema's own `properties` order, so field order here is compared - * against an ASCII sort of `properties` keys. That assumption holds for every schema below; - * `KNOWN_ORDER_EXCEPTIONS` records the real order for any future schema where oapi-codegen's - * Go-identifier sort diverges from a plain JSON-key sort. + * Struct fields are named from the schema's JSON keys and declared in alphabetical order, not + * the schema's own `properties` order, so field order here is compared against an ASCII sort of + * `properties` keys. That assumption holds for every schema below; `KNOWN_ORDER_EXCEPTIONS` + * records the real order for any future schema where the field-name sort diverges from a plain + * JSON-key sort. */ interface JsonSchema { @@ -51,23 +51,23 @@ function resolveSchema(schema: JsonSchema): JsonSchema { return resolved; } -interface GoStructField { +interface OutputShapeField { readonly json: string; - readonly type: GoType; + readonly type: OutputShape; } -function structFieldsOf(spec: GoType): ReadonlyArray { +function structFieldsOf(spec: OutputShape): ReadonlyArray { if (spec.kind !== "struct") { - throw new Error(`expected a struct GoType, got "${spec.kind}"`); + throw new Error(`expected a struct OutputShape, got "${spec.kind}"`); } return spec.fields; } -function isPointerType(type: GoType): boolean { +function isPointerType(type: OutputShape): boolean { return type.kind === "ptr" || type.kind === "nullable"; } -function unwrapPointer(type: GoType): GoType { +function unwrapPointer(type: OutputShape): OutputShape { return type.kind === "ptr" || type.kind === "nullable" ? type.elem : type; } @@ -79,7 +79,7 @@ interface DriftMismatch { } function compareFieldSet( - fields: ReadonlyArray, + fields: ReadonlyArray, schema: JsonSchema, path: string, ): ReadonlyArray { @@ -108,15 +108,15 @@ function compareFieldSet( } function comparePointerRequired( - fields: ReadonlyArray, + fields: ReadonlyArray, schema: JsonSchema, path: string, ): ReadonlyArray { const required = new Set(schema.required ?? []); const mismatches: Array = []; for (const field of fields) { - // oapi-codegen never pointer-wraps a Go map regardless of the schema's `required` list, so - // this is the one field kind the goPtr/required rule doesn't hold for. + // A map field is never pointer-wrapped regardless of the schema's `required` list, so + // this is the one field kind the shapePtr/required rule doesn't hold for. if (unwrapPointer(field.type).kind === "map") { continue; } @@ -126,8 +126,8 @@ function comparePointerRequired( mismatches.push({ path: `${path}.${field.json}`, message: isPointer - ? `spec marks "${field.json}" as goPtr, but the schema marks it required` - : `spec does not mark "${field.json}" as goPtr, but the schema marks it optional`, + ? `spec marks "${field.json}" as shapePtr, but the schema marks it required` + : `spec does not mark "${field.json}" as shapePtr, but the schema marks it optional`, }); } } @@ -135,7 +135,7 @@ function comparePointerRequired( } function compareValueType( - type: GoType, + type: OutputShape, schema: JsonSchema, path: string, ): ReadonlyArray { @@ -162,7 +162,7 @@ function compareValueType( return [ { path, - message: `expected type=string format=date-time (Go time.Time), got type=${resolved.type ?? ""} format=${resolved.format ?? ""}`, + message: `expected type=string format=date-time (timestamp), got type=${resolved.type ?? ""} format=${resolved.format ?? ""}`, }, ]; } @@ -232,7 +232,7 @@ function compareValueType( } function compareValueTypes( - fields: ReadonlyArray, + fields: ReadonlyArray, schema: JsonSchema, path: string, ): ReadonlyArray { @@ -248,7 +248,7 @@ function compareValueTypes( } function compareFieldOrder( - fields: ReadonlyArray, + fields: ReadonlyArray, schema: JsonSchema, schemaKey: string, path: string, @@ -269,13 +269,13 @@ function compareFieldOrder( } /** - * Walks a {@link GoType} struct spec and the corresponding OpenAPI schema in lockstep, returning + * Walks a {@link OutputShape} struct spec and the corresponding OpenAPI schema in lockstep, returning * every mismatch found. Recurses into a struct field, and into a slice field whose element is a * struct, so both `saml`'s nested fields and `domains`' array elements get the same field-set, * pointer/required, order, and value-type checks as the top-level struct. */ -function compareGoStructToSchema( - spec: GoType, +function compareShapeToSchema( + spec: OutputShape, schema: JsonSchema, schemaName: string, path: string, @@ -298,7 +298,7 @@ function compareGoStructToSchema( } if (inner.kind === "struct") { mismatches.push( - ...compareGoStructToSchema(inner, nestedSchema, schemaName, `${path}.${field.json}`), + ...compareShapeToSchema(inner, nestedSchema, schemaName, `${path}.${field.json}`), ); continue; } @@ -312,7 +312,7 @@ function compareGoStructToSchema( continue; } mismatches.push( - ...compareGoStructToSchema(elem, itemsSchema, schemaName, `${path}.${field.json}[]`), + ...compareShapeToSchema(elem, itemsSchema, schemaName, `${path}.${field.json}[]`), ); } } @@ -320,40 +320,44 @@ function compareGoStructToSchema( return mismatches; } -interface GoPayloadSpecEntry { +interface PayloadShapeEntry { readonly specName: string; - readonly spec: GoType; + readonly spec: OutputShape; readonly schemaName: string; } -const GO_PAYLOAD_SPEC_REGISTRY: ReadonlyArray = [ - { specName: "GO_BRANCH_RESPONSE", spec: GO_BRANCH_RESPONSE, schemaName: "BranchResponse_Output" }, +const PAYLOAD_SHAPE_REGISTRY: ReadonlyArray = [ { - specName: "GO_ORGANIZATION_RESPONSE", - spec: GO_ORGANIZATION_RESPONSE, + specName: "BRANCH_RESPONSE_SHAPE", + spec: BRANCH_RESPONSE_SHAPE, + schemaName: "BranchResponse_Output", + }, + { + specName: "ORGANIZATION_RESPONSE_SHAPE", + spec: ORGANIZATION_RESPONSE_SHAPE, schemaName: "OrganizationResponseV1_Output", }, { - specName: "GO_SSL_ENFORCEMENT_RESPONSE", - spec: GO_SSL_ENFORCEMENT_RESPONSE, + specName: "SSL_ENFORCEMENT_RESPONSE_SHAPE", + spec: SSL_ENFORCEMENT_RESPONSE_SHAPE, schemaName: "SslEnforcementResponse_Output", }, { - specName: "GO_SSO_PROVIDER_RESPONSE", - spec: GO_SSO_PROVIDER_RESPONSE, + specName: "SSO_PROVIDER_RESPONSE_SHAPE", + spec: SSO_PROVIDER_RESPONSE_SHAPE, schemaName: "GetProviderResponse_Output", }, ]; -describe("go-payload specs vs the OpenAPI schema (drift check)", () => { - it.each(GO_PAYLOAD_SPEC_REGISTRY)( +describe("response-shape specs vs the OpenAPI schema (drift check)", () => { + it.each(PAYLOAD_SHAPE_REGISTRY)( "$specName matches the $schemaName schema with zero drift", ({ spec, schemaName }) => { const schema = SCHEMAS[schemaName]; if (schema === undefined) { throw new Error(`missing OpenAPI schema "${schemaName}"`); } - expect(compareGoStructToSchema(spec, schema, schemaName, "$")).toEqual([]); + expect(compareShapeToSchema(spec, schema, schemaName, "$")).toEqual([]); }, ); @@ -368,8 +372,8 @@ describe("go-payload specs vs the OpenAPI schema (drift check)", () => { }, required: ["appliedSuccessfully", "currentConfig"], }; - const mismatches = compareGoStructToSchema( - GO_SSL_ENFORCEMENT_RESPONSE, + const mismatches = compareShapeToSchema( + SSL_ENFORCEMENT_RESPONSE_SHAPE, mutatedSchema, "SslEnforcementResponse", "$", @@ -382,7 +386,7 @@ describe("go-payload specs vs the OpenAPI schema (drift check)", () => { it("has teeth: reports a mismatch when a field's required-ness flips", () => { // A hand-mutated copy of the real BranchResponse schema with `git_branch` moved into - // `required`, though the spec still marks it `goPtr`. + // `required`, though the spec still marks it `shapePtr`. const mutatedSchema: JsonSchema = { type: "object", properties: { @@ -418,8 +422,8 @@ describe("go-payload specs vs the OpenAPI schema (drift check)", () => { "with_data", ], }; - const mismatches = compareGoStructToSchema( - GO_BRANCH_RESPONSE, + const mismatches = compareShapeToSchema( + BRANCH_RESPONSE_SHAPE, mutatedSchema, "BranchResponse", "$", @@ -427,7 +431,7 @@ describe("go-payload specs vs the OpenAPI schema (drift check)", () => { expect(mismatches).toContainEqual( expect.objectContaining({ path: "$.git_branch", - message: expect.stringContaining("goPtr"), + message: expect.stringContaining("shapePtr"), }), ); }); @@ -447,8 +451,8 @@ describe("go-payload specs vs the OpenAPI schema (drift check)", () => { }, required: ["appliedSuccessfully", "currentConfig"], }; - const mismatches = compareGoStructToSchema( - GO_SSL_ENFORCEMENT_RESPONSE, + const mismatches = compareShapeToSchema( + SSL_ENFORCEMENT_RESPONSE_SHAPE, mutatedSchema, "SslEnforcementResponse", "$", diff --git a/apps/cli/src/command-internal/viper-env.ts b/apps/cli/src/command-internal/supabase-env.ts similarity index 74% rename from apps/cli/src/command-internal/viper-env.ts rename to apps/cli/src/command-internal/supabase-env.ts index 01bc4257d7..0bf2ab15d1 100644 --- a/apps/cli/src/command-internal/viper-env.ts +++ b/apps/cli/src/command-internal/supabase-env.ts @@ -6,16 +6,16 @@ * so callers OR the parsed flag value with this read (flag-set wins). */ -const VIPER_TRUE = new Set(["1", "t", "T", "TRUE", "true", "True"]); +const TRUE_VALUES = new Set(["1", "t", "T", "TRUE", "true", "True"]); /** Truthiness for an already-resolved env value (see module doc). */ -function viperBool(raw: string | undefined): boolean { - return raw !== undefined && VIPER_TRUE.has(raw); +function parseEnvBool(raw: string | undefined): boolean { + return raw !== undefined && TRUE_VALUES.has(raw); } /** Reads a single `SUPABASE_*` boolean env var from `process.env` (see module doc). */ -export function viperEnvBool(name: string): boolean { - return viperBool(process.env[name]); +export function supabaseEnvBool(name: string): boolean { + return parseEnvBool(process.env[name]); } /** @@ -26,23 +26,23 @@ export function viperEnvBool(name: string): boolean { * project env, letting an opt-out gate default on while any present value * still disables. */ -export function viperEnvBoolWithProjectFallback( +export function supabaseEnvBoolWithProjectFallback( name: string, projectEnv: Record, opts: { readonly whenUnset?: boolean } = {}, ): boolean { const raw = process.env[name] ?? projectEnv[name]; if (raw === undefined) return opts.whenUnset ?? false; - return viperBool(raw); + return parseEnvBool(raw); } /** * Resolves a `SUPABASE_*` string with the same shell-presence-wins semantics - * as {@link viperEnvBoolWithProjectFallback}, but with no boolean coercion — + * as {@link supabaseEnvBoolWithProjectFallback}, but with no boolean coercion — * the raw merged string, or `""` when absent from both. `??` (not `||`) * encodes the presence check. */ -export function viperEnvStringWithProjectFallback( +export function supabaseEnvStringWithProjectFallback( name: string, projectEnv: Record, ): string { diff --git a/apps/cli/src/command-internal/supabase-env.unit.test.ts b/apps/cli/src/command-internal/supabase-env.unit.test.ts new file mode 100644 index 0000000000..a18e826e8e --- /dev/null +++ b/apps/cli/src/command-internal/supabase-env.unit.test.ts @@ -0,0 +1,121 @@ +import { afterEach, describe, expect, it } from "vitest"; + +import { + supabaseEnvBool, + supabaseEnvBoolWithProjectFallback, + supabaseEnvStringWithProjectFallback, +} from "./supabase-env.ts"; + +const KEY = "SUPABASE_TEST_ENV_BOOL"; +const STRING_KEY = "SUPABASE_TEST_ENV_STRING"; + +describe("supabaseEnvBool", () => { + afterEach(() => { + delete process.env[KEY]; + }); + + it("is true only for the boolean true set", () => { + for (const value of ["1", "t", "T", "TRUE", "true", "True"]) { + process.env[KEY] = value; + expect(supabaseEnvBool(KEY)).toBe(true); + } + }); + + it("is false for the false set and any unrecognized value", () => { + for (const value of ["0", "f", "F", "FALSE", "false", "False", "yes", "on", "", "nope"]) { + process.env[KEY] = value; + expect(supabaseEnvBool(KEY)).toBe(false); + } + }); + + it("is false when the env var is absent", () => { + delete process.env[KEY]; + expect(supabaseEnvBool(KEY)).toBe(false); + }); +}); + +describe("supabaseEnvBoolWithProjectFallback", () => { + afterEach(() => { + delete process.env[KEY]; + }); + + it("falls back to the project value only when the shell var is absent", () => { + delete process.env[KEY]; + expect(supabaseEnvBoolWithProjectFallback(KEY, { [KEY]: "true" })).toBe(true); + expect(supabaseEnvBoolWithProjectFallback(KEY, { [KEY]: "false" })).toBe(false); + expect(supabaseEnvBoolWithProjectFallback(KEY, {})).toBe(false); + }); + + it("keeps a false shell override even when the project .env says true", () => { + process.env[KEY] = "false"; + expect(supabaseEnvBoolWithProjectFallback(KEY, { [KEY]: "true" })).toBe(false); + }); + + it("treats an empty shell value as present (blocks the project value) and false", () => { + process.env[KEY] = ""; + expect(supabaseEnvBoolWithProjectFallback(KEY, { [KEY]: "true" })).toBe(false); + }); + + it("treats an unparsable shell value as present and false", () => { + process.env[KEY] = "banana"; + expect(supabaseEnvBoolWithProjectFallback(KEY, { [KEY]: "true" })).toBe(false); + }); + + it("keeps a true shell value over a false project value", () => { + process.env[KEY] = "true"; + expect(supabaseEnvBoolWithProjectFallback(KEY, { [KEY]: "false" })).toBe(true); + }); + + it("whenUnset: true resolves a key absent from both envs to true (opt-out gate default)", () => { + delete process.env[KEY]; + expect(supabaseEnvBoolWithProjectFallback(KEY, {}, { whenUnset: true })).toBe(true); + }); + + it("whenUnset: true still yields false for any present non-true value", () => { + process.env[KEY] = "0"; + expect(supabaseEnvBoolWithProjectFallback(KEY, {}, { whenUnset: true })).toBe(false); + process.env[KEY] = ""; + expect(supabaseEnvBoolWithProjectFallback(KEY, { [KEY]: "true" }, { whenUnset: true })).toBe( + false, + ); + process.env[KEY] = "banana"; + expect(supabaseEnvBoolWithProjectFallback(KEY, {}, { whenUnset: true })).toBe(false); + delete process.env[KEY]; + expect(supabaseEnvBoolWithProjectFallback(KEY, { [KEY]: "false" }, { whenUnset: true })).toBe( + false, + ); + }); +}); + +describe("supabaseEnvStringWithProjectFallback", () => { + afterEach(() => { + delete process.env[STRING_KEY]; + }); + + it("falls back to the project value only when the shell var is absent", () => { + delete process.env[STRING_KEY]; + expect( + supabaseEnvStringWithProjectFallback(STRING_KEY, { [STRING_KEY]: "project-value" }), + ).toBe("project-value"); + expect(supabaseEnvStringWithProjectFallback(STRING_KEY, {})).toBe(""); + }); + + it("keeps the shell value over a project value", () => { + process.env[STRING_KEY] = "shell-value"; + expect( + supabaseEnvStringWithProjectFallback(STRING_KEY, { [STRING_KEY]: "project-value" }), + ).toBe("shell-value"); + }); + + it("treats an empty shell value as present (blocks the project value)", () => { + process.env[STRING_KEY] = ""; + expect( + supabaseEnvStringWithProjectFallback(STRING_KEY, { [STRING_KEY]: "project-value" }), + ).toBe(""); + }); + + it("returns an empty string (not undefined) when absent from both", () => { + delete process.env[STRING_KEY]; + expect(supabaseEnvStringWithProjectFallback(STRING_KEY, {})).toBe(""); + }); +}); diff --git a/apps/cli/src/command-internal/tenant-versions.unit.test.ts b/apps/cli/src/command-internal/tenant-versions.unit.test.ts index da46e26ff8..6412841f52 100644 --- a/apps/cli/src/command-internal/tenant-versions.unit.test.ts +++ b/apps/cli/src/command-internal/tenant-versions.unit.test.ts @@ -12,7 +12,7 @@ describe("parsePostgrestVersion", () => { expect(parsePostgrestVersion({ info: { version: "12.2.0" } })).toEqual(Option.some("v12.2.0")); }); - it("uses only the first whitespace-delimited field (Go strings.Fields)", () => { + it("uses only the first whitespace-delimited field", () => { expect(parsePostgrestVersion({ info: { version: "12.2.0 (abc123)" } })).toEqual( Option.some("v12.2.0"), ); diff --git a/apps/cli/src/command-internal/test-db.integration.test.ts b/apps/cli/src/command-internal/test-db.integration.test.ts index 539b199f80..679ae17107 100644 --- a/apps/cli/src/command-internal/test-db.integration.test.ts +++ b/apps/cli/src/command-internal/test-db.integration.test.ts @@ -624,7 +624,7 @@ describe("test db integration", () => { }).pipe(Effect.provide(layer)); }); - it.live("json mode: streams TAP only — emits no result envelope (Go parity)", () => { + it.live("json mode: streams TAP only — emits no result envelope", () => { const { layer, out } = setup({ format: "json", exitCode: 0 }); return Effect.gen(function* () { yield* testDb(flags()); @@ -632,7 +632,7 @@ describe("test db integration", () => { }).pipe(Effect.provide(layer)); }); - it.live("stream-json mode: emits no result envelope (Go parity)", () => { + it.live("stream-json mode: emits no result envelope", () => { const { layer, out } = setup({ format: "stream-json", exitCode: 0 }); return Effect.gen(function* () { yield* testDb(flags()); @@ -721,7 +721,7 @@ describe("test db integration", () => { }).pipe(Effect.provide(layer)); }); - it.live("writes the connection diagnostic to stderr, keeping stdout for TAP (Go parity)", () => { + it.live("writes the connection diagnostic to stderr, keeping stdout for TAP", () => { const { layer, out } = setup(); return Effect.gen(function* () { yield* testDb(flags()); @@ -744,7 +744,7 @@ describe("test db integration", () => { }); const tempWorkdir = useTempWorkdir(); - it.live("sanitizes a configured project_id when naming the local network (Go parity)", () => { + it.live("sanitizes a configured project_id when naming the local network", () => { const workdir = tempWorkdir.current; mkdirSync(join(workdir, "supabase"), { recursive: true }); writeFileSync(join(workdir, "supabase", "config.toml"), 'project_id = "My Project"\n'); diff --git a/apps/cli/src/command-internal/timestamp.format.unit.test.ts b/apps/cli/src/command-internal/timestamp.format.unit.test.ts index c7efdef426..9b0a4157c8 100644 --- a/apps/cli/src/command-internal/timestamp.format.unit.test.ts +++ b/apps/cli/src/command-internal/timestamp.format.unit.test.ts @@ -12,7 +12,7 @@ describe("formatTimestamp", () => { }); it("falls back to the original value for already-formatted timestamps", () => { - // Go's time.Parse(time.RFC3339, ...) rejects "2026-02-08 16:44:07" (space, not T). + // RFC3339 parsing rejects "2026-02-08 16:44:07" (space, not T). expect(formatTimestamp("2026-02-08 16:44:07")).toBe("2026-02-08 16:44:07"); }); diff --git a/apps/cli/src/command-internal/upgrade-notice.unit.test.ts b/apps/cli/src/command-internal/upgrade-notice.unit.test.ts index 26d7067b1a..089f40101a 100644 --- a/apps/cli/src/command-internal/upgrade-notice.unit.test.ts +++ b/apps/cli/src/command-internal/upgrade-notice.unit.test.ts @@ -28,7 +28,7 @@ import { } from "./upgrade-notice.ts"; describe("updateNotifierDisabled", () => { - it.each(["1", "t", "T", "TRUE", "true", "True"])("suppresses for ParseBool-true %j", (value) => { + it.each(["1", "t", "T", "TRUE", "true", "True"])("suppresses for boolean-true %j", (value) => { expect(updateNotifierDisabled(value)).toBe(true); }); @@ -73,7 +73,7 @@ describe("isNewerCliVersion", () => { }); describe("formatUpgradeNotice", () => { - it("matches the Go CLI's message bytes modulo styling", () => { + it("emits the expected message bytes modulo styling", () => { expect(stripVTControlCharacters(formatUpgradeNotice("v2.114.0", "2.113.0"))).toBe( "A new version of Supabase CLI is available: v2.114.0 (currently installed v2.113.0)\n" + "We recommend updating regularly for new features and bug fixes: " + @@ -205,14 +205,14 @@ describe("runUpgradeNotice", () => { ctx.cleanup(); }); - it("SUPABASE_DEBUG surfaces fetch failures without the --debug flag, like viper's AutomaticEnv", async () => { + it("SUPABASE_DEBUG surfaces fetch failures without the --debug flag", async () => { const ctx = setup({ fetchFails: true, project: false, env: { SUPABASE_DEBUG: "1" } }); await runUpgradeNotice(ctx.deps); expect(ctx.stderr).toContain("Failed to fetch latest release"); ctx.cleanup(); }); - it("--debug=true surfaces fetch failures and --debug=false silences them, like pflag", async () => { + it("--debug=true surfaces fetch failures and --debug=false silences them", async () => { const on = setup({ fetchFails: true, project: false, args: ["db", "start", "--debug=true"] }); await runUpgradeNotice(on.deps); expect(on.stderr).toContain("Failed to fetch latest release"); @@ -230,7 +230,7 @@ describe("runUpgradeNotice", () => { off.cleanup(); }); - it("a built-in ignores SUPABASE_DEBUG but still honors the --debug flag, like cobra's init order", async () => { + it("a built-in ignores SUPABASE_DEBUG but still honors the --debug flag", async () => { const viaEnv = setup({ fetchFails: true, project: false, @@ -263,7 +263,7 @@ describe("runUpgradeNotice", () => { ctx.cleanup(); }); - it("a --debug operand after -- or consumed by a value flag is not the debug flag, like pflag", async () => { + it("a --debug operand after -- or consumed by a value flag is not the debug flag", async () => { const afterTerminator = setup({ fetchFails: true, project: false, @@ -298,7 +298,7 @@ describe("runUpgradeNotice", () => { ); it.skipIf(process.platform === "win32")( - "creates the cache directory and file with Go-compatible modes", + "creates the cache directory and file with the expected modes", async () => { const ctx = setup({}); const previousUmask = process.umask(0); @@ -333,7 +333,7 @@ describe("runUpgradeNotice", () => { }, ); - it("a --debug consumed by the leaf command's own value flag is not the debug flag, like pflag", async () => { + it("a --debug consumed by the leaf command's own value flag is not the debug flag", async () => { // `login --name --debug`: `--debug` is consumed as `--name`'s value; the // real CLI passes the resolved leaf's value-flag predicate into the hook. const ctx = setup({ fetchFails: true, project: false, args: ["login", "--name", "--debug"] }); @@ -360,7 +360,7 @@ describe("runUpgradeNotice", () => { }); it.skipIf(process.getuid?.() === 0)( - "an existing read-only cache file fails the backoff write, like Go's direct open", + "an existing read-only cache file fails the backoff write", async () => { const ctx = setup({ args: ["db", "start", "--debug"], @@ -376,7 +376,7 @@ describe("runUpgradeNotice", () => { }, ); - it("project dotenv SUPABASE_DEBUG surfaces diagnostics, like godotenv before the Execute tail", async () => { + it("project dotenv SUPABASE_DEBUG surfaces diagnostics", async () => { // The symlinked .temp disables the backoff write, so the fetch error is // what remains to log. const ctx = setup({ fetchFails: true }); @@ -397,7 +397,7 @@ describe("runUpgradeNotice", () => { blocked.cleanup(); }); - it("a failed fetch inside a project stays silent under --debug, matching Go's backoff", async () => { + it("a failed fetch inside a project stays silent under --debug", async () => { const ctx = setup({ fetchFails: true, args: ["db", "start", "--debug"] }); await runUpgradeNotice(ctx.deps); // The empty-cache backoff write succeeds, so no debug line is emitted. @@ -444,7 +444,7 @@ describe("runUpgradeNotice", () => { ctx.cleanup(); }); - it("an explicit empty --workdir beats SUPABASE_WORKDIR and falls back to the walk, like viper", async () => { + it("an explicit empty --workdir beats SUPABASE_WORKDIR and falls back to the walk", async () => { const ctx = setup({}); const envDir = join(workdir, "env-project"); mkdirSync(join(envDir, "supabase"), { recursive: true }); @@ -459,7 +459,7 @@ describe("runUpgradeNotice", () => { ctx.cleanup(); }); - it("a project dotenv SUPABASE_NO_UPDATE_NOTIFIER opt-out suppresses the notice, like godotenv", async () => { + it("a project dotenv SUPABASE_NO_UPDATE_NOTIFIER opt-out suppresses the notice", async () => { const ctx = setup({}); writeFileSync(join(workdir, "supabase", ".env"), "SUPABASE_NO_UPDATE_NOTIFIER=1\n"); await runUpgradeNotice(ctx.deps); @@ -468,7 +468,7 @@ describe("runUpgradeNotice", () => { ctx.cleanup(); }); - it("a shell env that defines the key beats the project dotenv, like godotenv's no-override", async () => { + it("a shell env that defines the key beats the project dotenv", async () => { // Defined-but-unparseable in the shell env still blocks the project // dotenv from overriding, and an empty value keeps the notifier on. const ctx = setup({ env: { SUPABASE_NO_UPDATE_NOTIFIER: "" } }); @@ -478,7 +478,7 @@ describe("runUpgradeNotice", () => { ctx.cleanup(); }); - it("--help ignores the project dotenv opt-out — Go never loads config for the built-ins", async () => { + it("--help ignores the project dotenv opt-out — built-ins never load config", async () => { const ctx = setup({ args: ["--help"] }); writeFileSync(join(workdir, "supabase", ".env"), "SUPABASE_NO_UPDATE_NOTIFIER=1\n"); await runUpgradeNotice(ctx.deps); @@ -486,7 +486,7 @@ describe("runUpgradeNotice", () => { ctx.cleanup(); }); - it("resolves --help and --version against the bare cwd, ignoring --workdir, like Go", async () => { + it("resolves --help and --version against the bare cwd, ignoring --workdir", async () => { const ctx = setup({ project: false }); const flagged = join(workdir, "flagged"); mkdirSync(join(flagged, "supabase"), { recursive: true }); @@ -514,7 +514,7 @@ describe("runUpgradeNotice", () => { ctx.cleanup(); }); - it("does not treat Effect's -v shorthand as Go's root version flag", async () => { + it("does not treat Effect's -v shorthand as the root version flag", async () => { const ctx = setup({ project: false }); const flagged = join(workdir, "flagged"); const cacheFile = join(flagged, "supabase", ".temp", "cli-latest"); @@ -545,7 +545,7 @@ describe("runUpgradeNotice", () => { ctx.cleanup(); }); - it("ignores a --workdir operand after the -- terminator, like cobra", async () => { + it("ignores a --workdir operand after the -- terminator", async () => { const ctx = setup({}); const elsewhere = join(workdir, "elsewhere"); mkdirSync(join(elsewhere, "supabase"), { recursive: true }); diff --git a/apps/cli/src/command-internal/vault-decrypt.unit.test.ts b/apps/cli/src/command-internal/vault-decrypt.unit.test.ts index 4e21dd66aa..aeb421c12e 100644 --- a/apps/cli/src/command-internal/vault-decrypt.unit.test.ts +++ b/apps/cli/src/command-internal/vault-decrypt.unit.test.ts @@ -20,7 +20,7 @@ describe("isEncryptedSecret", () => { }); describe("decryptSecret", () => { - it("decrypts Go's test vector to the expected plaintext", () => { + it("decrypts the test vector to the expected plaintext", () => { const result = decryptSecret(ENCRYPTED_VALUE, [PRIVATE_KEY]); expect(result).toEqual({ ok: true, value: "value" }); }); diff --git a/apps/cli/src/command-internal/viper-env.unit.test.ts b/apps/cli/src/command-internal/viper-env.unit.test.ts deleted file mode 100644 index 019307500e..0000000000 --- a/apps/cli/src/command-internal/viper-env.unit.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { afterEach, describe, expect, it } from "vitest"; - -import { - viperEnvBool, - viperEnvBoolWithProjectFallback, - viperEnvStringWithProjectFallback, -} from "./viper-env.ts"; - -const KEY = "SUPABASE_TEST_VIPER_BOOL"; -const STRING_KEY = "SUPABASE_TEST_VIPER_STRING"; - -describe("viperEnvBool", () => { - afterEach(() => { - delete process.env[KEY]; - }); - - it("is true only for strconv.ParseBool's true set (viper.GetBool parity)", () => { - for (const value of ["1", "t", "T", "TRUE", "true", "True"]) { - process.env[KEY] = value; - expect(viperEnvBool(KEY)).toBe(true); - } - }); - - it("is false for the false set and any unrecognized value", () => { - for (const value of ["0", "f", "F", "FALSE", "false", "False", "yes", "on", "", "nope"]) { - process.env[KEY] = value; - expect(viperEnvBool(KEY)).toBe(false); - } - }); - - it("is false when the env var is absent", () => { - delete process.env[KEY]; - expect(viperEnvBool(KEY)).toBe(false); - }); -}); - -describe("viperEnvBoolWithProjectFallback", () => { - afterEach(() => { - delete process.env[KEY]; - }); - - it("falls back to the project value only when the shell var is absent", () => { - delete process.env[KEY]; - expect(viperEnvBoolWithProjectFallback(KEY, { [KEY]: "true" })).toBe(true); - expect(viperEnvBoolWithProjectFallback(KEY, { [KEY]: "false" })).toBe(false); - expect(viperEnvBoolWithProjectFallback(KEY, {})).toBe(false); - }); - - it("keeps a false shell override even when the project .env says true", () => { - process.env[KEY] = "false"; - expect(viperEnvBoolWithProjectFallback(KEY, { [KEY]: "true" })).toBe(false); - }); - - it("treats an empty shell value as present (blocks the project value) and false", () => { - process.env[KEY] = ""; - expect(viperEnvBoolWithProjectFallback(KEY, { [KEY]: "true" })).toBe(false); - }); - - it("treats an unparsable shell value as present and false (cast.ToBool swallows the error)", () => { - process.env[KEY] = "banana"; - expect(viperEnvBoolWithProjectFallback(KEY, { [KEY]: "true" })).toBe(false); - }); - - it("keeps a true shell value over a false project value", () => { - process.env[KEY] = "true"; - expect(viperEnvBoolWithProjectFallback(KEY, { [KEY]: "false" })).toBe(true); - }); - - it("whenUnset: true resolves a key absent from both envs to true (opt-out gate default)", () => { - delete process.env[KEY]; - expect(viperEnvBoolWithProjectFallback(KEY, {}, { whenUnset: true })).toBe(true); - }); - - it("whenUnset: true still yields false for any present non-true value", () => { - process.env[KEY] = "0"; - expect(viperEnvBoolWithProjectFallback(KEY, {}, { whenUnset: true })).toBe(false); - process.env[KEY] = ""; - expect(viperEnvBoolWithProjectFallback(KEY, { [KEY]: "true" }, { whenUnset: true })).toBe( - false, - ); - process.env[KEY] = "banana"; - expect(viperEnvBoolWithProjectFallback(KEY, {}, { whenUnset: true })).toBe(false); - delete process.env[KEY]; - expect(viperEnvBoolWithProjectFallback(KEY, { [KEY]: "false" }, { whenUnset: true })).toBe( - false, - ); - }); -}); - -describe("viperEnvStringWithProjectFallback", () => { - afterEach(() => { - delete process.env[STRING_KEY]; - }); - - it("falls back to the project value only when the shell var is absent", () => { - delete process.env[STRING_KEY]; - expect(viperEnvStringWithProjectFallback(STRING_KEY, { [STRING_KEY]: "project-value" })).toBe( - "project-value", - ); - expect(viperEnvStringWithProjectFallback(STRING_KEY, {})).toBe(""); - }); - - it("keeps the shell value over a project value", () => { - process.env[STRING_KEY] = "shell-value"; - expect(viperEnvStringWithProjectFallback(STRING_KEY, { [STRING_KEY]: "project-value" })).toBe( - "shell-value", - ); - }); - - it("treats an empty shell value as present (blocks the project value)", () => { - process.env[STRING_KEY] = ""; - expect(viperEnvStringWithProjectFallback(STRING_KEY, { [STRING_KEY]: "project-value" })).toBe( - "", - ); - }); - - it("returns an empty string (not undefined) when absent from both, matching viper.GetString", () => { - delete process.env[STRING_KEY]; - expect(viperEnvStringWithProjectFallback(STRING_KEY, {})).toBe(""); - }); -}); diff --git a/apps/cli/src/commands/backups/list/SIDE_EFFECTS.md b/apps/cli/src/commands/backups/list/SIDE_EFFECTS.md index 1609312f06..b3689bd4b1 100644 --- a/apps/cli/src/commands/backups/list/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/backups/list/SIDE_EFFECTS.md @@ -56,7 +56,7 @@ The `--output {pretty,json,yaml,toml,env}` flag and the `--output-format {text,j ### `--output pretty` (default) / `--output-format text` -For PITR-only projects, prints a Glamour-styled markdown table with columns: `REGION`, `WALG`, `PITR`, `EARLIEST TIMESTAMP`, `LATEST TIMESTAMP`. For projects with logical/physical backups, prints columns: `REGION`, `BACKUP TYPE`, `STATUS`, `CREATED AT (UTC)`. The table is rendered byte-for-byte using `glamour.WithStandardStyle(styles.AsciiStyle)`. +For PITR-only projects, prints a styled markdown table with columns: `REGION`, `WALG`, `PITR`, `EARLIEST TIMESTAMP`, `LATEST TIMESTAMP`. For projects with logical/physical backups, prints columns: `REGION`, `BACKUP TYPE`, `STATUS`, `CREATED AT (UTC)`. ### `--output json` diff --git a/apps/cli/src/commands/backups/list/list.handler.ts b/apps/cli/src/commands/backups/list/list.handler.ts index adfaf43399..c6595152ff 100644 --- a/apps/cli/src/commands/backups/list/list.handler.ts +++ b/apps/cli/src/commands/backups/list/list.handler.ts @@ -9,45 +9,45 @@ import { OutputFlag } from "../../../command-internal/global-flags.ts"; import { Output } from "../../../shared/output/output.service.ts"; import { renderGlamourTable } from "../../../output/glamour-table.ts"; import { BackupListNetworkError, BackupListUnexpectedStatusError } from "../backups.errors.ts"; -import { encodeEnv, encodeGoJson } from "../../../command-internal/go-output.encoders.ts"; +import { encodeEnv, encodeSortedJson } from "../../../command-internal/output.encoders.ts"; import { - encodeGoToml, - encodeGoYaml, - goBool, - goInt, - goPtr, - goSlice, - goString, - goStruct, -} from "../../../command-internal/go-struct-output.encoders.ts"; + encodeStructToml, + encodeStructYaml, + shapeBool, + shapeInt, + shapePtr, + shapeSlice, + shapeString, + shapeStruct, +} from "../../../command-internal/struct-output.encoders.ts"; import { mapHttpError } from "../../../command-internal/http-errors.ts"; import { formatTimestamp } from "../../../command-internal/timestamp.format.ts"; import { formatRegion } from "../../../command-internal/region.format.ts"; import type { BackupsListFlags } from "./list.command.ts"; /** Struct shape for `-o yaml|toml` encoding of the backups response. */ -const GO_BACKUPS_RESPONSE = goStruct([ +const BACKUPS_RESPONSE_SHAPE = shapeStruct([ [ "backups", - goSlice( - goStruct([ - ["id", goInt], - ["inserted_at", goString], - ["is_physical_backup", goBool], - ["status", goString], + shapeSlice( + shapeStruct([ + ["id", shapeInt], + ["inserted_at", shapeString], + ["is_physical_backup", shapeBool], + ["status", shapeString], ]), ), ], [ "physical_backup_data", - goStruct([ - ["earliest_physical_backup_date_unix", goPtr(goInt)], - ["latest_physical_backup_date_unix", goPtr(goInt)], + shapeStruct([ + ["earliest_physical_backup_date_unix", shapePtr(shapeInt)], + ["latest_physical_backup_date_unix", shapePtr(shapeInt)], ]), ], - ["pitr_enabled", goBool], - ["region", goString], - ["walg_enabled", goBool], + ["pitr_enabled", shapeBool], + ["region", shapeString], + ["walg_enabled", shapeBool], ]); type BackupsResponse = typeof V1ListAllBackupsOutput.Type; @@ -91,7 +91,7 @@ function renderLogicalTable(response: BackupsResponse): string { export const backupsList = Effect.fn("backups.list")(function* (flags: BackupsListFlags) { const output = yield* Output; - const goOutputFlag = yield* OutputFlag; + const outputFlag = yield* OutputFlag; const api = yield* CommandPlatformApi; const resolver = yield* ProjectRefResolver; const linkedProjectCache = yield* LinkedProjectCache; @@ -110,31 +110,31 @@ export const backupsList = Effect.fn("backups.list")(function* (flags: BackupsLi ); yield* fetching?.clear ?? Effect.void; - const goFmt = Option.getOrUndefined(goOutputFlag); + const outputFlagFormat = Option.getOrUndefined(outputFlag); - if (goFmt === "json") { - yield* output.raw(encodeGoJson(response, { nullForEmptyArrays: ["backups"] })); + if (outputFlagFormat === "json") { + yield* output.raw(encodeSortedJson(response, { nullForEmptyArrays: ["backups"] })); return; } - if (goFmt === "yaml") { - yield* output.raw(encodeGoYaml(response, GO_BACKUPS_RESPONSE)); + if (outputFlagFormat === "yaml") { + yield* output.raw(encodeStructYaml(response, BACKUPS_RESPONSE_SHAPE)); return; } - if (goFmt === "toml") { + if (outputFlagFormat === "toml") { // Treats an empty backups list as absent, matching the nullForEmptyArrays JSON handling // above. yield* output.raw( - encodeGoToml( + encodeStructToml( { ...response, backups: response.backups.length > 0 ? response.backups : undefined, }, - GO_BACKUPS_RESPONSE, + BACKUPS_RESPONSE_SHAPE, ), ); return; } - if (goFmt === "env") { + if (outputFlagFormat === "env") { yield* output.raw(encodeEnv(response) + "\n"); return; } diff --git a/apps/cli/src/commands/backups/list/list.integration.test.ts b/apps/cli/src/commands/backups/list/list.integration.test.ts index d02a6126b9..16d4aa7e88 100644 --- a/apps/cli/src/commands/backups/list/list.integration.test.ts +++ b/apps/cli/src/commands/backups/list/list.integration.test.ts @@ -38,7 +38,7 @@ const LOGICAL_RESPONSE: typeof V1ListAllBackupsOutput.Type = { interface SetupOpts { format?: "text" | "json" | "stream-json"; - goOutput?: "env" | "pretty" | "json" | "toml" | "yaml"; + outputFlag?: "env" | "pretty" | "json" | "toml" | "yaml"; response?: typeof V1ListAllBackupsOutput.Type; status?: number; network?: "fail"; @@ -65,7 +65,7 @@ function setup(opts: SetupOpts = {}) { out, api, cliSettings, - goOutput: opts.goOutput === undefined ? Option.none() : Option.some(opts.goOutput), + outputFlag: opts.outputFlag === undefined ? Option.none() : Option.some(opts.outputFlag), }); return { layer, out, api }; } @@ -124,8 +124,8 @@ describe("backups list integration", () => { }).pipe(Effect.provide(layer)); }); - it.live("emits indented JSON to stdout for --output json (Go-compat)", () => { - const { layer, out } = setup({ goOutput: "json", response: PITR_RESPONSE }); + it.live("emits indented JSON to stdout for --output json", () => { + const { layer, out } = setup({ outputFlag: "json", response: PITR_RESPONSE }); return Effect.gen(function* () { yield* backupsList({ projectRef: Option.none() }); expect(out.stdoutText).toBe( @@ -142,7 +142,7 @@ describe("backups list integration", () => { }); it.live("emits YAML to stdout for --output yaml", () => { - const { layer, out } = setup({ goOutput: "yaml", response: PITR_RESPONSE }); + const { layer, out } = setup({ outputFlag: "yaml", response: PITR_RESPONSE }); return Effect.gen(function* () { yield* backupsList({ projectRef: Option.none() }); expect(out.stdoutText).toContain("region: ap-southeast-1"); @@ -151,7 +151,7 @@ describe("backups list integration", () => { }); it.live("emits TOML to stdout for --output toml", () => { - const { layer, out } = setup({ goOutput: "toml", response: PITR_RESPONSE }); + const { layer, out } = setup({ outputFlag: "toml", response: PITR_RESPONSE }); return Effect.gen(function* () { yield* backupsList({ projectRef: Option.none() }); expect(out.stdoutText).toContain('Region = "ap-southeast-1"'); @@ -160,7 +160,7 @@ describe("backups list integration", () => { }); it.live("emits [[Backups]] array-of-tables for --output toml with logical backups", () => { - const { layer, out } = setup({ goOutput: "toml", response: LOGICAL_RESPONSE }); + const { layer, out } = setup({ outputFlag: "toml", response: LOGICAL_RESPONSE }); return Effect.gen(function* () { yield* backupsList({ projectRef: Option.none() }); expect(out.stdoutText).toBe(`PitrEnabled = true @@ -179,7 +179,7 @@ WalgEnabled = true }); it.live("emits KEY=VALUE lines for --output env", () => { - const { layer, out } = setup({ goOutput: "env", response: PITR_RESPONSE }); + const { layer, out } = setup({ outputFlag: "env", response: PITR_RESPONSE }); return Effect.gen(function* () { yield* backupsList({ projectRef: Option.none() }); expect(out.stdoutText).toContain('REGION="ap-southeast-1"'); @@ -188,7 +188,7 @@ WalgEnabled = true }); it.live("treats --output pretty as identical to text mode (Glamour table)", () => { - const { layer, out } = setup({ goOutput: "pretty", response: PITR_RESPONSE }); + const { layer, out } = setup({ outputFlag: "pretty", response: PITR_RESPONSE }); return Effect.gen(function* () { yield* backupsList({ projectRef: Option.none() }); expect(out.stdoutText).toContain("Southeast Asia (Singapore)"); @@ -198,7 +198,7 @@ WalgEnabled = true it.live("--output flag value wins over --output-format when both provided", () => { const { layer, out } = setup({ format: "json", - goOutput: "yaml", + outputFlag: "yaml", response: PITR_RESPONSE, }); return Effect.gen(function* () { @@ -313,20 +313,17 @@ WalgEnabled = true }).pipe(Effect.provide(layer)); }); - it.live( - "sends User-Agent SupabaseCLI/ and no X-Supabase-Command headers (Go parity)", - () => { - const { layer, api } = setup({ - response: PITR_RESPONSE, - userAgent: "SupabaseCLI/1.42.0", - }); - return Effect.gen(function* () { - yield* backupsList({ projectRef: Option.none() }); - const headers = api.requests[0]?.headers; - expect(headers?.["user-agent"]).toBe("SupabaseCLI/1.42.0"); - expect(headers?.["x-supabase-command"]).toBeUndefined(); - expect(headers?.["x-supabase-command-run-id"]).toBeUndefined(); - }).pipe(Effect.provide(layer)); - }, - ); + it.live("sends User-Agent SupabaseCLI/ and no X-Supabase-Command headers", () => { + const { layer, api } = setup({ + response: PITR_RESPONSE, + userAgent: "SupabaseCLI/1.42.0", + }); + return Effect.gen(function* () { + yield* backupsList({ projectRef: Option.none() }); + const headers = api.requests[0]?.headers; + expect(headers?.["user-agent"]).toBe("SupabaseCLI/1.42.0"); + expect(headers?.["x-supabase-command"]).toBeUndefined(); + expect(headers?.["x-supabase-command-run-id"]).toBeUndefined(); + }).pipe(Effect.provide(layer)); + }); }); diff --git a/apps/cli/src/commands/backups/restore/restore.handler.ts b/apps/cli/src/commands/backups/restore/restore.handler.ts index de62569194..389351aac1 100644 --- a/apps/cli/src/commands/backups/restore/restore.handler.ts +++ b/apps/cli/src/commands/backups/restore/restore.handler.ts @@ -5,7 +5,7 @@ import { ProjectRefResolver } from "../../../config/project-ref.service.ts"; import { LinkedProjectCache } from "../../../telemetry/linked-project-cache.service.ts"; import { TelemetryState } from "../../../telemetry/telemetry-state.service.ts"; import { OutputFlag } from "../../../command-internal/global-flags.ts"; -import { encodeGoJson } from "../../../command-internal/go-output.encoders.ts"; +import { encodeSortedJson } from "../../../command-internal/output.encoders.ts"; import { Output } from "../../../shared/output/output.service.ts"; import { BackupRestoreNetworkError, @@ -23,7 +23,7 @@ const mapRestoreError = mapHttpError({ export const backupsRestore = Effect.fn("backups.restore")(function* (flags: BackupsRestoreFlags) { const output = yield* Output; - const goOutputFlag = yield* OutputFlag; + const outputFlag = yield* OutputFlag; const api = yield* CommandPlatformApi; const resolver = yield* ProjectRefResolver; const linkedProjectCache = yield* LinkedProjectCache; @@ -43,16 +43,19 @@ export const backupsRestore = Effect.fn("backups.restore")(function* (flags: Bac ); yield* restoring?.clear ?? Effect.void; - const goFmt = Option.getOrUndefined(goOutputFlag); + const outputFlagFormat = Option.getOrUndefined(outputFlag); // --output is ignored for restore except `json`, a TS-only structured payload; every other // value (including unset) writes the text line to stderr. - if (goFmt === "json") { - yield* output.raw(encodeGoJson({ message: "Started PITR restore", project_ref: ref })); + if (outputFlagFormat === "json") { + yield* output.raw(encodeSortedJson({ message: "Started PITR restore", project_ref: ref })); return; } - if (goFmt === undefined && (output.format === "json" || output.format === "stream-json")) { + if ( + outputFlagFormat === undefined && + (output.format === "json" || output.format === "stream-json") + ) { yield* output.success("Started PITR restore", { project_ref: ref }); return; } diff --git a/apps/cli/src/commands/backups/restore/restore.integration.test.ts b/apps/cli/src/commands/backups/restore/restore.integration.test.ts index 4877c63e84..bdbcc8b83c 100644 --- a/apps/cli/src/commands/backups/restore/restore.integration.test.ts +++ b/apps/cli/src/commands/backups/restore/restore.integration.test.ts @@ -15,7 +15,7 @@ import { backupsRestore } from "./restore.handler.ts"; interface SetupOpts { format?: "text" | "json" | "stream-json"; - goOutput?: "env" | "pretty" | "json" | "toml" | "yaml"; + outputFlag?: "env" | "pretty" | "json" | "toml" | "yaml"; status?: number; network?: "fail"; } @@ -34,7 +34,7 @@ function setup(opts: SetupOpts = {}) { out, api, cliSettings, - goOutput: opts.goOutput === undefined ? Option.none() : Option.some(opts.goOutput), + outputFlag: opts.outputFlag === undefined ? Option.none() : Option.some(opts.outputFlag), }); return { layer, out, api }; } @@ -63,7 +63,7 @@ describe("backups restore integration", () => { }).pipe(Effect.provide(layer)); }); - it.live("writes 'Started PITR restore: \\n' to stderr in text mode (Go parity)", () => { + it.live("writes 'Started PITR restore: \\n' to stderr in text mode", () => { const { layer, out } = setup(); return Effect.gen(function* () { yield* backupsRestore({ @@ -100,8 +100,8 @@ describe("backups restore integration", () => { }).pipe(Effect.provide(layer)); }); - it.live("emits indented JSON to stdout for --output json (Go-compat)", () => { - const { layer, out } = setup({ goOutput: "json" }); + it.live("emits indented JSON to stdout for --output json", () => { + const { layer, out } = setup({ outputFlag: "json" }); return Effect.gen(function* () { yield* backupsRestore({ projectRef: Option.none(), @@ -114,9 +114,9 @@ describe("backups restore integration", () => { }); it.live( - "renders the stderr text line for --output {pretty,yaml,toml,env} (Go ignores --output)", + "renders the stderr text line for --output {pretty,yaml,toml,env} (--output is ignored)", () => { - const { layer, out } = setup({ goOutput: "yaml" }); + const { layer, out } = setup({ outputFlag: "yaml" }); return Effect.gen(function* () { yield* backupsRestore({ projectRef: Option.none(), diff --git a/apps/cli/src/commands/bootstrap/SIDE_EFFECTS.md b/apps/cli/src/commands/bootstrap/SIDE_EFFECTS.md index 26e46f18c5..5099d23869 100644 --- a/apps/cli/src/commands/bootstrap/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/bootstrap/SIDE_EFFECTS.md @@ -23,15 +23,15 @@ command — see Notes). ## Files Written -| Path | Format | When | -| ----------------------------------------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `/supabase/config.toml` | TOML | blank/`scratch` path only (via `initProject`) | -| `/