diff --git a/api/analysis.go b/api/analysis.go index 7abd79e99..251fafefb 100644 --- a/api/analysis.go +++ b/api/analysis.go @@ -228,12 +228,17 @@ func (h AnalysisHandler) AppCreate(ctx *gin.Context) { _ = ctx.Error(result.Error) return } + err := h.archive(ctx) + if err != nil { + _ = ctx.Error(err) + return + } analysis := &model.Analysis{} analysis.ApplicationID = id analysis.CreateUser = h.BaseHandler.CurrentUser(ctx) db := h.DB(ctx) db.Logger = db.Logger.LogMode(logger.Error) - err := db.Create(analysis).Error + err = db.Create(analysis).Error if err != nil { _ = ctx.Error(err) return @@ -1728,13 +1733,77 @@ func (h *AnalysisHandler) depIDs(ctx *gin.Context, f qf.Filter) (q *gorm.DB) { return } +// +// archive +// - Set the 'archived' flag. +// - Set the 'summary' field with archived issues. +// - Delete issues. +// - Delete dependencies. +func (h *AnalysisHandler) archive(ctx *gin.Context) (err error) { + appId := h.pk(ctx) + var unarchived []model.Analysis + db := h.DB(ctx) + db = db.Where("ApplicationID", appId) + db = db.Where("Archived", false) + err = db.Find(&unarchived).Error + if err != nil { + return + } + for _, m := range unarchived { + db := h.DB(ctx) + db = db.Select( + "i.RuleSet", + "i.Rule", + "i.Name", + "i.Description", + "i.Category", + "i.Effort", + "COUNT(n.ID) Incidents") + db = db.Table("Issue i,") + db = db.Joins("Incident n") + db = db.Where("n.IssueID = i.ID") + db = db.Where("i.AnalysisID", m.ID) + db = db.Group("i.ID") + summary := []ArchivedIssue{} + err = db.Scan(&summary).Error + if err != nil { + return + } + db = h.DB(ctx) + db = db.Model(m) + db = db.Omit(clause.Associations) + m.Archived = true + m.Summary, _ = json.Marshal(summary) + err = db.Updates(h.fields(&m)).Error + if err != nil { + return + } + db = h.DB(ctx) + db = db.Where("AnalysisID", m.ID) + err = db.Delete(&model.Issue{}).Error + if err != nil { + return + } + db = h.DB(ctx) + db = db.Where("AnalysisID", m.ID) + err = db.Delete(&model.TechDependency{}).Error + if err != nil { + return + } + } + + return +} + // // Analysis REST resource. type Analysis struct { Resource `yaml:",inline"` Effort int `json:"effort"` + Archived bool `json:"archived,omitempty" yaml:",omitempty"` Issues []Issue `json:"issues,omitempty" yaml:",omitempty"` Dependencies []TechDependency `json:"dependencies,omitempty" yaml:",omitempty"` + Summary []ArchivedIssue `json:"summary,omitempty" yaml:",omitempty"` } // @@ -1742,6 +1811,7 @@ type Analysis struct { func (r *Analysis) With(m *model.Analysis) { r.Resource.With(&m.Model) r.Effort = m.Effort + r.Archived = m.Archived r.Issues = []Issue{} for i := range m.Issues { n := Issue{} @@ -1758,6 +1828,9 @@ func (r *Analysis) With(m *model.Analysis) { r.Dependencies, n) } + if m.Summary != nil { + _ = json.Unmarshal(m.Summary, &r.Summary) + } } // @@ -1932,6 +2005,10 @@ type Link struct { Title string `json:"title,omitempty" yaml:",omitempty"` } +// +// ArchivedIssue created when issues are archived. +type ArchivedIssue model.ArchivedIssue + // // RuleReport REST resource. type RuleReport struct { diff --git a/migration/pkg.go b/migration/pkg.go index c82490c0e..bf242d6e7 100644 --- a/migration/pkg.go +++ b/migration/pkg.go @@ -2,6 +2,7 @@ package migration import ( "github.com/jortel/go-utils/logr" + v10 "github.com/konveyor/tackle2-hub/migration/v10" "github.com/konveyor/tackle2-hub/migration/v2" v3 "github.com/konveyor/tackle2-hub/migration/v3" v4 "github.com/konveyor/tackle2-hub/migration/v4" @@ -51,5 +52,6 @@ func All() []Migration { v7.Migration{}, v8.Migration{}, v9.Migration{}, + v10.Migration{}, } } diff --git a/migration/v10/migrate.go b/migration/v10/migrate.go new file mode 100644 index 000000000..14dbc8a3f --- /dev/null +++ b/migration/v10/migrate.go @@ -0,0 +1,20 @@ +package v10 + +import ( + "github.com/jortel/go-utils/logr" + "github.com/konveyor/tackle2-hub/migration/v10/model" + "gorm.io/gorm" +) + +var log = logr.WithName("migration|v9") + +type Migration struct{} + +func (r Migration) Apply(db *gorm.DB) (err error) { + err = db.AutoMigrate(r.Models()...) + return +} + +func (r Migration) Models() []interface{} { + return model.All() +} diff --git a/migration/v10/model/analysis.go b/migration/v10/model/analysis.go new file mode 100644 index 000000000..baa7781df --- /dev/null +++ b/migration/v10/model/analysis.go @@ -0,0 +1,78 @@ +package model + +// +// Analysis report. +type Analysis struct { + Model + Effort int + Archived bool `json:"archived"` + Summary JSON `gorm:"type:json"` + Issues []Issue `gorm:"constraint:OnDelete:CASCADE"` + Dependencies []TechDependency `gorm:"constraint:OnDelete:CASCADE"` + ApplicationID uint `gorm:"index;not null"` + Application *Application +} + +// +// TechDependency report dependency. +type TechDependency struct { + Model + Provider string `gorm:"uniqueIndex:depA"` + Name string `gorm:"uniqueIndex:depA"` + Version string `gorm:"uniqueIndex:depA"` + SHA string `gorm:"uniqueIndex:depA"` + Indirect bool + Labels JSON `gorm:"type:json"` + AnalysisID uint `gorm:"index;uniqueIndex:depA;not null"` + Analysis *Analysis +} + +// +// Issue report issue (violation). +type Issue struct { + Model + RuleSet string `gorm:"uniqueIndex:issueA;not null"` + Rule string `gorm:"uniqueIndex:issueA;not null"` + Name string `gorm:"index"` + Description string + Category string `gorm:"index;not null"` + Incidents []Incident `gorm:"foreignKey:IssueID;constraint:OnDelete:CASCADE"` + Links JSON `gorm:"type:json"` + Facts JSON `gorm:"type:json"` + Labels JSON `gorm:"type:json"` + Effort int `gorm:"index;not null"` + AnalysisID uint `gorm:"index;uniqueIndex:issueA;not null"` + Analysis *Analysis +} + +// +// Incident report an issue incident. +type Incident struct { + Model + File string `gorm:"index;not null"` + Line int + Message string + CodeSnip string + Facts JSON `gorm:"type:json"` + IssueID uint `gorm:"index;not null"` + Issue *Issue +} + +// +// Link URL link. +type Link struct { + URL string `json:"url"` + Title string `json:"title,omitempty"` +} + +// +// ArchivedIssue resource created when issues are archived. +type ArchivedIssue struct { + RuleSet string `json:"ruleSet"` + Rule string `json:"rule"` + Name string `json:"name,omitempty" yaml:",omitempty"` + Description string `json:"description,omitempty" yaml:",omitempty"` + Category string `json:"category"` + Effort int `json:"effort"` + Incidents int `json:"incidents"` +} diff --git a/migration/v10/model/pkg.go b/migration/v10/model/pkg.go new file mode 100644 index 000000000..4d66fc80b --- /dev/null +++ b/migration/v10/model/pkg.go @@ -0,0 +1,87 @@ +package model + +import "github.com/konveyor/tackle2-hub/migration/v9/model" + +// +// JSON field (data) type. +type JSON = []byte + +type Model = model.Model +type Application = model.Application +type Archetype = model.Archetype +type Assessment = model.Assessment +type Bucket = model.Bucket +type BucketOwner = model.BucketOwner +type BusinessService = model.BusinessService +type Dependency = model.Dependency +type File = model.File +type Fact = model.Fact +type Identity = model.Identity +type Import = model.Import +type ImportSummary = model.ImportSummary +type ImportTag = model.ImportTag +type JobFunction = model.JobFunction +type MigrationWave = model.MigrationWave +type Proxy = model.Proxy +type Questionnaire = model.Questionnaire +type Review = model.Review +type Setting = model.Setting +type RuleSet = model.RuleSet +type Rule = model.Rule +type Stakeholder = model.Stakeholder +type StakeholderGroup = model.StakeholderGroup +type Tag = model.Tag +type TagCategory = model.TagCategory +type Target = model.Target +type Task = model.Task +type TaskGroup = model.TaskGroup +type TaskReport = model.TaskReport +type Ticket = model.Ticket +type Tracker = model.Tracker +type TTL = model.TTL +type ApplicationTag = model.ApplicationTag +type DependencyCyclicError = model.DependencyCyclicError + +// +// All builds all models. +// Models are enumerated such that each are listed after +// all the other models on which they may depend. +func All() []interface{} { + return []interface{}{ + Application{}, + TechDependency{}, + Incident{}, + Analysis{}, + Issue{}, + Bucket{}, + BusinessService{}, + Dependency{}, + File{}, + Fact{}, + Identity{}, + Import{}, + ImportSummary{}, + ImportTag{}, + JobFunction{}, + MigrationWave{}, + Proxy{}, + Review{}, + Setting{}, + RuleSet{}, + Rule{}, + Stakeholder{}, + StakeholderGroup{}, + Tag{}, + TagCategory{}, + Target{}, + Task{}, + TaskGroup{}, + TaskReport{}, + Ticket{}, + Tracker{}, + ApplicationTag{}, + Questionnaire{}, + Assessment{}, + Archetype{}, + } +} diff --git a/model/pkg.go b/model/pkg.go index 60361f4fb..3277bf076 100644 --- a/model/pkg.go +++ b/model/pkg.go @@ -1,7 +1,7 @@ package model import ( - "github.com/konveyor/tackle2-hub/migration/v9/model" + "github.com/konveyor/tackle2-hub/migration/v10/model" "gorm.io/datatypes" ) @@ -18,6 +18,7 @@ type Assessment = model.Assessment type TechDependency = model.TechDependency type Incident = model.Incident type Analysis = model.Analysis +type ArchivedIssue = model.ArchivedIssue type Issue = model.Issue type Bucket = model.Bucket type BucketOwner = model.BucketOwner