1
// Copyright 2014 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
4
package v5 // import "gopkg.in/juju/charmstore.v5-unstable/internal/v5"
11
"github.com/juju/utils/parallel"
13
"gopkg.in/juju/charmrepo.v2-unstable/csclient/params"
15
"gopkg.in/juju/charmstore.v5-unstable/internal/charmstore"
16
"gopkg.in/juju/charmstore.v5-unstable/internal/mongodoc"
17
"gopkg.in/juju/charmstore.v5-unstable/internal/router"
20
const maxConcurrency = 20
22
// GET search[?text=text][&autocomplete=1][&filter=value…][&limit=limit][&include=meta][&skip=count][&sort=field[+dir]]
23
// https://github.com/juju/charmstore/blob/v4/docs/API.md#get-search
24
func (h *ReqHandler) serveSearch(_ http.Header, req *http.Request) (interface{}, error) {
25
sp, err := ParseSearchParams(req)
29
auth, err := h.Authenticate(req)
31
logger.Infof("authorization failed on search request, granting no privileges: %v", err)
34
if auth.Username != "" {
35
sp.Groups = append(sp.Groups, auth.Username)
36
groups, err := h.Handler.GroupsForUser(auth.Username)
38
logger.Infof("cannot get groups for user %q, assuming no groups: %v", auth.Username, err)
40
sp.Groups = append(sp.Groups, groups...)
42
return h.Search(sp, req)
45
// Search performs the search specified by SearchParams. If sp
46
// specifies that additional metadata needs to be added to the results,
48
func (h *ReqHandler) Search(sp charmstore.SearchParams, req *http.Request) (interface{}, error) {
50
results, err := h.Store.Search(sp)
52
return nil, errgo.Notef(err, "error performing search")
54
return params.SearchResponse{
55
SearchTime: results.SearchTime,
57
Results: h.addMetaData(results.Results, sp.Include, req),
61
// addMetaData adds the requested meta data with the include list.
62
func (h *ReqHandler) addMetaData(results []*mongodoc.Entity, include []string, req *http.Request) []params.EntityResult {
63
entities := make([]params.EntityResult, len(results))
64
run := parallel.NewRun(maxConcurrency)
66
for i, ent := range results {
69
meta, err := h.Router.GetMetadata(charmstore.EntityResolvedURL(ent), include, req)
71
// Unfortunately it is possible to get errors here due to
72
// internal inconsistency, so rather than throwing away
73
// all the search results, we just log the error and move on.
74
logger.Errorf("cannot retrieve metadata for %v: %v", ent.PreferredURL(true), err)
75
atomic.AddInt32(&missing, 1)
78
entities[i] = params.EntityResult{
79
Id: ent.PreferredURL(true),
85
// We never return an error from the Do function above, so no need to
86
// check the error here.
91
// We're missing some results - shuffle all the results down to
94
for _, result := range entities {
104
// GET search/interesting[?limit=limit][&include=meta]
105
// https://github.com/juju/charmstore/blob/v4/docs/API.md#get-searchinteresting
106
func (h *ReqHandler) serveSearchInteresting(w http.ResponseWriter, req *http.Request) {
107
router.WriteError(w, errNotImplemented)
110
// ParseSearchParms extracts the search paramaters from the request
111
func ParseSearchParams(req *http.Request) (charmstore.SearchParams, error) {
112
sp := charmstore.SearchParams{}
114
for k, v := range req.Form {
119
sp.AutoComplete, err = router.ParseBool(v[0])
121
return charmstore.SearchParams{}, badRequestf(err, "invalid autocomplete parameter")
124
sp.Limit, err = strconv.Atoi(v[0])
126
return charmstore.SearchParams{}, badRequestf(err, "invalid limit parameter: could not parse integer")
129
return charmstore.SearchParams{}, badRequestf(nil, "invalid limit parameter: expected integer greater than zero")
132
for _, s := range v {
134
sp.Include = append(sp.Include, s)
137
case "description", "name", "owner", "provides", "requires", "series", "summary", "tags", "type":
138
if sp.Filters == nil {
139
sp.Filters = make(map[string][]string)
143
promulgated, err := router.ParseBool(v[0])
145
return charmstore.SearchParams{}, badRequestf(err, "invalid promulgated filter parameter")
147
if sp.Filters == nil {
148
sp.Filters = make(map[string][]string)
151
sp.Filters[k] = []string{"1"}
153
sp.Filters[k] = []string{"0"}
156
sp.Skip, err = strconv.Atoi(v[0])
158
return charmstore.SearchParams{}, badRequestf(err, "invalid skip parameter: could not parse integer")
161
return charmstore.SearchParams{}, badRequestf(nil, "invalid skip parameter: expected non-negative integer")
164
err = sp.ParseSortFields(v...)
166
return charmstore.SearchParams{}, badRequestf(err, "invalid sort field")
169
return charmstore.SearchParams{}, badRequestf(nil, "invalid parameter: %s", k)