Skip to content

Commit d799f23

Browse files
committed
feat(catalog): implement pagination and filtering
1 parent 45e4c55 commit d799f23

File tree

2 files changed

+45
-14
lines changed

2 files changed

+45
-14
lines changed

catalog/client_entities_list.go

+25-8
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@ import (
55
"encoding/json"
66
"net/http"
77
"net/url"
8+
"regexp"
89
"strconv"
10+
"strings"
911
)
1012

1113
// ListEntitiesRequest is the request to the [Client.ListEntities] method.
1214
type ListEntitiesRequest struct {
13-
// Filter for selecting only a subset of all entities.
14-
Filter string
15+
// Filters for selecting only a subset of all entities.
16+
Filters []string
1517
// Fields for selecting only parts of the full data structure of each entity.
16-
Fields string
18+
Fields []string
1719
// Offset for pagination.
1820
Offset int64
1921
// Limit for pagination.
@@ -26,8 +28,12 @@ type ListEntitiesRequest struct {
2628
type ListEntitiesResponse struct {
2729
// Entities in the response.
2830
Entities []*Entity
31+
// NextPageToken contains the next page token.
32+
NextPageToken string
2933
}
3034

35+
var linkURLRegexp = regexp.MustCompile(`<(.*)>; *rel="next"`)
36+
3137
// ListEntities lists entities in the catalog.
3238
//
3339
// See: https://backstage.io/docs/features/software-catalog/software-catalog-api/#get-entities
@@ -40,23 +46,34 @@ func (c *Client) ListEntities(ctx context.Context, request *ListEntitiesRequest)
4046
if request.Limit > 0 {
4147
query.Set("limit", strconv.FormatInt(request.Limit, 10))
4248
}
43-
if request.Filter != "" {
44-
query.Set("filter", request.Filter)
49+
for _, filter := range request.Filters {
50+
query.Add("filter", filter)
4551
}
46-
if request.Fields != "" {
47-
query.Set("fields", request.Fields)
52+
if len(request.Fields) > 0 {
53+
query.Set("fields", strings.Join(request.Fields, ","))
4854
}
4955
if request.After != "" {
5056
query.Set("after", request.After)
5157
}
5258
var rawEntities []json.RawMessage
59+
var nextPageToken string
5360
if err := c.get(ctx, path, query, func(response *http.Response) error {
61+
for _, link := range response.Header.Values("link") {
62+
if matches := linkURLRegexp.FindStringSubmatch(link); len(matches) > 1 {
63+
linkURL, err := url.ParseRequestURI(matches[1])
64+
if err != nil {
65+
return err
66+
}
67+
nextPageToken = linkURL.Query().Get("after")
68+
}
69+
}
5470
return json.NewDecoder(response.Body).Decode(&rawEntities)
5571
}); err != nil {
5672
return nil, err
5773
}
5874
response := ListEntitiesResponse{
59-
Entities: make([]*Entity, 0, len(rawEntities)),
75+
Entities: make([]*Entity, 0, len(rawEntities)),
76+
NextPageToken: nextPageToken,
6077
}
6178
for _, rawEntity := range rawEntities {
6279
entity := &Entity{

cmd/backstage/main.go

+20-6
Original file line numberDiff line numberDiff line change
@@ -111,17 +111,31 @@ func newListEntitiesCommand() *cobra.Command {
111111
Use: "list-entities",
112112
Short: "List entities in the catalog",
113113
}
114+
filters := cmd.Flags().StringArray("filter", nil, "select only a subset of all entities")
115+
fields := cmd.Flags().StringSlice("fields", nil, "select only parts of each entity")
114116
cmd.RunE = func(cmd *cobra.Command, args []string) error {
115117
client, err := newCatalogClient()
116118
if err != nil {
117119
return err
118120
}
119-
response, err := client.ListEntities(cmd.Context(), &catalog.ListEntitiesRequest{})
120-
if err != nil {
121-
return err
122-
}
123-
for _, entity := range response.Entities {
124-
printRawJSON(cmd, entity.Raw)
121+
var nextPageToken string
122+
for {
123+
response, err := client.ListEntities(cmd.Context(), &catalog.ListEntitiesRequest{
124+
Filters: *filters,
125+
Fields: *fields,
126+
Limit: 100,
127+
After: nextPageToken,
128+
})
129+
if err != nil {
130+
return err
131+
}
132+
for _, entity := range response.Entities {
133+
printRawJSON(cmd, entity.Raw)
134+
}
135+
nextPageToken = response.NextPageToken
136+
if nextPageToken == "" {
137+
break
138+
}
125139
}
126140
return nil
127141
}

0 commit comments

Comments
 (0)