diff --git a/cmd/gorse-cli/client.go b/cmd/gorse-cli/client.go index 9ff7d2a61..781db3f5e 100644 --- a/cmd/gorse-cli/client.go +++ b/cmd/gorse-cli/client.go @@ -18,17 +18,11 @@ import ( "fmt" "io" "net/url" + "sort" "strings" "time" "github.com/go-resty/resty/v2" - "github.com/go-viper/mapstructure/v2" - "github.com/gorse-io/gorse/common/monitor" - "github.com/gorse-io/gorse/config" - "github.com/gorse-io/gorse/master" - "github.com/gorse-io/gorse/server" - "github.com/gorse-io/gorse/storage/data" - "github.com/gorse-io/gorse/storage/meta" ) // AdminClient is a client for the Gorse admin API. @@ -36,6 +30,102 @@ type AdminClient struct { client *resty.Client } +type Status struct { + BinaryVersion string + NumServers int + NumWorkers int + NumUsers int + NumItems int + NumUserLabels int + NumItemLabels int + NumTotalPosFeedback int + NumValidPosFeedback int + NumValidNegFeedback int + PopularItemsUpdateTime time.Time + LatestItemsUpdateTime time.Time + MatchingModelFitTime time.Time + MatchingModelScore any + RankingModelFitTime time.Time + RankingModelScore any +} + +type FeedbackIterator struct { + Cursor string + Feedback []Feedback +} + +type Node struct { + UUID string + Hostname string + Type string + Version string + UpdateTime time.Time +} + +type Progress struct { + Tracer string + Name string + Status string + Error string + Count int + Total int + StartTime time.Time + FinishTime time.Time +} + +type Item struct { + ItemId string + IsHidden bool + Categories []string + Timestamp time.Time + Labels any + Comment string +} + +type User struct { + UserId string + Labels any + Comment string +} + +type FeedbackKey struct { + FeedbackType string + UserId string + ItemId string +} + +type Feedback struct { + FeedbackKey + Value float64 + Timestamp time.Time + Updated time.Time + Labels any + Comment string +} + +type ScoredItem struct { + Item + Score float64 +} + +type ScoreUser struct { + User + Score float64 +} + +type DumpStats struct { + Users int + Items int + Feedback int + Duration time.Duration +} + +func sortFeedback(feedback []Feedback) { + sort.Slice(feedback, func(i, j int) bool { + return feedback[i].Timestamp.After(feedback[j].Timestamp) + }) +} + // NewAdminClient creates a new client for the Gorse admin API. func NewAdminClient(endpoint, apiKey string) *AdminClient { client := resty.New() @@ -44,16 +134,16 @@ func NewAdminClient(endpoint, apiKey string) *AdminClient { return &AdminClient{client: client} } -func (c *AdminClient) GetCluster() ([]*meta.Node, error) { - return getJSON[[]*meta.Node](c, "/dashboard/cluster", nil) +func (c *AdminClient) GetCluster() ([]Node, error) { + return getJSON[[]Node](c, "/dashboard/cluster", nil) } -func (c *AdminClient) GetTasks() ([]monitor.Progress, error) { - return getJSON[[]monitor.Progress](c, "/dashboard/tasks", nil) +func (c *AdminClient) GetTasks() ([]Progress, error) { + return getJSON[[]Progress](c, "/dashboard/tasks", nil) } -func (c *AdminClient) GetConfig() (config.Config, error) { - return getJSON[config.Config](c, "/dashboard/config", nil) +func (c *AdminClient) GetConfig() (map[string]any, error) { + return getJSON[map[string]any](c, "/dashboard/config", nil) } func (c *AdminClient) GetConfigMap() (map[string]any, error) { @@ -64,8 +154,8 @@ func (c *AdminClient) GetConfigSchema() (map[string]any, error) { return getJSON[map[string]any](c, "/dashboard/config/schema", nil) } -func (c *AdminClient) UpdateConfig(configPatch map[string]any) (config.Config, error) { - var result config.Config +func (c *AdminClient) UpdateConfig(configPatch map[string]any) (map[string]any, error) { + var result map[string]any resp, err := c.client.R(). SetHeader("Content-Type", "application/json"). SetBody(configPatch). @@ -94,12 +184,14 @@ func (c *AdminClient) ResetConfig() (map[string]any, error) { return result, nil } -func configToMap(configValue config.Config) (map[string]any, error) { - var configMap map[string]any - if err := mapstructure.Decode(configValue, &configMap); err != nil { - return nil, fmt.Errorf("failed to encode config: %w", err) +func getConfigValue(configMap map[string]any, names ...string) (string, any, bool) { + for _, name := range names { + value, ok := configMap[name] + if ok { + return name, value, true + } } - return formatConfigMap(configMap), nil + return "", nil, false } func formatConfigMap(configMap map[string]any) map[string]any { @@ -138,62 +230,62 @@ func (c *AdminClient) GetCategories() ([]string, error) { return getJSON[[]string](c, "/dashboard/categories", nil) } -func (c *AdminClient) GetStats() (master.Status, error) { - return getJSON[master.Status](c, "/dashboard/stats", nil) +func (c *AdminClient) GetStats() (Status, error) { + return getJSON[Status](c, "/dashboard/stats", nil) } -func (c *AdminClient) GetFeedback(n int) (server.FeedbackIterator, error) { +func (c *AdminClient) GetFeedback(n int) (FeedbackIterator, error) { params := url.Values{} addIntParam(params, "n", n) - return getJSON[server.FeedbackIterator](c, "/feedback", params) + return getJSON[FeedbackIterator](c, "/feedback", params) } -func (c *AdminClient) GetTypedFeedback(feedbackType string, n int) (server.FeedbackIterator, error) { +func (c *AdminClient) GetTypedFeedback(feedbackType string, n int) (FeedbackIterator, error) { params := url.Values{} addIntParam(params, "n", n) - return getJSON[server.FeedbackIterator](c, "/feedback/"+url.PathEscape(feedbackType), params) + return getJSON[FeedbackIterator](c, "/feedback/"+url.PathEscape(feedbackType), params) } -func (c *AdminClient) GetUserItemFeedback(userID, itemID string) ([]data.Feedback, error) { - return getJSON[[]data.Feedback](c, "/feedback/"+url.PathEscape(userID)+"/"+url.PathEscape(itemID), nil) +func (c *AdminClient) GetUserItemFeedback(userID, itemID string) ([]Feedback, error) { + return getJSON[[]Feedback](c, "/feedback/"+url.PathEscape(userID)+"/"+url.PathEscape(itemID), nil) } -func (c *AdminClient) GetTypedUserItemFeedback(feedbackType, userID, itemID string) (data.Feedback, error) { - return getJSON[data.Feedback](c, "/feedback/"+url.PathEscape(feedbackType)+"/"+url.PathEscape(userID)+"/"+url.PathEscape(itemID), nil) +func (c *AdminClient) GetTypedUserItemFeedback(feedbackType, userID, itemID string) (Feedback, error) { + return getJSON[Feedback](c, "/feedback/"+url.PathEscape(feedbackType)+"/"+url.PathEscape(userID)+"/"+url.PathEscape(itemID), nil) } -func (c *AdminClient) GetUserFeedback(userID string) ([]data.Feedback, error) { - return getJSON[[]data.Feedback](c, "/user/"+url.PathEscape(userID)+"/feedback", nil) +func (c *AdminClient) GetUserFeedback(userID string) ([]Feedback, error) { + return getJSON[[]Feedback](c, "/user/"+url.PathEscape(userID)+"/feedback", nil) } -func (c *AdminClient) GetTypedUserFeedback(userID, feedbackType string) ([]data.Feedback, error) { - return getJSON[[]data.Feedback](c, "/user/"+url.PathEscape(userID)+"/feedback/"+url.PathEscape(feedbackType), nil) +func (c *AdminClient) GetTypedUserFeedback(userID, feedbackType string) ([]Feedback, error) { + return getJSON[[]Feedback](c, "/user/"+url.PathEscape(userID)+"/feedback/"+url.PathEscape(feedbackType), nil) } -func (c *AdminClient) GetItemFeedback(itemID string) ([]data.Feedback, error) { - return getJSON[[]data.Feedback](c, "/item/"+url.PathEscape(itemID)+"/feedback/", nil) +func (c *AdminClient) GetItemFeedback(itemID string) ([]Feedback, error) { + return getJSON[[]Feedback](c, "/item/"+url.PathEscape(itemID)+"/feedback/", nil) } -func (c *AdminClient) GetTypedItemFeedback(itemID, feedbackType string) ([]data.Feedback, error) { - return getJSON[[]data.Feedback](c, "/item/"+url.PathEscape(itemID)+"/feedback/"+url.PathEscape(feedbackType), nil) +func (c *AdminClient) GetTypedItemFeedback(itemID, feedbackType string) ([]Feedback, error) { + return getJSON[[]Feedback](c, "/item/"+url.PathEscape(itemID)+"/feedback/"+url.PathEscape(feedbackType), nil) } -func (c *AdminClient) GetLatest(n int, categories []string) ([]master.ScoredItem, error) { +func (c *AdminClient) GetLatest(n int, categories []string) ([]ScoredItem, error) { params := url.Values{} addIntParam(params, "n", n) addStringArrayParam(params, "category", categories) - return getJSON[[]master.ScoredItem](c, "/dashboard/latest", params) + return getJSON[[]ScoredItem](c, "/dashboard/latest", params) } -func (c *AdminClient) GetNonPersonalized(name string, n int, userID string, categories []string) ([]master.ScoredItem, error) { +func (c *AdminClient) GetNonPersonalized(name string, n int, userID string, categories []string) ([]ScoredItem, error) { params := url.Values{} addIntParam(params, "n", n) addStringParam(params, "user-id", userID) addStringArrayParam(params, "category", categories) - return getJSON[[]master.ScoredItem](c, "/dashboard/non-personalized/"+url.PathEscape(name), params) + return getJSON[[]ScoredItem](c, "/dashboard/non-personalized/"+url.PathEscape(name), params) } -func (c *AdminClient) GetRecommend(userID, recommender, name string, n int, categories []string) ([]master.ScoredItem, error) { +func (c *AdminClient) GetRecommend(userID, recommender, name string, n int, categories []string) ([]ScoredItem, error) { path := "/dashboard/recommend/" + url.PathEscape(userID) if recommender != "" { path += "/" + url.PathEscape(recommender) @@ -204,24 +296,24 @@ func (c *AdminClient) GetRecommend(userID, recommender, name string, n int, cate params := url.Values{} addIntParam(params, "n", n) addStringArrayParam(params, "category", categories) - return getJSON[[]master.ScoredItem](c, path, params) + return getJSON[[]ScoredItem](c, path, params) } -func (c *AdminClient) GetItemToItem(name, itemID string, n int, categories []string) ([]master.ScoredItem, error) { +func (c *AdminClient) GetItemToItem(name, itemID string, n int, categories []string) ([]ScoredItem, error) { params := url.Values{} addIntParam(params, "n", n) addStringArrayParam(params, "category", categories) - return getJSON[[]master.ScoredItem](c, "/dashboard/item-to-item/"+url.PathEscape(name)+"/"+url.PathEscape(itemID), params) + return getJSON[[]ScoredItem](c, "/dashboard/item-to-item/"+url.PathEscape(name)+"/"+url.PathEscape(itemID), params) } -func (c *AdminClient) GetUserToUser(name, userID string, n int) ([]master.ScoreUser, error) { +func (c *AdminClient) GetUserToUser(name, userID string, n int) ([]ScoreUser, error) { params := url.Values{} addIntParam(params, "n", n) - return getJSON[[]master.ScoreUser](c, "/dashboard/user-to-user/"+url.PathEscape(name)+"/"+url.PathEscape(userID), params) + return getJSON[[]ScoreUser](c, "/dashboard/user-to-user/"+url.PathEscape(name)+"/"+url.PathEscape(userID), params) } -func (c *AdminClient) Restore(reader io.Reader) (*master.DumpStats, error) { - result := new(master.DumpStats) +func (c *AdminClient) Restore(reader io.Reader) (*DumpStats, error) { + result := new(DumpStats) resp, err := c.client.R(). SetHeader("Content-Type", "application/octet-stream"). SetBody(reader). diff --git a/cmd/gorse-cli/context.go b/cmd/gorse-cli/context.go index efd647dad..34904bacf 100644 --- a/cmd/gorse-cli/context.go +++ b/cmd/gorse-cli/context.go @@ -289,6 +289,13 @@ func fatal(cmd *cobra.Command, message string, suggestions ...string) { os.Exit(1) } +func fatalErr(cmd *cobra.Command, message string, err error) { + if err != nil { + message += ": " + err.Error() + } + fatal(cmd, message) +} + var contextCmd = &cobra.Command{ Use: "context", Short: "Manage Gorse CLI contexts", diff --git a/cmd/gorse-cli/main.go b/cmd/gorse-cli/main.go index 65a2b2b8c..c54d48082 100644 --- a/cmd/gorse-cli/main.go +++ b/cmd/gorse-cli/main.go @@ -26,11 +26,8 @@ import ( jsonpatch "github.com/evanphx/json-patch/v5" gorse "github.com/gorse-io/gorse-go" "github.com/gorse-io/gorse/cmd/version" - "github.com/gorse-io/gorse/common/log" - "github.com/gorse-io/gorse/storage/data" "github.com/samber/lo" "github.com/spf13/cobra" - "go.uber.org/zap" "gopkg.in/yaml.v3" ) @@ -131,7 +128,7 @@ var getClusterCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { cluster, err := newAdminClient(cmd).GetCluster() if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } printArrayTable(cmd, cluster) }, @@ -143,7 +140,7 @@ var psCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { tasks, err := newAdminClient(cmd).GetTasks() if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } rows := make([][]string, len(tasks)) for i, task := range tasks { @@ -167,20 +164,17 @@ var pipelineGetCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { configValue, err := newAdminClient(cmd).GetConfig() if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } - configMap, err := configToMap(configValue) - if err != nil { - log.Logger().Fatal("failed to encode config", zap.Error(err)) - } - recommend, ok := configMap["recommend"] + configMap := formatConfigMap(configValue) + _, recommend, ok := getConfigValue(configMap, "recommend", "Recommend") if !ok { - log.Logger().Fatal("recommend config not found") + fatal(cmd, "recommend config not found") } encoder := yaml.NewEncoder(cmd.OutOrStdout()) defer encoder.Close() if err = encoder.Encode(recommend); err != nil { - log.Logger().Fatal("failed to encode config", zap.Error(err)) + fatalErr(cmd, "failed to encode config", err) } }, } @@ -191,12 +185,12 @@ var pipelineSchemaCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { schema, err := newAdminClient(cmd).GetConfigSchema() if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } encoder := yaml.NewEncoder(cmd.OutOrStdout()) defer encoder.Close() if err = encoder.Encode(schema); err != nil { - log.Logger().Fatal("failed to encode schema", zap.Error(err)) + fatalErr(cmd, "failed to encode schema", err) } }, } @@ -211,11 +205,11 @@ var dumpCmd = &cobra.Command{ outputPath := args[0] file, err := os.Create(outputPath) if err != nil { - log.Logger().Fatal("failed to create dump file", zap.String("file", outputPath), zap.Error(err)) + fatalErr(cmd, fmt.Sprintf("failed to create dump file %q", outputPath), err) } defer file.Close() if err = newAdminClient(cmd).Dump(file); err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } fmt.Fprintln(cmd.OutOrStdout(), "Data dumped to "+outputPath) }, @@ -231,7 +225,7 @@ var restoreCmd = &cobra.Command{ fmt.Fprintf(cmd.OutOrStdout(), "Restore data from %s? Existing users, items, feedback, and cache will be overwritten. Confirm [y/N]: ", args[0]) input, err := bufio.NewReader(cmd.InOrStdin()).ReadString('\n') if err != nil && !errors.Is(err, io.EOF) { - log.Logger().Fatal("failed to read confirmation", zap.Error(err)) + fatalErr(cmd, "failed to read confirmation", err) } if !strings.EqualFold(strings.TrimSpace(input), "y") { fmt.Fprintln(cmd.OutOrStdout(), "Restore canceled") @@ -239,12 +233,12 @@ var restoreCmd = &cobra.Command{ } file, err := os.Open(args[0]) if err != nil { - log.Logger().Fatal("failed to open restore file", zap.String("file", args[0]), zap.Error(err)) + fatalErr(cmd, fmt.Sprintf("failed to open restore file %q", args[0]), err) } defer file.Close() stats, err := newAdminClient(cmd).Restore(file) if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } fmt.Fprintf(cmd.OutOrStdout(), "Restored %d users, %d items, %d feedback in %s.\n", stats.Users, stats.Items, stats.Feedback, stats.Duration) @@ -264,34 +258,34 @@ var pipelinePatchCmd = &cobra.Command{ client := newAdminClient(cmd) currentConfigMap, err := client.GetConfigMap() if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } - recommendConfig, ok := currentConfigMap["recommend"] + recommendKey, recommendConfig, ok := getConfigValue(currentConfigMap, "recommend", "Recommend") if !ok { - log.Logger().Fatal("recommend config not found") + fatal(cmd, "recommend config not found") } configBytes, err := json.Marshal(recommendConfig) if err != nil { - log.Logger().Fatal("failed to apply JSON patch", zap.Error(fmt.Errorf("failed to encode config: %w", err))) + fatalErr(cmd, "failed to apply JSON patch: failed to encode config", err) } patch, err := jsonpatch.DecodePatch([]byte(args[0])) if err != nil { - log.Logger().Fatal("failed to apply JSON patch", zap.Error(fmt.Errorf("failed to decode JSON patch: %w", err))) + fatalErr(cmd, "failed to apply JSON patch: failed to decode JSON patch", err) } patchedConfigBytes, err := patch.Apply(configBytes) if err != nil { - log.Logger().Fatal("failed to apply JSON patch", zap.Error(fmt.Errorf("failed to apply JSON patch: %w", err))) + fatalErr(cmd, "failed to apply JSON patch", err) } var configPatch map[string]any if err = json.Unmarshal(patchedConfigBytes, &configPatch); err != nil { - log.Logger().Fatal("failed to apply JSON patch", zap.Error(fmt.Errorf("failed to decode patched config: %w", err))) + fatalErr(cmd, "failed to apply JSON patch: failed to decode patched config", err) } - currentConfigMap["recommend"] = configPatch + currentConfigMap[recommendKey] = configPatch updatedConfig, err := client.UpdateConfig(currentConfigMap) if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } printStruct(cmd, updatedConfig) @@ -305,7 +299,7 @@ var pipelineResetCmd = &cobra.Command{ fmt.Fprint(cmd.OutOrStdout(), "Reset recommendation pipeline configuration to file defaults? Current pipeline settings will be overwritten. Confirm [y/N]: ") input, err := bufio.NewReader(cmd.InOrStdin()).ReadString('\n') if err != nil && !errors.Is(err, io.EOF) { - log.Logger().Fatal("failed to read confirmation", zap.Error(err)) + fatalErr(cmd, "failed to read confirmation", err) } if !strings.EqualFold(strings.TrimSpace(input), "y") { fmt.Fprintln(cmd.OutOrStdout(), "Pipeline reset canceled") @@ -313,7 +307,7 @@ var pipelineResetCmd = &cobra.Command{ } result, err := newAdminClient(cmd).ResetConfig() if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } if len(result) > 0 { printStruct(cmd, result) @@ -329,7 +323,7 @@ var getCategoriesCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { categories, err := newAdminClient(cmd).GetCategories() if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } printArrayTable(cmd, categories) }, @@ -341,7 +335,7 @@ var getStatsCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { stats, err := newAdminClient(cmd).GetStats() if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } printStruct(cmd, stats) }, @@ -354,7 +348,7 @@ var getUserCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { user, err := newGorseClient(cmd).GetUser(cmd.Context(), args[0]) if err != nil { - log.Logger().Fatal("API request failed", zap.Error(err)) + fatalErr(cmd, "API request failed", err) } printStruct(cmd, user) }, @@ -367,7 +361,7 @@ var getItemCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { item, err := newGorseClient(cmd).GetItem(cmd.Context(), args[0]) if err != nil { - log.Logger().Fatal("API request failed", zap.Error(err)) + fatalErr(cmd, "API request failed", err) } printStruct(cmd, item) }, @@ -383,7 +377,7 @@ var getUsersCmd = &cobra.Command{ } users, err := newGorseClient(cmd).GetUsers(cmd.Context(), n, "") if err != nil { - log.Logger().Fatal("API request failed", zap.Error(err)) + fatalErr(cmd, "API request failed", err) } printArrayTable(cmd, users.Users) }, @@ -409,7 +403,7 @@ var getItemsCmd = &cobra.Command{ items, err = client.GetItems(cmd.Context(), n, "") } if err != nil { - log.Logger().Fatal("API request failed", zap.Error(err)) + fatalErr(cmd, "API request failed", err) } printArrayTable(cmd, items.Items) }, @@ -429,14 +423,14 @@ var getFeedbackCmd = &cobra.Command{ itemID := lo.Must(cmd.Flags().GetString("item")) client := newAdminClient(cmd) var ( - feedback []data.Feedback + feedback []Feedback err error ) switch { case userID != "" && itemID != "" && feedbackType != "": record, requestErr := client.GetTypedUserItemFeedback(feedbackType, userID, itemID) err = requestErr - feedback = []data.Feedback{record} + feedback = []Feedback{record} case userID != "" && itemID != "": feedback, err = client.GetUserItemFeedback(userID, itemID) case userID != "" && feedbackType != "": @@ -457,9 +451,9 @@ var getFeedbackCmd = &cobra.Command{ feedback = iterator.Feedback } if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } - data.SortFeedbacks(feedback) + sortFeedback(feedback) if n > 0 && n < len(feedback) { feedback = feedback[:n] } @@ -478,7 +472,7 @@ var getLatestCmd = &cobra.Command{ } latest, err := newAdminClient(cmd).GetLatest(n, categories) if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } printArrayTable(cmd, latest) }, @@ -496,7 +490,7 @@ var getNonPersonalizedCmd = &cobra.Command{ lo.Must(cmd.Flags().GetStringArray("category")), ) if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } printArrayTable(cmd, recommendations) }, @@ -521,7 +515,7 @@ var recommendUserCmd = &cobra.Command{ } recommendations, err := newAdminClient(cmd).GetRecommend(args[0], recommender, name, n, categories) if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } printArrayTable(cmd, recommendations) }, @@ -539,7 +533,7 @@ var getItemToItemCmd = &cobra.Command{ lo.Must(cmd.Flags().GetStringArray("category")), ) if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } printArrayTable(cmd, recommendations) }, @@ -552,7 +546,7 @@ var getUserToUserCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { recommendations, err := newAdminClient(cmd).GetUserToUser(args[0], args[1], lo.Must(cmd.Flags().GetInt("n"))) if err != nil { - log.Logger().Fatal("admin API request failed", zap.Error(err)) + fatalErr(cmd, "admin API request failed", err) } printArrayTable(cmd, recommendations) }, diff --git a/cmd/gorse-cli/main_test.go b/cmd/gorse-cli/main_test.go index 32ad97974..a6021ff71 100644 --- a/cmd/gorse-cli/main_test.go +++ b/cmd/gorse-cli/main_test.go @@ -569,9 +569,9 @@ func waitForInitialTask(t *testing.T, endpoint string) { for _, task := range tasks { if task.Name == "Load Dataset" { switch task.Status { - case monitor.StatusComplete: + case string(monitor.StatusComplete): return - case monitor.StatusFailed: + case string(monitor.StatusFailed): require.Failf(t, "initial load dataset task failed", task.Error) } }