diff --git a/api/v1/models.go b/api/v1/models.go index f063417cd4..664d1945f5 100644 --- a/api/v1/models.go +++ b/api/v1/models.go @@ -98,18 +98,12 @@ func (v Vulnerability) DatabaseModel() (database.Vulnerability, error) { var dbFeatures []database.FeatureVersion for _, feature := range v.FixedIn { - version, err := types.NewVersion(feature.Version) + dbFeature, err := feature.DatabaseModel() if err != nil { return database.Vulnerability{}, err } - dbFeatures = append(dbFeatures, database.FeatureVersion{ - Feature: database.Feature{ - Name: feature.Name, - Namespace: database.Namespace{Name: feature.Namespace}, - }, - Version: version, - }) + dbFeatures = append(dbFeatures, dbFeature) } return database.Vulnerability{ @@ -153,6 +147,21 @@ type Feature struct { Vulnerabilities []Vulnerability `json:"Vulnerabilities,omitempty"` } +func (f Feature) DatabaseModel() (database.FeatureVersion, error) { + version, err := types.NewVersion(f.Version) + if err != nil { + return database.FeatureVersion{}, err + } + + return database.FeatureVersion{ + Feature: database.Feature{ + Name: f.Name, + Namespace: database.Namespace{Name: f.Namespace}, + }, + Version: version, + }, nil +} + type Notification struct { Name string `json:"Name,omitempty"` Created string `json:"Created,omitempty"` @@ -235,6 +244,12 @@ type NotificationEnvelope struct { Error *Error `json:"Error,omitempty"` } +type FeatureEnvelope struct { + Feature *Feature `json:"Feature,omitempty"` + Features *[]Feature `json:"Features,omitempty"` + Error *Error `json:"Error,omitempty"` +} + func pageStringToDBPageNumber(pageStr string) (database.VulnerabilityNotificationPageNumber, error) { // TODO(jzelinskie): turn pagination into an encrypted token var old, new int diff --git a/api/v1/router.go b/api/v1/router.go index dac7d37a97..c4c6662ad9 100644 --- a/api/v1/router.go +++ b/api/v1/router.go @@ -40,7 +40,6 @@ func NewRouter(ctx *context.RouteContext) *httprouter.Router { router.DELETE("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName", context.HTTPHandler(deleteVulnerability, ctx)) // Fixes - router.POST("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName/fixes", context.HTTPHandler(postFix, ctx)) router.GET("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName/fixes", context.HTTPHandler(getFixes, ctx)) router.PUT("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName/fixes/:fixName", context.HTTPHandler(putFix, ctx)) router.DELETE("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName/fixes/:fixName", context.HTTPHandler(deleteFix, ctx)) diff --git a/api/v1/routes.go b/api/v1/routes.go index e74b7a0bec..89f5e55abd 100644 --- a/api/v1/routes.go +++ b/api/v1/routes.go @@ -214,17 +214,68 @@ func deleteVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Pa return writeHeader(w, http.StatusOK) } -func postFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) int { - return 0 -} func getFixes(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) int { - return 0 + dbVuln, err := ctx.Store.FindVulnerability(p.ByName("namespaceName"), p.ByName("vulnerabilityName")) + if err == cerrors.ErrNotFound { + writeResponse(w, FeatureEnvelope{Error: &Error{err.Error()}}) + return writeHeader(w, http.StatusNotFound) + } else if err != nil { + writeResponse(w, FeatureEnvelope{Error: &Error{err.Error()}}) + return writeHeader(w, http.StatusInternalServerError) + } + + vuln := VulnerabilityFromDatabaseModel(dbVuln, true) + writeResponse(w, FeatureEnvelope{Features: &vuln.FixedIn}) + return writeHeader(w, http.StatusOK) } + func putFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) int { - return 0 + request := FeatureEnvelope{} + err := decodeJSON(r, &request) + if err != nil { + writeResponse(w, FeatureEnvelope{Error: &Error{err.Error()}}) + return writeHeader(w, http.StatusBadRequest) + } + + if request.Feature == nil { + writeResponse(w, FeatureEnvelope{Error: &Error{"failed to provide feature"}}) + return writeHeader(w, http.StatusBadRequest) + } + + if request.Feature.Name != p.ByName("fixName") { + writeResponse(w, FeatureEnvelope{Error: &Error{"feature name in URL and JSON do not match"}}) + return writeHeader(w, http.StatusBadRequest) + } + + dbFix, err := request.Feature.DatabaseModel() + if err != nil { + writeResponse(w, FeatureEnvelope{Error: &Error{err.Error()}}) + return writeHeader(w, http.StatusBadRequest) + } + + err = ctx.Store.InsertVulnerabilityFixes(p.ByName("vulnerabilityNamespace"), p.ByName("vulnerabilityName"), []database.FeatureVersion{dbFix}) + if err == cerrors.ErrNotFound { + writeResponse(w, FeatureEnvelope{Error: &Error{err.Error()}}) + return writeHeader(w, http.StatusNotFound) + } else if err != nil { + writeResponse(w, FeatureEnvelope{Error: &Error{err.Error()}}) + return writeHeader(w, http.StatusInternalServerError) + } + + return writeHeader(w, http.StatusCreated) } + func deleteFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) int { - return 0 + err := ctx.Store.DeleteVulnerabilityFix(p.ByName("vulnerabilityNamespace"), p.ByName("vulnerabilityName"), p.ByName("fixName")) + if err == cerrors.ErrNotFound { + writeResponse(w, FeatureEnvelope{Error: &Error{err.Error()}}) + return writeHeader(w, http.StatusNotFound) + } else if err != nil { + writeResponse(w, FeatureEnvelope{Error: &Error{err.Error()}}) + return writeHeader(w, http.StatusInternalServerError) + } + + return writeHeader(w, http.StatusOK) } func getNotification(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) int {