forked from sonatype-nexus-community/gonexus
/
search.go
134 lines (111 loc) · 3.7 KB
/
search.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package nexusiq
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/url"
nexus "github.com/sonatype-nexus-community/gonexus"
)
const restSearchComponent = "api/v2/search/component"
type searchResponse struct {
Criteria criteria `json:"criteria"`
Results []SearchResult `json:"results"`
}
type criteria struct {
StageID string `json:"stageId"`
Hash string `json:"hash"`
PackageURL string `json:"packageUrl"`
ComponentIdentifier ComponentIdentifier `json:"componentIdentifier"`
}
// SearchResult describes a component found based on search criteria
type SearchResult struct {
ApplicationID string `json:"applicationId"`
ApplicationName string `json:"applicationName"`
ReportURL string `json:"reportUrl"`
Hash string `json:"hash"`
PackageURL string `json:"packageUrl"`
ComponentIdentifier ComponentIdentifier `json:"componentIdentifier"`
}
// SearchQueryBuilder allows you to build a search query
type SearchQueryBuilder struct {
criteria map[string]string
}
func (b *SearchQueryBuilder) addCriteria(c, v string) *SearchQueryBuilder {
b.criteria[c] = v
return b
}
func (b *SearchQueryBuilder) addCriteriaEncoded(c, v string) *SearchQueryBuilder {
return b.addCriteria(c, url.QueryEscape(v))
}
// Build will build the assembled search query
func (b *SearchQueryBuilder) Build() string {
var (
buf bytes.Buffer
hasStage bool
)
for k, v := range b.criteria {
if k == "stageId" {
hasStage = true
}
buf.WriteString("&")
buf.WriteString(k)
buf.WriteString("=")
buf.WriteString(v)
}
if !hasStage {
buf.WriteString("&stageId=build")
}
return buf.String()
}
// Hash allows specifiying a sha1 hash to filter by
func (b *SearchQueryBuilder) Hash(v string) *SearchQueryBuilder {
return b.addCriteria("hash", v)
}
// Format allows specifiying a format to filter by
func (b *SearchQueryBuilder) Format(v string) *SearchQueryBuilder {
return b.addCriteria("format", v)
}
// ComponentIdentifier allows specifiying a component identifier to filter by
func (b *SearchQueryBuilder) ComponentIdentifier(c ComponentIdentifier) *SearchQueryBuilder {
v, err := json.Marshal(c)
if err != nil {
return b
}
return b.addCriteriaEncoded("componentIdentifier", string(v))
}
// PackageURL allows specifiying a purl to filter by
func (b *SearchQueryBuilder) PackageURL(v string) *SearchQueryBuilder {
return b.addCriteriaEncoded("packageUrl", v)
}
// Coordinates allows specifiying component coordinates to filter by
func (b *SearchQueryBuilder) Coordinates(c Coordinates) *SearchQueryBuilder {
v, err := json.Marshal(c)
if err != nil {
return b
}
return b.addCriteriaEncoded("coordinates", string(v))
}
// Stage allows specifiying a stage to filter by
func (b *SearchQueryBuilder) Stage(v string) *SearchQueryBuilder {
return b.addCriteria("stageId", v)
}
// NewSearchQueryBuilder creates a new instance of SearchQueryBuilder
func NewSearchQueryBuilder() *SearchQueryBuilder {
b := new(SearchQueryBuilder)
b.criteria = make(map[string]string)
return b
}
// SearchComponents allows searching the indicated IQ instance for specific components
func SearchComponents(iq IQ, query nexus.SearchQueryBuilder) ([]SearchResult, error) {
endpoint := restSearchComponent + "?" + query.Build()
body, resp, err := iq.Get(endpoint)
if err != nil || resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("could not find component: %v", err)
}
var searchResp searchResponse
if err := json.Unmarshal(body, &searchResp); err != nil {
return nil, fmt.Errorf("could not process response: %v", err)
}
return searchResp.Results, nil
}