Skip to content

Commit

Permalink
✨ Add Dep.provider. (#421)
Browse files Browse the repository at this point in the history
Add Provider to: TechDependency model and REST resources.

Add _new_ report to list applications with a specific dependency
identified by (provider,name, version,sha).

Needed to change route: 
```
/analyses/report/applications
```
To (for symmetry): 
```
/analyses/report/issues/applications
```

Adds route: 
```
/analyses/report/dependencies/applications
```

Adds migration 6.

Unrelated:
Fix next-migration script.
Add repository to hack/add/application.sh
Fixes unique index (issueA) on Issue to include AnalysisID. RuleSet,Rule
must be unique **within** an analysis.
Fixes unique index (depA) on TechDependency to include AnalysisID.
Provider,Name,Version,SHA must be unique **within** an analysis.

---

Routes:
```
/analyses/:id             
/analyses/dependencies    
/analyses/issues          
/analyses/issues/:id      
/analyses/issues/:id/incidents 
/analyses/report/rules    
/analyses/report/applications/:id/issues 
/analyses/report/issues/applications 
/analyses/report/issues/:id/files 
/analyses/report/dependencies 
/analyses/report/dependencies/applications
```

---------

Signed-off-by: Jeff Ortel <jortel@redhat.com>
  • Loading branch information
jortel authored Jun 27, 2023
1 parent d1b4e46 commit 1747a20
Show file tree
Hide file tree
Showing 8 changed files with 389 additions and 26 deletions.
233 changes: 208 additions & 25 deletions api/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,24 @@ import (
//
// Routes
const (
AnalysesRoot = "/analyses"
AnalysisRoot = AnalysesRoot + "/:" + ID
AnalysesDepsRoot = AnalysesRoot + "/dependencies"
AnalysesIssuesRoot = AnalysesRoot + "/issues"
AnalysesIssueRoot = AnalysesIssuesRoot + "/:" + ID
AnalysisIncidentsRoot = AnalysesIssueRoot + "/incidents"
AnalysesReportRoot = AnalysesRoot + "/report"
AnalysisReportDepRoot = AnalysesReportRoot + "/dependencies"
AnalysisReportRuleRoot = AnalysesReportRoot + "/rules"
AnalysisReportAppRoot = AnalysesReportRoot + "/applications"
AnalysisReportIssueRoot = AnalysisReportAppRoot + "/:" + ID + "/issues"
AnalysisReportFileRoot = AnalysesReportRoot + "/issues/:" + ID + "/files"

AnalysesRoot = "/analyses"
AnalysisRoot = AnalysesRoot + "/:" + ID
AnalysesDepsRoot = AnalysesRoot + "/dependencies"
AnalysesIssuesRoot = AnalysesRoot + "/issues"
AnalysesIssueRoot = AnalysesIssuesRoot + "/:" + ID
AnalysisIncidentsRoot = AnalysesIssueRoot + "/incidents"
//
AnalysesReportRoot = AnalysesRoot + "/report"
AnalysisReportDepsRoot = AnalysesReportRoot + "/dependencies"
AnalysisReportRuleRoot = AnalysesReportRoot + "/rules"
AnalysisReportIssuesRoot = AnalysesReportRoot + "/issues"
AnalysisReportAppsRoot = AnalysesReportRoot + "/applications"
AnalysisReportIssueRoot = AnalysisReportIssuesRoot + "/:" + ID
AnalysisReportIssuesAppsRoot = AnalysisReportIssuesRoot + "/applications"
AnalysisReportDepsAppsRoot = AnalysisReportDepsRoot + "/applications"
AnalysisReportAppsIssuesRoot = AnalysisReportAppsRoot + "/:" + ID + "/issues"
AnalysisReportFileRoot = AnalysisReportIssueRoot + "/files"
//
AppAnalysesRoot = ApplicationRoot + "/analyses"
AppAnalysisRoot = ApplicationRoot + "/analysis"
AppAnalysisDepsRoot = AppAnalysisRoot + "/dependencies"
Expand Down Expand Up @@ -58,11 +63,13 @@ func (h AnalysisHandler) AddRoutes(e *gin.Engine) {
routeGroup.GET(AnalysesIssuesRoot, h.Issues)
routeGroup.GET(AnalysesIssueRoot, h.Issue)
routeGroup.GET(AnalysisIncidentsRoot, h.Incidents)
//
routeGroup.GET(AnalysisReportRuleRoot, h.RuleReports)
routeGroup.GET(AnalysisReportIssueRoot, h.IssueReports)
routeGroup.GET(AnalysisReportAppRoot, h.AppReports)
routeGroup.GET(AnalysisReportAppsIssuesRoot, h.AppIssueReports)
routeGroup.GET(AnalysisReportIssuesAppsRoot, h.IssueAppReports)
routeGroup.GET(AnalysisReportFileRoot, h.FileReports)
routeGroup.GET(AnalysisReportDepRoot, h.DepReports)
routeGroup.GET(AnalysisReportDepsRoot, h.DepReports)
routeGroup.GET(AnalysisReportDepsAppsRoot, h.DepAppReports)
//
routeGroup.POST(AppAnalysesRoot, h.AppCreate)
routeGroup.GET(AppAnalysesRoot, h.AppList)
Expand Down Expand Up @@ -815,7 +822,7 @@ func (h AnalysisHandler) RuleReports(ctx *gin.Context) {
h.Respond(ctx, http.StatusOK, resources)
}

// IssueReports godoc
// AppIssueReports godoc
// @summary List application issue reports.
// @description Each report collates issues by ruleset/rule.
// @description filters:
Expand All @@ -835,7 +842,7 @@ func (h AnalysisHandler) RuleReports(ctx *gin.Context) {
// @success 200 {object} []api.IssueReport
// @router /analyses/report/applications/{id}/issues [get]
// @param id path string true "Application ID"
func (h AnalysisHandler) IssueReports(ctx *gin.Context) {
func (h AnalysisHandler) AppIssueReports(ctx *gin.Context) {
resources := []*IssueReport{}
type M struct {
model.Issue
Expand Down Expand Up @@ -934,7 +941,7 @@ func (h AnalysisHandler) IssueReports(ctx *gin.Context) {
h.Respond(ctx, http.StatusOK, resources)
}

// AppReports godoc
// IssueAppReports godoc
// @summary List application reports.
// @description List application reports.
// @description filters:
Expand Down Expand Up @@ -964,12 +971,12 @@ func (h AnalysisHandler) IssueReports(ctx *gin.Context) {
// @description - effort
// @description - incidents
// @description - files
// @tags appreports
// @tags issueappreports
// @produce json
// @success 200 {object} []api.AppReport
// @router /analyses/report/applications [get]
func (h AnalysisHandler) AppReports(ctx *gin.Context) {
resources := []AppReport{}
func (h AnalysisHandler) IssueAppReports(ctx *gin.Context) {
resources := []IssueAppReport{}
type M struct {
ID uint
Name string
Expand Down Expand Up @@ -1069,7 +1076,7 @@ func (h AnalysisHandler) AppReports(ctx *gin.Context) {
// Render
for i := range list {
m := &list[i]
r := AppReport{}
r := IssueAppReport{}
r.ID = m.ID
r.Name = m.Name
r.Description = m.Description
Expand Down Expand Up @@ -1233,6 +1240,7 @@ func (h AnalysisHandler) Deps(ctx *gin.Context) {
}
// Find
db := h.DB(ctx)
db = db.Model(&model.TechDependency{})
db = db.Where("AnalysisID IN (?)", h.analysisIDs(ctx, filter))
db = db.Where("ID IN (?)", h.depIDs(ctx, filter))
db = sort.Sorted(db)
Expand Down Expand Up @@ -1271,6 +1279,7 @@ func (h AnalysisHandler) Deps(ctx *gin.Context) {
// @summary List dependency reports.
// @description Each report collates dependencies by name and SHA.
// @description filters:
// @description - provider
// @description - name
// @description - version
// @description - sha
Expand All @@ -1282,6 +1291,7 @@ func (h AnalysisHandler) Deps(ctx *gin.Context) {
// @description - businessService.name
// @description - tag.id
// @description sort:
// @description - provider
// @description - name
// @description - version
// @description - sha
Expand All @@ -1298,6 +1308,7 @@ func (h AnalysisHandler) DepReports(ctx *gin.Context) {
// Filter
filter, err := qf.New(ctx,
[]qf.Assert{
{Field: "provider", Kind: qf.STRING},
{Field: "name", Kind: qf.STRING},
{Field: "version", Kind: qf.STRING},
{Field: "sha", Kind: qf.STRING},
Expand Down Expand Up @@ -1377,6 +1388,157 @@ func (h AnalysisHandler) DepReports(ctx *gin.Context) {
h.Respond(ctx, http.StatusOK, resources)
}

// DepAppReports godoc
// @summary List application reports.
// @description List application reports.
// @description filters:
// @description - id
// @description - name
// @description - description
// @description - businessService
// @description - provider
// @description - name
// @description - version
// @description - sha
// @description - indirect
// @description - dep.provider
// @description - dep.name
// @description - dep.version
// @description - dep.sha
// @description - dep.indirect
// @description - dep.labels
// @description - application.id
// @description - application.name
// @description - businessService.id
// @description - businessService.name
// @description sort:
// @description - name
// @description - description
// @description - businessService
// @description - provider
// @description - name
// @description - version
// @description - sha
// @description - indirect
// @tags depappreports
// @produce json
// @success 200 {object} []api.AppReport
// @router /analyses/report/applications [get]
func (h AnalysisHandler) DepAppReports(ctx *gin.Context) {
resources := []DepAppReport{}
type M struct {
ID uint
Name string
Description string
BusinessService string
DepID uint
Provider string
DepName string
Version string
SHA string
Indirect bool
}
// Filter
filter, err := qf.New(ctx,
[]qf.Assert{
{Field: "id", Kind: qf.STRING},
{Field: "name", Kind: qf.STRING},
{Field: "description", Kind: qf.STRING},
{Field: "businessService", Kind: qf.STRING},
{Field: "provider", Kind: qf.LITERAL},
{Field: "name", Kind: qf.LITERAL},
{Field: "version", Kind: qf.LITERAL},
{Field: "sha", Kind: qf.LITERAL},
{Field: "indirect", Kind: qf.LITERAL},
{Field: "dep.provider", Kind: qf.LITERAL},
{Field: "dep.name", Kind: qf.LITERAL},
{Field: "dep.version", Kind: qf.LITERAL},
{Field: "dep.sha", Kind: qf.LITERAL},
{Field: "dep.indirect", Kind: qf.LITERAL},
{Field: "dep.labels", Kind: qf.LITERAL},
{Field: "application.id", Kind: qf.LITERAL},
{Field: "application.name", Kind: qf.STRING},
{Field: "businessService.id", Kind: qf.LITERAL},
{Field: "businessService.name", Kind: qf.STRING},
{Field: "tag.id", Kind: qf.LITERAL, Relation: true},
})
if err != nil {
_ = ctx.Error(err)
return
}
// Sort
sort := Sort{}
err = sort.With(ctx, &M{})
if err != nil {
_ = ctx.Error(err)
return
}
// Inner Query
q := h.DB(ctx)
q = q.Select(
"app.ID",
"app.Name",
"app.Description",
"b.Name BusinessService",
"d.ID DepID",
"d.Provider",
"d.Name DepName",
"d.Version",
"d.SHA",
"d.Indirect")
q = q.Table("TechDependency d")
q = q.Joins("LEFT JOIN Analysis a ON a.ID = d.AnalysisID")
q = q.Joins("LEFT JOIN Application app ON app.ID = a.ApplicationID")
q = q.Joins("LEFT OUTER JOIN BusinessService b ON b.ID = app.BusinessServiceID")
q = q.Where("a.ID IN (?)", h.analysisIDs(ctx, filter))
q = q.Where("d.ID IN (?)", h.depIDs(ctx, filter.Resource("dep")))
// Find
db := h.DB(ctx)
db = db.Select("*")
db = db.Table("(?)", q)
db = filter.Where(db)
db = sort.Sorted(db)
var list []M
var m M
page := Page{}
page.With(ctx)
cursor := Cursor{}
cursor.With(db, page)
defer func() {
cursor.Close()
}()
for cursor.Next(&m) {
if cursor.Error != nil {
_ = ctx.Error(cursor.Error)
return
}
list = append(list, m)
}
err = h.WithCount(ctx, cursor.Count())
if err != nil {
_ = ctx.Error(err)
return
}
// Render
for i := range list {
m := &list[i]
r := DepAppReport{}
r.ID = m.ID
r.Name = m.Name
r.Description = m.Description
r.BusinessService = m.BusinessService
r.Dependency.ID = m.DepID
r.Dependency.Provider = m.Provider
r.Dependency.Name = m.DepName
r.Dependency.Version = m.Version
r.Dependency.SHA = m.SHA
r.Dependency.Indirect = m.Indirect
resources = append(resources, r)
}

h.Respond(ctx, http.StatusOK, resources)
}

//
// appIDs provides application IDs.
// filter:
Expand Down Expand Up @@ -1630,6 +1792,7 @@ func (r *Issue) Model() (m *model.Issue) {
// TechDependency REST resource.
type TechDependency struct {
Resource `yaml:",inline"`
Provider string `json:"provider" yaml:",omitempty"`
Name string `json:"name" binding:"required"`
Version string `json:"version,omitempty" yaml:",omitempty"`
Indirect bool `json:"indirect,omitempty" yaml:",omitempty"`
Expand All @@ -1641,6 +1804,7 @@ type TechDependency struct {
// With updates the resource with the model.
func (r *TechDependency) With(m *model.TechDependency) {
r.Resource.With(&m.Model)
r.Provider = m.Provider
r.Name = m.Name
r.Version = m.Version
r.Indirect = m.Indirect
Expand All @@ -1656,6 +1820,7 @@ func (r *TechDependency) Model() (m *model.TechDependency) {
m = &model.TechDependency{}
m.Name = r.Name
m.Version = r.Version
m.Provider = r.Provider
m.Indirect = r.Indirect
m.Labels, _ = json.Marshal(r.Labels)
m.SHA = r.SHA
Expand Down Expand Up @@ -1732,8 +1897,8 @@ type IssueReport struct {
}

//
// AppReport REST resource.
type AppReport struct {
// IssueAppReport REST resource.
type IssueAppReport struct {
ID uint `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Expand Down Expand Up @@ -1761,13 +1926,31 @@ type FileReport struct {
//
// DepReport REST resource.
type DepReport struct {
Provider string `json:"provider"`
Name string `json:"name"`
Version string `json:"version"`
SHA string `json:"sha"`
Labels []string `json:"labels"`
Applications int `json:"applications"`
}

//
// DepAppReport REST resource.
type DepAppReport struct {
ID uint `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
BusinessService string `json:"businessService"`
Dependency struct {
ID uint `json:"id"`
Provider string `json:"provider"`
Name string `json:"name"`
Version string `json:"version"`
SHA string `json:"rule"`
Indirect bool `json:"indirect"`
} `json:"dependency"`
}

//
// FactMap map.
type FactMap map[string]interface{}
3 changes: 3 additions & 0 deletions hack/add/application.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ name: Dog
description: Dog application.
businessService:
id: 1
repository:
kind: git
url: https://github.com/WASdev/sample.daytrader7.git
identities:
- id: 1
- id: 2
Expand Down
4 changes: 4 additions & 0 deletions hack/next-migration.sh
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ pkg=$(cat << EOF
package model
import "${importRoot}/${current}/model"
//
// JSON field (data) type.
type JSON = []byte
EOF
)

Expand Down
2 changes: 2 additions & 0 deletions migration/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
v3 "github.com/konveyor/tackle2-hub/migration/v3"
v4 "github.com/konveyor/tackle2-hub/migration/v4"
v5 "github.com/konveyor/tackle2-hub/migration/v5"
v6 "github.com/konveyor/tackle2-hub/migration/v6"
"github.com/konveyor/tackle2-hub/settings"
"gorm.io/gorm"
)
Expand Down Expand Up @@ -43,5 +44,6 @@ func All() []Migration {
v3.Migration{},
v4.Migration{},
v5.Migration{},
v6.Migration{},
}
}
Loading

0 comments on commit 1747a20

Please sign in to comment.