1
0
mirror of https://github.com/rancher/steve.git synced 2025-04-27 02:51:10 +00:00
steve/pkg/schemaserver/parse/parse.go
Darren Shepherd 8b42d0aff8 Refactor
2020-01-30 22:37:59 -07:00

169 lines
3.4 KiB
Go

package parse
import (
"net/http"
"net/url"
"strings"
"github.com/rancher/steve/pkg/schemaserver/types"
"github.com/rancher/steve/pkg/schemaserver/urlbuilder"
)
const (
maxFormSize = 2 * 1 << 20
)
var (
allowedFormats = map[string]bool{
"html": true,
"json": true,
"yaml": true,
}
)
type ParsedURL struct {
Type string
Name string
Link string
Method string
Action string
Prefix string
SubContext map[string]string
Query url.Values
}
type URLParser func(rw http.ResponseWriter, req *http.Request, schemas *types.APISchemas) (ParsedURL, error)
type Parser func(apiOp *types.APIRequest, urlParser URLParser) error
func Parse(apiOp *types.APIRequest, urlParser URLParser) error {
var err error
if apiOp.Request == nil {
apiOp.Request, err = http.NewRequest("GET", "/", nil)
if err != nil {
return err
}
}
apiOp = types.StoreAPIContext(apiOp)
if apiOp.Method == "" {
apiOp.Method = parseMethod(apiOp.Request)
}
if apiOp.ResponseFormat == "" {
apiOp.ResponseFormat = parseResponseFormat(apiOp.Request)
}
// The response format is guaranteed to be set even in the event of an error
parsedURL, err := urlParser(apiOp.Response, apiOp.Request, apiOp.Schemas)
// wait to check error, want to set as much as possible
if apiOp.Type == "" {
apiOp.Type = parsedURL.Type
}
if apiOp.Name == "" {
apiOp.Name = parsedURL.Name
}
if apiOp.Link == "" {
apiOp.Link = parsedURL.Link
}
if apiOp.Action == "" {
apiOp.Action = parsedURL.Action
}
if apiOp.Query == nil {
apiOp.Query = parsedURL.Query
}
if apiOp.Method == "" && parsedURL.Method != "" {
apiOp.Method = parsedURL.Method
}
if apiOp.URLPrefix == "" {
apiOp.URLPrefix = parsedURL.Prefix
}
if apiOp.URLBuilder == nil {
// make error local to not override the outer error we have yet to check
var err error
apiOp.URLBuilder, err = urlbuilder.New(apiOp.Request, &urlbuilder.DefaultPathResolver{
Prefix: apiOp.URLPrefix,
}, apiOp.Schemas)
if err != nil {
return err
}
}
if err != nil {
return err
}
if apiOp.Schema == nil && apiOp.Schemas != nil {
apiOp.Schema = apiOp.Schemas.LookupSchema(apiOp.Type)
}
if apiOp.Schema != nil && apiOp.Type == "" {
apiOp.Type = apiOp.Schema.ID
}
if err := ValidateMethod(apiOp); err != nil {
return err
}
return nil
}
func parseResponseFormat(req *http.Request) string {
format := req.URL.Query().Get("_format")
if format != "" {
format = strings.TrimSpace(strings.ToLower(format))
}
/* Format specified */
if allowedFormats[format] {
return format
}
// User agent has Mozilla and browser accepts */*
if IsBrowser(req, true) {
return "html"
}
if isYaml(req) {
return "yaml"
}
return "json"
}
func isYaml(req *http.Request) bool {
return strings.Contains(req.Header.Get("Accept"), "application/yaml")
}
func parseMethod(req *http.Request) string {
method := req.URL.Query().Get("_method")
if method == "" {
method = req.Method
}
return method
}
func Body(req *http.Request) (types.APIObject, error) {
req.ParseMultipartForm(maxFormSize)
if req.MultipartForm != nil {
return valuesToBody(req.MultipartForm.Value), nil
}
if req.PostForm != nil && len(req.PostForm) > 0 {
return valuesToBody(map[string][]string(req.Form)), nil
}
return ReadBody(req)
}
func valuesToBody(input map[string][]string) types.APIObject {
result := map[string]interface{}{}
for k, v := range input {
result[k] = v
}
return toAPI(result)
}