mirror of
https://github.com/rancher/steve.git
synced 2025-04-27 02:51:10 +00:00
259 lines
6.4 KiB
Go
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
|
|
}
|