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

259 lines
6.4 KiB
Go

package server
import (
"net/http"
"github.com/rancher/steve/pkg/schemaserver/handlers"
"github.com/rancher/steve/pkg/schemaserver/parse"
"github.com/rancher/steve/pkg/schemaserver/types"
"github.com/rancher/steve/pkg/schemaserver/writer"
"github.com/rancher/wrangler/pkg/merr"
"github.com/rancher/wrangler/pkg/schemas/validation"
)
type RequestHandler interface {
http.Handler
GetSchemas() *types.APISchemas
Handle(apiOp *types.APIRequest)
}
type Server struct {
ResponseWriters map[string]types.ResponseWriter
Schemas *types.APISchemas
Defaults Defaults
AccessControl types.AccessControl
Parser parse.Parser
URLParser parse.URLParser
}
type Defaults struct {
ByIDHandler types.RequestHandler
ListHandler types.RequestListHandler
CreateHandler types.RequestHandler
DeleteHandler types.RequestHandler
UpdateHandler types.RequestHandler
Store types.Store
ErrorHandler types.ErrorHandler
}
func DefaultAPIServer() *Server {
s := &Server{
Schemas: types.EmptyAPISchemas(),
ResponseWriters: map[string]types.ResponseWriter{
"json": &writer.EncodingResponseWriter{
ContentType: "application/json",
Encoder: types.JSONEncoder,
},
"html": &writer.HTMLResponseWriter{
EncodingResponseWriter: writer.EncodingResponseWriter{
Encoder: types.JSONEncoder,
ContentType: "application/json",
},
},
"yaml": &writer.EncodingResponseWriter{
ContentType: "application/yaml",
Encoder: types.YAMLEncoder,
},
},
AccessControl: &AllAccess{},
Defaults: Defaults{
ByIDHandler: handlers.ByIDHandler,
CreateHandler: handlers.CreateHandler,
DeleteHandler: handlers.DeleteHandler,
UpdateHandler: handlers.UpdateHandler,
ListHandler: handlers.ListHandler,
ErrorHandler: handlers.ErrorHandler,
},
Parser: parse.Parse,
URLParser: parse.MuxURLParser,
}
return s
}
func (s *Server) setDefaults(ctx *types.APIRequest) {
if ctx.ResponseWriter == nil {
ctx.ResponseWriter = s.ResponseWriters[ctx.ResponseFormat]
if ctx.ResponseWriter == nil {
ctx.ResponseWriter = s.ResponseWriters["json"]
}
}
ctx.AccessControl = s.AccessControl
if ctx.Schemas == nil {
ctx.Schemas = s.Schemas
}
}
func (s *Server) AddSchemas(schemas *types.APISchemas) error {
var errs []error
for _, schema := range schemas.Schemas {
if err := s.addSchema(*schema); err != nil {
errs = append(errs, err)
}
}
return merr.NewErrors(errs...)
}
func (s *Server) addSchema(schema types.APISchema) error {
s.setupDefaults(&schema)
return s.Schemas.AddSchema(schema)
}
func (s *Server) setupDefaults(schema *types.APISchema) {
if schema.Store == nil {
schema.Store = s.Defaults.Store
}
if schema.ListHandler == nil {
schema.ListHandler = s.Defaults.ListHandler
}
if schema.CreateHandler == nil {
schema.CreateHandler = s.Defaults.CreateHandler
}
if schema.ByIDHandler == nil {
schema.ByIDHandler = s.Defaults.ByIDHandler
}
if schema.UpdateHandler == nil {
schema.UpdateHandler = s.Defaults.UpdateHandler
}
if schema.DeleteHandler == nil {
schema.DeleteHandler = s.Defaults.DeleteHandler
}
if schema.ErrorHandler == nil {
schema.ErrorHandler = s.Defaults.ErrorHandler
}
}
func (s *Server) GetSchemas() *types.APISchemas {
return s.Schemas
}
func (s *Server) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
s.Handle(&types.APIRequest{
Request: req,
Response: rw,
})
}
func (s *Server) Handle(apiOp *types.APIRequest) {
s.handle(apiOp, s.Parser)
}
func (s *Server) handle(apiOp *types.APIRequest, parser parse.Parser) {
if err := parser(apiOp, parse.MuxURLParser); err != nil {
// ensure defaults set so writer is assigned
s.setDefaults(apiOp)
s.handleError(apiOp, err)
return
}
s.setDefaults(apiOp)
if code, data, err := s.handleOp(apiOp); err != nil {
s.handleError(apiOp, err)
} else if obj, ok := data.(types.APIObject); ok {
apiOp.WriteResponse(code, obj)
} else if list, ok := data.(types.APIObjectList); ok {
apiOp.WriteResponseList(code, list)
}
}
func (s *Server) handleOp(apiOp *types.APIRequest) (int, interface{}, error) {
if err := CheckCSRF(apiOp); err != nil {
return 0, nil, err
}
action, err := ValidateAction(apiOp)
if err != nil {
return 0, nil, err
}
if apiOp.Schema == nil {
return http.StatusNotFound, nil, nil
}
if action != nil {
return http.StatusOK, nil, handleAction(apiOp)
}
switch apiOp.Method {
case http.MethodGet:
if apiOp.Name == "" {
data, err := handleList(apiOp, apiOp.Schema.ListHandler, s.Defaults.ListHandler)
return http.StatusOK, data, err
}
data, err := handle(apiOp, apiOp.Schema.ByIDHandler, s.Defaults.ByIDHandler)
return http.StatusOK, data, err
case http.MethodPatch:
fallthrough
case http.MethodPut:
data, err := handle(apiOp, apiOp.Schema.UpdateHandler, s.Defaults.UpdateHandler)
return http.StatusOK, data, err
case http.MethodPost:
data, err := handle(apiOp, apiOp.Schema.CreateHandler, s.Defaults.CreateHandler)
return http.StatusCreated, data, err
case http.MethodDelete:
data, err := handle(apiOp, apiOp.Schema.DeleteHandler, s.Defaults.DeleteHandler)
return http.StatusOK, data, err
}
return http.StatusNotFound, nil, nil
}
func handleList(apiOp *types.APIRequest, custom types.RequestListHandler, handler types.RequestListHandler) (types.APIObjectList, error) {
if custom != nil {
return custom(apiOp)
}
return handler(apiOp)
}
func handle(apiOp *types.APIRequest, custom types.RequestHandler, handler types.RequestHandler) (types.APIObject, error) {
if custom != nil {
return custom(apiOp)
}
return handler(apiOp)
}
func handleAction(context *types.APIRequest) error {
if err := context.AccessControl.CanAction(context, context.Schema, context.Action); err != nil {
return err
}
if handler, ok := context.Schema.ActionHandlers[context.Action]; ok {
handler.ServeHTTP(context.Response, context.Request)
return validation.ErrComplete
}
return nil
}
func (s *Server) handleError(apiOp *types.APIRequest, err error) {
if apiOp.Schema != nil && apiOp.Schema.ErrorHandler != nil {
apiOp.Schema.ErrorHandler(apiOp, err)
} else if s.Defaults.ErrorHandler != nil {
s.Defaults.ErrorHandler(apiOp, err)
}
}
func (s *Server) CustomAPIUIResponseWriter(cssURL, jsURL, version writer.StringGetter) {
wi, ok := s.ResponseWriters["html"]
if !ok {
return
}
w, ok := wi.(*writer.HTMLResponseWriter)
if !ok {
return
}
w.CSSURL = cssURL
w.JSURL = jsURL
w.APIUIVersion = version
}