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
14 changes: 14 additions & 0 deletions cmd/src/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ func parseTemplate(text string) (*template.Template, error) {
}
return humanize.Time(t), nil
},
"searchJobIDNumber": func(id string) string {
sjid, err := ParseSearchJobID(id)
if err != nil {
return id
}
return fmt.Sprintf("%d", sjid.Number())
},
"searchJobIDCanonical": func(id string) string {
sjid, err := ParseSearchJobID(id)
if err != nil {
return id
}
return sjid.Canonical()
},

// Register search-specific template functions
"searchSequentialLineNumber": searchTemplateFuncs["searchSequentialLineNumber"],
Expand Down
79 changes: 60 additions & 19 deletions cmd/src/search_jobs.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package main

import (
"encoding/base64"
"flag"
"fmt"
"text/template"
"os"
"regexp"
"strconv"

"github.com/sourcegraph/src-cli/internal/cmderrors"
)

// searchJobFragment is a GraphQL fragment that defines the fields to be queried
Expand Down Expand Up @@ -51,6 +54,8 @@ The commands are:
delete deletes a search job by ID
get gets a search job by ID
list lists search jobs
logs outputs the logs for a search job by ID
results outputs the results for a search job by ID

Use "src search-jobs [command] -h" for more information about a command.
`
Expand All @@ -71,22 +76,12 @@ Use "src search-jobs [command] -h" for more information about a command.
})
}

// printSearchJob formats and prints a search job to stdout using the provided format template.
// Returns an error if the template parsing or execution fails.
func printSearchJob(job *SearchJob, format string) error {
tmpl, err := template.New("searchJob").Parse(format)
if err != nil {
return err
}
return tmpl.Execute(os.Stdout, job)
}

// SearchJob represents a search job with its metadata, including the search query,
// execution state, creator information, timestamps, URLs, and repository statistics.
type SearchJob struct {
ID string
Query string
State string
ID string
Query string
State string
Creator struct {
Username string
}
Expand All @@ -96,9 +91,55 @@ type SearchJob struct {
URL string
LogURL string
RepoStats struct {
Total int
Completed int
Failed int
InProgress int
Total int
Completed int
Failed int
InProgress int
}
}

type SearchJobID struct {
number uint64
}

func ParseSearchJobID(input string) (*SearchJobID, error) {
// accept either:
// - the numeric job id (non-negative integer)
// - the plain text SearchJob:<integer> form of the id
// - the base64-encoded "SearchJob:<integer>" string

if input == "" {
return nil, cmderrors.Usage("must provide a search job ID")
}

// Try to decode if it's base64 first
if decoded, err := base64.StdEncoding.DecodeString(input); err == nil {
input = string(decoded)
}

// Match either "SearchJob:<integer>" or "<integer>"
re := regexp.MustCompile(`^(?:SearchJob:)?(\d+)$`)
matches := re.FindStringSubmatch(input)
if matches == nil {
return nil, fmt.Errorf("invalid ID format: must be a non-negative integer, 'SearchJob:<integer>', or that string base64-encoded")
}

number, err := strconv.ParseUint(matches[1], 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid ID format: must be a 64-bit non-negative integer")
}

return &SearchJobID{number: number}, nil
}

func (id *SearchJobID) String() string {
return fmt.Sprintf("SearchJob:%d", id.Number())
}

func (id *SearchJobID) Canonical() string {
return base64.StdEncoding.EncodeToString([]byte(id.String()))
}

func (id *SearchJobID) Number() uint64 {
return id.number
}
15 changes: 8 additions & 7 deletions cmd/src/search_jobs_cancel.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"context"
"flag"
"fmt"

"github.com/sourcegraph/src-cli/internal/api"
"github.com/sourcegraph/src-cli/internal/cmderrors"
)

const CancelSearchJobMutation = `mutation CancelSearchJob($id: ID!) {
Expand All @@ -23,7 +23,7 @@ Examples:

Cancel a search job:

$ src search-jobs cancel --id U2VhcmNoSm9iOjY5
$ src search-jobs cancel -id 999
`
flagSet := flag.NewFlagSet("cancel", flag.ExitOnError)
usageFunc := func() {
Expand All @@ -33,8 +33,8 @@ Examples:
}

var (
idFlag = flagSet.String("id", "", "ID of the search job to cancel")
apiFlags = api.NewFlags(flagSet)
idFlag = flagSet.String("id", "", "ID of the search job to cancel")
apiFlags = api.NewFlags(flagSet)
)

handler := func(args []string) error {
Expand All @@ -49,8 +49,9 @@ Examples:
Flags: apiFlags,
})

if *idFlag == "" {
return cmderrors.Usage("must provide a search job ID")
jobID, err := ParseSearchJobID(*idFlag)
if err != nil {
return err
}

query := CancelSearchJobMutation
Expand All @@ -62,7 +63,7 @@ Examples:
}

if ok, err := client.NewRequest(query, map[string]interface{}{
"id": *idFlag,
"id": api.NullString(jobID.Canonical()),
}).Do(context.Background(), &result); err != nil || !ok {
return err
}
Expand Down
7 changes: 4 additions & 3 deletions cmd/src/search_jobs_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"flag"
"fmt"

"github.com/sourcegraph/src-cli/internal/api"
"github.com/sourcegraph/src-cli/internal/cmderrors"
)
Expand Down Expand Up @@ -43,9 +44,9 @@ Examples:
}

var (
queryFlag = flagSet.String("query", "", "Search query")
formatFlag = flagSet.String("f", "{{.ID}}: {{.Creator.Username}} {{.State}} ({{.Query}})", `Format for the output, using the syntax of Go package text/template. (e.g. "{{.ID}}: {{.Creator.Username}} ({{.Query}})" or "{{.|json}}")`)
apiFlags = api.NewFlags(flagSet)
queryFlag = flagSet.String("query", "", "Search query")
formatFlag = flagSet.String("f", "{{searchJobIDNumber .ID}}: {{.Creator.Username}} {{.State}} ({{.Query}})", `Format for the output, using the syntax of Go package text/template. (e.g. "{{.ID}}: {{.Creator.Username}} ({{.Query}})" or "{{.|json}}")`)
apiFlags = api.NewFlags(flagSet)
)

handler := func(args []string) error {
Expand Down
13 changes: 7 additions & 6 deletions cmd/src/search_jobs_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"context"
"flag"
"fmt"

"github.com/sourcegraph/src-cli/internal/api"
"github.com/sourcegraph/src-cli/internal/cmderrors"
)

const DeleteSearchJobQuery = `mutation DeleteSearchJob($id: ID!) {
Expand All @@ -23,7 +23,7 @@ Examples:

Delete a search job by ID:

$ src search-jobs delete U2VhcmNoSm9iOjY5
$ src search-jobs delete -id 999
`

flagSet := flag.NewFlagSet("delete", flag.ExitOnError)
Expand All @@ -34,7 +34,7 @@ Examples:
}

var (
idFlag = flagSet.String("id", "", "ID of the search job to delete")
idFlag = flagSet.String("id", "", "ID of the search job to delete")
apiFlags = api.NewFlags(flagSet)
)

Expand All @@ -50,8 +50,9 @@ Examples:
Flags: apiFlags,
})

if *idFlag == "" {
return cmderrors.Usage("must provide a search job ID")
jobID, err := ParseSearchJobID(*idFlag)
if err != nil {
return err
}

var result struct {
Expand All @@ -61,7 +62,7 @@ Examples:
}

if ok, err := client.NewRequest(DeleteSearchJobQuery, map[string]interface{}{
"id": *idFlag,
"id": api.NullString(jobID.Canonical()),
}).Do(context.Background(), &result); err != nil || !ok {
return err
}
Expand Down
Loading
Loading