Skip to content

Commit

Permalink
Add rpm list api (#5)
Browse files Browse the repository at this point in the history
* Add rpm list api

* Use prepared statements

* switch to embeded named params

* move existing apis to new query

* remove struct

* add mock

* address feedback
  • Loading branch information
jlsherrill authored Mar 6, 2024
1 parent c546e5d commit 358a433
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 61 deletions.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,32 @@ if err != nil {
}
defer t.Close()

// Use Tangy to list RPMs with pagination for one or more repository versions, with name filtering
versionHref := "/api/pulp/e1c6bee3/api/v3/repositories/rpm/rpm/018c1c95-4281-76eb-b277-842cbad524f4/versions/1/"
rows, err := t.r.tangy.RpmRepositoryVersionPackageList(context.Background(), []string{versionHref}, tangy.RpmListFilters{Name: "kernel"}, tangy.PageOptions{Offset: 100, Limit: 20})
if err != nil {
return err
}

// Use Tangy to search for RPMs, by name, that are associated to a specific repository version, returning up to the first 100 results
versionHref := "/api/pulp/e1c6bee3/api/v3/repositories/rpm/rpm/018c1c95-4281-76eb-b277-842cbad524f4/versions/1/"
rows, err := t.RpmRepositoryVersionPackageSearch(context.Background(), []string{versionHref}, "bear", 100)
if err != nil {
return err
return err
}

// Use Tangy to search for RPM Package Groups, by name, that are associated to a specific repository version, returning up to the first 100 results
versionHref := "/api/pulp/e1c6bee3/api/v3/repositories/rpm/rpm/018c1c95-4281-76eb-b277-842cbad524f4/versions/1/"
rows, err := t.RpmRepositoryVersionPackageGroupSearch(context.Background(), []string{versionHref}, "mammals", 100)
if err != nil {
return err
return err
}

// Use Tangy to search for RPM Environments, by name, that are associated to a specific repository version, returning up to the first 100 results
versionHref := "/api/pulp/e1c6bee3/api/v3/repositories/rpm/rpm/018c1c95-4281-76eb-b277-842cbad524f4/versions/1/"
rows, err := t.RpmRepositoryVersionPackageGroupSearch(context.Background(), []string{versionHref}, "animals", 100)
if err != nil {
return err
return err
}
```
See example.go for a complete example.
Expand Down
84 changes: 77 additions & 7 deletions internal/test/integration/rpm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,11 @@ const testRepoName = "zoo"
const testRepoURL = "https://rverdile.fedorapeople.org/dummy-repos/comps/repo1/"
const testRepoURLTwo = "https://rverdile.fedorapeople.org/dummy-repos/comps/repo2/"

func (r *RpmSuite) CreateTestRepository(t *testing.T) {
domainName := RandStringBytes(10)
r.domainName = domainName

_, err := r.client.LookupOrCreateDomain(domainName)
func (r *RpmSuite) CreateTestRepository(t *testing.T, repoName string) {
_, err := r.client.LookupOrCreateDomain(r.domainName)
require.NoError(t, err)

repoHref, remoteHref, err := r.client.CreateRepository(domainName, testRepoName, testRepoURL)
repoHref, remoteHref, err := r.client.CreateRepository(r.domainName, repoName, testRepoURL)
require.NoError(t, err)

r.repoHref = repoHref
Expand Down Expand Up @@ -78,7 +75,10 @@ func TestRpmSuite(t *testing.T) {
r := RpmSuite{}
r.client = &rpmZest
r.tangy = ta
r.CreateTestRepository(t)

r.domainName = RandStringBytes(10)

r.CreateTestRepository(t, testRepoName)

// Get first version href
resp, err := r.client.GetRpmRepositoryByName(r.domainName, testRepoName)
Expand Down Expand Up @@ -244,6 +244,76 @@ func (r *RpmSuite) TestRpmRepositoryVersionEnvironmentSearch() {
assert.Len(r.T(), search, 0)
}

func (r *RpmSuite) TestRpmRepositoryVersionPackageListNameFilter() {
resp, err := r.client.GetRpmRepositoryByName(r.domainName, testRepoName)
require.NoError(r.T(), err)
firstVersionHref := resp.LatestVersionHref
require.NotNil(r.T(), firstVersionHref)

// no filter
singleList, total, err := r.tangy.RpmRepositoryVersionPackageList(context.Background(), []string{*firstVersionHref}, tangy.RpmListFilters{Name: ""}, tangy.PageOptions{})
require.NoError(r.T(), err)
assert.NotEmpty(r.T(), singleList)
assert.Equal(r.T(), total, 4)

// exact match
singleList, total, err = r.tangy.RpmRepositoryVersionPackageList(context.Background(), []string{*firstVersionHref}, tangy.RpmListFilters{Name: "bear"}, tangy.PageOptions{})
require.NoError(r.T(), err)
assert.NotEmpty(r.T(), singleList)
assert.Equal(r.T(), total, 1)

// partial match
singleList, total, err = r.tangy.RpmRepositoryVersionPackageList(context.Background(), []string{*firstVersionHref}, tangy.RpmListFilters{Name: "bea"}, tangy.PageOptions{})
require.NoError(r.T(), err)
assert.NotEmpty(r.T(), singleList)
assert.Equal(r.T(), total, 1)

// no match
singleList, total, err = r.tangy.RpmRepositoryVersionPackageList(context.Background(), []string{*firstVersionHref}, tangy.RpmListFilters{Name: "wal"}, tangy.PageOptions{})
require.NoError(r.T(), err)
assert.Empty(r.T(), singleList)
assert.Equal(r.T(), total, 0)
}

// RpmRepositoryVersionPackageList
func (r *RpmSuite) TestRpmRepositoryVersionPackageListNoDuplicates() {
firstVersionHref := r.firstVersionHref
secondVersionHref := r.secondVersionHref

doubleList, total, err := r.tangy.RpmRepositoryVersionPackageList(context.Background(), []string{firstVersionHref, secondVersionHref}, tangy.RpmListFilters{}, tangy.PageOptions{})
require.NoError(r.T(), err)
assert.NotEmpty(r.T(), doubleList)
assert.Equal(r.T(), total, 5)

singleList, total, err := r.tangy.RpmRepositoryVersionPackageList(context.Background(), []string{firstVersionHref}, tangy.RpmListFilters{}, tangy.PageOptions{})
require.NoError(r.T(), err)
assert.NotEmpty(r.T(), singleList)
assert.Equal(r.T(), total, 3)
}

func (r *RpmSuite) TestRpmRepositoryVersionPackageListOffsetLimit() {
firstVersionHref := r.firstVersionHref
secondVersionHref := r.secondVersionHref

list, total, err := r.tangy.RpmRepositoryVersionPackageList(context.Background(), []string{firstVersionHref, secondVersionHref}, tangy.RpmListFilters{}, tangy.PageOptions{Offset: 1, Limit: 4})
require.NoError(r.T(), err)
assert.NotEmpty(r.T(), list)
assert.Equal(r.T(), 4, len(list))
assert.Equal(r.T(), 5, total)

list, total, err = r.tangy.RpmRepositoryVersionPackageList(context.Background(), []string{firstVersionHref, secondVersionHref}, tangy.RpmListFilters{}, tangy.PageOptions{Offset: 4, Limit: 1})
require.NoError(r.T(), err)
assert.NotEmpty(r.T(), list)
assert.Equal(r.T(), 1, len(list))
assert.Equal(r.T(), 5, total)

list, total, err = r.tangy.RpmRepositoryVersionPackageList(context.Background(), []string{firstVersionHref, secondVersionHref}, tangy.RpmListFilters{}, tangy.PageOptions{Offset: 100, Limit: 100})
require.NoError(r.T(), err)
assert.Empty(r.T(), list)
assert.Equal(r.T(), 0, len(list))
assert.Equal(r.T(), 5, total)
}

func RandStringBytes(n int) string {
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
b := make([]byte, n)
Expand Down
1 change: 1 addition & 0 deletions pkg/tangy/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type Tangy interface {
RpmRepositoryVersionPackageSearch(ctx context.Context, hrefs []string, search string, limit int) ([]RpmPackageSearch, error)
RpmRepositoryVersionPackageGroupSearch(ctx context.Context, hrefs []string, search string, limit int) ([]RpmPackageGroupSearch, error)
RpmRepositoryVersionEnvironmentSearch(ctx context.Context, hrefs []string, search string, limit int) ([]RpmEnvironmentSearch, error)
RpmRepositoryVersionPackageList(ctx context.Context, hrefs []string, filterOpts RpmListFilters, pageOpts PageOptions) ([]RpmListItem, int, error)
Close()
}

Expand Down
42 changes: 42 additions & 0 deletions pkg/tangy/queries.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package tangy

import (
"fmt"
"strings"

"github.com/jackc/pgx/v5"
"golang.org/x/exp/rand"
)

// contentIdsInVersion forms a single query to fetch a list of content ids in a repository version
//
// It uses randomized query parameter names and modifies the passed in namedArgs to include the key/values for these named query parameters.
// By using randomized query parameter names, this query can be included multiple times with different repository
// versions as multiple subqueries.
func contentIdsInVersion(repoId string, versionNum int, namedArgs *pgx.NamedArgs) string {
ran := rand.Int()
repoIdName := fmt.Sprintf("%v%v", "repoName", ran)
versionNumName := fmt.Sprintf("%v%v", "versionNum", ran)
query := `
SELECT crc.content_id
FROM core_repositorycontent crc
INNER JOIN core_repositoryversion crv ON (crc.version_added_id = crv.pulp_id)
LEFT OUTER JOIN core_repositoryversion crv2 ON (crc.version_removed_id = crv2.pulp_id)
WHERE crv.repository_id = @%v AND crv.number <= @%v AND NOT (crv2.number <= @%v AND crv2.number IS NOT NULL)
`
(*namedArgs)[repoIdName] = repoId
(*namedArgs)[versionNumName] = versionNum
return fmt.Sprintf(query, repoIdName, versionNumName, versionNumName)
}

// Creates a sub query (including parenthesis) to lookup the content IDs of a list of repository versions.
//
// Takes in a pointer to Named args in order to add required named arguments for the query. Multiple queries are created
// and UNION'd together
func contentIdsInVersions(repoVerMap []ParsedRepoVersion, namedArgs *pgx.NamedArgs) string {
queries := []string{}
for _, parsed := range repoVerMap {
queries = append(queries, contentIdsInVersion(parsed.RepositoryUUID, parsed.Version, namedArgs))
}
return fmt.Sprintf("( %v ) ", strings.Join(queries, " UNION "))
}
Loading

0 comments on commit 358a433

Please sign in to comment.