Skip to content

Commit

Permalink
✨ Analysis archiving (#493)
Browse files Browse the repository at this point in the history
Add analysis archiving.

Adds (2) fields to the Analysis:
- Archived (bool)
- Summary JSON which contains the analysis summary when archived.

When an analysis is created, all previously _unarchived_
(archived=FALSE) resources will be archived. There should not be more
than one but the logic will do them all just to be sure. Archived is:
- Summary is created. This is an array of archived issues.
- Archived flag set.
- Issues deleted (cascade delete incidents).
- TechDependencies deleted.

fixes: #428

---------

Signed-off-by: Jeff Ortel <jortel@redhat.com>
  • Loading branch information
jortel authored Sep 27, 2023
1 parent d850515 commit bb3d5f0
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 2 deletions.
79 changes: 78 additions & 1 deletion api/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1728,20 +1733,85 @@ 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"`
}

//
// With updates the resource with the model.
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{}
Expand All @@ -1758,6 +1828,9 @@ func (r *Analysis) With(m *model.Analysis) {
r.Dependencies,
n)
}
if m.Summary != nil {
_ = json.Unmarshal(m.Summary, &r.Summary)
}
}

//
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions migration/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -51,5 +52,6 @@ func All() []Migration {
v7.Migration{},
v8.Migration{},
v9.Migration{},
v10.Migration{},
}
}
20 changes: 20 additions & 0 deletions migration/v10/migrate.go
Original file line number Diff line number Diff line change
@@ -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()
}
78 changes: 78 additions & 0 deletions migration/v10/model/analysis.go
Original file line number Diff line number Diff line change
@@ -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"`
}
87 changes: 87 additions & 0 deletions migration/v10/model/pkg.go
Original file line number Diff line number Diff line change
@@ -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{},
}
}
3 changes: 2 additions & 1 deletion model/pkg.go
Original file line number Diff line number Diff line change
@@ -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"
)

Expand All @@ -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
Expand Down

0 comments on commit bb3d5f0

Please sign in to comment.