diff --git a/pkg/cmd/rename.go b/pkg/cmd/rename.go new file mode 100644 index 0000000..f154355 --- /dev/null +++ b/pkg/cmd/rename.go @@ -0,0 +1,84 @@ +package cmd + +import ( + "context" + "fmt" + "net/http" + "net/url" + + "github.com/dedalus-labs/dedalus-cli/internal/apiquery" + "github.com/dedalus-labs/dedalus-go" + "github.com/dedalus-labs/dedalus-go/option" + "github.com/urfave/cli/v3" +) + +type renameMachineResponse struct { + MachineID string `json:"machine_id"` + Name string `json:"name"` +} + +func init() { + Command.Commands = append(Command.Commands, &cli.Command{ + Name: "rename", + Usage: "Rename a machine", + UsageText: "dedalus rename ", + Category: "MACHINE", + Suggest: true, + Action: handleRename, + HideHelpCommand: true, + }) +} + +func handleRename(ctx context.Context, cmd *cli.Command) error { + args := cmd.Args().Slice() + if len(args) != 2 { + return fmt.Errorf("expected a current machine ID or name and a new name; usage: dedalus rename ") + } + current, newName := args[0], args[1] + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatRepeat, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + client := dedalus.NewClient(getDefaultRequestOptions(cmd)...) + result, err := renameMachine(ctx, &client, current, newName, options...) + if err != nil { + return err + } + acceptedName := result.Name + if acceptedName == "" { + acceptedName = newName + } + + fmt.Printf("Successfully renamed %s to %s\nmachine-id: %s\n", current, acceptedName, result.MachineID) + return nil +} + +func renameMachine( + ctx context.Context, + client *dedalus.Client, + current string, + newName string, + options ...option.RequestOption, +) (*renameMachineResponse, error) { + var result renameMachineResponse + err := client.Execute( + ctx, + http.MethodPatch, + fmt.Sprintf("v1/machines/%s", url.PathEscape(current)), + map[string]string{"name": newName}, + &result, + options..., + ) + if err != nil { + return nil, err + } + return &result, nil +} diff --git a/pkg/cmd/rename_test.go b/pkg/cmd/rename_test.go new file mode 100644 index 0000000..ab27c76 --- /dev/null +++ b/pkg/cmd/rename_test.go @@ -0,0 +1,55 @@ +package cmd + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/dedalus-labs/dedalus-go" + "github.com/dedalus-labs/dedalus-go/option" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRenameMachine(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { + assert.Equal(t, http.MethodPatch, request.Method) + assert.Equal(t, "/v1/machines/hotel-california", request.URL.Path) + + var body map[string]string + require.NoError(t, json.NewDecoder(request.Body).Decode(&body)) + assert.Equal(t, map[string]string{"name": "breakfast"}, body) + + response.Header().Set("Content-Type", "application/json") + _, _ = response.Write([]byte(`{"machine_id":"dm-0198abc123","name":"breakfast"}`)) + })) + defer server.Close() + + client := dedalus.NewClient(option.WithBaseURL(server.URL)) + result, err := renameMachine(context.Background(), &client, "hotel-california", "breakfast") + + require.NoError(t, err) + assert.Equal(t, "dm-0198abc123", result.MachineID) + assert.Equal(t, "breakfast", result.Name) +} + +func TestRenameMachineSurfacesServerMessage(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, _ *http.Request) { + response.Header().Set("Content-Type", "application/json") + response.WriteHeader(http.StatusUnprocessableEntity) + _ = json.NewEncoder(response).Encode(map[string]string{ + "error_code": "MACHINE_NAME_INVALID", + "message": `invalid name "My Box": names are lowercase with no spaces`, + }) + })) + defer server.Close() + + client := dedalus.NewClient(option.WithBaseURL(server.URL)) + _, err := renameMachine(context.Background(), &client, "dm-0198abc123", "My Box") + + require.Error(t, err) + assert.Contains(t, err.Error(), "MACHINE_NAME_INVALID") + assert.Contains(t, err.Error(), "names are lowercase with no spaces") +}