comply/internal/render/controller.go

153 lines
3.2 KiB
Go

package render
import (
"fmt"
"sort"
"time"
"github.com/strongdm/comply/internal/config"
"github.com/strongdm/comply/internal/model"
)
type project struct {
OrganizationName string
Name string
}
type stats struct {
ControlsTotal int
ControlsSatisfied int
ProcedureTotal int
ProcedureOpen int
ProcedureOldestDays int
AuditOpen int
AuditClosed int
AuditTotal int
}
type renderData struct {
// duplicates Project.OrganizationName
Name string
Project *project
Stats *stats
Narratives []*model.Narrative
Policies []*model.Policy
Procedures []*model.Procedure
Standards []*model.Standard
Tickets []*model.Ticket
Controls []*control
Links *model.TicketLinks
}
type control struct {
Standard string
ControlKey string
Name string
Description string
Satisfied bool
SatisfiedBy []string
}
func load() (*model.Data, *renderData, error) {
modelData, err := model.ReadData()
if err != nil {
return nil, nil, err
}
cfg := config.Config()
project := &project{
OrganizationName: cfg.Name,
Name: fmt.Sprintf("%s Compliance Program", cfg.Name),
}
satisfied := model.ControlsSatisfied(modelData)
controls := make([]*control, 0)
for _, standard := range modelData.Standards {
for key, c := range standard.Controls {
satisfactions, ok := satisfied[key]
satisfied := ok && len(satisfactions) > 0
controls = append(controls, &control{
Standard: standard.Name,
ControlKey: key,
Name: c.Name,
Description: c.Description,
Satisfied: satisfied,
SatisfiedBy: satisfactions,
})
}
}
sort.Slice(controls, func(i, j int) bool {
return controls[i].ControlKey < controls[j].ControlKey
})
rd := &renderData{}
rd.Narratives = modelData.Narratives
rd.Policies = modelData.Policies
rd.Procedures = modelData.Procedures
rd.Standards = modelData.Standards
rd.Tickets = modelData.Tickets
rd.Links = &model.TicketLinks{}
rd.Project = project
rd.Name = project.OrganizationName
rd.Controls = controls
// TODO: unhardcode plugin
tp := model.GetPlugin(model.GitHub)
if tp.Configured() {
links := tp.Links()
rd.Links = &links
}
return modelData, rd, nil
}
func loadWithStats() (*model.Data, *renderData, error) {
modelData, renderData, err := load()
if err != nil {
return nil, nil, err
}
addStats(modelData, renderData)
return modelData, renderData, nil
}
func addStats(modelData *model.Data, renderData *renderData) {
stats := &stats{}
satisfied := model.ControlsSatisfied(modelData)
for _, std := range renderData.Standards {
stats.ControlsTotal += len(std.Controls)
for controlKey := range std.Controls {
if _, ok := satisfied[controlKey]; ok {
stats.ControlsSatisfied++
}
}
}
for _, t := range renderData.Tickets {
if t.Bool("audit") {
stats.AuditTotal++
}
if t.State == model.Open {
if t.Bool("procedure") {
stats.ProcedureOpen++
if t.CreatedAt != nil {
age := int(time.Since(*t.CreatedAt).Hours() / float64(24))
if stats.ProcedureOldestDays < age {
stats.ProcedureOldestDays = age
}
}
}
if t.Bool("audit") {
stats.AuditOpen++
}
}
}
renderData.Stats = stats
}