diff --git a/base/database/setup.go b/base/database/setup.go index 45c665b79..1d635d7ec 100644 --- a/base/database/setup.go +++ b/base/database/setup.go @@ -1,7 +1,6 @@ package database import ( - "app/base/models" "app/base/utils" "fmt" "time" @@ -23,7 +22,6 @@ var ( DB *gorm.DB DBReadReplica *gorm.DB OtherAdvisoryTypes []string - AdvisoryTypes map[int]string globalPgConfig map[DBMode]PostgreSQLConfig = make(map[DBMode]PostgreSQLConfig) ) @@ -172,20 +170,4 @@ func loadAdditionalParamsFromDB() { if err != nil { panic(err) } - - // Load AdvisoryTypes - var types []models.AdvisoryType - - err = DB.Table("advisory_type"). - Select("id, name"). - Scan(&types).Error - utils.LogDebug("advisory_types", types, "Advisory types loaded from DB") - if err != nil { - panic(err) - } - - AdvisoryTypes = make(map[int]string) - for _, at := range types { - AdvisoryTypes[at.ID] = at.Name - } } diff --git a/base/database/setup_test.go b/base/database/setup_test.go index f69c5923b..37148ddd0 100644 --- a/base/database/setup_test.go +++ b/base/database/setup_test.go @@ -2,8 +2,9 @@ package database import ( "app/base/utils" - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func TestDBCheck(t *testing.T) { @@ -15,5 +16,5 @@ func TestAdditionalParams(t *testing.T) { utils.SkipWithoutDB(t) Configure() - assert.True(t, len(AdvisoryTypes) == 5) + assert.True(t, len(OtherAdvisoryTypes) == 2) } diff --git a/base/models/models.go b/base/models/models.go index 8a3eb087f..e418f80bd 100644 --- a/base/models/models.go +++ b/base/models/models.go @@ -4,6 +4,7 @@ import ( "time" "github.com/lib/pq" + "gorm.io/datatypes" ) type RhAccount struct { @@ -261,10 +262,10 @@ type AdvisoryMetadata struct { ModifiedDate *time.Time URL *string SeverityID *int - PackageData []byte - CveList []byte + PackageData datatypes.JSONSlice[string] `gorm:"type:jsonb"` + CveList datatypes.JSONSlice[string] `gorm:"type:jsonb"` RebootRequired bool - ReleaseVersions []byte + ReleaseVersions datatypes.JSONSlice[string] `gorm:"type:jsonb"` Synced bool } diff --git a/base/vmaas/vmaas.go b/base/vmaas/vmaas.go index 1a48889eb..09a016ccb 100644 --- a/base/vmaas/vmaas.go +++ b/base/vmaas/vmaas.go @@ -210,23 +210,23 @@ type ErrataResponse struct { } type ErrataResponseErrataList struct { - Updated string `json:"updated,omitempty"` - Severity string `json:"severity,omitempty"` - ReferenceList *[]string `json:"reference_list,omitempty"` - Issued string `json:"issued,omitempty"` - Description string `json:"description,omitempty"` - Solution *string `json:"solution,omitempty"` - Summary string `json:"summary,omitempty"` - URL *string `json:"url,omitempty"` - Synopsis string `json:"synopsis,omitempty"` - CveList *[]string `json:"cve_list,omitempty"` - BugzillaList *[]string `json:"bugzilla_list,omitempty"` - PackageList []string `json:"package_list,omitempty"` - SourcePackageList *[]string `json:"source_package_list,omitempty"` - Type string `json:"type,omitempty"` - ThirdParty *bool `json:"third_party,omitempty"` - RequiresReboot bool `json:"requires_reboot,omitempty"` - ReleaseVersions *[]string `json:"release_versions,omitempty"` + Updated string `json:"updated,omitempty"` + Severity string `json:"severity,omitempty"` + ReferenceList []string `json:"reference_list,omitempty"` + Issued string `json:"issued,omitempty"` + Description string `json:"description,omitempty"` + Solution *string `json:"solution,omitempty"` + Summary string `json:"summary,omitempty"` + URL *string `json:"url,omitempty"` + Synopsis string `json:"synopsis,omitempty"` + CveList []string `json:"cve_list,omitempty"` + BugzillaList []string `json:"bugzilla_list,omitempty"` + PackageList []string `json:"package_list,omitempty"` + SourcePackageList []string `json:"source_package_list,omitempty"` + Type string `json:"type,omitempty"` + ThirdParty *bool `json:"third_party,omitempty"` + RequiresReboot bool `json:"requires_reboot,omitempty"` + ReleaseVersions []string `json:"release_versions,omitempty"` } type PkgListRequest struct { diff --git a/docs/v3/openapi.json b/docs/v3/openapi.json index 2cb091aa9..19960c647 100644 --- a/docs/v3/openapi.json +++ b/docs/v3/openapi.json @@ -123,6 +123,20 @@ "type": "integer" } }, + { + "name": "filter[severity_name]", + "in": "query", + "description": "Filter", + "schema": { + "type": "string", + "enum": [ + "Low", + "Medium", + "High", + "Critical" + ] + } + }, { "name": "filter[installable_systems]", "in": "query", @@ -659,6 +673,20 @@ "type": "integer" } }, + { + "name": "filter[severity_name]", + "in": "query", + "description": "Filter", + "schema": { + "type": "string", + "enum": [ + "Low", + "Medium", + "High", + "Critical" + ] + } + }, { "name": "filter[applicable_systems]", "in": "query", @@ -1698,6 +1726,20 @@ "minimum": 1, "type": "integer" } + }, + { + "name": "filter[severity_name]", + "in": "query", + "description": "Filter", + "schema": { + "type": "string", + "enum": [ + "Low", + "Medium", + "High", + "Critical" + ] + } } ], "responses": { @@ -4806,6 +4848,20 @@ "minimum": 1, "type": "integer" } + }, + { + "name": "filter[severity_name]", + "in": "query", + "description": "Filter", + "schema": { + "type": "string", + "enum": [ + "Low", + "Medium", + "High", + "Critical" + ] + } } ], "responses": { @@ -6071,6 +6127,9 @@ "severity": { "type": "integer" }, + "severity_name": { + "type": "string" + }, "synopsis": { "type": "string" } @@ -6161,6 +6220,9 @@ "severity": { "type": "integer" }, + "severity_name": { + "type": "string" + }, "solution": { "type": "string" }, @@ -6242,6 +6304,9 @@ "severity": { "type": "integer" }, + "severity_name": { + "type": "string" + }, "synopsis": { "type": "string" } @@ -6791,6 +6856,9 @@ "severity": { "type": "integer" }, + "severity_name": { + "type": "string" + }, "status": { "type": "string" }, @@ -6859,6 +6927,9 @@ "severity": { "type": "integer" }, + "severity_name": { + "type": "string" + }, "status": { "type": "string" }, diff --git a/evaluator/notifications.go b/evaluator/notifications.go index 5c66cc57c..dc9a70efe 100644 --- a/evaluator/notifications.go +++ b/evaluator/notifications.go @@ -2,7 +2,6 @@ package evaluator import ( "app/base" - "app/base/database" "app/base/models" "app/base/mqueue" ntf "app/base/notification" @@ -32,35 +31,18 @@ func getUnnotifiedAdvisories(tx *gorm.DB, accountID int, newAdvs SystemAdvisoryM advIDs = append(advIDs, a.AdvisoryID) } - var advNames []string err := tx.Table("advisory_account_data as acd"). - Select("am.name"). + Select("am.id as advisory_id, am.name as advisory_name, at.name as advisory_type, am.synopsis"). Joins("inner join advisory_metadata am on am.id = acd.advisory_id"). + Joins("inner join advisory_type at on at.id = am.advisory_type_id"). Where("acd.rh_account_id = ? AND acd.advisory_id IN (?)"+ "AND acd.notified IS NULL AND acd.systems_installable > 0", accountID, advIDs). Order("am.name ASC"). - Scan(&advNames).Error + Scan(&unAdvs).Error if err != nil { return nil, errors.Wrap(err, "querying unnotified advisories from DB failed") } - if len(advNames) == 0 { - return nil, nil - } - - for _, n := range advNames { - if a, ok := newAdvs[n]; ok { - unAdvs = append( - unAdvs, - ntf.Advisory{ - AdvisoryID: a.AdvisoryID, - AdvisoryName: a.Advisory.Name, - AdvisoryType: database.AdvisoryTypes[a.Advisory.AdvisoryTypeID], - Synopsis: a.Advisory.Synopsis, - }) - } - } - return unAdvs, nil } diff --git a/go.mod b/go.mod index 46dcc5faf..e4fedebec 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,7 @@ require ( require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 // indirect + filippo.io/edwards25519 v1.1.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -69,6 +70,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.30.1 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect @@ -134,6 +136,8 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/datatypes v1.2.7 // indirect + gorm.io/driver/mysql v1.5.6 // indirect ) replace github.com/segmentio/kafka-go v0.4.49 => github.com/MichaelMraka/kafka-go v0.0.0-20251014134425-230b5408c208 diff --git a/go.sum b/go.sum index c55ed94f2..a13971c41 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 h1:PMmTMyvHScV9Mn8wc6ASge9uRcHy0jtqPd+fM35LmsQ= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= @@ -111,6 +113,9 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= @@ -467,8 +472,13 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk= +gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY= +gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= +gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= diff --git a/manager/controllers/advisories.go b/manager/controllers/advisories.go index 80f3eaebb..cb8492ec0 100644 --- a/manager/controllers/advisories.go +++ b/manager/controllers/advisories.go @@ -54,12 +54,10 @@ type AdvisoryItemAttributesCommon struct { Synopsis string `json:"synopsis" csv:"synopsis" query:"am.synopsis" gorm:"column:synopsis"` AdvisoryTypeName string `json:"advisory_type_name" csv:"advisory_type_name" query:"at.name" order_query:"at.preference" gorm:"column:advisory_type_name"` // Advisory type name, proper ordering ensured (unknown, unspecified, other, enhancement, bugfix, security) Severity *int `json:"severity,omitempty" csv:"severity" query:"am.severity_id" gorm:"column:severity"` + SeverityName *string `json:"severity_name,omitempty" csv:"severity_name" query:"sev.name" gorm:"column:severity_name"` CveCount int `json:"cve_count" csv:"cve_count" query:"CASE WHEN jsonb_typeof(am.cve_list) = 'array' THEN jsonb_array_length(am.cve_list) ELSE 0 END" gorm:"column:cve_count"` RebootRequired bool `json:"reboot_required" csv:"reboot_required" query:"am.reboot_required" gorm:"column:reboot_required"` - ReleaseVersions RelList `json:"release_versions" csv:"release_versions" query:"null" gorm:"-"` - - // helper field to get release_version json from db and parse it to ReleaseVersions field - ReleaseVersionsJSONB []byte `json:"-" csv:"-" query:"am.release_versions" gorm:"column:release_versions_json"` + ReleaseVersions RelList `json:"release_versions" csv:"release_versions" query:"am.release_versions" gorm:"column:release_versions" swaggertype:"array,string"` } // nolint: lll @@ -121,6 +119,7 @@ func advisoriesCommon(c *gin.Context) (*gorm.DB, *ListMeta, []string, error) { // @Param filter[synopsis] query string false "Filter" // @Param filter[advisory_type_name] query string false "Filter" Enums(unknown,unspecified,other,enhancement,bugfix,security) // @Param filter[severity] query int false "Filter" minimum(1) maximum(4) +// @Param filter[severity_name] query string false "Filter" Enums(Low,Medium,High,Critical) // @Param filter[installable_systems] query int false "Filter" // @Param filter[applicable_systems] query int false "Filter" // @Param tags query []string false "Tag filter" @@ -211,6 +210,7 @@ func buildQueryAdvisories(db *gorm.DB, account int) *gorm.DB { query := database.AdvisoryMetadata(db). Select(AdvisoriesSelect). Joins("JOIN advisory_account_data aad ON am.id = aad.advisory_id"). + Joins("LEFT JOIN advisory_severity sev ON am.severity_id = sev.id"). Where("aad.rh_account_id = ?", account). Where("aad.systems_applicable > 0") return query @@ -234,7 +234,8 @@ func buildQueryAdvisoriesTagged(db *gorm.DB, filters map[string]FilterData, acco query := database.AdvisoryMetadata(db). Select(AdvisoriesSelect). - Joins("JOIN (?) aad ON am.id = aad.advisory_id", subq) + Joins("JOIN (?) aad ON am.id = aad.advisory_id", subq). + Joins("LEFT JOIN advisory_severity sev ON am.severity_id = sev.id") return query } @@ -257,7 +258,6 @@ func buildAdvisoriesData(advisories []AdvisoriesDBLookup) ([]AdvisoryItem, int, data := make([]AdvisoryItem, len(advisories)) for i := 0; i < len(advisories); i++ { advisory := (advisories)[i] - advisory.AdvisoryItemAttributesCommon = fillAdvisoryItemAttributeReleaseVersion(advisory.AdvisoryItemAttributesCommon) data[i] = AdvisoryItem{ Attributes: AdvisoryItemAttributes{ AdvisoryItemAttributesCommon: advisory.AdvisoryItemAttributesCommon, diff --git a/manager/controllers/advisories_export.go b/manager/controllers/advisories_export.go index 2d0036054..90e34e933 100644 --- a/manager/controllers/advisories_export.go +++ b/manager/controllers/advisories_export.go @@ -23,6 +23,7 @@ import ( // @Param filter[synopsis] query string false "Filter" // @Param filter[advisory_type_name] query string false "Filter" Enums(unknown,unspecified,other,enhancement,bugfix,security) // @Param filter[severity] query int false "Filter" minimum(1) maximum(4) +// @Param filter[severity_name] query string false "Filter" Enums(Low,Medium,High,Critical) // @Param filter[applicable_systems] query int false "Filter" // @Success 200 {array} AdvisoriesDBLookup // @Failure 415 {object} utils.ErrorResponse @@ -59,11 +60,5 @@ func AdvisoriesExportHandler(c *gin.Context) { return } - // update release_version field - for i := range advisories { - advisories[i].AdvisoryItemAttributesCommon = - fillAdvisoryItemAttributeReleaseVersion(advisories[i].AdvisoryItemAttributesCommon) - } - OutputExportData(c, advisories) } diff --git a/manager/controllers/advisories_export_test.go b/manager/controllers/advisories_export_test.go index 7a2ae45e1..6e2328ba2 100644 --- a/manager/controllers/advisories_export_test.go +++ b/manager/controllers/advisories_export_test.go @@ -39,7 +39,7 @@ func TestAdvisoriesExportCSV(t *testing.T) { lines := strings.Split(body, "\r\n") assert.Equal(t, 14, len(lines)) - assert.Equal(t, "RH-1,adv-1-des,2016-09-22T16:00:00Z,adv-1-syn,enhancement,,0,false,\"7.0,7Server\",4,6", lines[3]) + assert.Equal(t, "RH-1,adv-1-des,2016-09-22T16:00:00Z,adv-1-syn,enhancement,,,0,false,\"7.0,7Server\",4,6", lines[3]) } func TestAdvisoriesExportWrongFormat(t *testing.T) { @@ -62,7 +62,7 @@ func TestAdvisoriesExportCSVFilter(t *testing.T) { lines := strings.Split(body, "\r\n") assert.Equal(t, 3, len(lines)) - assert.Equal(t, "RH-1,adv-1-des,2016-09-22T16:00:00Z,adv-1-syn,enhancement,,0,false,\"7.0,7Server\",4,6", lines[1]) + assert.Equal(t, "RH-1,adv-1-des,2016-09-22T16:00:00Z,adv-1-syn,enhancement,,,0,false,\"7.0,7Server\",4,6", lines[1]) assert.Equal(t, "", lines[2]) } } diff --git a/manager/controllers/advisory_detail.go b/manager/controllers/advisory_detail.go index d086a2626..bfe29ce20 100644 --- a/manager/controllers/advisory_detail.go +++ b/manager/controllers/advisory_detail.go @@ -2,7 +2,6 @@ package controllers import ( "app/base/database" - "app/base/models" "app/base/utils" "app/manager/config" "app/manager/middlewares" @@ -12,6 +11,7 @@ import ( "github.com/gin-gonic/gin" lru "github.com/hashicorp/golang-lru/v2" "github.com/pkg/errors" + "gorm.io/datatypes" "gorm.io/gorm" ) @@ -30,23 +30,26 @@ type AdvisoryDetailItem struct { } type AdvisoryDetailAttributes struct { - Description string `json:"description"` - ModifiedDate *time.Time `json:"modified_date"` - PublicDate *time.Time `json:"public_date"` - Topic string `json:"topic"` - Synopsis string `json:"synopsis"` - Solution *string `json:"solution"` - AdvisoryTypeName string `json:"advisory_type_name"` - Severity *int `json:"severity"` - Fixes *string `json:"fixes"` - Cves []string `json:"cves"` - References []string `json:"references"` - RebootRequired bool `json:"reboot_required"` - ReleaseVersions []string `json:"release_versions"` - Packages packages `json:"packages"` + Description string `json:"description"` + ModifiedDate *time.Time `json:"modified_date"` + PublicDate *time.Time `json:"public_date"` + Topic string `json:"topic" gorm:"column:summary"` + Synopsis string `json:"synopsis"` + Solution *string `json:"solution"` + AdvisoryTypeName string `json:"advisory_type_name"` + Severity *int `json:"severity" gorm:"column:severity_id"` + SeverityName *string `json:"severity_name,omitempty"` + Fixes *string `json:"fixes"` + Cves datatypes.JSONSlice[string] `json:"cves" gorm:"column:cve_list" swaggertype:"array,string"` + References []string `json:"references" query:"null" gorm:"-"` + RebootRequired bool `json:"reboot_required"` + ReleaseVersions datatypes.JSONSlice[string] `json:"release_versions" swaggertype:"array,string"` + Packages datatypes.JSONSlice[string] `json:"packages" gorm:"column:package_data" swaggertype:"array,string"` } -type packages []string +func (AdvisoryDetailAttributes) TableName() string { + return "advisory_metadata AS am" +} // @Summary Show me details an advisory by given advisory name // @Description Show me details an advisory by given advisory name @@ -88,67 +91,25 @@ func AdvisoryDetailHandler(c *gin.Context) { } func getAdvisoryFromDB(db *gorm.DB, advisoryName string) (*AdvisoryDetailResponse, error) { - var advisory models.AdvisoryMetadata + var advisory AdvisoryDetailAttributes err := db.Table(advisory.TableName()). - Take(&advisory, "name = ?", advisoryName).Error + Select("am.*", "sev.name as severity_name", "at.name as advisory_type_name"). + Joins("LEFT JOIN advisory_severity sev ON am.severity_id = sev.id"). + Joins("JOIN advisory_type at ON am.advisory_type_id = at.id"). + Take(&advisory, "am.name = ?", advisoryName).Error if err != nil { return nil, err } - cves, err := parseJSONList(advisory.CveList) - if err != nil { - return nil, errors.Wrap(err, "CVEs parsing error") - } - - releaseVersions, err := parseJSONList(advisory.ReleaseVersions) - if err != nil { - return nil, errors.Wrap(err, "release_versions parsing error") - } - - ada := AdvisoryDetailAttributes{ - Description: advisory.Description, - ModifiedDate: advisory.ModifiedDate, - PublicDate: advisory.PublicDate, - Topic: advisory.Summary, - Synopsis: advisory.Synopsis, - Solution: advisory.Solution, - Severity: advisory.SeverityID, - AdvisoryTypeName: database.AdvisoryTypes[advisory.AdvisoryTypeID], - Fixes: nil, - Cves: cves, - References: []string{}, - RebootRequired: advisory.RebootRequired, - ReleaseVersions: releaseVersions, - } - - pkgs, err := parsePackages(advisory.PackageData) - if err != nil { - return nil, errors.Wrap(err, "packages parsing error") - } - var resp = AdvisoryDetailResponse{Data: AdvisoryDetailItem{ - ID: advisory.Name, + ID: advisoryName, Type: "advisory", - Attributes: ada, + Attributes: advisory, }} - resp.Data.Attributes.Packages = pkgs return &resp, nil } -func parsePackages(jsonb []byte) (packages, error) { - if jsonb == nil { - return packages{}, nil - } - - var err error - pkgs, err := parseJSONList(jsonb) - if err != nil { - return nil, err - } - return pkgs, nil -} - func InitAdvisoryDetailCache() { middlewares.AdvisoryDetailGauge.Set(0) if !config.EnableAdvisoryDetailCache { diff --git a/manager/controllers/advisory_detail_test.go b/manager/controllers/advisory_detail_test.go index cbf0084ef..2d9d9f9e2 100644 --- a/manager/controllers/advisory_detail_test.go +++ b/manager/controllers/advisory_detail_test.go @@ -9,6 +9,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "gorm.io/datatypes" ) func TestAdvisoryDetailDefault(t *testing.T) { @@ -32,11 +33,11 @@ func (r *AdvisoryDetailResponse) checkRH9Fields(t *testing.T) { assert.Equal(t, "2018-09-22 20:00:00 +0000 UTC", r.Data.Attributes.ModifiedDate.String()) assert.Equal(t, 2, len(r.Data.Attributes.Packages)) assert.Equal( - t, packages{"firefox-77.0.1-1.fc31.x86_64", "firefox-77.0.1-1.fc31.s390"}, + t, datatypes.JSONSlice[string]{"firefox-77.0.1-1.fc31.x86_64", "firefox-77.0.1-1.fc31.s390"}, r.Data.Attributes.Packages, ) assert.Equal(t, false, r.Data.Attributes.RebootRequired) - assert.Equal(t, []string{"8.2", "8.4"}, r.Data.Attributes.ReleaseVersions) + assert.Equal(t, datatypes.JSONSlice[string]{"8.2", "8.4"}, r.Data.Attributes.ReleaseVersions) assert.Nil(t, r.Data.Attributes.Severity) } diff --git a/manager/controllers/system_advisories.go b/manager/controllers/system_advisories.go index 060e74316..a8bc46c88 100644 --- a/manager/controllers/system_advisories.go +++ b/manager/controllers/system_advisories.go @@ -5,9 +5,11 @@ import ( "app/base/models" "app/base/utils" "app/manager/middlewares" + "database/sql/driver" "net/http" "strings" + "gorm.io/datatypes" "gorm.io/gorm" "github.com/gin-gonic/gin" @@ -24,7 +26,7 @@ var SystemAdvisoriesOpts = ListOpts{ SearchFields: []string{"am.name", "am.synopsis"}, } -type RelList []string +type RelList datatypes.JSONSlice[string] type SystemAdvisoriesDBLookup struct { ID string `json:"id" csv:"id" query:"am.name" gorm:"column:id"` @@ -60,6 +62,14 @@ func (v RelList) String() string { return strings.Join(v, ",") } +func (v RelList) Value() (driver.Value, error) { + return (datatypes.JSONSlice[string])(v).Value() +} + +func (v *RelList) Scan(value interface{}) error { + return (*datatypes.JSONSlice[string])(v).Scan(value) +} + func systemAdvisoriesCommon(c *gin.Context) (*gorm.DB, *ListMeta, []string, error) { account := c.GetInt(utils.KeyAccount) groups := c.GetStringMapString(utils.KeyInventoryGroups) @@ -119,6 +129,7 @@ func systemAdvisoriesCommon(c *gin.Context) (*gorm.DB, *ListMeta, []string, erro // @Param filter[synopsis] query string false "Filter" // @Param filter[advisory_type_name] query string false "Filter" Enums(unknown,unspecified,other,enhancement,bugfix,security) // @Param filter[severity] query int false "Filter" minimum(1) maximum(4) +// @Param filter[severity_name] query string false "Filter" Enums(Low,Medium,High,Critical) // @Success 200 {object} SystemAdvisoriesResponse // @Failure 400 {object} utils.ErrorResponse // @Failure 404 {object} utils.ErrorResponse @@ -193,6 +204,7 @@ func buildSystemAdvisoriesQuery(db *gorm.DB, account int, groups map[string]stri query := database.SystemAdvisoriesByInventoryID(db, account, groups, inventoryID, database.JoinAdvisoryMetadata, database.JoinAdvisoryType). Joins("JOIN status ON sa.status_id = status.id"). + Joins("LEFT JOIN advisory_severity sev ON am.severity_id = sev.id"). Select(SystemAdvisoriesSelect) return query } @@ -204,7 +216,6 @@ func buildSystemAdvisoriesData(models []SystemAdvisoriesDBLookup) ([]SystemAdvis } data := make([]SystemAdvisoryItem, len(models)) for i, advisory := range models { - advisory.AdvisoryItemAttributesCommon = fillAdvisoryItemAttributeReleaseVersion(advisory.AdvisoryItemAttributesCommon) item := SystemAdvisoryItem{ ID: advisory.ID, Type: "advisory", diff --git a/manager/controllers/system_advisories_export.go b/manager/controllers/system_advisories_export.go index 29c5b3896..8e5bdd1f3 100644 --- a/manager/controllers/system_advisories_export.go +++ b/manager/controllers/system_advisories_export.go @@ -25,6 +25,7 @@ import ( // @Param filter[synopsis] query string false "Filter" // @Param filter[advisory_type_name] query string false "Filter" Enums(unknown,unspecified,other,enhancement,bugfix,security) // @Param filter[severity] query int false "Filter" minimum(1) maximum(4) +// @Param filter[severity_name] query string false "Filter" Enums(Low,Medium,High,Critical) // @Success 200 {array} SystemAdvisoriesDBLookup // @Failure 400 {object} utils.ErrorResponse // @Failure 404 {object} utils.ErrorResponse @@ -69,10 +70,6 @@ func SystemAdvisoriesExportHandler(c *gin.Context) { var advisories []SystemAdvisoriesDBLookup err = query.Find(&advisories).Error - for i := 0; i < len(advisories); i++ { - advisories[i].AdvisoryItemAttributesCommon = - fillAdvisoryItemAttributeReleaseVersion(advisories[i].AdvisoryItemAttributesCommon) - } if err != nil { utils.LogAndRespError(c, err, "db error") return diff --git a/manager/controllers/system_advisories_export_test.go b/manager/controllers/system_advisories_export_test.go index 09893c49f..8ee3f29d6 100644 --- a/manager/controllers/system_advisories_export_test.go +++ b/manager/controllers/system_advisories_export_test.go @@ -30,9 +30,9 @@ func TestSystemAdvisoriesExportCSV(t *testing.T) { lines := strings.Split(body, "\r\n") assert.Equal(t, 10, len(lines)) - assert.Equal(t, "id,description,public_date,synopsis,advisory_type_name,severity,cve_count,"+ + assert.Equal(t, "id,description,public_date,synopsis,advisory_type_name,severity,severity_name,cve_count,"+ "reboot_required,release_versions,status", lines[0]) - assert.Equal(t, "RH-1,adv-1-des,2016-09-22T16:00:00Z,adv-1-syn,enhancement,,0,"+ + assert.Equal(t, "RH-1,adv-1-des,2016-09-22T16:00:00Z,adv-1-syn,enhancement,,,0,"+ "false,\"7.0,7Server\",Installable", lines[1]) } diff --git a/manager/controllers/utils.go b/manager/controllers/utils.go index fb832438f..d9bb7966f 100644 --- a/manager/controllers/utils.go +++ b/manager/controllers/utils.go @@ -558,29 +558,6 @@ func systemsSatelliteIDs(c *gin.Context, systems []SystemsSatelliteManagedID, me return resp, nil } -func fillAdvisoryItemAttributeReleaseVersion(advisory AdvisoryItemAttributesCommon) AdvisoryItemAttributesCommon { - // parse release version from json to []strings - var err error - advisory.ReleaseVersions, err = parseJSONList(advisory.ReleaseVersionsJSONB) - if err != nil { - utils.LogWarn("err", err.Error(), "json", advisory.ReleaseVersionsJSONB, "Unable to parse json list") - } - return advisory -} - -func parseJSONList(jsonb []byte) ([]string, error) { - if jsonb == nil { - return []string{}, nil - } - - var items []string - err := sonic.Unmarshal(jsonb, &items) - if err != nil { - return nil, err - } - return items, nil -} - func isFilterInURLValid(c *gin.Context) bool { if strings.Contains(c.Request.URL.String(), "filter") { utils.LogAndRespBadRequest(c, errors.New(FilterNotSupportedMsg), FilterNotSupportedMsg) diff --git a/tasks/vmaas_sync/advisory_sync.go b/tasks/vmaas_sync/advisory_sync.go index 304bee72e..a83867027 100644 --- a/tasks/vmaas_sync/advisory_sync.go +++ b/tasks/vmaas_sync/advisory_sync.go @@ -14,6 +14,7 @@ import ( "github.com/bytedance/sonic" "github.com/pkg/errors" + "gorm.io/datatypes" ) const SyncBatchSize = 1000 // Should be < 5000 @@ -110,11 +111,6 @@ func vmaasData2AdvisoryMetadata(errataName string, vmaasData vmaas.ErrataRespons return nil, nil } - packageData, cvesData, releaseVersionsData, err := getJSONFields(&vmaasData) - if err != nil { - return nil, errors.Wrap(err, "Unable to get JSON fields data") - } - advisory := models.AdvisoryMetadata{ Name: errataName, AdvisoryTypeID: advisoryTypes[strings.ToLower(vmaasData.Type)], @@ -123,13 +119,13 @@ func vmaasData2AdvisoryMetadata(errataName string, vmaasData vmaas.ErrataRespons Summary: vmaasData.Summary, Solution: utils.EmptyToNil(vmaasData.Solution), SeverityID: getSeverityID(&vmaasData, severities), - CveList: cvesData, + CveList: datatypes.JSONSlice[string](vmaasData.CveList), PublicDate: &issued, ModifiedDate: &modified, URL: utils.EmptyToNil(vmaasData.URL), - PackageData: packageData, + PackageData: datatypes.JSONSlice[string](vmaasData.PackageList), RebootRequired: vmaasData.RequiresReboot, - ReleaseVersions: releaseVersionsData, + ReleaseVersions: datatypes.JSONSlice[string](vmaasData.ReleaseVersions), Synced: true, } return &advisory, nil diff --git a/tasks/vmaas_sync/advisory_sync_test.go b/tasks/vmaas_sync/advisory_sync_test.go index c69ab5a90..16dea03cf 100644 --- a/tasks/vmaas_sync/advisory_sync_test.go +++ b/tasks/vmaas_sync/advisory_sync_test.go @@ -12,6 +12,7 @@ import ( "github.com/bytedance/sonic" "github.com/stretchr/testify/assert" + "gorm.io/datatypes" ) func TestInit(_ *testing.T) { @@ -97,21 +98,21 @@ func TestParseAdvisories(t *testing.T) { "ER1": { Updated: "2004-09-02T00:00:00+00:00", Severity: "", - ReferenceList: &[]string{}, + ReferenceList: []string{}, Issued: "2004-09-02T00:00:00+00:00", Description: "DESC", Solution: utils.PtrString("SOL"), Summary: "SUM", URL: utils.PtrString("URL"), Synopsis: "SYN", - CveList: utils.PtrSliceString([]string{"CVE-1", "CVE-2", "CVE-3"}), - BugzillaList: &[]string{}, + CveList: []string{"CVE-1", "CVE-2", "CVE-3"}, + BugzillaList: []string{}, PackageList: []string{}, - SourcePackageList: &[]string{}, + SourcePackageList: []string{}, Type: "bugfix", ThirdParty: new(bool), RequiresReboot: true, - ReleaseVersions: utils.PtrSliceString([]string{"8.0", "8.1"}), + ReleaseVersions: []string{"8.0", "8.1"}, }, } @@ -132,8 +133,8 @@ func TestParseAdvisories(t *testing.T) { assert.Equal(t, 2, adv.AdvisoryTypeID) assert.Equal(t, 2, adv.AdvisoryTypeID) assert.Equal(t, true, adv.RebootRequired) - assert.Equal(t, `["CVE-1","CVE-2","CVE-3"]`, string(adv.CveList)) - assert.Equal(t, `["8.0","8.1"]`, string(adv.ReleaseVersions)) + assert.Equal(t, datatypes.JSONSlice[string]{"CVE-1", "CVE-2", "CVE-3"}, adv.CveList) + assert.Equal(t, datatypes.JSONSlice[string]{"8.0", "8.1"}, adv.ReleaseVersions) } func TestSaveAdvisories(t *testing.T) {