Skip to content

Commit

Permalink
✨ Add confidence and risk to Application and Archetype resources. (
Browse files Browse the repository at this point in the history
…#530)

* Add `confidence` and `risk` fields to Application and Archetype
resources. Overall confidence calculated using the algorithm from
Pathfinder for each assessment, then summed and divided by total number
of assessments. Overall risk level is `green` if all assessments are
`green`, otherwise the overall risk is `red`, `unknown`, or `yellow` in
that priority order if any assessments have that risk level.
* Moved some assessment status logic out of the resource and into the
assessment package where it belongs.
* Added ArchetypeResolver to better encapsulate the related behaviors
and for symmetry with ApplicationResolver.
* Simplified the Application and Archetype endpoints by replacing
several related resource `With*` methods with `WithResolver`.
* Improved consistency of the APIs within the `assessment` package.

---------

Signed-off-by: Sam Lucidi <slucidi@redhat.com>
  • Loading branch information
mansam committed Oct 19, 2023
1 parent 5aa81a9 commit 77fa5af
Show file tree
Hide file tree
Showing 10 changed files with 416 additions and 240 deletions.
107 changes: 45 additions & 62 deletions api/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,28 +134,13 @@ func (h ApplicationHandler) Get(ctx *gin.Context) {
return
}
resolver := assessment.NewApplicationResolver(m, tagsResolver, membership, questionnaire)
archetypes, err := resolver.Archetypes()
if err != nil {
_ = ctx.Error(err)
return
}
archetypeTags, err := resolver.ArchetypeTags()
if err != nil {
_ = ctx.Error(err)
return
}

r := Application{}
r.With(m, tags)
r.WithArchetypes(archetypes)
r.WithVirtualTags(archetypeTags, SourceArchetype)
r.WithVirtualTags(resolver.AssessmentTags(), SourceAssessment)
r.Assessed, err = resolver.Assessed()
err = r.WithResolver(resolver)
if err != nil {
_ = ctx.Error(err)
return
}

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

Expand Down Expand Up @@ -198,22 +183,9 @@ func (h ApplicationHandler) List(ctx *gin.Context) {
return
}
resolver := assessment.NewApplicationResolver(&list[i], tagsResolver, membership, questionnaire)
archetypes, aErr := resolver.Archetypes()
if aErr != nil {
_ = ctx.Error(aErr)
return
}
archetypeTags, aErr := resolver.ArchetypeTags()
if aErr != nil {
_ = ctx.Error(aErr)
return
}
r := Application{}
r.With(&list[i], tags)
r.WithArchetypes(archetypes)
r.WithVirtualTags(archetypeTags, SourceArchetype)
r.WithVirtualTags(resolver.AssessmentTags(), SourceAssessment)
r.Assessed, err = resolver.Assessed()
err = r.WithResolver(resolver)
if err != nil {
_ = ctx.Error(err)
return
Expand Down Expand Up @@ -274,27 +246,12 @@ func (h ApplicationHandler) Create(ctx *gin.Context) {
return
}
resolver := assessment.NewApplicationResolver(m, tagsResolver, membership, questionnaire)
archetypes, err := resolver.Archetypes()
if err != nil {
_ = ctx.Error(err)
return
}
archetypeTags, err := resolver.ArchetypeTags()
if err != nil {
_ = ctx.Error(err)
return
}

r.With(m, tags)
r.WithArchetypes(archetypes)
r.WithVirtualTags(archetypeTags, SourceArchetype)
r.WithVirtualTags(resolver.AssessmentTags(), SourceArchetype)
r.Assessed, err = resolver.Assessed()
err = r.WithResolver(resolver)
if err != nil {
_ = ctx.Error(err)
return
}

h.Respond(ctx, http.StatusCreated, r)
}

Expand Down Expand Up @@ -1018,12 +975,12 @@ func (h ApplicationHandler) AssessmentList(ctx *gin.Context) {
_ = ctx.Error(err)
return
}

assessments := m.Assessments
for _, a := range archetypes {
assessments = append(assessments, a.Assessments...)
for _, arch := range archetypes {
for _, a := range arch.Assessments {
assessments = append(assessments, *a.Assessment)
}
}

resources := []Assessment{}
for i := range assessments {
r := Assessment{}
Expand Down Expand Up @@ -1112,13 +1069,15 @@ type Application struct {
Comments string `json:"comments"`
Identities []Ref `json:"identities"`
Tags []TagRef `json:"tags"`
BusinessService *Ref `json:"businessService"`
BusinessService *Ref `json:"businessService" yaml:"businessService"`
Owner *Ref `json:"owner"`
Contributors []Ref `json:"contributors"`
MigrationWave *Ref `json:"migrationWave"`
MigrationWave *Ref `json:"migrationWave" yaml:"migrationWave"`
Archetypes []Ref `json:"archetypes"`
Assessments []Ref `json:"assessments"`
Assessed bool `json:"assessed"`
Risk string `json:"risk"`
Confidence int `json:"confidence"`
}

//
Expand Down Expand Up @@ -1168,16 +1127,6 @@ func (r *Application) With(m *model.Application, tags []model.ApplicationTag) {
}
}

//
// WithArchetypes updates the resource with archetypes.
func (r *Application) WithArchetypes(archetypes []model.Archetype) {
for _, a := range archetypes {
ref := Ref{}
ref.With(a.ID, a.Name)
r.Archetypes = append(r.Archetypes, ref)
}
}

//
// WithVirtualTags updates the resource with tags derived from assessments.
func (r *Application) WithVirtualTags(tags []model.Tag, source string) {
Expand All @@ -1188,6 +1137,40 @@ func (r *Application) WithVirtualTags(tags []model.Tag, source string) {
}
}

//
// WithResolver uses an ApplicationResolver to update the resource with
// values derived from the application's assessments and archetypes.
func (r *Application) WithResolver(resolver *assessment.ApplicationResolver) (err error) {
archetypes, err := resolver.Archetypes()
if err != nil {
return
}
for _, a := range archetypes {
ref := Ref{}
ref.With(a.ID, a.Name)
r.Archetypes = append(r.Archetypes, ref)
}
archetypeTags, err := resolver.ArchetypeTags()
if err != nil {
return
}
r.WithVirtualTags(archetypeTags, SourceArchetype)
r.WithVirtualTags(resolver.AssessmentTags(), SourceAssessment)
r.Assessed, err = resolver.Assessed()
if err != nil {
return
}
r.Confidence, err = resolver.Confidence()
if err != nil {
return
}
r.Risk, err = resolver.Risk()
if err != nil {
return
}
return
}

//
// Model builds a model.
func (r *Application) Model() (m *model.Application) {
Expand Down
57 changes: 23 additions & 34 deletions api/archetype.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,32 +57,24 @@ func (h ArchetypeHandler) Get(ctx *gin.Context) {
_ = ctx.Error(result.Error)
return
}

membership := assessment.NewMembershipResolver(h.DB(ctx))
applications, err := membership.Applications(m)
if err != nil {
_ = ctx.Error(err)
return
}

questionnaires, err := assessment.NewQuestionnaireResolver(h.DB(ctx))
if err != nil {
_ = ctx.Error(err)
return
}

tags, err := assessment.NewTagResolver(h.DB(ctx))
if err != nil {
_ = ctx.Error(err)
}

resolver := assessment.NewArchetypeResolver(m, tags)

resolver := assessment.NewArchetypeResolver(m, tags, membership, questionnaires)
r := Archetype{}
r.With(m)
r.WithApplications(applications)
r.WithAssessmentTags(resolver.AssessmentTags())
r.Assessed = questionnaires.Assessed(m.Assessments)
err = r.WithResolver(resolver)
if err != nil {
_ = ctx.Error(err)
return
}
h.Respond(ctx, http.StatusOK, r)
}

Expand All @@ -101,7 +93,6 @@ func (h ArchetypeHandler) List(ctx *gin.Context) {
_ = ctx.Error(result.Error)
return
}

questionnaires, err := assessment.NewQuestionnaireResolver(h.DB(ctx))
if err != nil {
_ = ctx.Error(err)
Expand All @@ -111,22 +102,18 @@ func (h ArchetypeHandler) List(ctx *gin.Context) {
if err != nil {
_ = ctx.Error(err)
}

membership := assessment.NewMembershipResolver(h.DB(ctx))
resources := []Archetype{}
for i := range list {
m := &list[i]
resolver := assessment.NewArchetypeResolver(m, tags, membership, questionnaires)
r := Archetype{}
applications, err := membership.Applications(m)
r.With(m)
err = r.WithResolver(resolver)
if err != nil {
_ = ctx.Error(err)
return
}
resolver := assessment.NewArchetypeResolver(m, tags)
r.With(m)
r.WithApplications(applications)
r.WithAssessmentTags(resolver.AssessmentTags())
r.Assessed = questionnaires.Assessed(m.Assessments)
resources = append(resources, r)
}

Expand Down Expand Up @@ -165,14 +152,13 @@ func (h ArchetypeHandler) Create(ctx *gin.Context) {
}

membership := assessment.NewMembershipResolver(h.DB(ctx))
applications, err := membership.Applications(m)
resolver := assessment.NewArchetypeResolver(m, nil, membership, nil)
r.With(m)
err = r.WithResolver(resolver)
if err != nil {
_ = ctx.Error(err)
return
}

r.With(m)
r.WithApplications(applications)
h.Respond(ctx, http.StatusCreated, r)
}

Expand Down Expand Up @@ -356,6 +342,8 @@ type Archetype struct {
Applications []Ref `json:"applications" yaml:"applications"`
Assessments []Ref `json:"assessments" yaml:"assessments"`
Assessed bool `json:"assessed"`
Risk string `json:"risk"`
Confidence int `json:"confidence"`
Review *Ref `json:"review"`
}

Expand Down Expand Up @@ -398,23 +386,24 @@ func (r *Archetype) With(m *model.Archetype) {
}

//
// WithApplications updates the Archetype resource with the applications.
func (r *Archetype) WithApplications(apps []model.Application) {
// WithResolver uses an ArchetypeResolver to update the resource with
// values derived from the archetype's assessments.
func (r *Archetype) WithResolver(resolver *assessment.ArchetypeResolver) (err error) {
r.Risk = resolver.Risk()
r.Confidence = resolver.Confidence()
r.Assessed = resolver.Assessed()
apps, err := resolver.Applications()
for i := range apps {
ref := Ref{}
ref.With(apps[i].ID, apps[i].Name)
r.Applications = append(r.Applications, ref)
}
}

//
// WithAssessmentTags updates the Archetype resource with tags inherited from assessments.
func (r *Archetype) WithAssessmentTags(tags []model.Tag) {
for _, t := range tags {
for _, t := range resolver.AssessmentTags() {
ref := TagRef{}
ref.With(t.ID, t.Name, SourceAssessment, true)
r.Tags = append(r.Tags, ref)
}
return
}

//
Expand Down
Loading

0 comments on commit 77fa5af

Please sign in to comment.