1
0
Fork 0
mirror of https://github.com/gocsaf/csaf.git synced 2025-12-22 05:40:11 +01:00

Impove Jsonpath matcher

* Simplifed mass jsonpath extractions json document
This commit is contained in:
Sascha L. Teichmann 2022-05-04 16:56:41 +02:00 committed by GitHub
parent de4f50787d
commit 41e4029b0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 69 deletions

View file

@ -9,7 +9,6 @@
package csaf
import (
"errors"
"time"
"github.com/csaf-poc/csaf_distribution/util"
@ -36,83 +35,28 @@ type AdvisorySummary struct {
TLPLabel string
}
type extractFunc func(string) (interface{}, error)
// NewAdvisorySummary creates a summary from an advisory doc
// with the help of an expression evaluator expr.
func NewAdvisorySummary(
expr *util.PathEval,
pe *util.PathEval,
doc interface{},
) (*AdvisorySummary, error) {
e := new(AdvisorySummary)
path := func(s string) (interface{}, error) {
return expr.Eval(s, doc)
e := &AdvisorySummary{
Publisher: new(Publisher),
}
for _, fn := range []func(extractFunc) error{
extractText(idExpr, &e.ID),
extractText(titleExpr, &e.Title),
extractTime(currentReleaseDateExpr, &e.CurrentReleaseDate),
extractTime(initialReleaseDateExpr, &e.InitialReleaseDate),
extractText(summaryExpr, &e.Summary),
extractText(tlpLabelExpr, &e.TLPLabel),
e.extractPublisher,
} {
if err := fn(path); err != nil {
return nil, err
}
if err := pe.Match([]util.PathEvalMatcher{
{Expr: idExpr, Action: util.StringMatcher(&e.ID)},
{Expr: titleExpr, Action: util.StringMatcher(&e.Title)},
{Expr: currentReleaseDateExpr, Action: util.TimeMatcher(&e.CurrentReleaseDate, time.RFC3339)},
{Expr: initialReleaseDateExpr, Action: util.TimeMatcher(&e.InitialReleaseDate, time.RFC3339)},
{Expr: summaryExpr, Action: util.StringMatcher(&e.Summary)},
{Expr: tlpLabelExpr, Action: util.StringMatcher(&e.TLPLabel)},
{Expr: publisherExpr, Action: util.ReMarshalMatcher(e.Publisher)},
}, doc); err != nil {
return nil, err
}
return e, nil
}
func extractText(expr string, store *string) func(extractFunc) error {
return func(path extractFunc) error {
s, err := path(expr)
if text, ok := s.(string); ok && err == nil {
*store = text
}
return nil
}
}
func extractTime(expr string, store *time.Time) func(extractFunc) error {
return func(path extractFunc) error {
s, err := path(expr)
if err != nil {
return err
}
text, ok := s.(string)
if !ok {
return errors.New("not a string")
}
date, err := time.Parse(time.RFC3339, text)
if err == nil {
*store = date.UTC()
}
return err
}
}
func (e *AdvisorySummary) extractPublisher(path extractFunc) error {
p, err := path(publisherExpr)
if err != nil {
return err
}
// XXX: It's a bit cumbersome to serialize and deserialize
// it into our own structure.
publisher := new(Publisher)
if err := util.ReMarshalJSON(publisher, p); err != nil {
return err
}
if err := publisher.Validate(); err != nil {
return err
}
e.Publisher = publisher
return nil
}

View file

@ -12,6 +12,7 @@ import (
"context"
"encoding/json"
"errors"
"time"
"github.com/PaesslerAG/gval"
"github.com/PaesslerAG/jsonpath"
@ -56,3 +57,61 @@ func (pe *PathEval) Eval(expr string, doc interface{}) (interface{}, error) {
}
return eval(context.Background(), doc)
}
// PathEvalMatcher is a pair of an expression and an action
// when doing extractions via PathEval.Match.
type PathEvalMatcher struct {
// Expr is the expression to evaluate
Expr string
// Action is executed with the result of the match.
Action func(interface{}) error
}
// ReMarshalMatcher is an action to re-marshal the result to another type.
func ReMarshalMatcher(dst interface{}) func(interface{}) error {
return func(src interface{}) error {
return ReMarshalJSON(dst, src)
}
}
// StringMatcher stores the matched result in a string.
func StringMatcher(dst *string) func(interface{}) error {
return func(x interface{}) error {
s, ok := x.(string)
if !ok {
return errors.New("not a string")
}
*dst = s
return nil
}
}
// TimeMatcher stores a time with a given format.
func TimeMatcher(dst *time.Time, format string) func(interface{}) error {
return func(x interface{}) error {
s, ok := x.(string)
if !ok {
return errors.New("not a string")
}
t, err := time.Parse(format, s)
if err != nil {
return nil
}
*dst = t
return nil
}
}
// Match matches a list of PathEvalMatcher pairs against a document.
func (pe *PathEval) Match(matcher []PathEvalMatcher, doc interface{}) error {
for _, m := range matcher {
x, err := pe.Eval(m.Expr, doc)
if err != nil {
return err
}
if err := m.Action(x); err != nil {
return err
}
}
return nil
}