Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add capabilities parameter in search endpoint #1054

Merged
merged 4 commits into from
Jul 14, 2023
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* Add new query parameter "capabilities" in search endpoint [#1054](https://github.com/elastic/package-registry/pull/1054)

### Deprecated

### Known Issues
Expand Down
16 changes: 15 additions & 1 deletion packages/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ type KibanaConditions struct {

// ElasticConditions defines conditions related to Elastic subscriptions or partnerships.
type ElasticConditions struct {
Subscription string `config:"subscription" json:"subscription" yaml:"subscription"`
Subscription string `config:"subscription" json:"subscription" yaml:"subscription"`
Capabilities []string `config:"capabilities,omitempty" json:"capabilities,omitempty" yaml:"capabilities,omitempty"`
}

type Version struct {
Expand Down Expand Up @@ -387,6 +388,19 @@ func (p *Package) HasKibanaVersion(version *semver.Version) bool {
return p.Conditions.Kibana.constraint.Check(version)
}

func (p *Package) WorksWithCapabilities(capabilities []string) bool {
if p.Conditions == nil || p.Conditions.Elastic == nil || p.Conditions.Elastic.Capabilities == nil || capabilities == nil {
return true
}

for _, requiredCapability := range p.Conditions.Elastic.Capabilities {
if !util.StringsContains(capabilities, requiredCapability) {
return false
}
}
return true
}

func (p *Package) IsNewerOrEqual(pp *Package) bool {
return !p.versionSemVer.LessThan(pp.versionSemVer)
}
Expand Down
13 changes: 13 additions & 0 deletions packages/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ type Filter struct {
PackageName string
PackageVersion string
PackageType string
Capabilities []string

// Deprecated, release tags to be removed.
Experimental bool
Expand Down Expand Up @@ -342,6 +343,12 @@ func (f *Filter) Apply(ctx context.Context, packages Packages) Packages {
continue
}

if f.Capabilities != nil {
if valid := p.WorksWithCapabilities(f.Capabilities); !valid {
continue
}
}

addPackage := true
if !f.AllVersions {
// Check if the version exists and if it should be added or not.
Expand Down Expand Up @@ -405,6 +412,12 @@ func (f *Filter) legacyApply(ctx context.Context, packages Packages) Packages {
continue
}

if f.Capabilities != nil {
if valid := p.WorksWithCapabilities(f.Capabilities); !valid {
continue
}
}

addPackage := true
if !f.AllVersions {
// Check if the version exists and if it should be added or not.
Expand Down
119 changes: 111 additions & 8 deletions packages/packages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,36 @@ func TestPackagesFilter(t *testing.T) {
Type: "integration",
KibanaVersion: "^8.0.0",
},
{
Name: "obs_package",
Version: "1.1.0",
Type: "integration",
Capabilities: []string{"observability"},
},
{
Name: "obs_sec_package",
Version: "1.0.0",
Type: "integration",
Capabilities: []string{"observability", "security"},
},
{
Name: "obs_sec_package",
Version: "2.0.0-rc1",
Type: "integration",
Capabilities: []string{"observability", "security"},
},
{
Name: "obs_sec_package",
Version: "2.0.0",
Type: "integration",
Capabilities: []string{"observability", "security"},
},
{
Name: "obs_sec_uptime_package",
Version: "2.0.0",
Type: "integration",
Capabilities: []string{"observability", "security", "uptime"},
},
}
packages := buildFilterTestPackages(filterTestPackages)

Expand Down Expand Up @@ -164,6 +194,9 @@ func TestPackagesFilter(t *testing.T) {
{Name: "apache", Version: "1.0.0"},
{Name: "nginx", Version: "2.0.0"},
{Name: "redisenterprise", Version: "1.0.0"},
{Name: "obs_package", Version: "1.1.0"},
{Name: "obs_sec_package", Version: "2.0.0"},
{Name: "obs_sec_uptime_package", Version: "2.0.0"},
},
},
{
Expand Down Expand Up @@ -231,6 +264,7 @@ func TestPackagesFilter(t *testing.T) {
filterTestPackage{Name: "apache", Version: "1.0.0-rc1"},
filterTestPackage{Name: "apache", Version: "2.0.0-rc2"},
filterTestPackage{Name: "redisenterprise", Version: "0.1.1"},
filterTestPackage{Name: "obs_sec_package", Version: "2.0.0-rc1"},
),
},
{
Expand Down Expand Up @@ -350,6 +384,60 @@ func TestPackagesFilter(t *testing.T) {
{Name: "etcd", Version: "1.0.0-rc2"},
},
},
{
Title: "non existing capabilities search",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Filter: Filter{
Capabilities: []string{"no_match"},
},
Expected: []filterTestPackage{
{Name: "apache", Version: "1.0.0"},
{Name: "nginx", Version: "2.0.0"},
{Name: "redisenterprise", Version: "1.0.0"},
},
},
{
Title: "observability capabilities search",
Filter: Filter{
Capabilities: []string{"observability"},
},
Expected: []filterTestPackage{
{Name: "apache", Version: "1.0.0"},
{Name: "nginx", Version: "2.0.0"},
{Name: "redisenterprise", Version: "1.0.0"},
{Name: "obs_package", Version: "1.1.0"},
},
},
{
Title: "observability and security capabilities search",
Filter: Filter{
Capabilities: []string{"observability", "security"},
},
Expected: []filterTestPackage{
{Name: "apache", Version: "1.0.0"},
{Name: "nginx", Version: "2.0.0"},
{Name: "redisenterprise", Version: "1.0.0"},
{Name: "obs_package", Version: "1.1.0"},
{Name: "obs_sec_package", Version: "2.0.0"},
},
},
{
Title: "observability, security and uptime capabilities search - legacy kibana",
Filter: Filter{
Experimental: true,
Capabilities: []string{"observability", "security", "uptime"},
},
Expected: []filterTestPackage{
{Name: "apache", Version: "1.0.0"},
{Name: "nginx", Version: "2.0.0"},
{Name: "mysql", Version: "0.9.0"},
{Name: "logstash", Version: "1.1.0"},
{Name: "etcd", Version: "1.0.0-rc2"},
{Name: "redisenterprise", Version: "1.0.0"},
{Name: "obs_package", Version: "1.1.0"},
{Name: "obs_sec_package", Version: "2.0.0"},
{Name: "obs_sec_uptime_package", Version: "2.0.0"},
},
},
}

for _, c := range cases {
Expand All @@ -366,6 +454,7 @@ type filterTestPackage struct {
Release string
Type string
KibanaVersion string
Capabilities []string
}

func (p filterTestPackage) Build() *Package {
Expand All @@ -377,15 +466,29 @@ func (p filterTestPackage) Build() *Package {
build.Release = p.Release
build.Type = p.Type

constraints, err := semver.NewConstraint(p.KibanaVersion)
if err != nil {
panic(err)
if p.KibanaVersion != "" {
constraints, err := semver.NewConstraint(p.KibanaVersion)
if err != nil {
panic(err)
}
build.Conditions = &Conditions{
Kibana: &KibanaConditions{
Version: p.KibanaVersion,
constraint: constraints,
},
}
}
build.Conditions = &Conditions{
Kibana: &KibanaConditions{
Version: p.KibanaVersion,
constraint: constraints,
},
if p.Capabilities != nil {
elasticConditions := ElasticConditions{
Capabilities: p.Capabilities,
}
if build.Conditions != nil {
build.Conditions.Elastic = &elasticConditions
} else {
build.Conditions = &Conditions{
Elastic: &elasticConditions,
}
}
}
return &build
}
Expand Down
5 changes: 5 additions & 0 deletions search.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"net/url"
"sort"
"strconv"
"strings"
"time"

"github.com/Masterminds/semver/v3"
Expand Down Expand Up @@ -99,6 +100,10 @@ func newSearchFilterFromQuery(query url.Values) (*packages.Filter, error) {
filter.PackageType = v
}

if v := query.Get("capabilities"); v != "" {
filter.Capabilities = strings.Split(v, ",")
}

if v := query.Get("all"); v != "" {
// Default is false, also on error
filter.AllVersions, err = strconv.ParseBool(v)
Expand Down