diff --git a/internal/model/fs.go b/internal/model/fs.go index a1c27b1..cdf2693 100644 --- a/internal/model/fs.go +++ b/internal/model/fs.go @@ -91,8 +91,8 @@ func ReadStandards() ([]*Standard, error) { } // ReadNarratives loads narrative descriptions from the filesystem. -func ReadNarratives() ([]*Narrative, error) { - var narratives []*Narrative +func ReadNarratives() ([]*Document, error) { + var narratives []*Document files, err := path.Narratives() if err != nil { @@ -100,7 +100,7 @@ func ReadNarratives() ([]*Narrative, error) { } for _, f := range files { - n := &Narrative{} + n := &Document{} mdmd := loadMDMD(f.FullPath) err = yaml.Unmarshal([]byte(mdmd.yaml), &n) if err != nil { @@ -141,8 +141,8 @@ func ReadProcedures() ([]*Procedure, error) { } // ReadPolicies loads policy documents from the filesystem. -func ReadPolicies() ([]*Policy, error) { - var policies []*Policy +func ReadPolicies() ([]*Document, error) { + var policies []*Document files, err := path.Policies() if err != nil { @@ -150,7 +150,7 @@ func ReadPolicies() ([]*Policy, error) { } for _, f := range files { - p := &Policy{} + p := &Document{} mdmd := loadMDMD(f.FullPath) err = yaml.Unmarshal([]byte(mdmd.yaml), &p) if err != nil { diff --git a/internal/model/model.go b/internal/model/model.go index fccdee1..6ed2c35 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -2,8 +2,8 @@ package model type Data struct { Standards []*Standard - Narratives []*Narrative - Policies []*Policy + Narratives []*Document + Policies []*Document Procedures []*Procedure Tickets []*Ticket Audits []*Audit diff --git a/internal/model/narrative.go b/internal/model/narrative.go deleted file mode 100644 index d9d2dcd..0000000 --- a/internal/model/narrative.go +++ /dev/null @@ -1,15 +0,0 @@ -package model - -import "time" - -type Narrative struct { - Name string `yaml:"name"` - Acronym string `yaml:"acronym"` - - Revisions []Revision `yaml:"majorRevisions"` - Satisfies Satisfaction `yaml:"satisfies"` - FullPath string - OutputFilename string - ModifiedAt time.Time - Body string -} diff --git a/internal/model/policy.go b/internal/model/policy.go deleted file mode 100644 index dbec304..0000000 --- a/internal/model/policy.go +++ /dev/null @@ -1,15 +0,0 @@ -package model - -import "time" - -type Policy struct { - Name string `yaml:"name"` - Acronym string `yaml:"acronym"` - - Revisions []Revision `yaml:"majorRevisions"` - Satisfies Satisfaction `yaml:"satisfies"` - FullPath string - OutputFilename string - ModifiedAt time.Time - Body string -} diff --git a/internal/render/controller.go b/internal/render/controller.go index 1eb5411..2778e54 100644 --- a/internal/render/controller.go +++ b/internal/render/controller.go @@ -32,8 +32,8 @@ type renderData struct { Name string Project *project Stats *stats - Narratives []*model.Narrative - Policies []*model.Policy + Narratives []*model.Document + Policies []*model.Document Procedures []*model.Procedure Standards []*model.Standard Tickets []*model.Ticket diff --git a/internal/render/policy.go b/internal/render/document.go similarity index 85% rename from internal/render/policy.go rename to internal/render/document.go index afabadb..da5fe40 100644 --- a/internal/render/policy.go +++ b/internal/render/document.go @@ -17,20 +17,20 @@ import ( ) // TODO: refactor and eliminate duplication among narrative, policy renderers -func renderPolicyToDisk(wg *sync.WaitGroup, errOutputCh chan error, data *renderData, policy *model.Policy, live bool) { +func renderToFilesystem(wg *sync.WaitGroup, errOutputCh chan error, data *renderData, doc *model.Document, live bool) { // only files that have been touched - if !isNewer(policy.FullPath, policy.ModifiedAt) { + if !isNewer(doc.FullPath, doc.ModifiedAt) { return } - recordModified(policy.FullPath, policy.ModifiedAt) + recordModified(doc.FullPath, doc.ModifiedAt) wg.Add(1) - go func(p *model.Policy) { + go func(p *model.Document) { defer wg.Done() outputFilename := p.OutputFilename // save preprocessed markdown - err := preprocessPolicy(data, p, filepath.Join(".", "output", outputFilename+".md")) + err := preprocessDoc(data, p, filepath.Join(".", "output", outputFilename+".md")) if err != nil { errOutputCh <- errors.Wrap(err, "unable to preprocess") return @@ -50,10 +50,10 @@ func renderPolicyToDisk(wg *sync.WaitGroup, errOutputCh chan error, data *render rel = p.FullPath } fmt.Printf("%s -> %s\n", rel, filepath.Join("output", p.OutputFilename)) - }(policy) + }(doc) } -func preprocessPolicy(data *renderData, pol *model.Policy, fullPath string) error { +func preprocessDoc(data *renderData, pol *model.Document, fullPath string) error { cfg := config.Config() var w bytes.Buffer diff --git a/internal/render/narrative.go b/internal/render/narrative.go deleted file mode 100644 index 3aeaff7..0000000 --- a/internal/render/narrative.go +++ /dev/null @@ -1,124 +0,0 @@ -package render - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - "sync" - "text/template" - "time" - - "github.com/pkg/errors" - "github.com/strongdm/comply/internal/config" - "github.com/strongdm/comply/internal/model" -) - -// TODO: refactor and eliminate duplication among narrative, policy renderers -func renderNarrativeToDisk(wg *sync.WaitGroup, errOutputCh chan error, data *renderData, narrative *model.Narrative, live bool) { - // only files that have been touched - if !isNewer(narrative.FullPath, narrative.ModifiedAt) { - return - } - recordModified(narrative.FullPath, narrative.ModifiedAt) - - wg.Add(1) - go func(p *model.Narrative) { - defer wg.Done() - - outputFilename := p.OutputFilename - // save preprocessed markdown - err := preprocessNarrative(data, p, filepath.Join(".", "output", outputFilename+".md")) - if err != nil { - errOutputCh <- errors.Wrap(err, "unable to preprocess") - return - } - - pandoc(outputFilename, errOutputCh) - - // remove preprocessed markdown - err = os.Remove(filepath.Join(".", "output", outputFilename+".md")) - if err != nil { - errOutputCh <- err - return - } - - rel, err := filepath.Rel(config.ProjectRoot(), p.FullPath) - if err != nil { - rel = p.FullPath - } - fmt.Printf("%s -> %s\n", rel, filepath.Join("output", p.OutputFilename)) - - }(narrative) -} - -func preprocessNarrative(data *renderData, pol *model.Narrative, fullPath string) error { - cfg := config.Config() - - var w bytes.Buffer - bodyTemplate, err := template.New("body").Parse(pol.Body) - if err != nil { - w.WriteString(fmt.Sprintf("# Error processing template:\n\n%s\n", err.Error())) - } else { - bodyTemplate.Execute(&w, data) - } - body := w.String() - - revisionTable := "" - satisfiesTable := "" - - // ||Date|Comment| - // |---+------| - // | 4 Jan 2018 | Initial Version | - // Table: Document history - - if len(pol.Satisfies) > 0 { - rows := "" - for standard, keys := range pol.Satisfies { - rows += fmt.Sprintf("| %s | %s |\n", standard, strings.Join(keys, ", ")) - } - satisfiesTable = fmt.Sprintf("|Standard|Controls Satisfied|\n|-------+--------------------------------------------|\n%s\nTable: Control satisfaction\n", rows) - } - - if len(pol.Revisions) > 0 { - rows := "" - for _, rev := range pol.Revisions { - rows += fmt.Sprintf("| %s | %s |\n", rev.Date, rev.Comment) - } - revisionTable = fmt.Sprintf("|Date|Comment|\n|---+--------------------------------------------|\n%s\nTable: Document history\n", rows) - } - - doc := fmt.Sprintf(`%% %s -%% %s -%% %s - ---- -header-includes: yes -head-content: "%s" -foot-content: "%s confidential %d" ---- - -%s - -%s - -\newpage -%s`, - pol.Name, - cfg.Name, - fmt.Sprintf("%s %d", pol.ModifiedAt.Month().String(), pol.ModifiedAt.Year()), - pol.Name, - cfg.Name, - time.Now().Year(), - satisfiesTable, - revisionTable, - body, - ) - err = ioutil.WriteFile(fullPath, []byte(doc), os.FileMode(0644)) - if err != nil { - return errors.Wrap(err, "unable to write preprocessed narrative to disk") - } - return nil -} diff --git a/internal/render/pdf.go b/internal/render/pdf.go index 9124b58..122a966 100644 --- a/internal/render/pdf.go +++ b/internal/render/pdf.go @@ -25,7 +25,7 @@ func pdf(output string, live bool, errCh chan error, wg *sync.WaitGroup) { return } for _, policy := range policies { - renderPolicyToDisk(&pdfWG, errOutputCh, data, policy, live) + renderToFilesystem(&pdfWG, errOutputCh, data, policy, live) } narratives, err := model.ReadNarratives() @@ -35,7 +35,7 @@ func pdf(output string, live bool, errCh chan error, wg *sync.WaitGroup) { } for _, narrative := range narratives { - renderNarrativeToDisk(&pdfWG, errOutputCh, data, narrative, live) + renderToFilesystem(&pdfWG, errOutputCh, data, narrative, live) } pdfWG.Wait()