diff --git a/api/builtin/api_root.go b/api/builtin/api_root.go new file mode 100644 index 000000000..7ff1b366d --- /dev/null +++ b/api/builtin/api_root.go @@ -0,0 +1,123 @@ +package builtin + +import ( + "github.com/rancher/norman/store/empty" + "github.com/rancher/norman/types" + "github.com/rancher/norman/types/convert" +) + +func APIRootFormatter(apiContext *types.APIContext, resource *types.RawResource) { + path, _ := resource.Values["path"].(string) + if path == "" { + return + } + + delete(resource.Values, "path") + + resource.Links["root"] = apiContext.URLBuilder.RelativeToRoot(path) + + data, _ := resource.Values["apiVersion"].(map[string]interface{}) + apiVersion := apiVersionFromMap(apiContext.Schemas, data) + + resource.Links["self"] = apiContext.URLBuilder.Version(apiVersion) + + if len(apiVersion.SubContexts) > 0 { + subContextToSchema := apiContext.Schemas.SubContextSchemas() + if len(subContextToSchema) > 0 { + for _, schema := range subContextToSchema { + addCollectionLink(apiContext, schema, resource.Links) + } + + for _, schema := range getNonReferencedSchemas(apiContext.Schemas.SchemasForVersion(apiVersion), + subContextToSchema) { + addCollectionLink(apiContext, schema, resource.Links) + } + + return + } + } + + for _, schema := range apiContext.Schemas.SchemasForVersion(apiVersion) { + addCollectionLink(apiContext, schema, resource.Links) + } + + return +} + +func getNonReferencedSchemas(schemas map[string]*types.Schema, subContexts map[string]*types.Schema) []*types.Schema { + var result []*types.Schema + typeNames := map[string]bool{} + + for _, subContext := range subContexts { + ref := convert.ToReference(subContext.ID) + fullRef := convert.ToFullReference(subContext.Version.Path, subContext.ID) + typeNames[ref] = true + typeNames[fullRef] = true + } + +outer: + for _, schema := range schemas { + for _, field := range schema.ResourceFields { + if typeNames[field.Type] { + continue outer + } + } + + result = append(result, schema) + } + + return result +} + +func addCollectionLink(apiContext *types.APIContext, schema *types.Schema, links map[string]string) { + collectionLink := getSchemaCollectionLink(apiContext, schema, nil) + if collectionLink != "" { + links[schema.PluralName] = collectionLink + } +} + +type APIRootStore struct { + empty.Store + roots []string +} + +func NewAPIRootStore(roots []string) types.Store { + return &APIRootStore{roots: roots} +} + +func (a *APIRootStore) ByID(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) { + for _, version := range apiContext.Schemas.Versions() { + if version.Path == id { + return apiVersionToAPIRootMap(version), nil + } + } + return nil, nil +} + +func (a *APIRootStore) List(apiContext *types.APIContext, schema *types.Schema, opt types.QueryOptions) ([]map[string]interface{}, error) { + var roots []map[string]interface{} + + for _, version := range apiContext.Schemas.Versions() { + roots = append(roots, apiVersionToAPIRootMap(version)) + } + + for _, root := range a.roots { + roots = append(roots, map[string]interface{}{ + "path": root, + }) + } + + return roots, nil +} + +func apiVersionToAPIRootMap(version types.APIVersion) map[string]interface{} { + return map[string]interface{}{ + "type": "/v1-meta/schemas/apiRoot", + "apiVersion": map[string]interface{}{ + "version": version.Version, + "group": version.Group, + "path": version.Path, + }, + "path": version.Path, + } +} diff --git a/api/builtin/builtin.go b/api/builtin/schema.go similarity index 53% rename from api/builtin/builtin.go rename to api/builtin/schema.go index b3cfe3d25..10bc37fb0 100644 --- a/api/builtin/builtin.go +++ b/api/builtin/schema.go @@ -3,16 +3,15 @@ package builtin import ( "net/http" - "github.com/rancher/norman/store/empty" "github.com/rancher/norman/store/schema" "github.com/rancher/norman/types" ) var ( Version = types.APIVersion{ - Group: "io.cattle.builtin", - Version: "v3", - Path: "/v3", + Group: "meta.cattle.io", + Version: "v1", + Path: "/v1-meta", } Schema = types.Schema{ @@ -23,7 +22,7 @@ var ( ResourceFields: map[string]types.Field{ "collectionActions": {Type: "map[json]"}, "collectionFields": {Type: "map[json]"}, - "collectionFitlers": {Type: "map[json]"}, + "collectionFilters": {Type: "map[json]"}, "collectionMethods": {Type: "array[string]"}, "pluralName": {Type: "string"}, "resourceActions": {Type: "map[json]"}, @@ -41,10 +40,11 @@ var ( ResourceMethods: []string{}, CollectionMethods: []string{}, ResourceFields: map[string]types.Field{ - "code": {Type: "string"}, - "detail": {Type: "string"}, - "message": {Type: "string"}, - "status": {Type: "int"}, + "code": {Type: "string"}, + "detail": {Type: "string", Nullable: true}, + "message": {Type: "string", Nullable: true}, + "fieldName": {Type: "string", Nullable: true}, + "status": {Type: "int"}, }, } @@ -81,32 +81,46 @@ var ( AddSchema(&APIRoot) ) -func apiVersionFromMap(apiVersion map[string]interface{}) types.APIVersion { +func apiVersionFromMap(schemas *types.Schemas, apiVersion map[string]interface{}) types.APIVersion { path, _ := apiVersion["path"].(string) version, _ := apiVersion["version"].(string) group, _ := apiVersion["group"].(string) - return types.APIVersion{ + apiVersionObj := types.APIVersion{ Path: path, Version: version, Group: group, } + + for _, testVersion := range schemas.Versions() { + if testVersion.Equals(&apiVersionObj) { + return testVersion + } + } + + return apiVersionObj } func SchemaFormatter(apiContext *types.APIContext, resource *types.RawResource) { data, _ := resource.Values["version"].(map[string]interface{}) - apiVersion := apiVersionFromMap(data) + apiVersion := apiVersionFromMap(apiContext.Schemas, data) schema := apiContext.Schemas.Schema(&apiVersion, resource.ID) - collectionLink := getSchemaCollectionLink(apiContext, schema) + if schema == nil { + return + } + + collectionLink := getSchemaCollectionLink(apiContext, schema, &apiVersion) if collectionLink != "" { resource.Links["collection"] = collectionLink } + + resource.Links["self"] = apiContext.URLBuilder.SchemaLink(schema) } -func getSchemaCollectionLink(apiContext *types.APIContext, schema *types.Schema) string { +func getSchemaCollectionLink(apiContext *types.APIContext, schema *types.Schema, apiVersion *types.APIVersion) string { if schema != nil && contains(schema.CollectionMethods, http.MethodGet) { - return apiContext.URLBuilder.Collection(schema) + return apiContext.URLBuilder.Collection(schema, apiVersion) } return "" } @@ -119,68 +133,3 @@ func contains(list []string, needle string) bool { } return false } - -func APIRootFormatter(apiContext *types.APIContext, resource *types.RawResource) { - path, _ := resource.Values["path"].(string) - if path == "" { - return - } - - resource.Links["root"] = apiContext.URLBuilder.RelativeToRoot(path) - - data, _ := resource.Values["apiVersion"].(map[string]interface{}) - apiVersion := apiVersionFromMap(data) - - for name, schema := range apiContext.Schemas.SchemasForVersion(apiVersion) { - collectionLink := getSchemaCollectionLink(apiContext, schema) - if collectionLink != "" { - resource.Links[name] = collectionLink - } - } -} - -type APIRootStore struct { - empty.Store - roots []string -} - -func NewAPIRootStore(roots []string) types.Store { - return &APIRootStore{roots: roots} -} - -func (a *APIRootStore) ByID(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) { - for _, version := range apiContext.Schemas.Versions() { - if version.Path == id { - return apiVersionToAPIRootMap(version), nil - } - } - return nil, nil -} - -func (a *APIRootStore) List(apiContext *types.APIContext, schema *types.Schema, opt *types.QueryOptions) ([]map[string]interface{}, error) { - roots := []map[string]interface{}{} - - for _, version := range apiContext.Schemas.Versions() { - roots = append(roots, apiVersionToAPIRootMap(version)) - } - - for _, root := range a.roots { - roots = append(roots, map[string]interface{}{ - "path": root, - }) - } - - return roots, nil -} - -func apiVersionToAPIRootMap(version types.APIVersion) map[string]interface{} { - return map[string]interface{}{ - "type": "/v3/apiRoot", - "apiVersion": map[string]interface{}{ - "version": version.Version, - "group": version.Group, - "path": version.Path, - }, - "path": version.Path, - } -} diff --git a/api/formatter/subcontext_filter.go b/api/formatter/subcontext_filter.go new file mode 100644 index 000000000..1a1c0ee1d --- /dev/null +++ b/api/formatter/subcontext_filter.go @@ -0,0 +1,25 @@ +package formatter + +import ( + "github.com/rancher/norman/types" + "github.com/rancher/norman/types/convert" +) + +func SubContextFormatter(apiContext *types.APIContext, resource *types.RawResource) { + if resource.Schema.SubContext == "" { + return + } + + ref := convert.ToReference(resource.Schema.ID) + fullRef := convert.ToFullReference(resource.Schema.Version.Path, resource.Schema.ID) + +outer: + for _, schema := range apiContext.Schemas.Schemas() { + for _, field := range schema.ResourceFields { + if (field.Type == ref || field.Type == fullRef) && schema.Version.SubContexts[resource.Schema.SubContext] { + resource.Links[schema.PluralName] = apiContext.URLBuilder.SubContextCollection(resource.Schema, resource.ID, schema) + continue outer + } + } + } +} diff --git a/api/handler/create.go b/api/handler/create.go new file mode 100644 index 000000000..8f3f1249d --- /dev/null +++ b/api/handler/create.go @@ -0,0 +1,27 @@ +package handler + +import ( + "net/http" + + "github.com/rancher/norman/types" +) + +func CreateHandler(apiContext *types.APIContext) error { + var err error + + data, err := ParseAndValidateBody(apiContext) + if err != nil { + return err + } + + store := apiContext.Schema.Store + if store != nil { + data, err = store.Create(apiContext, apiContext.Schema, data) + if err != nil { + return err + } + } + + apiContext.WriteResponse(http.StatusCreated, data) + return nil +} diff --git a/api/handlers/delete.go b/api/handler/delete.go similarity index 94% rename from api/handlers/delete.go rename to api/handler/delete.go index b6718bf27..348ea5e64 100644 --- a/api/handlers/delete.go +++ b/api/handler/delete.go @@ -1,4 +1,4 @@ -package handlers +package handler import ( "net/http" diff --git a/api/handlers/list.go b/api/handler/list.go similarity index 76% rename from api/handlers/list.go rename to api/handler/list.go index 714a31059..3f664eefa 100644 --- a/api/handlers/list.go +++ b/api/handler/list.go @@ -1,4 +1,4 @@ -package handlers +package handler import ( "net/http" @@ -19,8 +19,8 @@ func ListHandler(request *types.APIContext) error { } if request.ID == "" { - request.QueryOptions = parse.QueryOptions(request.Request, request.Schema) - data, err = store.List(request, request.Schema, request.QueryOptions) + opts := parse.QueryOptions(request, request.Schema) + data, err = store.List(request, request.Schema, opts) } else if request.Link == "" { data, err = store.ByID(request, request.Schema, request.ID) } else { diff --git a/api/handler/query.go b/api/handler/query.go new file mode 100644 index 000000000..e199a8838 --- /dev/null +++ b/api/handler/query.go @@ -0,0 +1,121 @@ +package handler + +import ( + "sort" + + "github.com/rancher/norman/types" + "github.com/rancher/norman/types/convert" +) + +func QueryFilter(opts types.QueryOptions, data []map[string]interface{}) []map[string]interface{} { + return ApplyQueryOptions(opts, data) +} + +func ApplyQueryOptions(options types.QueryOptions, data []map[string]interface{}) []map[string]interface{} { + data = ApplyQueryConditions(options.Conditions, data) + data = ApplySort(options.Sort, data) + return ApplyPagination(options.Pagination, data) +} + +func ApplySort(sortOpts types.Sort, data []map[string]interface{}) []map[string]interface{} { + name := sortOpts.Name + if name == "" { + name = "id" + } + + sort.Slice(data, func(i, j int) bool { + left, right := i, j + if sortOpts.Order == types.DESC { + left, right = j, i + } + + return convert.ToString(data[left][name]) < convert.ToString(data[right][name]) + }) + + return data +} + +func ApplyQueryConditions(conditions []*types.QueryCondition, data []map[string]interface{}) []map[string]interface{} { + var result []map[string]interface{} + +outer: + for _, item := range data { + for _, condition := range conditions { + if !condition.Valid(item) { + continue outer + } + } + + result = append(result, item) + } + + return result +} + +func ApplyPagination(pagination *types.Pagination, data []map[string]interface{}) []map[string]interface{} { + if pagination == nil || pagination.Limit == nil { + return data + } + + limit := *pagination.Limit + if limit < 0 { + limit = 0 + } + + total := int64(len(data)) + + // Reset fields + pagination.Next = "" + pagination.Previous = "" + pagination.Partial = false + pagination.Total = &total + pagination.First = "" + + if len(data) == 0 { + return data + } + + // startIndex is guaranteed to be a valid index + startIndex := int64(0) + if pagination.Marker != "" { + for i, item := range data { + id, _ := item["id"].(string) + if id == pagination.Marker { + startIndex = int64(i) + break + } + } + } + + previousIndex := startIndex - limit + if previousIndex <= 0 { + previousIndex = 0 + } + nextIndex := startIndex + limit + if nextIndex > int64(len(data)) { + nextIndex = int64(len(data)) + } + + if previousIndex < startIndex { + pagination.Previous, _ = data[previousIndex]["id"].(string) + } + + if nextIndex > startIndex && nextIndex < int64(len(data)) { + pagination.Next, _ = data[nextIndex]["id"].(string) + } + + if startIndex > 0 || nextIndex < int64(len(data)) { + pagination.Partial = true + } + + if pagination.Partial { + pagination.First, _ = data[0]["id"].(string) + + lastIndex := int64(len(data)) - limit + if lastIndex > 0 && lastIndex < int64(len(data)) { + pagination.Last, _ = data[lastIndex]["id"].(string) + } + } + + return data[startIndex:nextIndex] +} diff --git a/api/handler/update.go b/api/handler/update.go new file mode 100644 index 000000000..47e584a31 --- /dev/null +++ b/api/handler/update.go @@ -0,0 +1,25 @@ +package handler + +import ( + "net/http" + + "github.com/rancher/norman/types" +) + +func UpdateHandler(apiContext *types.APIContext) error { + data, err := ParseAndValidateBody(apiContext) + if err != nil { + return err + } + + store := apiContext.Schema.Store + if store != nil { + data, err = store.Update(apiContext, apiContext.Schema, data, apiContext.ID) + if err != nil { + return err + } + } + + apiContext.WriteResponse(http.StatusOK, data) + return nil +} diff --git a/api/handler/validate.go b/api/handler/validate.go new file mode 100644 index 000000000..c83b87b60 --- /dev/null +++ b/api/handler/validate.go @@ -0,0 +1,26 @@ +package handler + +import ( + "github.com/rancher/norman/parse" + "github.com/rancher/norman/parse/builder" + "github.com/rancher/norman/types" +) + +func ParseAndValidateBody(apiContext *types.APIContext) (map[string]interface{}, error) { + data, err := parse.Body(apiContext.Request) + if err != nil { + return nil, err + } + + b := builder.NewBuilder(apiContext) + + data, err = b.Construct(apiContext.Schema, data, builder.Create) + validator := apiContext.Schema.Validator + if validator != nil { + if err := validator(apiContext, data); err != nil { + return nil, err + } + } + + return data, nil +} diff --git a/api/handlers/create.go b/api/handlers/create.go deleted file mode 100644 index 68384d313..000000000 --- a/api/handlers/create.go +++ /dev/null @@ -1,31 +0,0 @@ -package handlers - -import ( - "net/http" - - "github.com/rancher/norman/types" -) - -func CreateHandler(request *types.APIContext) error { - var err error - - validator := request.Schema.Validator - if validator != nil { - if err := validator(request, request.Body); err != nil { - return err - } - } - - data := request.Body - - store := request.Schema.Store - if store != nil { - data, err = store.Create(request, request.Schema, data) - if err != nil { - return err - } - } - - request.WriteResponse(http.StatusCreated, data) - return nil -} diff --git a/api/handlers/update.go b/api/handlers/update.go deleted file mode 100644 index 94a5978ec..000000000 --- a/api/handlers/update.go +++ /dev/null @@ -1,30 +0,0 @@ -package handlers - -import ( - "net/http" - - "github.com/rancher/norman/types" -) - -func UpdateHandler(request *types.APIContext) error { - var err error - - validator := request.Schema.Validator - if validator != nil { - if err := validator(request, request.Body); err != nil { - return err - } - } - - data := request.Body - store := request.Schema.Store - if store != nil { - data, err = store.Update(request, request.Schema, data, request.ID) - if err != nil { - return err - } - } - - request.WriteResponse(http.StatusOK, data) - return nil -} diff --git a/api/headers.go b/api/headers.go index fa253e891..ed49c169c 100644 --- a/api/headers.go +++ b/api/headers.go @@ -16,7 +16,7 @@ func addSchemasHeader(apiContext *types.APIContext) error { return nil } - apiContext.Response.Header().Set("X-Api-Schemas", apiContext.URLBuilder.Collection(schema)) + apiContext.Response.Header().Set("X-Api-Schemas", apiContext.URLBuilder.Collection(schema, apiContext.Version)) return nil } diff --git a/api/server.go b/api/server.go index 909cfdfe1..130e97fb0 100644 --- a/api/server.go +++ b/api/server.go @@ -1,27 +1,35 @@ package api import ( - "context" "net/http" + "sync" + "github.com/rancher/norman/api/builtin" - "github.com/rancher/norman/api/handlers" + "github.com/rancher/norman/api/handler" "github.com/rancher/norman/api/writer" "github.com/rancher/norman/authorization" "github.com/rancher/norman/httperror" "github.com/rancher/norman/parse" - "github.com/rancher/norman/parse/builder" + "github.com/rancher/norman/store/wrapper" "github.com/rancher/norman/types" ) +type StoreWrapper func(types.Store) types.Store + type Parser func(rw http.ResponseWriter, req *http.Request) (*types.APIContext, error) type Server struct { - IgnoreBuiltin bool - Parser Parser - ResponseWriters map[string]ResponseWriter - schemas *types.Schemas - Defaults Defaults + initBuiltin sync.Once + IgnoreBuiltin bool + Parser Parser + Resolver parse.ResolverFunc + SubContextAttributeProvider types.SubContextAttributeProvider + ResponseWriters map[string]ResponseWriter + schemas *types.Schemas + QueryFilter types.QueryFilter + StoreWrapper StoreWrapper + Defaults Defaults } type Defaults struct { @@ -42,35 +50,44 @@ func NewAPIServer() *Server { "json": &writer.JSONResponseWriter{}, "html": &writer.HTMLResponseWriter{}, }, + SubContextAttributeProvider: &parse.DefaultSubContextAttributeProvider{}, + Resolver: parse.DefaultResolver, Defaults: Defaults{ - CreateHandler: handlers.CreateHandler, - DeleteHandler: handlers.DeleteHandler, - UpdateHandler: handlers.UpdateHandler, - ListHandler: handlers.ListHandler, + CreateHandler: handler.CreateHandler, + DeleteHandler: handler.DeleteHandler, + UpdateHandler: handler.UpdateHandler, + ListHandler: handler.ListHandler, LinkHandler: func(*types.APIContext) error { - return httperror.NewAPIError(httperror.NOT_FOUND, "Link not found") + return httperror.NewAPIError(httperror.NotFound, "Link not found") }, ErrorHandler: httperror.ErrorHandler, }, + StoreWrapper: wrapper.Wrap, + QueryFilter: handler.QueryFilter, } - s.Parser = func(rw http.ResponseWriter, req *http.Request) (*types.APIContext, error) { - ctx, err := parse.Parse(rw, req, s.schemas) - ctx.ResponseWriter = s.ResponseWriters[ctx.ResponseFormat] - if ctx.ResponseWriter == nil { - ctx.ResponseWriter = s.ResponseWriters["json"] - } + s.Parser = s.parser + return s +} - ctx.AccessControl = &authorization.AllAccess{} +func (s *Server) parser(rw http.ResponseWriter, req *http.Request) (*types.APIContext, error) { + ctx, err := parse.Parse(rw, req, s.schemas, s.Resolver) + ctx.ResponseWriter = s.ResponseWriters[ctx.ResponseFormat] + if ctx.ResponseWriter == nil { + ctx.ResponseWriter = s.ResponseWriters["json"] + } - return ctx, err + if ctx.QueryFilter == nil { + ctx.QueryFilter = s.QueryFilter } - return s -} + if ctx.SubContextAttributeProvider == nil { + ctx.SubContextAttributeProvider = s.SubContextAttributeProvider + } -func (s *Server) Start(ctx context.Context) error { - return s.addBuiltins(ctx) + ctx.AccessControl = &authorization.AllAccess{} + + return ctx, err } func (s *Server) AddSchemas(schemas *types.Schemas) error { @@ -78,6 +95,16 @@ func (s *Server) AddSchemas(schemas *types.Schemas) error { return schemas.Err() } + s.initBuiltin.Do(func() { + if s.IgnoreBuiltin { + return + } + for _, schema := range builtin.Schemas.Schemas() { + s.setupDefaults(schema) + s.schemas.AddSchema(schema) + } + }) + for _, schema := range schemas.Schemas() { s.setupDefaults(schema) s.schemas.AddSchema(schema) @@ -118,6 +145,10 @@ func (s *Server) setupDefaults(schema *types.Schema) { if schema.ErrorHandler == nil { schema.ErrorHandler = s.Defaults.ErrorHandler } + + if schema.Store != nil && s.StoreWrapper != nil { + schema.Store = s.StoreWrapper(schema.Store) + } } func (s *Server) ServeHTTP(rw http.ResponseWriter, req *http.Request) { @@ -149,23 +180,17 @@ func (s *Server) handle(rw http.ResponseWriter, req *http.Request) (*types.APICo return apiRequest, nil } - b := builder.NewBuilder(apiRequest) - if action == nil && apiRequest.Type != "" { var handler types.RequestHandler switch apiRequest.Method { case http.MethodGet: handler = apiRequest.Schema.ListHandler - apiRequest.Body = nil case http.MethodPost: handler = apiRequest.Schema.CreateHandler - apiRequest.Body, err = b.Construct(apiRequest.Schema, apiRequest.Body, builder.Create) case http.MethodPut: handler = apiRequest.Schema.UpdateHandler - apiRequest.Body, err = b.Construct(apiRequest.Schema, apiRequest.Body, builder.Update) case http.MethodDelete: handler = apiRequest.Schema.DeleteHandler - apiRequest.Body = nil } if err != nil { @@ -173,7 +198,7 @@ func (s *Server) handle(rw http.ResponseWriter, req *http.Request) (*types.APICo } if handler == nil { - return apiRequest, httperror.NewAPIError(httperror.NOT_FOUND, "") + return apiRequest, httperror.NewAPIError(httperror.NotFound, "") } return apiRequest, handler(apiRequest) @@ -195,15 +220,3 @@ func (s *Server) handleError(apiRequest *types.APIContext, err error) { apiRequest.Schema.ErrorHandler(apiRequest, err) } } - -func (s *Server) addBuiltins(ctx context.Context) error { - if s.IgnoreBuiltin { - return nil - } - - if err := s.AddSchemas(builtin.Schemas); err != nil { - return err - } - - return nil -} diff --git a/api/validate.go b/api/validate.go index a2c1b6f7d..0b417f106 100644 --- a/api/validate.go +++ b/api/validate.go @@ -28,17 +28,17 @@ func ValidateAction(request *types.APIContext) (*types.Action, error) { action, ok := actions[request.Action] if !ok { - return nil, httperror.NewAPIError(httperror.INVALID_ACTION, fmt.Sprintf("Invalid action: %s", request.Action)) + return nil, httperror.NewAPIError(httperror.InvalidAction, fmt.Sprintf("Invalid action: %s", request.Action)) } if request.ID != "" { resource := request.ReferenceValidator.Lookup(request.Type, request.ID) if resource == nil { - return nil, httperror.NewAPIError(httperror.NOT_FOUND, fmt.Sprintf("Failed to find type: %s id: %s", request.Type, request.ID)) + return nil, httperror.NewAPIError(httperror.NotFound, fmt.Sprintf("Failed to find type: %s id: %s", request.Type, request.ID)) } if _, ok := resource.Actions[request.Action]; !ok { - return nil, httperror.NewAPIError(httperror.INVALID_ACTION, fmt.Sprintf("Invalid action: %s", request.Action)) + return nil, httperror.NewAPIError(httperror.InvalidAction, fmt.Sprintf("Invalid action: %s", request.Action)) } } @@ -55,7 +55,7 @@ func CheckCSRF(rw http.ResponseWriter, req *http.Request) error { bytes := make([]byte, 5) _, err := rand.Read(bytes) if err != nil { - return httperror.WrapAPIError(err, httperror.SERVER_ERROR, "Failed in CSRF processing") + return httperror.WrapAPIError(err, httperror.ServerError, "Failed in CSRF processing") } cookie = &http.Cookie{ @@ -63,7 +63,7 @@ func CheckCSRF(rw http.ResponseWriter, req *http.Request) error { Value: hex.EncodeToString(bytes), } } else if err != nil { - return httperror.NewAPIError(httperror.INVALID_CSRF_TOKEN, "Failed to parse cookies") + return httperror.NewAPIError(httperror.InvalidCSRFToken, "Failed to parse cookies") } else if req.Method != http.MethodGet { /* * Very important to use request.getMethod() and not httpRequest.getMethod(). The client can override the HTTP method with _method @@ -73,7 +73,7 @@ func CheckCSRF(rw http.ResponseWriter, req *http.Request) error { } else if cookie.Value == req.URL.Query().Get(csrfCookie) { // Good } else { - return httperror.NewAPIError(httperror.INVALID_CSRF_TOKEN, "Invalid CSRF token") + return httperror.NewAPIError(httperror.InvalidCSRFToken, "Invalid CSRF token") } } diff --git a/api/writer/html.go b/api/writer/html.go index 59b33635e..f12a0aa38 100644 --- a/api/writer/html.go +++ b/api/writer/html.go @@ -3,6 +3,7 @@ package writer import ( "strings" + "github.com/rancher/norman/api/builtin" "github.com/rancher/norman/types" ) @@ -11,8 +12,8 @@ var ( - - + +