Skip to content
This repository has been archived by the owner on Jun 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #753 from puppetlabs/rql
Browse files Browse the repository at this point in the history
Merge rql branch into master
  • Loading branch information
ekinanp authored Mar 9, 2020
2 parents 38226d7 + 0d15a77 commit 957a02f
Show file tree
Hide file tree
Showing 108 changed files with 9,673 additions and 24 deletions.
8 changes: 8 additions & 0 deletions api/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,14 @@ func invalidBoolParam(name, value string) *errorResponse {
)}
}

func invalidIntParam(name, value string) *errorResponse {
return &errorResponse{http.StatusBadRequest, newErrorObj(
apitypes.InvalidInt,
fmt.Sprintf("Invalid int value '%v' given for %v parameter", value, name),
apitypes.ErrorFields{"value": value},
)}
}

func invalidPathsResponse() *errorResponse {
return &errorResponse{http.StatusBadRequest, newErrorObj(
apitypes.InvalidPaths,
Expand Down
97 changes: 97 additions & 0 deletions api/find.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package api

import (
"encoding/json"
"fmt"
"io"
"net/http"

"github.com/puppetlabs/wash/activity"
"github.com/puppetlabs/wash/api/rql"
"github.com/puppetlabs/wash/api/rql/ast"
apitypes "github.com/puppetlabs/wash/api/types"
"github.com/puppetlabs/wash/plugin"
)

// swagger:route GET /fs/find find listEntries
//
// Recursively descends the given path returning it and its children.
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http
//
// Responses:
// 200: entryList
// 400: errorResp
// 404: errorResp
// 500: errorResp
var findHandler = handler{fn: func(w http.ResponseWriter, r *http.Request) *errorResponse {
ctx := r.Context()
entry, path, errResp := getEntryFromRequest(r)
if errResp != nil {
return errResp
}

if !plugin.ListAction().IsSupportedOn(entry) {
return unsupportedActionResponse(path, plugin.ListAction())
}

minDepth, hasMinDepth, errResp := getIntParam(r.URL, "mindepth")
if errResp != nil {
return errResp
}
maxDepth, hasMaxDepth, errResp := getIntParam(r.URL, "maxdepth")
if errResp != nil {
return errResp
}
fullMeta, errResp := getBoolParam(r.URL, "fullmeta")
if errResp != nil {
return errResp
}
var rawQuery interface{}
if err := json.NewDecoder(r.Body).Decode(&rawQuery); err != nil {
if err != io.EOF {
return badRequestResponse(fmt.Sprintf("could not decode the RQL query: %v", err))
}
rawQuery = true
}
query := ast.Query()
if err := query.Unmarshal(rawQuery); err != nil {
return badRequestResponse(fmt.Sprintf("could not decode the RQL query: %v", err))
}

opts := rql.NewOptions()
opts.Fullmeta = fullMeta
if hasMinDepth {
opts.Mindepth = minDepth
}
if hasMaxDepth {
opts.Maxdepth = maxDepth
}

rqlEntries, err := rql.Find(ctx, entry, query, opts)
if err != nil {
return unknownErrorResponse(err)
}

result := []apitypes.Entry{}
for _, rqlEntry := range rqlEntries {
apiEntry := rqlEntry.Entry
// Make sure all paths are absolute paths
apiEntry.Path = path + "/" + apiEntry.Path
result = append(result, apiEntry)
}

activity.Record(ctx, "API: Find %v %v items", path, len(result))

jsonEncoder := json.NewEncoder(w)
if err = jsonEncoder.Encode(result); err != nil {
return unknownErrorResponse(fmt.Errorf("Could not marshal find results for %v: %v", path, err))
}
return nil
}}
33 changes: 13 additions & 20 deletions api/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,6 @@ import (
"github.com/puppetlabs/wash/plugin"
)

func toAPIEntry(e plugin.Entry) apitypes.Entry {
return apitypes.Entry{
TypeID: plugin.TypeID(e),
Name: plugin.Name(e),
CName: plugin.CName(e),
Actions: plugin.SupportedActionsOf(e),
Attributes: plugin.Attributes(e),
Metadata: plugin.PartialMetadata(e),
}
}

func toAPIEntrySchema(s *plugin.EntrySchema) *apitypes.EntrySchema {
if s == nil {
return nil
}
return &apitypes.EntrySchema{
EntrySchema: (*s),
}
}

func findEntry(ctx context.Context, root plugin.Entry, segments []string) (plugin.Entry, *errorResponse) {
path := strings.Join(segments, "/")
curEntry, err := plugin.FindEntry(ctx, root, segments)
Expand Down Expand Up @@ -152,3 +132,16 @@ func getBoolParam(u *url.URL, key string) (bool, *errorResponse) {
}
return false, nil
}

// return is (n, found, err)
func getIntParam(u *url.URL, key string) (int, bool, *errorResponse) {
val := u.Query().Get(key)
if val != "" {
n, err := strconv.ParseInt(val, 10, 32)
if err != nil {
return 0, false, invalidIntParam(key, val)
}
return int(n), true, nil
}
return 0, false, nil
}
38 changes: 38 additions & 0 deletions api/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,44 @@ func (suite *HelpersTestSuite) TestGetBoolParam() {
suite.Error(err)
}

func (suite *HelpersTestSuite) TestGetIntParam() {
var u url.URL
// tc => testCase
type tc struct {
query string
expectedVal int
expectedFound bool
expectedErrRegex string
}
tcs := []tc{
tc{
query: "",
expectedFound: false,
},
tc{
query: "param=10",
expectedVal: 10,
expectedFound: true,
},
tc{
query: "param=foo",
expectedErrRegex: ".*int.*",
},
}
for _, tc := range tcs {
u.RawQuery = tc.query
val, found, err := getIntParam(&u, "param")
if tc.expectedErrRegex != "" {
suite.Regexp(tc.expectedErrRegex, err)
} else {
suite.Equal(tc.expectedFound, found)
if found {
suite.Equal(tc.expectedVal, val)
}
}
}
}

func TestHelpers(t *testing.T) {
suite.Run(t, new(HelpersTestSuite))
}
4 changes: 3 additions & 1 deletion api/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"encoding/json"
"fmt"
"net/http"

apitypes "github.com/puppetlabs/wash/api/types"
)

// swagger:route GET /fs/info info entryInfo
Expand All @@ -30,7 +32,7 @@ var infoHandler = handler{fn: func(w http.ResponseWriter, r *http.Request) *erro

jsonEncoder := json.NewEncoder(w)
// TODO: Include the entry's full metadata?
apiEntry := toAPIEntry(entry)
apiEntry := apitypes.NewEntry(entry)
apiEntry.Path = path
if err := jsonEncoder.Encode(&apiEntry); err != nil {
return unknownErrorResponse(fmt.Errorf("Could not marshal %v: %v", path, err))
Expand Down
2 changes: 1 addition & 1 deletion api/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ var listHandler = handler{fn: func(w http.ResponseWriter, r *http.Request) *erro

result := make([]apitypes.Entry, 0, entries.Len())
entries.Range(func(_ string, entry plugin.Entry) bool {
apiEntry := toAPIEntry(entry)
apiEntry := apitypes.NewEntry(entry)
apiEntry.Path = path + "/" + apiEntry.CName
result = append(result, apiEntry)
return true
Expand Down
Loading

0 comments on commit 957a02f

Please sign in to comment.