Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/cli-go/api/overlay.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,9 @@ actions:
update:
schema:
type: string
- target: $.components.schemas.V1CreateProjectBody.properties
description: Adds high availability create-project flag hidden from the generated NestJS spec
update:
high_availability:
type: boolean
description: Whether to enable high availability for the project.
15 changes: 10 additions & 5 deletions apps/cli-go/cmd/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ var (
Short: "Manage Supabase projects",
}

interactive bool
projectName string
orgId string
dbPassword string
region = utils.EnumFlag{
interactive bool
projectName string
orgId string
dbPassword string
highAvailability bool
region = utils.EnumFlag{
Allowed: utils.AwsRegions(),
}
size = utils.EnumFlag{
Expand Down Expand Up @@ -81,6 +82,9 @@ var (
if cmd.Flags().Changed("size") {
body.DesiredInstanceSize = (*api.V1CreateProjectBodyDesiredInstanceSize)(&size.Value)
}
if cmd.Flags().Changed("high-availability") {
body.HighAvailability = cast.Ptr(highAvailability)
}
return create.Run(cmd.Context(), body, afero.NewOsFs())
},
}
Expand Down Expand Up @@ -137,6 +141,7 @@ func init() {
markFlagTelemetrySafe(createFlags.Lookup("org-id"))
createFlags.StringVar(&dbPassword, "db-password", "", "Database password of the project.")
createFlags.Var(&region, "region", "Select a region close to you for the best performance.")
createFlags.BoolVar(&highAvailability, "high-availability", false, "Enable high availability for the project.")
createFlags.String("plan", "", "Select a plan that suits your needs.")
cobra.CheckErr(createFlags.MarkHidden("plan"))
createFlags.Var(&size, "size", "Select a desired instance size for your project.")
Expand Down
28 changes: 28 additions & 0 deletions apps/cli-go/internal/projects/create/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,34 @@ func TestProjectCreateCommand(t *testing.T) {
assert.Empty(t, apitest.ListUnmatchedRequests())
})

t.Run("creates a new high availability project", func(t *testing.T) {
// Setup in-memory fs
fsys := afero.NewMemMapFs()
// Setup valid access token
token := apitest.RandomAccessToken(t)
t.Setenv("SUPABASE_ACCESS_TOKEN", string(token))
// Flush pending mocks after test execution
defer gock.OffAll()
highAvailabilityParams := params
highAvailabilityParams.HighAvailability = cast.Ptr(true)
gock.New(utils.DefaultApiHost).
Post("/v1/projects").
MatchType("json").
JSON(highAvailabilityParams).
Reply(201).
JSON(api.V1ProjectResponse{
Id: apitest.RandomProjectRef(),
OrganizationSlug: highAvailabilityParams.OrganizationSlug,
Name: highAvailabilityParams.Name,
Region: string(*highAvailabilityParams.Region),
CreatedAt: "2022-04-25T02:14:55.906498Z",
})
// Run test
assert.NoError(t, Run(context.Background(), highAvailabilityParams, fsys))
// Validate api
assert.Empty(t, apitest.ListUnmatchedRequests())
})

t.Run("throws error on failure to load token", func(t *testing.T) {
assert.Error(t, Run(context.Background(), params, afero.NewMemMapFs()))
})
Expand Down
3 changes: 3 additions & 0 deletions apps/cli-go/pkg/api/types.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 14 additions & 12 deletions apps/cli/src/legacy/commands/projects/create/SIDE_EFFECTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@

## API Routes

| Method | Path | Auth | Request body | Response (used fields) |
| ------ | -------------- | ------------ | --------------------------------------------------------------------------- | --------------------------------------------------- |
| `POST` | `/v1/projects` | Bearer token | `{name, organization_slug, db_pass, region, desired_instance_size?}` (JSON) | `{id, name, organization_slug, region, created_at}` |
| Method | Path | Auth | Request body | Response (used fields) |
| ------ | -------------- | ------------ | ----------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| `POST` | `/v1/projects` | Bearer token | `{name, organization_slug, db_pass, region, desired_instance_size?, high_availability?}` (JSON) | `{id, name, organization_slug, region, created_at}` |

## Environment Variables

Expand All @@ -38,15 +38,16 @@

## Flags

| Flag | Type | Required (non-interactive) | Description |
| ---------------- | ------ | -------------------------- | ----------------------------------------------- |
| `[project name]` | arg | yes (non-interactive) | Name of the project (positional argument) |
| `--org-id` | string | yes (non-interactive) | Organization ID (slug) to create the project in |
| `--db-password` | string | yes (non-interactive) | Database password for the project |
| `--region` | enum | yes (non-interactive) | AWS region for the project |
| `--size` | enum | no | Desired instance size |
| `--interactive` | bool | no (default: true) | Enable interactive mode (hidden flag) |
| `--plan` | string | no | Plan selection (hidden flag) |
| Flag | Type | Required (non-interactive) | Description |
| --------------------- | ------ | -------------------------- | ----------------------------------------------- |
| `[project name]` | arg | yes (non-interactive) | Name of the project (positional argument) |
| `--org-id` | string | yes (non-interactive) | Organization ID (slug) to create the project in |
| `--db-password` | string | yes (non-interactive) | Database password for the project |
| `--region` | enum | yes (non-interactive) | AWS region for the project |
| `--size` | enum | no | Desired instance size |
| `--high-availability` | bool | no | Enable high availability for the project |
| `--interactive` | bool | no (default: true) | Enable interactive mode (hidden flag) |
| `--plan` | string | no | Plan selection (hidden flag) |

## Output

Expand Down Expand Up @@ -83,4 +84,5 @@ One `result` event on success.
- In non-interactive mode (when stdin is not a TTY or `--interactive=false`), all three
flags and the positional project name argument are required.
- The `--size` flag, when provided, sets the `desired_instance_size` field in the request body.
- The `--high-availability` flag, when provided, sets the `high_availability` field in the request body.
- The `--plan` flag is hidden and reserved.
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ const config = {
Flag.withDescription("Select a desired instance size for your project."),
Flag.optional,
),
highAvailability: Flag.boolean("high-availability").pipe(
Flag.withDescription("Enable high availability for the project."),
Flag.optional,
),
interactive: withHidden(
Flag.boolean("interactive").pipe(
Flag.withDescription("Enables interactive mode."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export const legacyProjectsCreate = Effect.fn("legacy.projects.create")(function
if (Option.isSome(flags.dbPassword)) args.push("--db-password", flags.dbPassword.value);
if (Option.isSome(flags.region)) args.push("--region", flags.region.value);
if (Option.isSome(flags.size)) args.push("--size", flags.size.value);
if (Option.isSome(flags.highAvailability))
args.push(`--high-availability=${flags.highAvailability.value ? "true" : "false"}`);
if (Option.isSome(flags.interactive))
args.push(`--interactive=${flags.interactive.value ? "true" : "false"}`);
if (Option.isSome(flags.plan)) args.push("--plan", flags.plan.value);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { describe, expect, it } from "@effect/vitest";
import { Effect, Layer, Option } from "effect";

import { LegacyGoProxy } from "../../../../shared/legacy/go-proxy.service.ts";
import { legacyProjectsCreate } from "./create.handler.ts";
import type { LegacyProjectsCreateFlags } from "./create.command.ts";

function setupLegacyProjectsCreate() {
const calls: Array<ReadonlyArray<string>> = [];
const layer = Layer.succeed(LegacyGoProxy, {
exec: (args) =>
Effect.sync(() => {
calls.push([...args]);
}),
});

return { layer, calls };
}

const baseFlags: LegacyProjectsCreateFlags = {
name: Option.some("my-project"),
orgId: Option.some("cool-green-pqdr0qc"),
dbPassword: Option.some("redacted"),
region: Option.some("us-east-1"),
size: Option.none(),
highAvailability: Option.none(),
interactive: Option.none(),
plan: Option.none(),
};

describe("legacy projects create", () => {
it.live("forwards the high availability flag to the Go CLI", () => {
const { layer, calls } = setupLegacyProjectsCreate();
return Effect.gen(function* () {
yield* legacyProjectsCreate({
...baseFlags,
highAvailability: Option.some(true),
});
expect(calls).toEqual([
[
"projects",
"create",
"my-project",
"--org-id",
"cool-green-pqdr0qc",
"--db-password",
"redacted",
"--region",
"us-east-1",
"--high-availability=true",
],
]);
}).pipe(Effect.provide(layer));
});
});
Loading