Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions triples/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,28 @@ func findFirstSubject(db repository.Repository, subject *Node) (*dbt.Entity, err
return nil, errors.New("subject cannot be nil")
}

// A regular expression key cannot be expressed as an exact content filter, so
// fetch the entities of the subject's type and match them in Go, the same way
// regex matching is already handled for the object of a triple.
if subject.Regexp != nil {
since := subject.Since
if since.IsZero() {
since = time.Unix(0, 0)
}

ents, err := db.FindEntitiesByType(context.Background(), subject.Type, since, 0)
if err != nil {
return nil, fmt.Errorf("failed to find the subject in the database: %v", err)
}

for _, ent := range ents {
if valueMatch(ent.Asset.Key(), subject.Key, subject.Regexp) {
return ent, nil
}
}
return nil, errors.New("failed to find a subject matching the regular expression")
}

filter, err := subjectToAsset(subject)
if err != nil {
return nil, fmt.Errorf("failed to convert subject to asset: %v", err)
Expand Down
30 changes: 30 additions & 0 deletions triples/extract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net/netip"
"os"
"path/filepath"
"regexp"
"testing"
"time"

Expand Down Expand Up @@ -406,3 +407,32 @@ func teardownTempSQLite(repo *sqlite3.SqliteRepository, dir string) {
}
_ = os.RemoveAll(dir)
}

func TestFindFirstSubjectRegex(t *testing.T) {
db, dir, err := setupTempSQLite()
assert.NoError(t, err, "Failed to create the sqlite database")
assert.NotNil(t, db, "Asset database should not be nil")
defer teardownTempSQLite(db, dir)

ctx := context.Background()
match := "192.168.1.2"
other := "10.0.0.5"

_, err = db.CreateAsset(ctx, &oamnet.IPAddress{Address: netip.MustParseAddr(match), Type: "IPv4"})
assert.NoError(t, err, "Failed to create the matching IP address asset")
_, err = db.CreateAsset(ctx, &oamnet.IPAddress{Address: netip.MustParseAddr(other), Type: "IPv4"})
assert.NoError(t, err, "Failed to create the non-matching IP address asset")

// A regex in the subject position must be evaluated against stored entities,
// not passed through as a literal key.
entity, err := findFirstSubject(db, &Node{
Type: oam.IPAddress,
Key: `#/^192\./#`,
Regexp: regexp.MustCompile(`^192\.`),
})
assert.NoError(t, err, "A regex subject should match a stored entity")
assert.NotNil(t, entity, "Entity should not be nil for a matching regex subject")
if entity != nil {
assert.Equal(t, match, entity.Asset.Key(), "Should return the entity matching the regex")
}
}