// Copyright (c) 2026 Probo Inc . // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package commonthirdparty import ( "context" "fmt" "github.com/spf13/cobra" "go.gearno.de/kit/pg" clicmdutil "go.probo.inc/probo/pkg/cmd/cmdutil" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/proboctl/cmdutil" ) func newCmdList(f *cmdutil.Factory) *cobra.Command { var ( flagName string flagCategory string flagKeyword string flagState string flagStatus string flagSort string flagOrder string ) cmd := &cobra.Command{ Use: "list", Short: "List common third parties with filters and sorting", Args: cobra.NoArgs, } output := clicmdutil.AddOutputFlag(cmd) cmd.Flags().StringVar(&flagName, "name", "", "Filter by name substring") cmd.Flags().StringVar(&flagCategory, "category", "", "Filter by category") cmd.Flags().StringVar(&flagKeyword, "keyword", "", "Filter by name/slug substring") cmd.Flags().StringVar(&flagState, "state", "", "Filter by enrichment state (queued, enriched, unenriched)") cmd.Flags().StringVar(&flagStatus, "status", "", "Filter by last enrichment status (done, partial, failed)") cmd.Flags().StringVar(&flagSort, "sort", "name", "Sort field: name, created, updated") cmd.Flags().StringVar(&flagOrder, "order", "", "Sort order: asc, desc (default depends on field)") pageFlags := cmdutil.AddPageFlags(cmd) cmd.RunE = func(cmd *cobra.Command, args []string) error { if err := clicmdutil.ValidateOutputFlag(output); err != nil { return err } orderBy, err := parseOrderBy(flagSort, flagOrder) if err != nil { return err } cursor, err := cmdutil.NewCursorFromFlags(pageFlags, orderBy) if err != nil { return err } filter := coredata.NewCommonThirdPartyFilter(optionalString(flagName)) if flagCategory != "" { cat := coredata.ThirdPartyCategory(flagCategory) if !cat.IsValid() { return fmt.Errorf("invalid --category value %q", flagCategory) } filter.WithCategory(&cat) } if flagKeyword != "" { filter.WithKeyword(&flagKeyword) } if flagState != "" { st, err := parseEnrichmentState(flagState) if err != nil { return err } filter.WithState(&st) } if flagStatus != "" { if _, ok := validEnrichmentStatuses[flagStatus]; !ok { return fmt.Errorf("invalid --status value %q: valid values are done, partial, failed", flagStatus) } filter.WithEnrichmentStatus(&flagStatus) } pgClient, err := f.PgClient() if err != nil { return err } var ( parties coredata.CommonThirdParties pageInfo cmdutil.PageInfo ) if err := pgClient.WithConn( cmd.Context(), func(ctx context.Context, conn pg.Querier) error { p, err := cmdutil.FetchPage( ctx, cursor, func(ctx context.Context, cursor *page.Cursor[coredata.CommonThirdPartyOrderField]) ([]*coredata.CommonThirdParty, error) { var ts coredata.CommonThirdParties if err := ts.Load(ctx, conn, cursor, filter); err != nil { return nil, err } return ts, nil }, ) if err != nil { return err } parties = p.Data pageInfo = cmdutil.NewPageInfo(p) return nil }, ); err != nil { return err } if *output == clicmdutil.OutputJSON { return clicmdutil.PrintJSON(f.IOStreams.Out, cmdutil.PageOutput{Items: parties, PageInfo: pageInfo}) } if len(parties) == 0 { _, _ = fmt.Fprintln(f.IOStreams.Out, "No common third parties found.") return nil } table := clicmdutil.NewTable("ID", "NAME", "SLUG", "CATEGORY", "STATE", "STATUS", "LAST ATTEMPT", "UPDATED") for _, p := range parties { lastAttempt := "" if p.LastEnrichmentAttemptAt != nil { lastAttempt = p.LastEnrichmentAttemptAt.Format("2006-01-02 15:04:05") } table.Row( p.ID.String(), p.Name, p.Slug, string(p.Category), enrichmentState(p), enrichmentStatus(p), lastAttempt, p.UpdatedAt.Format("2006-01-02 15:04:05"), ) } _, _ = fmt.Fprintln(f.IOStreams.Out, table.Render()) cmdutil.PrintPageInfo(f.IOStreams.Out, pageInfo) _, _ = fmt.Fprintf(f.IOStreams.ErrOut, "Showing %d common third parties.\n", len(parties)) return nil } return cmd } func optionalString(s string) *string { if s == "" { return nil } return &s }