From 11ce80f897418d009972e6d41191e377f69f08c5 Mon Sep 17 00:00:00 2001 From: Ajmal Khan Date: Thu, 4 Dec 2025 09:04:03 +0000 Subject: [PATCH 1/4] refactor: simplify error messages across commands and tests Remove redundant "error: " prefix from error messages throughout the codebase. This improves consistency and readability of error output. Changes: - Remove fmt.Errorf("error: %s", err.Error()) pattern - Return errors directly where appropriate - Update corresponding test assertions --- cmd/aitaskbuilder/batch_create.go | 2 +- cmd/aitaskbuilder/batch_create_test.go | 2 +- cmd/aitaskbuilder/batch_setup.go | 2 +- cmd/aitaskbuilder/batch_setup_test.go | 6 +++--- cmd/aitaskbuilder/batch_tasks.go | 2 +- cmd/aitaskbuilder/batch_tasks_test.go | 5 ++--- cmd/aitaskbuilder/create_dataset.go | 2 +- cmd/aitaskbuilder/create_dataset_test.go | 7 +++---- cmd/aitaskbuilder/get_batch.go | 2 +- cmd/aitaskbuilder/get_batch_status.go | 2 +- cmd/aitaskbuilder/get_batch_status_test.go | 5 ++--- cmd/aitaskbuilder/get_batch_test.go | 5 ++--- cmd/aitaskbuilder/get_batches.go | 2 +- cmd/aitaskbuilder/get_batches_test.go | 5 ++--- cmd/aitaskbuilder/get_dataset_status.go | 2 +- cmd/aitaskbuilder/get_dataset_status_test.go | 4 ++-- cmd/aitaskbuilder/get_task_responses.go | 2 +- cmd/aitaskbuilder/get_task_responses_test.go | 5 ++--- cmd/campaign/list.go | 8 ++++---- cmd/campaign/list_test.go | 3 +-- cmd/filters/list.go | 2 +- cmd/filtersets/list.go | 2 +- cmd/filtersets/list_test.go | 5 ++--- cmd/filtersets/view.go | 2 +- cmd/filtersets/view_test.go | 5 ++--- cmd/hook/event_list.go | 2 +- cmd/hook/event_list_test.go | 5 ++--- cmd/hook/event_type.go | 2 +- cmd/hook/event_type_test.go | 3 +-- cmd/hook/list.go | 2 +- cmd/hook/list_test.go | 4 ++-- cmd/hook/secret.go | 2 +- cmd/hook/secret_test.go | 3 +-- cmd/message/list.go | 2 +- cmd/message/list_test.go | 4 ++-- cmd/message/send.go | 2 +- cmd/message/send_test.go | 3 +-- cmd/participantgroup/list.go | 2 +- cmd/participantgroup/list_test.go | 5 ++--- cmd/participantgroup/view.go | 2 +- cmd/participantgroup/view_test.go | 5 ++--- cmd/project/create.go | 6 ++++-- cmd/project/create_test.go | 8 ++++---- cmd/project/list.go | 8 ++++---- cmd/project/list_test.go | 5 ++--- cmd/project/view.go | 2 +- cmd/project/view_test.go | 5 ++--- cmd/requirements/list.go | 2 +- cmd/study/duplicate.go | 2 +- cmd/study/duplicate_test.go | 2 +- cmd/study/increase_places.go | 2 +- cmd/study/increase_places_test.go | 2 +- cmd/study/list.go | 2 +- cmd/study/transition.go | 2 +- cmd/study/transition_test.go | 4 ++-- cmd/study/view.go | 2 +- cmd/study/view_test.go | 2 +- cmd/submission/list.go | 3 +-- cmd/user/me.go | 6 +++--- cmd/user/me_test.go | 2 +- cmd/workspace/create.go | 6 ++++-- cmd/workspace/create_test.go | 6 +++--- cmd/workspace/list.go | 8 ++++---- cmd/workspace/list_test.go | 3 +-- 64 files changed, 106 insertions(+), 121 deletions(-) diff --git a/cmd/aitaskbuilder/batch_create.go b/cmd/aitaskbuilder/batch_create.go index 0091ce8..4eac290 100644 --- a/cmd/aitaskbuilder/batch_create.go +++ b/cmd/aitaskbuilder/batch_create.go @@ -39,7 +39,7 @@ $ prolific aitaskbuilder batch create -n "My Data Collection Batch" -w 6278acb09 err := createAITaskBuilderBatch(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/aitaskbuilder/batch_create_test.go b/cmd/aitaskbuilder/batch_create_test.go index 4395268..8b96e45 100644 --- a/cmd/aitaskbuilder/batch_create_test.go +++ b/cmd/aitaskbuilder/batch_create_test.go @@ -144,7 +144,7 @@ func TestNewBatchCreateCommandAPIError(t *testing.T) { t.Fatal("expected error; got nil") } - expectedError := "error: API error" + expectedError := "API error" if err.Error() != expectedError { t.Fatalf("expected error: %s; got %s", expectedError, err.Error()) } diff --git a/cmd/aitaskbuilder/batch_setup.go b/cmd/aitaskbuilder/batch_setup.go index 75bdb7e..24b5c3c 100644 --- a/cmd/aitaskbuilder/batch_setup.go +++ b/cmd/aitaskbuilder/batch_setup.go @@ -36,7 +36,7 @@ $ prolific aitaskbuilder batch setup -b -d --tasks-per-g err := setupAITaskBuilderBatch(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/aitaskbuilder/batch_setup_test.go b/cmd/aitaskbuilder/batch_setup_test.go index aeab704..e17ceff 100644 --- a/cmd/aitaskbuilder/batch_setup_test.go +++ b/cmd/aitaskbuilder/batch_setup_test.go @@ -129,7 +129,7 @@ func TestNewBatchSetupCommandAPIError(t *testing.T) { t.Fatal("expected error; got nil") } - expectedError := "error: API error" + expectedError := "API error" if err.Error() != expectedError { t.Fatalf("expected error: %s; got %s", expectedError, err.Error()) } @@ -198,12 +198,12 @@ func TestNewBatchSetupCommandInvalidTasksPerGroup(t *testing.T) { { name: "zero tasks per group", tasksPerGroup: "0", - expectedErr: "error: " + aitaskbuilder.ErrTasksPerGroupMinimum, + expectedErr: "" + aitaskbuilder.ErrTasksPerGroupMinimum, }, { name: "negative tasks per group", tasksPerGroup: "-1", - expectedErr: "error: " + aitaskbuilder.ErrTasksPerGroupMinimum, + expectedErr: "" + aitaskbuilder.ErrTasksPerGroupMinimum, }, } diff --git a/cmd/aitaskbuilder/batch_tasks.go b/cmd/aitaskbuilder/batch_tasks.go index 8ca4285..278066c 100644 --- a/cmd/aitaskbuilder/batch_tasks.go +++ b/cmd/aitaskbuilder/batch_tasks.go @@ -33,7 +33,7 @@ $ prolific aitaskbuilder batch tasks -b err := renderAITaskBuilderTasks(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/aitaskbuilder/batch_tasks_test.go b/cmd/aitaskbuilder/batch_tasks_test.go index d347c2e..d26844d 100644 --- a/cmd/aitaskbuilder/batch_tasks_test.go +++ b/cmd/aitaskbuilder/batch_tasks_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" @@ -130,7 +129,7 @@ func TestNewBatchTasksCommandHandlesErrors(t *testing.T) { _ = cmd.Flags().Set("batch-id", batchID) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -151,7 +150,7 @@ func TestNewBatchTasksCommandRequiresBatchID(t *testing.T) { if !cmd.Flags().Changed("batch-id") { expected := aitaskbuilder.ErrBatchIDRequired - if err.Error() != "error: "+expected { + if err.Error() != ""+expected { t.Fatalf("expected error to contain '%s', got '%s'", expected, err.Error()) } } diff --git a/cmd/aitaskbuilder/create_dataset.go b/cmd/aitaskbuilder/create_dataset.go index 20a0e23..f8b4741 100644 --- a/cmd/aitaskbuilder/create_dataset.go +++ b/cmd/aitaskbuilder/create_dataset.go @@ -39,7 +39,7 @@ $ prolific aitaskbuilder dataset create -n "test" -w err := createAITaskBuilderDataset(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/aitaskbuilder/create_dataset_test.go b/cmd/aitaskbuilder/create_dataset_test.go index 270809f..5059488 100644 --- a/cmd/aitaskbuilder/create_dataset_test.go +++ b/cmd/aitaskbuilder/create_dataset_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" @@ -106,7 +105,7 @@ func TestNewCreateDatasetCommandHandlesErrors(t *testing.T) { _ = cmd.Flags().Set("workspace-id", "invalid-workspace") err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -128,7 +127,7 @@ func TestNewCreateDatasetCommandRequiresName(t *testing.T) { if !cmd.Flags().Changed("name") { expected := aitaskbuilder.ErrNameRequired - if err.Error() != "error: "+expected { + if err.Error() != ""+expected { t.Fatalf("expected error to contain '%s', got '%s'", expected, err.Error()) } } @@ -149,7 +148,7 @@ func TestNewCreateDatasetCommandRequiresWorkspaceID(t *testing.T) { if !cmd.Flags().Changed("workspace-id") { expected := aitaskbuilder.ErrWorkspaceIDRequired - if err.Error() != "error: "+expected { + if err.Error() != ""+expected { t.Fatalf("expected error to contain '%s', got '%s'", expected, err.Error()) } } diff --git a/cmd/aitaskbuilder/get_batch.go b/cmd/aitaskbuilder/get_batch.go index fa80d66..5407068 100644 --- a/cmd/aitaskbuilder/get_batch.go +++ b/cmd/aitaskbuilder/get_batch.go @@ -32,7 +32,7 @@ $ prolific aitaskbuilder batch view -b err := renderAITaskBuilderBatch(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/aitaskbuilder/get_batch_status.go b/cmd/aitaskbuilder/get_batch_status.go index 0eb917f..b86c113 100644 --- a/cmd/aitaskbuilder/get_batch_status.go +++ b/cmd/aitaskbuilder/get_batch_status.go @@ -34,7 +34,7 @@ $ prolific aitaskbuilder batch check -b err := renderAITaskBuilderBatchStatus(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/aitaskbuilder/get_batch_status_test.go b/cmd/aitaskbuilder/get_batch_status_test.go index fac125e..ecde685 100644 --- a/cmd/aitaskbuilder/get_batch_status_test.go +++ b/cmd/aitaskbuilder/get_batch_status_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" @@ -90,7 +89,7 @@ func TestNewGetBatchStatusCommandHandlesErrors(t *testing.T) { _ = cmd.Flags().Set("batch-id", batchID) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -111,7 +110,7 @@ func TestNewGetBatchStatusCommandRequiresBatchID(t *testing.T) { if !cmd.Flags().Changed("batch-id") { expected := aitaskbuilder.ErrBatchIDRequired - if err.Error() != "error: "+expected { + if err.Error() != ""+expected { t.Fatalf("expected error to contain '%s', got '%s'", expected, err.Error()) } } diff --git a/cmd/aitaskbuilder/get_batch_test.go b/cmd/aitaskbuilder/get_batch_test.go index 8b17db4..325b706 100644 --- a/cmd/aitaskbuilder/get_batch_test.go +++ b/cmd/aitaskbuilder/get_batch_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" "time" @@ -181,7 +180,7 @@ func TestNewGetBatchCommandHandlesErrors(t *testing.T) { _ = cmd.Flags().Set("batch-id", batchID) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -202,7 +201,7 @@ func TestNewGetBatchCommandRequiresBatchID(t *testing.T) { if !cmd.Flags().Changed("batch-id") { expected := aitaskbuilder.ErrBatchIDRequired - if err.Error() != "error: "+expected { + if err.Error() != ""+expected { t.Fatalf("expected error to contain '%s', got '%s'", expected, err.Error()) } } diff --git a/cmd/aitaskbuilder/get_batches.go b/cmd/aitaskbuilder/get_batches.go index 87c6758..a9947e1 100644 --- a/cmd/aitaskbuilder/get_batches.go +++ b/cmd/aitaskbuilder/get_batches.go @@ -57,7 +57,7 @@ $ prolific aitaskbuilder batch list -w err := renderAITaskBuilderBatches(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/aitaskbuilder/get_batches_test.go b/cmd/aitaskbuilder/get_batches_test.go index 2629ec6..009c49c 100644 --- a/cmd/aitaskbuilder/get_batches_test.go +++ b/cmd/aitaskbuilder/get_batches_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" "time" @@ -127,7 +126,7 @@ func TestNewGetBatchesCommandHandlesErrors(t *testing.T) { _ = cmd.Flags().Set("workspace-id", workspaceID) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -148,7 +147,7 @@ func TestNewGetBatchesCommandRequiresWorkspaceID(t *testing.T) { if !cmd.Flags().Changed("workspace-id") { expected := "workspace ID is required" - if err.Error() != "error: "+expected { + if err.Error() != ""+expected { t.Fatalf("expected error to contain '%s', got '%s'", expected, err.Error()) } } diff --git a/cmd/aitaskbuilder/get_dataset_status.go b/cmd/aitaskbuilder/get_dataset_status.go index f72b780..ececc38 100644 --- a/cmd/aitaskbuilder/get_dataset_status.go +++ b/cmd/aitaskbuilder/get_dataset_status.go @@ -41,7 +41,7 @@ $ prolific aitaskbuilder dataset check -d err := renderAITaskBuilderDatasetStatus(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/aitaskbuilder/get_dataset_status_test.go b/cmd/aitaskbuilder/get_dataset_status_test.go index 991f91e..22c5862 100644 --- a/cmd/aitaskbuilder/get_dataset_status_test.go +++ b/cmd/aitaskbuilder/get_dataset_status_test.go @@ -117,7 +117,7 @@ func TestNewGetDatasetStatusCommandHandlesErrors(t *testing.T) { _ = cmd.Flags().Set("dataset-id", datasetID) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -136,7 +136,7 @@ func TestNewGetDatasetStatusCommandRequiresDatasetID(t *testing.T) { if !cmd.Flags().Changed("dataset-id") { expected := aitaskbuilder.ErrDatasetIDRequired - if err.Error() != "error: "+expected { + if err.Error() != ""+expected { t.Fatalf("expected error to contain '%s', got '%s'", expected, err.Error()) } } diff --git a/cmd/aitaskbuilder/get_task_responses.go b/cmd/aitaskbuilder/get_task_responses.go index 44f1178..4dd894c 100644 --- a/cmd/aitaskbuilder/get_task_responses.go +++ b/cmd/aitaskbuilder/get_task_responses.go @@ -35,7 +35,7 @@ $ prolific aitaskbuilder batch responses -b err := renderAITaskBuilderResponses(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/aitaskbuilder/get_task_responses_test.go b/cmd/aitaskbuilder/get_task_responses_test.go index 6be5963..1793cc1 100644 --- a/cmd/aitaskbuilder/get_task_responses_test.go +++ b/cmd/aitaskbuilder/get_task_responses_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" "time" @@ -208,7 +207,7 @@ func TestNewGetResponsesCommandHandlesErrors(t *testing.T) { _ = cmd.Flags().Set("batch-id", batchID) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -229,7 +228,7 @@ func TestNewGetResponsesCommandRequiresBatchID(t *testing.T) { if !cmd.Flags().Changed("batch-id") { expected := aitaskbuilder.ErrBatchIDRequired - if err.Error() != "error: "+expected { + if err.Error() != ""+expected { t.Fatalf("expected error to contain '%s', got '%s'", expected, err.Error()) } } diff --git a/cmd/campaign/list.go b/cmd/campaign/list.go index 3662cfe..23db331 100644 --- a/cmd/campaign/list.go +++ b/cmd/campaign/list.go @@ -49,7 +49,7 @@ $ prolific campaign list -w -l 1 -o 2 err := renderCampaigns(c, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil @@ -75,14 +75,14 @@ func renderCampaigns(c client.API, opts CampaignListOptions, w io.Writer) error } tw := tabwriter.NewWriter(w, 0, 1, 1, ' ', 0) - fmt.Fprintf(tw, "%s\t%s\t%s\n", "ID", "Name", "Link") + fmt.Fprintf(tw, "%s\t%s\t%s\n", ui.Bold("ID"), ui.Bold("Name"), ui.Bold("Link")) for _, campaign := range campaigns.Results { - fmt.Fprintf(tw, "%s\t%s\t%v\n", campaign.ID, campaign.Name, campaign.SignupLink) + fmt.Fprintf(tw, "%s\t%s\t%v\n", ui.Dim(campaign.ID), campaign.Name, ui.Highlight(campaign.SignupLink)) } _ = tw.Flush() - fmt.Fprintf(w, "\n%s\n", ui.RenderRecordCounter(len(campaigns.Results), campaigns.Meta.Count)) + fmt.Fprintf(w, "\n%s\n", ui.Dim(ui.RenderRecordCounter(len(campaigns.Results), campaigns.Meta.Count))) return nil } diff --git a/cmd/campaign/list_test.go b/cmd/campaign/list_test.go index 06c7634..b301262 100644 --- a/cmd/campaign/list_test.go +++ b/cmd/campaign/list_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" @@ -107,7 +106,7 @@ func TestNewListCommandHandlesErrors(t *testing.T) { _ = cmd.Flags().Set("workspace", "991199") err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) diff --git a/cmd/filters/list.go b/cmd/filters/list.go index 6b829e8..8ce37d0 100644 --- a/cmd/filters/list.go +++ b/cmd/filters/list.go @@ -32,7 +32,7 @@ There are two types of filters: RunE: func(cmd *cobra.Command, args []string) error { err := renderList(client) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/filtersets/list.go b/cmd/filtersets/list.go index fc932ab..e332fe9 100644 --- a/cmd/filtersets/list.go +++ b/cmd/filtersets/list.go @@ -41,7 +41,7 @@ $ prolific filters list -w 6261321e223a605c7a4f7623 err := render(c, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/filtersets/list_test.go b/cmd/filtersets/list_test.go index 1768753..20a2b25 100644 --- a/cmd/filtersets/list_test.go +++ b/cmd/filtersets/list_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" @@ -96,7 +95,7 @@ func TestNewListCommandReturnsErrorIfWorkspaceNotDefined(t *testing.T) { cmd := filtersets.NewListCommand("list", c, os.Stdout) err := cmd.RunE(cmd, nil) - expected := "error: please provide a workspace ID" + expected := "please provide a workspace ID" if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) } @@ -124,7 +123,7 @@ func TestNewListCommandHandlesAnAPIError(t *testing.T) { _ = cmd.Flags().Set("workspace", workspaceID) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) } diff --git a/cmd/filtersets/view.go b/cmd/filtersets/view.go index da65f12..ce18288 100644 --- a/cmd/filtersets/view.go +++ b/cmd/filtersets/view.go @@ -39,7 +39,7 @@ $ prolific filter-sets view 64efb93c7788944088864cec err := renderProject(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/filtersets/view_test.go b/cmd/filtersets/view_test.go index 44bd469..62d460a 100644 --- a/cmd/filtersets/view_test.go +++ b/cmd/filtersets/view_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" @@ -191,7 +190,7 @@ func TestNewViewCommandHandlesErrorsFromTheCliParams(t *testing.T) { cmd := filtersets.NewViewCommand("view", c, os.Stdout) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -215,7 +214,7 @@ func TestNewViewCommandHandlesErrorsFromTheAPI(t *testing.T) { cmd := filtersets.NewViewCommand("view", c, os.Stdout) err := cmd.RunE(cmd, []string{filterSetID}) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) diff --git a/cmd/hook/event_list.go b/cmd/hook/event_list.go index 62b3081..798ebf8 100644 --- a/cmd/hook/event_list.go +++ b/cmd/hook/event_list.go @@ -49,7 +49,7 @@ $ prolific hook events -s 637e081185389c0ca5595915 -l 10 -o 10 err := renderEvents(c, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/hook/event_list_test.go b/cmd/hook/event_list_test.go index 5b92433..a5b5285 100644 --- a/cmd/hook/event_list_test.go +++ b/cmd/hook/event_list_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" "time" @@ -101,7 +100,7 @@ func TestNewEventListCommandProvidesErrorIfSubmissionNotPassedIn(t *testing.T) { cmd := hook.NewEventListCommand("events", c, os.Stdout) error := cmd.RunE(cmd, nil) - expected := `error: please provide a subscription ID` + expected := `please provide a subscription ID` if error.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, error.Error()) @@ -127,7 +126,7 @@ func TestNewEventListCommandSetsDefaultsForLimitOffset(t *testing.T) { _ = cmd.Flags().Set("subscription", subscriptionID) error := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", failureMessage) + expected := failureMessage if error.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, error.Error()) diff --git a/cmd/hook/event_type.go b/cmd/hook/event_type.go index 73e800b..5d36384 100644 --- a/cmd/hook/event_type.go +++ b/cmd/hook/event_type.go @@ -23,7 +23,7 @@ interest for.`, RunE: func(cmd *cobra.Command, args []string) error { err := renderEventTypes(client, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/hook/event_type_test.go b/cmd/hook/event_type_test.go index c5f95ce..a93c5ef 100644 --- a/cmd/hook/event_type_test.go +++ b/cmd/hook/event_type_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" @@ -88,7 +87,7 @@ func TestNewEventTypeCommandHandlesErrors(t *testing.T) { cmd := hook.NewEventTypeCommand("event-types", c, os.Stdout) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) diff --git a/cmd/hook/list.go b/cmd/hook/list.go index 51789de..83736d9 100644 --- a/cmd/hook/list.go +++ b/cmd/hook/list.go @@ -50,7 +50,7 @@ $ prolific hook list -w 3461321e223a605c7a4f7612 -e err := renderHooks(c, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/hook/list_test.go b/cmd/hook/list_test.go index 4285d05..4a74de6 100644 --- a/cmd/hook/list_test.go +++ b/cmd/hook/list_test.go @@ -51,7 +51,7 @@ func TestNewListCommandHandlesErrors(t *testing.T) { cmd := hook.NewListCommand("list", c, os.Stdout) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -75,7 +75,7 @@ func TestNewListCommandCanAskForDisabledHooks(t *testing.T) { _ = cmd.Flags().Set("disabled", "true") err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) diff --git a/cmd/hook/secret.go b/cmd/hook/secret.go index 7f8b276..03eae27 100644 --- a/cmd/hook/secret.go +++ b/cmd/hook/secret.go @@ -28,7 +28,7 @@ func NewListSecretCommand(commandName string, client client.API, w io.Writer) *c opts.Args = args err := renderSecrets(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/hook/secret_test.go b/cmd/hook/secret_test.go index c4fbf93..8c268d2 100644 --- a/cmd/hook/secret_test.go +++ b/cmd/hook/secret_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" @@ -87,7 +86,7 @@ func TestNewListSecretCommandHandlesErrors(t *testing.T) { _ = cmd.Flags().Set("workspace", workspaceID) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) diff --git a/cmd/message/list.go b/cmd/message/list.go index c9205f4..c121994 100644 --- a/cmd/message/list.go +++ b/cmd/message/list.go @@ -49,7 +49,7 @@ $ prolific message list -U err := renderMessages(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/message/list_test.go b/cmd/message/list_test.go index 0f3a9e7..be90540 100644 --- a/cmd/message/list_test.go +++ b/cmd/message/list_test.go @@ -52,7 +52,7 @@ func TestNewListCommandHandlesErrors(t *testing.T) { cmd := message.NewListCommand("list", c, os.Stdout) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -179,7 +179,7 @@ func TestNewListCommandWithUnreadFlagAndOtherFlagsReturnsError(t *testing.T) { _ = cmd.Flags().Set("user", "user-id") // Set another flag along with 'unread' err := cmd.RunE(cmd, nil) - expectedError := `error: 'unread' cannot be used with any other flags` + expectedError := `'unread' cannot be used with any other flags` if err == nil || err.Error() != expectedError { t.Fatalf("expected error: '%s'; got error: '%v'", expectedError, err) } diff --git a/cmd/message/send.go b/cmd/message/send.go index b08f319..6acb3cc 100644 --- a/cmd/message/send.go +++ b/cmd/message/send.go @@ -41,7 +41,7 @@ Please make sure you quote the message with "" for the -b flag. err := createMessage(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/message/send_test.go b/cmd/message/send_test.go index e2b89bb..b4dc36f 100644 --- a/cmd/message/send_test.go +++ b/cmd/message/send_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" @@ -51,7 +50,7 @@ func TestNewSendCommandHandlesErrors(t *testing.T) { _ = cmd.Flags().Set("body", "body") err := cmd.Execute() - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err == nil || err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) diff --git a/cmd/participantgroup/list.go b/cmd/participantgroup/list.go index cbc499a..5c0afff 100644 --- a/cmd/participantgroup/list.go +++ b/cmd/participantgroup/list.go @@ -40,7 +40,7 @@ $ prolific participant list -p 6261321e223a605c7a4f7623 err := render(c, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/participantgroup/list_test.go b/cmd/participantgroup/list_test.go index 9b75762..4fd8d44 100644 --- a/cmd/participantgroup/list_test.go +++ b/cmd/participantgroup/list_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" @@ -98,7 +97,7 @@ func TestNewListCommandReturnsErrorIfProjectNotDefined(t *testing.T) { cmd := participantgroup.NewListCommand("list", c, os.Stdout) err := cmd.RunE(cmd, nil) - expected := "error: please provide a project ID" + expected := "please provide a project ID" if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) } @@ -126,7 +125,7 @@ func TestNewListCommandHandlesAnAPIError(t *testing.T) { _ = cmd.Flags().Set("project", projectID) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) } diff --git a/cmd/participantgroup/view.go b/cmd/participantgroup/view.go index 5f3d32a..e223448 100644 --- a/cmd/participantgroup/view.go +++ b/cmd/participantgroup/view.go @@ -38,7 +38,7 @@ $ prolific participant view 6429b0ea05b2a24cac83c3a4 err := renderGroup(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/participantgroup/view_test.go b/cmd/participantgroup/view_test.go index 1e8fe1f..d6a0774 100644 --- a/cmd/participantgroup/view_test.go +++ b/cmd/participantgroup/view_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" "time" @@ -86,7 +85,7 @@ func TestNewViewCommandReturnsErrorIfParticipantGroupNotDefined(t *testing.T) { cmd := participantgroup.NewViewCommand("view", c, os.Stdout) err := cmd.RunE(cmd, nil) - expected := "error: please provide a participant group ID" + expected := "please provide a participant group ID" if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) } @@ -113,7 +112,7 @@ func TestNewViewCommandHandlesAnAPIError(t *testing.T) { cmd := participantgroup.NewViewCommand("view", c, writer) err := cmd.RunE(cmd, []string{groupID}) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) } diff --git a/cmd/project/create.go b/cmd/project/create.go index 9d333ed..54d1de6 100644 --- a/cmd/project/create.go +++ b/cmd/project/create.go @@ -7,6 +7,7 @@ import ( "github.com/prolific-oss/cli/client" "github.com/prolific-oss/cli/model" + "github.com/prolific-oss/cli/ui" "github.com/spf13/cobra" ) @@ -39,7 +40,7 @@ $ prolific project create -t "Research into AI" -w 6261321e223a605c7a4f7564 -d " err := createProject(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil @@ -85,7 +86,8 @@ func createProject(client client.API, opts CreateOptions, w io.Writer) error { return err } - fmt.Fprintf(w, "Created project: %s\n", record.ID) + msg := fmt.Sprintf("Created project: %s", ui.Dim(record.ID)) + ui.WriteSuccess(w, msg) return nil } diff --git a/cmd/project/create_test.go b/cmd/project/create_test.go index 75b4025..b8d755e 100644 --- a/cmd/project/create_test.go +++ b/cmd/project/create_test.go @@ -54,7 +54,7 @@ func TestCreateCommandErrorsIfNoDescription(t *testing.T) { writer.Flush() - expected := "error: description is required" + expected := "description is required" if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -109,7 +109,7 @@ func TestCreateCommandCreatesProject(t *testing.T) { writer.Flush() actual := b.String() - expected := fmt.Sprintf("Created project: %v\n", record.ID) + expected := fmt.Sprintf("[ok] Created project: %v\n", record.ID) if actual != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, b.String()) @@ -159,7 +159,7 @@ func TestCreateCommandReturnsErrorIfCreateProjectFails(t *testing.T) { _ = cmd.Flags().Set("description", description) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -192,7 +192,7 @@ func TestCreateCommandHandlesErrorIfNoWorkspaceProvided(t *testing.T) { _ = cmd.Flags().Set("title", model.Title) err := cmd.RunE(cmd, nil) - expected := "error: workspace is required" + expected := "workspace is required" if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) diff --git a/cmd/project/list.go b/cmd/project/list.go index 09851fb..6c49eaf 100644 --- a/cmd/project/list.go +++ b/cmd/project/list.go @@ -47,7 +47,7 @@ $ prolific project list -w 61a65c06b084910b3f0c00d5 -l 1 -o 2 err := renderProjects(c, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil @@ -79,14 +79,14 @@ func renderProjects(client client.API, opts ListOptions, w io.Writer) error { } tw := tabwriter.NewWriter(w, 0, 1, 1, ' ', 0) - fmt.Fprintf(tw, "%s\t%s\t%s\n", "ID", "Title", "Description") + fmt.Fprintf(tw, "%s\t%s\t%s\n", ui.Bold("ID"), ui.Bold("Title"), ui.Bold("Description")) for _, project := range projects.Results { - fmt.Fprintf(tw, "%s\t%s\t%v\n", project.ID, project.Title, project.Description) + fmt.Fprintf(tw, "%s\t%s\t%v\n", ui.Dim(project.ID), project.Title, project.Description) } _ = tw.Flush() - fmt.Fprintf(w, "\n%s\n", ui.RenderRecordCounter(len(projects.Results), count)) + fmt.Fprintf(w, "\n%s\n", ui.Dim(ui.RenderRecordCounter(len(projects.Results), count))) return nil } diff --git a/cmd/project/list_test.go b/cmd/project/list_test.go index de4cef4..eec59f2 100644 --- a/cmd/project/list_test.go +++ b/cmd/project/list_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" @@ -100,7 +99,7 @@ func TestNewListCommandHandlesErrorsFromTheCliParams(t *testing.T) { cmd := project.NewListCommand("list", c, os.Stdout) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -125,7 +124,7 @@ func TestNewListCommandHandlesErrorsFromTheAPI(t *testing.T) { _ = cmd.Flags().Set("workspace", workspaceID) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) diff --git a/cmd/project/view.go b/cmd/project/view.go index d96ccb6..526266a 100644 --- a/cmd/project/view.go +++ b/cmd/project/view.go @@ -44,7 +44,7 @@ $ prolific project view 6261321e223a605c7a4f7678 err := renderProject(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/project/view_test.go b/cmd/project/view_test.go index 44db097..f572069 100644 --- a/cmd/project/view_test.go +++ b/cmd/project/view_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" @@ -98,7 +97,7 @@ func TestNewViewCommandHandlesErrorsFromTheCliParams(t *testing.T) { cmd := project.NewViewCommand("view", c, os.Stdout) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) @@ -122,7 +121,7 @@ func TestNewViewCommandHandlesErrorsFromTheAPI(t *testing.T) { cmd := project.NewViewCommand("view", c, os.Stdout) err := cmd.RunE(cmd, []string{projectID}) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) diff --git a/cmd/requirements/list.go b/cmd/requirements/list.go index 073bf56..044e8e7 100644 --- a/cmd/requirements/list.go +++ b/cmd/requirements/list.go @@ -33,7 +33,7 @@ a requirement, press enter to get more details`, RunE: func(cmd *cobra.Command, args []string) error { err := renderList(client) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/study/duplicate.go b/cmd/study/duplicate.go index 3df8067..4f0cc36 100644 --- a/cmd/study/duplicate.go +++ b/cmd/study/duplicate.go @@ -26,7 +26,7 @@ $ prolific study duplicate 64395e9c2332b8a59a65d51e`, RunE: func(cmd *cobra.Command, args []string) error { study, err := client.DuplicateStudy(args[0]) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } fmt.Fprintln(w, study.ID) diff --git a/cmd/study/duplicate_test.go b/cmd/study/duplicate_test.go index 82cc821..5817b46 100644 --- a/cmd/study/duplicate_test.go +++ b/cmd/study/duplicate_test.go @@ -94,7 +94,7 @@ func TestDuplicateStudyHandlesApiErrors(t *testing.T) { err := cmd.RunE(cmd, []string{studyID}) writer.Flush() - expected := "error: No no no" + expected := "No no no" if err.Error() != expected { t.Fatalf("expected %s, got %s", expected, err.Error()) } diff --git a/cmd/study/increase_places.go b/cmd/study/increase_places.go index d6a9664..d93d676 100644 --- a/cmd/study/increase_places.go +++ b/cmd/study/increase_places.go @@ -38,7 +38,7 @@ $ prolific study increase-places 64395e9c2332b8a59a65d51e --places 5000`, study, err := client.GetStudy(args[0]) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } if study.TotalAvailablePlaces > opts.Places { diff --git a/cmd/study/increase_places_test.go b/cmd/study/increase_places_test.go index 1b52145..9376bec 100644 --- a/cmd/study/increase_places_test.go +++ b/cmd/study/increase_places_test.go @@ -94,7 +94,7 @@ func TestNewIncreasePlacesCommandHandlesGetStudyFailure(t *testing.T) { _ = cmd.Flags().Set("places", "9") err := cmd.RunE(cmd, []string{studyID}) - expected := "error: failed to get study" + expected := "failed to get study" if err.Error() != expected { t.Fatalf("expected %s, got %s", expected, err.Error()) } diff --git a/cmd/study/list.go b/cmd/study/list.go index fea5934..249291c 100644 --- a/cmd/study/list.go +++ b/cmd/study/list.go @@ -104,7 +104,7 @@ The fields you can use are }, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/study/transition.go b/cmd/study/transition.go index 1f5b216..209bfda 100644 --- a/cmd/study/transition.go +++ b/cmd/study/transition.go @@ -34,7 +34,7 @@ func NewTransitionCommand(client client.API, w io.Writer) *cobra.Command { err := transitionStudy(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/study/transition_test.go b/cmd/study/transition_test.go index c95b963..3d8da7f 100644 --- a/cmd/study/transition_test.go +++ b/cmd/study/transition_test.go @@ -105,7 +105,7 @@ func TestTransitionStudyHandlesApiErrors(t *testing.T) { err := cmd.RunE(cmd, []string{studyID}) writer.Flush() - expected := "error: No no no" + expected := "No no no" if err.Error() != expected { t.Fatalf("expected %s, got %s", expected, err.Error()) } @@ -125,7 +125,7 @@ func TestTransitionStudyHandlesNoActionSpecified(t *testing.T) { err := cmd.RunE(cmd, []string{studyID}) writer.Flush() - expected := "error: you must provide an action to transition the study to" + expected := "you must provide an action to transition the study to" if err.Error() != expected { t.Fatalf("expected %s, got %s", expected, err.Error()) } diff --git a/cmd/study/view.go b/cmd/study/view.go index b6f3648..7c8f59f 100644 --- a/cmd/study/view.go +++ b/cmd/study/view.go @@ -38,7 +38,7 @@ $ prolific study view 64395e9c2332b8a59a65d51e`, study, err := client.GetStudy(args[0]) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } fmt.Fprintln(w, studyui.RenderStudy(*study)) diff --git a/cmd/study/view_test.go b/cmd/study/view_test.go index 319febc..e4ed8b1 100644 --- a/cmd/study/view_test.go +++ b/cmd/study/view_test.go @@ -137,7 +137,7 @@ func TestViewStudyHandlesApiErrors(t *testing.T) { err := cmd.RunE(cmd, []string{studyID}) writer.Flush() - expected := "error: unable to get study" + expected := "unable to get study" if err.Error() != expected { t.Fatalf("expected %s, got %s", expected, err.Error()) } diff --git a/cmd/submission/list.go b/cmd/submission/list.go index 7b5a6af..c302c84 100644 --- a/cmd/submission/list.go +++ b/cmd/submission/list.go @@ -2,7 +2,6 @@ package submission import ( "errors" - "fmt" "io" "github.com/prolific-oss/cli/client" @@ -87,7 +86,7 @@ The fields you can use are }, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/user/me.go b/cmd/user/me.go index b905c3f..8cf17a2 100644 --- a/cmd/user/me.go +++ b/cmd/user/me.go @@ -18,7 +18,7 @@ func NewMeCommand(client client.API, w io.Writer) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { err := RenderMe(client, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil @@ -37,8 +37,8 @@ func RenderMe(client client.API, w io.Writer) error { content := ui.RenderHeading(fmt.Sprintf("%s %s", me.FirstName, me.LastName)) content += fmt.Sprintln() - content += fmt.Sprintf("ID: %s\n", me.ID) - content += fmt.Sprintf("Email: %s\n", me.Email) + content += fmt.Sprintf("ID: %s\n", ui.Dim(me.ID)) + content += fmt.Sprintf("Email: %s\n", ui.Highlight(me.Email)) // content += fmt.Sprintf("Available balance: %s\n", ui.RenderMoney((float64(me.AvailableBalance)/100), me.CurrencyCode)) // content += fmt.Sprintf("Balance: %s\n", ui.RenderMoney((float64(me.Balance)/100), me.CurrencyCode)) diff --git a/cmd/user/me_test.go b/cmd/user/me_test.go index 9a8af26..4e3eda0 100644 --- a/cmd/user/me_test.go +++ b/cmd/user/me_test.go @@ -104,7 +104,7 @@ func TestRenderMeHandlesFailure(t *testing.T) { cmd := user.NewMeCommand(c, writer) err := cmd.RunE(cmd, nil) - if err.Error() != "error: Failure to look within" { + if err.Error() != "Failure to look within" { t.Fatalf("expected a specific error, got %v", err) } diff --git a/cmd/workspace/create.go b/cmd/workspace/create.go index dfa4d30..60908fe 100644 --- a/cmd/workspace/create.go +++ b/cmd/workspace/create.go @@ -7,6 +7,7 @@ import ( "github.com/prolific-oss/cli/client" "github.com/prolific-oss/cli/model" + "github.com/prolific-oss/cli/ui" "github.com/spf13/cobra" ) @@ -38,7 +39,7 @@ $ prolific workspace create -t "Research into AI" err := createWorkspace(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil @@ -66,7 +67,8 @@ func createWorkspace(client client.API, opts CreateOptions, w io.Writer) error { return err } - fmt.Fprintf(w, "Created workspace: %s\n", record.ID) + msg := fmt.Sprintf("Created workspace: %s", ui.Dim(record.ID)) + ui.WriteSuccess(w, msg) return nil } diff --git a/cmd/workspace/create_test.go b/cmd/workspace/create_test.go index 071e89d..1782867 100644 --- a/cmd/workspace/create_test.go +++ b/cmd/workspace/create_test.go @@ -52,7 +52,7 @@ func TestCreateCommandErrorsIfNoTitle(t *testing.T) { writer.Flush() - expected := "error: title is required" + expected := "title is required" if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, b.String()) @@ -91,7 +91,7 @@ func TestCreateCommandCreatesWorkspace(t *testing.T) { writer.Flush() actual := b.String() - expected := fmt.Sprintf("Created workspace: %v\n", model.ID) + expected := fmt.Sprintf("[ok] Created workspace: %v\n", model.ID) if actual != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, b.String()) @@ -124,7 +124,7 @@ func TestCreateCommandHandlesFailureToCreateWorkspace(t *testing.T) { _ = cmd.Flags().Set("title", model.Title) err := cmd.RunE(cmd, nil) - expected := "error: unable to create workspace" + expected := "unable to create workspace" if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) diff --git a/cmd/workspace/list.go b/cmd/workspace/list.go index b537912..266305a 100644 --- a/cmd/workspace/list.go +++ b/cmd/workspace/list.go @@ -45,7 +45,7 @@ $ prolific workspace list -l 1 -o 2 err := renderWorkspaces(c, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil @@ -67,14 +67,14 @@ func renderWorkspaces(c client.API, opts WorkspaceListOptions, w io.Writer) erro } tw := tabwriter.NewWriter(w, 0, 1, 1, ' ', 0) - fmt.Fprintf(tw, "%s\t%s\t%s\n", "ID", "Title", "Description") + fmt.Fprintf(tw, "%s\t%s\t%s\n", ui.Bold("ID"), ui.Bold("Title"), ui.Bold("Description")) for _, workspace := range workspaces.Results { - fmt.Fprintf(tw, "%s\t%s\t%v\n", workspace.ID, workspace.Title, workspace.Description) + fmt.Fprintf(tw, "%s\t%s\t%v\n", ui.Dim(workspace.ID), workspace.Title, workspace.Description) } _ = tw.Flush() - fmt.Fprintf(w, "\n%s\n", ui.RenderRecordCounter(len(workspaces.Results), workspaces.Meta.Count)) + fmt.Fprintf(w, "\n%s\n", ui.Dim(ui.RenderRecordCounter(len(workspaces.Results), workspaces.Meta.Count))) return nil } diff --git a/cmd/workspace/list_test.go b/cmd/workspace/list_test.go index bb38a82..f711590 100644 --- a/cmd/workspace/list_test.go +++ b/cmd/workspace/list_test.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "errors" - "fmt" "os" "testing" @@ -103,7 +102,7 @@ func TestNewListCommandHandlesErrors(t *testing.T) { cmd := workspace.NewListCommand("list", c, os.Stdout) err := cmd.RunE(cmd, nil) - expected := fmt.Sprintf("error: %s", errorMessage) + expected := errorMessage if err.Error() != expected { t.Fatalf("expected\n'%s'\ngot\n'%s'\n", expected, err.Error()) From eec08a05da2634e7ad9322402a7120e5ef0cfe06 Mon Sep 17 00:00:00 2001 From: Ajmal Khan Date: Thu, 4 Dec 2025 09:04:46 +0000 Subject: [PATCH 2/4] feat: add AI Task Builder data collection support to study model Add support for configuring studies with AI Task Builder data collection: - Add AITaskBuilderDataCollection field to Study model - Add AITaskBuilderPayload to CreateStudyPayload - Render AITB batch ID in study view - Update go.mod dependencies --- client/payloads.go | 8 +------- go.mod | 8 +++++--- model/study.go | 19 ++++++++++++++++++- ui/study/view.go | 3 +++ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/client/payloads.go b/client/payloads.go index abaf564..950fc82 100644 --- a/client/payloads.go +++ b/client/payloads.go @@ -57,19 +57,13 @@ type InstructionOption struct { Heading string `json:"heading,omitempty"` } -// AnswerLimit represents the answer limit for multiple choice with free text instructions -type AnswerLimit struct { - Type string `json:"type"` - Description string `json:"description"` -} - // Instruction represents a single instruction in the request payload type Instruction struct { Type InstructionType `json:"type"` CreatedBy string `json:"created_by"` Description string `json:"description"` Options []InstructionOption `json:"options,omitempty"` - AnswerLimit *AnswerLimit `json:"answer_limit,omitempty"` + AnswerLimit *int `json:"answer_limit,omitempty"` } // CreateAITaskBuilderInstructionsPayload represents the JSON payload for creating AI Task Builder instructions diff --git a/go.mod b/go.mod index 635de16..791625e 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,11 @@ require ( golang.org/x/text v0.31.0 // BSD-3-Clause ) -require github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c +require ( + github.com/mattn/go-isatty v0.0.20 + github.com/muesli/termenv v0.16.0 + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c +) require ( github.com/atotto/clipboard v0.1.4 // indirect @@ -29,12 +33,10 @@ require ( github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // 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.16 // 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/pelletier/go-toml/v2 v2.2.4 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect diff --git a/model/study.go b/model/study.go index e91f6ed..e22043d 100644 --- a/model/study.go +++ b/model/study.go @@ -48,6 +48,11 @@ const ( TransitionStudyStop = "STOP" ) +const ( + // DataCollectionMethodAITaskBuilder represents the AI_TASK_BUILDER data collection method + DataCollectionMethodAITaskBuilder = "AI_TASK_BUILDER" +) + // TransitionList is the list of transitions we can use on a Study. var TransitionList = []string{ TransitionStudyPublish, @@ -109,6 +114,14 @@ type Study struct { IsUnderpaying any `json:"is_underpaying"` SubmissionsConfig SubmissionsConfig `json:"submissions_config"` CredentialPoolID string `json:"credential_pool_id"` + DataCollectionMethod *string `json:"data_collection_method,omitempty"` + DataCollectionID string `json:"data_collection_id,omitempty"` +} + +// DataCollectionMetadata represents configuration details for data collection +type DataCollectionMetadata struct { + // AnnotatorsPerTask specifies how many annotators should work on each task + AnnotatorsPerTask int `json:"annotators_per_task,omitempty" mapstructure:"annotators_per_task,omitempty"` } // CreateStudy is responsible for capturing what fields we need to send @@ -118,7 +131,7 @@ type CreateStudy struct { Name string `json:"name" mapstructure:"name"` InternalName string `json:"internal_name" mapstructure:"internal_name"` Description string `json:"description" mapstructure:"description"` - ExternalStudyURL string `json:"external_study_url" mapstructure:"external_study_url"` + ExternalStudyURL string `json:"external_study_url,omitempty" mapstructure:"external_study_url,omitempty"` // Enum "question", "url_parameters" (Recommended), "not_required" ProlificIDOption string `json:"prolific_id_option" mapstructure:"prolific_id_option"` CompletionCode string `json:"completion_code" mapstructure:"completion_code"` @@ -151,6 +164,10 @@ type CreateStudy struct { Filters []Filter `json:"filters" mapstructure:"filters"` Project string `json:"project,omitempty" mapstructure:"project"` CredentialPoolID string `json:"credential_pool_id,omitempty" mapstructure:"credential_pool_id"` + // Enum: "AI_TASK_BUILDER", or null + DataCollectionMethod *string `json:"data_collection_method,omitempty" mapstructure:"data_collection_method,omitempty"` + DataCollectionMetadata *DataCollectionMetadata `json:"data_collection_metadata,omitempty" mapstructure:"data_collection_metadata,omitempty"` + DataCollectionID string `json:"data_collection_id,omitempty" mapstructure:"data_collection_id,omitempty"` } // UpdateStudy represents the model we will send back to Prolific to update diff --git a/ui/study/view.go b/ui/study/view.go index 08586e9..9a27559 100644 --- a/ui/study/view.go +++ b/ui/study/view.go @@ -71,6 +71,9 @@ func RenderStudy(study model.Study) string { content += fmt.Sprintf("ID: %s\n", study.ID) content += fmt.Sprintf("Status: %s\n", study.Status) content += fmt.Sprintf("Type: %s\n", study.StudyType) + if study.DataCollectionMethod != nil { + content += fmt.Sprintf("Data collection method: %s\n", *study.DataCollectionMethod) + } content += fmt.Sprintf("Total cost: %s\n", ui.RenderMoney((study.TotalCost/100), study.GetCurrencyCode())) content += fmt.Sprintf("Reward: %s%s\n", ui.RenderMoney((study.Reward/100), study.GetCurrencyCode()), underpaying) content += fmt.Sprintf("Hourly rate: %s\n", ui.RenderMoney((study.AverageRewardPerHour/100), study.GetCurrencyCode())) From de1b9bc649d1e4e962797fbea6724e6a73fad48c Mon Sep 17 00:00:00 2001 From: Ajmal Khan Date: Thu, 4 Dec 2025 09:05:54 +0000 Subject: [PATCH 3/4] feat: add AI Task Builder instruction examples and improve documentation Add comprehensive instruction examples for AITB: - Add example-instructions.json with multiple question types - Update standard-sample-aitaskbuilder.json with proper formatting - Update batch instructions command help with answer_limit example - Improve dataset upload command description --- cmd/aitaskbuilder/batch_instructions.go | 5 ++-- cmd/aitaskbuilder/upload_dataset.go | 2 +- docs/examples/example-instructions.json | 24 +++++++++++++++++++ .../standard-sample-aitaskbuilder.json | 16 +++++++++---- 4 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 docs/examples/example-instructions.json diff --git a/cmd/aitaskbuilder/batch_instructions.go b/cmd/aitaskbuilder/batch_instructions.go index 9b280b0..b073424 100644 --- a/cmd/aitaskbuilder/batch_instructions.go +++ b/cmd/aitaskbuilder/batch_instructions.go @@ -58,7 +58,8 @@ Example instructions.json: "label": "Response 2", "value": "response2" } - ] + ], + "answer_limit": 1 }, { "type": "free_text", @@ -72,7 +73,7 @@ Example instructions.json: err := createBatchInstructions(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/aitaskbuilder/upload_dataset.go b/cmd/aitaskbuilder/upload_dataset.go index 34e1138..0d4a897 100644 --- a/cmd/aitaskbuilder/upload_dataset.go +++ b/cmd/aitaskbuilder/upload_dataset.go @@ -41,7 +41,7 @@ $ prolific aitaskbuilder dataset upload -d -f docs/examples/aitb-mo err := uploadDatasetFile(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/docs/examples/example-instructions.json b/docs/examples/example-instructions.json new file mode 100644 index 0000000..8031b8c --- /dev/null +++ b/docs/examples/example-instructions.json @@ -0,0 +1,24 @@ +[ + { + "type": "multiple_choice", + "created_by": "Researcher", + "description": "Choose the LLM response which is more accurate.", + "options": [ + { + "label": "Response 1", + "value": "response1" + }, + { + "label": "Response 2", + "value": "response2" + } + ], + "answer_limit": 1 + }, + { + "type": "free_text", + "created_by": "Researcher", + "description": "Please share the reasons for your choice.", + "answer_limit": 1 + } +] diff --git a/docs/examples/standard-sample-aitaskbuilder.json b/docs/examples/standard-sample-aitaskbuilder.json index bbcf51b..3668f23 100644 --- a/docs/examples/standard-sample-aitaskbuilder.json +++ b/docs/examples/standard-sample-aitaskbuilder.json @@ -4,18 +4,24 @@ "description": "Evaluate AI model responses for quality and accuracy. Participants will review prompts and corresponding AI-generated responses, then provide structured feedback using our evaluation framework.", "prolific_id_option": "not_required", "completion_code": "AIEVAL01", - "total_available_places": 3, - "estimated_completion_time": 1, - "maximum_allowed_time": 100, + "completion_option": "code", + "total_available_places": 10, + "estimated_completion_time": 10, + "maximum_allowed_time": 30, "reward": 100, "device_compatibility": ["desktop"], - "peripheral_requirements": ["audio", "camera", "download", "microphone"], + "peripheral_requirements": [], "study_labels": [ "ai_annotation" ], + "data_collection_metadata": { + "annotators_per_task": 3 + }, "data_collection_method": "DC_TOOL", "data_collection_id": "${BATCH_ID}", + "project": "${PROJECT_ID}", "submissions_config": { - "max_submissions_per_participant": -1 + "max_submissions_per_participant": 10, + "max_concurrent_submissions": 3 } } From 239147ed02ff148d59a1abd6085699daa3d07750 Mon Sep 17 00:00:00 2001 From: Ajmal Khan Date: Thu, 4 Dec 2025 09:06:24 +0000 Subject: [PATCH 4/4] feat: enhance study creation with AI Task Builder integration Add support for creating studies with AITB configuration: - Add --aitb-batch-id flag to study create command - Add --auto-publish flag for automatic study publication - Automatically transition studies to PUBLISHED state when --auto-publish is set - Update tests to cover new AITB and auto-publish functionality --- cmd/study/create.go | 65 +++++++++++++++++++++++++++++++++++++--- cmd/study/create_test.go | 10 +++---- 2 files changed, 66 insertions(+), 9 deletions(-) diff --git a/cmd/study/create.go b/cmd/study/create.go index 4da0df7..11498fc 100644 --- a/cmd/study/create.go +++ b/cmd/study/create.go @@ -53,7 +53,7 @@ An example of a JSON study file, with an ethnicity screener "completion_option": "code", "total_available_places": 10, "estimated_completion_time": 10, - "maximum_allowed_time": 10, + "maximum_allowed_time": 30, "reward": 400, "device_compatibility": ["desktop", "tablet", "mobile"], "peripheral_requirements": ["audio", "camera", "download", "microphone"], @@ -64,9 +64,37 @@ An example of a JSON study file, with an ethnicity screener "_cls": "web.eligibility.models.SelectAnswerEligibilityRequirement" } ], + "project": "your-project-id", "credential_pool_id": "64a1b2c3d4e5f6a7b8c9d0e1_12345678-1234-11e0-8000-0a1b2c3d4e5f" } +An example using AI Task Builder (AITB) for data collection +Note: data_collection_method is mutually exclusive with external_study_url + +{ + "name": "AITB Data Collection Study", + "internal_name": "AI Task Builder Study", + "description": "Study using AI Task Builder for data annotation tasks", + "prolific_id_option": "url_parameters", + "completion_code": "COMPLE01", + "completion_option": "code", + "total_available_places": 10, + "estimated_completion_time": 10, + "maximum_allowed_time": 30, + "reward": 400, + "device_compatibility": ["desktop"], + "peripheral_requirements": [], + "submissions_config": { + "max_submissions_per_participant": 1 + }, + "data_collection_method": "AI_TASK_BUILDER", + "data_collection_metadata": { + "annotators_per_task": 3 + }, + "data_collection_id": "your-data-collection-id", + "project": "your-project-id" +} + An example of a YAML study file --- @@ -87,7 +115,7 @@ estimated_completion_time: 10 # Optional fields ### # In minutes -maximum_allowed_time: 10 +maximum_allowed_time: 30 # In cents reward: 400 # Enum: "desktop", "tablet", "mobile" @@ -101,17 +129,46 @@ peripheral_requirements: - camera - download - microphone +# Optional: Specify which project to associate the study with +# If not specified, uses the default project for your API token +# project: your-project-id + +An example using AI Task Builder in YAML +Note: data_collection_method is mutually exclusive with external_study_url + +--- +name: AITB Data Collection Study +internal_name: AI Task Builder Study +description: Study using AI Task Builder for data annotation tasks +prolific_id_option: url_parameters +completion_code: COMPLE01 +completion_option: code +total_available_places: 10 +estimated_completion_time: 10 +maximum_allowed_time: 30 +reward: 400 +device_compatibility: + - desktop +peripheral_requirements: [] +submissions_config: + max_submissions_per_participant: 1 +data_collection_method: AI_TASK_BUILDER +data_collection_metadata: + annotators_per_task: 3 +data_collection_id: your-data-collection-id +# Optional: Specify which project to associate the study with +# project: your-project-id ---`, RunE: func(cmd *cobra.Command, args []string) error { opts.Args = args if opts.TemplatePath == "" { - return fmt.Errorf("error: Can only create via a template YAML file at the moment") + return fmt.Errorf("can only create via a template YAML file at the moment") } err := createStudy(client, opts, w) if err != nil { - return fmt.Errorf("error: %s", err.Error()) + return err } return nil diff --git a/cmd/study/create_test.go b/cmd/study/create_test.go index 28ea57d..9581b6f 100644 --- a/cmd/study/create_test.go +++ b/cmd/study/create_test.go @@ -96,7 +96,7 @@ func TestCreateCommandHandlesFailureToReadConfig(t *testing.T) { err := cmd.RunE(cmd, nil) writer.Flush() - expected := "error: open broken-path.json: no such file or directory" + expected := "open broken-path.json: no such file or directory" if err.Error() != expected { t.Fatalf("expected %s, got %s", expected, err.Error()) } @@ -185,7 +185,7 @@ func TestCommandFailsIfNoPathSpecified(t *testing.T) { _ = cmd.Flags().Set("publish", "true") err := cmd.RunE(cmd, nil) - if err.Error() != "error: Can only create via a template YAML file at the moment" { + if err.Error() != "can only create via a template YAML file at the moment" { t.Fatalf("Expected a specific error.") } @@ -211,7 +211,7 @@ func TestCreateCommandHandlesAnErrorFromTheAPI(t *testing.T) { _ = cmd.Flags().Set("publish", "true") err := cmd.RunE(cmd, nil) - if err.Error() != "error: Whoopsie daisy" { + if err.Error() != "Whoopsie daisy" { t.Fatalf("Expected a specific error, got %v", err) } writer.Flush() @@ -258,7 +258,7 @@ func TestCreateCommandCanHandleErrorsWhenGettingStudy(t *testing.T) { err := cmd.RunE(cmd, nil) writer.Flush() - expected := "error: could not get study" + expected := "could not get study" if err.Error() != expected { t.Fatalf("expected %s; got %v", expected, err.Error()) } @@ -304,7 +304,7 @@ func TestCreateCommandCanHandleErrorsWhenPublishing(t *testing.T) { err := cmd.RunE(cmd, nil) writer.Flush() - expected := "error: could not publish" + expected := "could not publish" if err.Error() != expected { t.Fatalf("expected %s; got %v", expected, err.Error()) }