Skip to content
Merged
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 .changeset/field-remove-rename.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"ggt": minor
---

Add `ggt field remove` and `ggt field rename` subcommands for managing fields
on models.
65 changes: 64 additions & 1 deletion spec/commands/__snapshots__/completion.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -260,12 +260,18 @@ _ggt_completions() {
done

if [[ -z "$sub_cmd" ]]; then
COMPREPLY=($(compgen -W "add --application -a --app --environment -e --env --allow-different-app --allow-unknown-directory --allow --allow-all --help -h --version --verbose -v --telemetry --json" -- "$cur"))
COMPREPLY=($(compgen -W "add remove rename --application -a --app --environment -e --env --allow-different-app --allow-unknown-directory --allow --allow-all --help -h --version --verbose -v --telemetry --json" -- "$cur"))
else
case "$sub_cmd" in
add)
COMPREPLY=($(compgen -W "--application -a --app --environment -e --env --allow-different-app --allow-unknown-directory --allow --allow-all --help -h --version --verbose -v --telemetry --json" -- "$cur"))
;;
remove)
COMPREPLY=($(compgen -W "--application -a --app --environment -e --env --allow-different-app --allow-unknown-directory --allow --allow-all --force -f --help -h --version --verbose -v --telemetry --json" -- "$cur"))
;;
rename)
COMPREPLY=($(compgen -W "--application -a --app --environment -e --env --allow-different-app --allow-unknown-directory --allow --allow-all --help -h --version --verbose -v --telemetry --json" -- "$cur"))
;;
*)
COMPREPLY=($(compgen -W "--application -a --app --environment -e --env --allow-different-app --allow-unknown-directory --allow --allow-all --help -h --version --verbose -v --telemetry --json" -- "$cur"))
;;
Expand Down Expand Up @@ -886,16 +892,29 @@ complete -c ggt -n '__fish_seen_subcommand_from field' -l allow-unknown-director
complete -c ggt -n '__fish_seen_subcommand_from field' -l allow -x -d 'Enable allow flags (comma-separated)'
complete -c ggt -n '__fish_seen_subcommand_from field' -l allow-all -d 'Enable all --allow-* flags'
complete -c ggt -n '__ggt_needs_subcommand field -- --application -a --app --environment -e --env --allow' -a 'add' -d 'Add a field to an existing model'
complete -c ggt -n '__ggt_needs_subcommand field -- --application -a --app --environment -e --env --allow' -a 'remove' -d 'Remove a field from a model'
complete -c ggt -n '__ggt_needs_subcommand field -- --application -a --app --environment -e --env --allow' -a 'rename' -d 'Rename a field on a model'
complete -c ggt -n '__ggt_seen_subcommand field -- remove -- --application -a --app --environment -e --env --allow' -l force -s f -d 'Skip confirmation'
complete -c ggt -n '__fish_seen_subcommand_from field' -l help -s h -d 'Show command help'
complete -c ggt -n '__ggt_seen_subcommand field -- add -- --application -a --app --environment -e --env --allow' -l help -s h -d 'Show command help'
complete -c ggt -n '__ggt_seen_subcommand field -- remove -- --application -a --app --environment -e --env --allow' -l help -s h -d 'Show command help'
complete -c ggt -n '__ggt_seen_subcommand field -- rename -- --application -a --app --environment -e --env --allow' -l help -s h -d 'Show command help'
complete -c ggt -n '__fish_seen_subcommand_from field' -l version -d 'Print the ggt version'
complete -c ggt -n '__ggt_seen_subcommand field -- add -- --application -a --app --environment -e --env --allow' -l version -d 'Print the ggt version'
complete -c ggt -n '__ggt_seen_subcommand field -- remove -- --application -a --app --environment -e --env --allow' -l version -d 'Print the ggt version'
complete -c ggt -n '__ggt_seen_subcommand field -- rename -- --application -a --app --environment -e --env --allow' -l version -d 'Print the ggt version'
complete -c ggt -n '__fish_seen_subcommand_from field' -l verbose -s v -d 'Increase output verbosity (-vv for debug, -vvv for trace)'
complete -c ggt -n '__ggt_seen_subcommand field -- add -- --application -a --app --environment -e --env --allow' -l verbose -s v -d 'Increase output verbosity (-vv for debug, -vvv for trace)'
complete -c ggt -n '__ggt_seen_subcommand field -- remove -- --application -a --app --environment -e --env --allow' -l verbose -s v -d 'Increase output verbosity (-vv for debug, -vvv for trace)'
complete -c ggt -n '__ggt_seen_subcommand field -- rename -- --application -a --app --environment -e --env --allow' -l verbose -s v -d 'Increase output verbosity (-vv for debug, -vvv for trace)'
complete -c ggt -n '__fish_seen_subcommand_from field' -l telemetry -d 'Enable telemetry'
complete -c ggt -n '__ggt_seen_subcommand field -- add -- --application -a --app --environment -e --env --allow' -l telemetry -d 'Enable telemetry'
complete -c ggt -n '__ggt_seen_subcommand field -- remove -- --application -a --app --environment -e --env --allow' -l telemetry -d 'Enable telemetry'
complete -c ggt -n '__ggt_seen_subcommand field -- rename -- --application -a --app --environment -e --env --allow' -l telemetry -d 'Enable telemetry'
complete -c ggt -n '__fish_seen_subcommand_from field' -l json -d 'Output as JSON where supported'
complete -c ggt -n '__ggt_seen_subcommand field -- add -- --application -a --app --environment -e --env --allow' -l json -d 'Output as JSON where supported'
complete -c ggt -n '__ggt_seen_subcommand field -- remove -- --application -a --app --environment -e --env --allow' -l json -d 'Output as JSON where supported'
complete -c ggt -n '__ggt_seen_subcommand field -- rename -- --application -a --app --environment -e --env --allow' -l json -d 'Output as JSON where supported'

# shopify
complete -c ggt -n '__fish_seen_subcommand_from shopify' -l application -s a -rfa '(__ggt_complete)' -d 'Gadget app to use'
Expand Down Expand Up @@ -1661,6 +1680,8 @@ _ggt_field() {
subcommand)
subcommands=(
'add:Add a field to an existing model'
'remove:Remove a field from a model'
'rename:Rename a field on a model'
)
_describe -t subcommands 'field subcommand' subcommands
;;
Expand All @@ -1686,6 +1707,48 @@ _ggt_field() {
'--allow[Enable allow flags (comma-separated)]:value: ' \\
'--allow-all[Enable all --allow-* flags]'
;;
remove)
_arguments \\
'(--help -h)''--help[Show command help]' \\
'(--help -h)''-h[Show command help]' \\
'--version[Print the ggt version]' \\
'(--verbose -v)''--verbose[Increase output verbosity (-vv for debug, -vvv for trace)]' \\
'(--verbose -v)''-v[Increase output verbosity (-vv for debug, -vvv for trace)]' \\
'--telemetry[Enable telemetry]' \\
'--json[Output as JSON where supported]' \\
'(--application -a --app)''--application[Gadget app to use]:value:_ggt_dynamic' \\
'(--application -a --app)''-a[Gadget app to use]:value:_ggt_dynamic' \\
'(--application -a --app)''--app[Gadget app to use]:value:_ggt_dynamic' \\
'(--environment -e --env)''--environment[Environment to use]:value:_ggt_dynamic' \\
'(--environment -e --env)''-e[Environment to use]:value:_ggt_dynamic' \\
'(--environment -e --env)''--env[Environment to use]:value:_ggt_dynamic' \\
'--allow-different-app[Allow syncing with a different app]' \\
'--allow-unknown-directory[Allow syncing to an existing directory]' \\
'--allow[Enable allow flags (comma-separated)]:value: ' \\
'--allow-all[Enable all --allow-* flags]' \\
'(--force -f)''--force[Skip confirmation]' \\
'(--force -f)''-f[Skip confirmation]'
;;
rename)
_arguments \\
'(--help -h)''--help[Show command help]' \\
'(--help -h)''-h[Show command help]' \\
'--version[Print the ggt version]' \\
'(--verbose -v)''--verbose[Increase output verbosity (-vv for debug, -vvv for trace)]' \\
'(--verbose -v)''-v[Increase output verbosity (-vv for debug, -vvv for trace)]' \\
'--telemetry[Enable telemetry]' \\
'--json[Output as JSON where supported]' \\
'(--application -a --app)''--application[Gadget app to use]:value:_ggt_dynamic' \\
'(--application -a --app)''-a[Gadget app to use]:value:_ggt_dynamic' \\
'(--application -a --app)''--app[Gadget app to use]:value:_ggt_dynamic' \\
'(--environment -e --env)''--environment[Environment to use]:value:_ggt_dynamic' \\
'(--environment -e --env)''-e[Environment to use]:value:_ggt_dynamic' \\
'(--environment -e --env)''--env[Environment to use]:value:_ggt_dynamic' \\
'--allow-different-app[Allow syncing with a different app]' \\
'--allow-unknown-directory[Allow syncing to an existing directory]' \\
'--allow[Enable allow flags (comma-separated)]:value: ' \\
'--allow-all[Enable all --allow-* flags]'
;;
esac
;;
esac
Expand Down
156 changes: 153 additions & 3 deletions spec/commands/__snapshots__/root.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1878,6 +1878,144 @@ Run ggt field add --help for more information.
"
`;

exports[`root > when field is given > prints the usage for remove when --help is passed 1`] = `
"Remove a field from a model

USAGE
ggt field remove <model/field> [flags]

ARGUMENTS
model/field
Model path and field name. Format is model/field (e.g. post/title).

FLAGS
-a, --app, --application <app>
Gadget app to use. The app slug is the subdomain portion of your app URL
(e.g., my-app from my-app.gadget.app). Can be omitted when
.gadget/sync.json already records the app.

-e, --env, --environment <env>
Environment to use. Defaults to the development environment. Production
is read-only for most commands.

-f, --force
Skip confirmation

ADDITIONAL FLAGS
--allow <flag,...>
Enable allow flags (comma-separated)

--allow-all
Enable all --allow-* flags

--allow-different-app
Allow syncing with a different app. Overrides the app recorded in
.gadget/sync.json with the one specified by --app. Use this when you
want to reuse an existing directory for a different app.

--allow-unknown-directory
Allow syncing to an existing directory. Normally, a directory must be
empty or already contain .gadget/sync.json. Use this when you want to
initialize sync in a directory with existing content.

EXAMPLES
$ ggt field remove post/title
$ ggt field remove post/title --force
"
`;

exports[`root > when field is given > prints the usage for remove when -h is passed 1`] = `
"Remove a field from a model

USAGE
ggt field remove <model/field> [flags]

ARGUMENTS
model/field Model path and field name

FLAGS
-a, --app, --application <app> Gadget app to use
-e, --env, --environment <env> Environment to use
-f, --force Skip confirmation

EXAMPLES
$ ggt field remove post/title
$ ggt field remove post/title --force

Run ggt field remove --help for more information.
"
`;

exports[`root > when field is given > prints the usage for rename when --help is passed 1`] = `
"Rename a field on a model

USAGE
ggt field rename <model/field> <model/new-field-name> [flags]

ARGUMENTS
model/field
Current model path and field name. Format is model/field (e.g.
post/title).

model/new-field-name
Model path and new field name. Format is model/newField (e.g.
post/heading). Must reference the same model.

FLAGS
-a, --app, --application <app>
Gadget app to use. The app slug is the subdomain portion of your app URL
(e.g., my-app from my-app.gadget.app). Can be omitted when
.gadget/sync.json already records the app.

-e, --env, --environment <env>
Environment to use. Defaults to the development environment. Production
is read-only for most commands.

ADDITIONAL FLAGS
--allow <flag,...>
Enable allow flags (comma-separated)

--allow-all
Enable all --allow-* flags

--allow-different-app
Allow syncing with a different app. Overrides the app recorded in
.gadget/sync.json with the one specified by --app. Use this when you
want to reuse an existing directory for a different app.

--allow-unknown-directory
Allow syncing to an existing directory. Normally, a directory must be
empty or already contain .gadget/sync.json. Use this when you want to
initialize sync in a directory with existing content.

EXAMPLES
$ ggt field rename post/title post/heading
$ ggt field rename shopify/order/note shopify/order/internalNote
"
`;

exports[`root > when field is given > prints the usage for rename when -h is passed 1`] = `
"Rename a field on a model

USAGE
ggt field rename <model/field> <model/new-field-name> [flags]

ARGUMENTS
model/field Current model path and field name
model/new-field-name Model path and new field name

FLAGS
-a, --app, --application <app> Gadget app to use
-e, --env, --environment <env> Environment to use

EXAMPLES
$ ggt field rename post/title post/heading
$ ggt field rename shopify/order/note shopify/order/internalNote

Run ggt field rename --help for more information.
"
`;

exports[`root > when field is given > prints the usage when --help is passed 1`] = `
"Manage fields on your models

Expand All @@ -1888,7 +2026,9 @@ USAGE
ggt field <command> [flags]

COMMANDS
add Add a field to an existing model
add Add a field to an existing model
remove Remove a field from a model
rename Rename a field on a model

FLAGS
-a, --app, --application <app>
Expand Down Expand Up @@ -1920,6 +2060,8 @@ ADDITIONAL FLAGS
EXAMPLES
$ ggt field add post/title:string
$ ggt field add mystore/order/note:string
$ ggt field remove post/title
$ ggt field rename post/title post/heading
"
`;

Expand All @@ -1930,7 +2072,9 @@ USAGE
ggt field <command> [flags]

COMMANDS
add Add a field to an existing model
add Add a field to an existing model
remove Remove a field from a model
rename Rename a field on a model

FLAGS
-a, --app, --application <app> Gadget app to use
Expand All @@ -1939,6 +2083,8 @@ FLAGS
EXAMPLES
$ ggt field add post/title:string
$ ggt field add mystore/order/note:string
$ ggt field remove post/title
$ ggt field rename post/title post/heading

Run ggt field --help for more information.
"
Expand All @@ -1951,7 +2097,9 @@ USAGE
ggt field <command> [flags]

COMMANDS
add Add a field to an existing model
add Add a field to an existing model
remove Remove a field from a model
rename Rename a field on a model

FLAGS
-a, --app, --application <app> Gadget app to use
Expand All @@ -1960,6 +2108,8 @@ FLAGS
EXAMPLES
$ ggt field add post/title:string
$ ggt field add mystore/order/note:string
$ ggt field remove post/title
$ ggt field rename post/title post/heading

Run ggt field --help for more information.
"
Expand Down
14 changes: 10 additions & 4 deletions spec/commands/add.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { beforeEach, describe, expect, it } from "vitest";

import { EnvironmentStatus } from "../../src/__generated__/graphql.ts";
import add, { AddClientError } from "../../src/commands/add.ts";
import add, { EditClientError } from "../../src/commands/add.ts";
import { GADGET_GLOBAL_ACTIONS_QUERY, GADGET_META_MODELS_QUERY } from "../../src/services/app/api/operation.ts";
import {
CREATE_ACTION_MUTATION,
Expand All @@ -21,12 +21,12 @@ import { nockApiResponse, nockEditResponse } from "../__support__/graphql.ts";
import { mockSystemTime } from "../__support__/time.ts";
import { loginTestUser } from "../__support__/user.ts";

describe("AddClientError", () => {
describe("EditClientError", () => {
it("does not double-bullet messages that already have a bullet prefix", () => {
const cause = [{ message: "• already bulleted" }, { message: "plain text" }] as unknown as import("graphql").GraphQLError[];

const clientError = new ClientError(undefined, cause);
const error = new AddClientError(clientError);
const error = new EditClientError(clientError);

// Each line should have exactly one bullet
expect(error.message).toMatchInlineSnapshot(`
Expand Down Expand Up @@ -139,7 +139,13 @@ describe("add", () => {
expect(error.sprint()).toContain("Failed to add field, invalid field definition");
});

it.each(["user/field", "user/field:", "user/:"])("returns missing field type FlagError if the input is %s", async (partialInput) => {
it("returns missing field type FlagError if the input is user/field", async () => {
const error = await expectError(() => runCommand(testCtx, add, "field", "user/field"));
expect(error).toBeInstanceOf(FlagError);
expect(error.sprint()).toContain("invalid field definition");
});

it.each(["user/field:", "user/:"])("returns missing field type FlagError if the input is %s", async (partialInput) => {
const error = await expectError(() => runCommand(testCtx, add, "field", partialInput));
expect(error).toBeInstanceOf(FlagError);
expect(error.sprint()).toContain("is not a valid field definition");
Expand Down
Loading
Loading