feat: add catalog delete (#1377)

This commit is contained in:
Christopher Angelo Phillips 2022-12-12 12:55:12 -05:00 committed by GitHub
parent 17aa8287e6
commit 23a3173c9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 225 additions and 19 deletions

View File

@ -26,6 +26,15 @@ loopNewIDs:
}
}
func (s *orderedIDSet) delete(id artifact.ID) {
for i, existingID := range s.slice {
if existingID == id {
s.slice = append(s.slice[:i], s.slice[i+1:]...)
return
}
}
}
// Catalog represents a collection of Packages.
type Catalog struct {
byID map[artifact.ID]Package
@ -92,11 +101,12 @@ func (c *Catalog) Packages(ids []artifact.ID) (result []Package) {
return result
}
// Add a package to the Catalog.
func (c *Catalog) Add(p Package) {
// Add n packages to the catalog.
func (c *Catalog) Add(pkgs ...Package) {
c.lock.Lock()
defer c.lock.Unlock()
for _, p := range pkgs {
id := p.ID()
if id == "" {
log.Warnf("found package with empty ID while adding to the catalog: %+v", p)
@ -117,6 +127,7 @@ func (c *Catalog) Add(p Package) {
c.addToIndex(p)
}
}
func (c *Catalog) addToIndex(p Package) {
c.byID[p.id] = p
@ -157,6 +168,59 @@ func (c *Catalog) addPathToIndex(id artifact.ID, path string) {
c.idsByPath[path] = pathIndex
}
func (c *Catalog) Delete(ids ...artifact.ID) {
c.lock.Lock()
defer c.lock.Unlock()
for _, id := range ids {
p, exists := c.byID[id]
if !exists {
return
}
delete(c.byID, id)
c.deleteNameFromIndex(p)
c.deleteTypeFromIndex(p)
c.deletePathsFromIndex(p)
}
}
func (c *Catalog) deleteNameFromIndex(p Package) {
nameIndex := c.idsByName[p.Name]
nameIndex.delete(p.id)
c.idsByName[p.Name] = nameIndex
}
func (c *Catalog) deleteTypeFromIndex(p Package) {
typeIndex := c.idsByType[p.Type]
typeIndex.delete(p.id)
c.idsByType[p.Type] = typeIndex
}
func (c *Catalog) deletePathsFromIndex(p Package) {
observedPaths := internal.NewStringSet()
for _, l := range p.Locations.ToSlice() {
if l.RealPath != "" && !observedPaths.Contains(l.RealPath) {
c.deletePathFromIndex(p.id, l.RealPath)
observedPaths.Add(l.RealPath)
}
if l.VirtualPath != "" && l.RealPath != l.VirtualPath && !observedPaths.Contains(l.VirtualPath) {
c.deletePathFromIndex(p.id, l.VirtualPath)
observedPaths.Add(l.VirtualPath)
}
}
}
func (c *Catalog) deletePathFromIndex(id artifact.ID, path string) {
pathIndex := c.idsByPath[path]
pathIndex.delete(id)
if len(pathIndex.slice) == 0 {
delete(c.idsByPath, path)
} else {
c.idsByPath[path] = pathIndex
}
}
// Enumerate all packages for the given type(s), enumerating all packages if no type is specified.
func (c *Catalog) Enumerate(types ...Type) <-chan Package {
channel := make(chan Package)

View File

@ -16,6 +16,148 @@ type expectedIndexes struct {
byPath map[string]*strset.Set
}
func TestCatalogDeleteRemovesPackages(t *testing.T) {
tests := []struct {
name string
pkgs []Package
deleteIDs []artifact.ID
expectedIndexes expectedIndexes
}{
{
name: "delete one package",
pkgs: []Package{
{
id: "pkg:deb/debian/1",
Name: "debian",
Version: "1",
Type: DebPkg,
Locations: source.NewLocationSet(
source.NewVirtualLocation("/c/path", "/another/path1"),
),
},
{
id: "pkg:deb/debian/2",
Name: "debian",
Version: "2",
Type: DebPkg,
Locations: source.NewLocationSet(
source.NewVirtualLocation("/d/path", "/another/path2"),
),
},
},
deleteIDs: []artifact.ID{
artifact.ID("pkg:deb/debian/1"),
},
expectedIndexes: expectedIndexes{
byType: map[Type]*strset.Set{
DebPkg: strset.New("pkg:deb/debian/2"),
},
byPath: map[string]*strset.Set{
"/d/path": strset.New("pkg:deb/debian/2"),
"/another/path2": strset.New("pkg:deb/debian/2"),
},
},
},
{
name: "delete multiple packages",
pkgs: []Package{
{
id: "pkg:deb/debian/1",
Name: "debian",
Version: "1",
Type: DebPkg,
Locations: source.NewLocationSet(
source.NewVirtualLocation("/c/path", "/another/path1"),
),
},
{
id: "pkg:deb/debian/2",
Name: "debian",
Version: "2",
Type: DebPkg,
Locations: source.NewLocationSet(
source.NewVirtualLocation("/d/path", "/another/path2"),
),
},
{
id: "pkg:deb/debian/3",
Name: "debian",
Version: "3",
Type: DebPkg,
Locations: source.NewLocationSet(
source.NewVirtualLocation("/e/path", "/another/path3"),
),
},
},
deleteIDs: []artifact.ID{
artifact.ID("pkg:deb/debian/1"),
artifact.ID("pkg:deb/debian/3"),
},
expectedIndexes: expectedIndexes{
byType: map[Type]*strset.Set{
DebPkg: strset.New("pkg:deb/debian/2"),
},
byPath: map[string]*strset.Set{
"/d/path": strset.New("pkg:deb/debian/2"),
"/another/path2": strset.New("pkg:deb/debian/2"),
},
},
},
{
name: "delete non-existent package",
pkgs: []Package{
{
id: artifact.ID("pkg:deb/debian/1"),
Name: "debian",
Version: "1",
Type: DebPkg,
Locations: source.NewLocationSet(
source.NewVirtualLocation("/c/path", "/another/path1"),
),
},
{
id: artifact.ID("pkg:deb/debian/2"),
Name: "debian",
Version: "2",
Type: DebPkg,
Locations: source.NewLocationSet(
source.NewVirtualLocation("/d/path", "/another/path2"),
),
},
},
deleteIDs: []artifact.ID{
artifact.ID("pkg:deb/debian/3"),
},
expectedIndexes: expectedIndexes{
byType: map[Type]*strset.Set{
DebPkg: strset.New("pkg:deb/debian/1", "pkg:deb/debian/2"),
},
byPath: map[string]*strset.Set{
"/c/path": strset.New("pkg:deb/debian/1"),
"/another/path1": strset.New("pkg:deb/debian/1"),
"/d/path": strset.New("pkg:deb/debian/2"),
"/another/path2": strset.New("pkg:deb/debian/2"),
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
c := NewCatalog()
for _, p := range test.pkgs {
c.Add(p)
}
for _, id := range test.deleteIDs {
c.Delete(id)
}
assertIndexes(t, c, test.expectedIndexes)
})
}
}
func TestCatalogAddPopulatesIndex(t *testing.T) {
var pkgs = []Package{