Finished event to subgraph conversion.

Wrote expander pattern for custom rules.
This commit is contained in:
Jay
2026-03-03 12:54:36 -05:00
parent e734fc77ed
commit 96f1ceb362
4 changed files with 173 additions and 2 deletions

View File

@@ -4,7 +4,7 @@ import (
roots "git.wisehodl.dev/jay/go-roots/events" roots "git.wisehodl.dev/jay/go-roots/events"
) )
func EventToSubgraph(e roots.Event) *Subgraph { func EventToSubgraph(e roots.Event, exp ExpanderRegistry) *Subgraph {
subgraph := NewSubgraph() subgraph := NewSubgraph()
// Create Event node // Create Event node
@@ -45,6 +45,11 @@ func EventToSubgraph(e roots.Event) *Subgraph {
subgraph.AddRel(rel) subgraph.AddRel(rel)
} }
// Run expanders
for _, expander := range exp {
expander(e, subgraph)
}
return subgraph return subgraph
} }

View File

@@ -83,11 +83,79 @@ func TestEventToSubgraph(t *testing.T) {
return s return s
}(), }(),
}, },
{
name: "e tag with valid hex64",
event: roots.Event{
ID: ids["a"], PubKey: ids["b"],
CreatedAt: static.CreatedAt, Kind: static.Kind, Content: static.Content,
Tags: []roots.Tag{{"e", ids["c"]}},
},
expected: func() *Subgraph {
s, eventNode, _ := baseSubgraph(ids["a"], ids["b"])
tagNode := NewTagNode("e", ids["c"])
referencedEvent := NewEventNode(ids["c"])
s.AddNode(tagNode)
s.AddNode(referencedEvent)
s.AddRel(NewTaggedRel(eventNode, tagNode, nil))
s.AddRel(NewReferencesEventRel(tagNode, referencedEvent, nil))
return s
}(),
},
{
name: "e tag with invalid value",
event: roots.Event{
ID: ids["a"], PubKey: ids["b"],
CreatedAt: static.CreatedAt, Kind: static.Kind, Content: static.Content,
Tags: []roots.Tag{{"e", "notvalid"}},
},
expected: func() *Subgraph {
s, eventNode, _ := baseSubgraph(ids["a"], ids["b"])
tagNode := NewTagNode("e", "notvalid")
s.AddNode(tagNode)
s.AddRel(NewTaggedRel(eventNode, tagNode, nil))
return s
}(),
},
{
name: "p tag with valid hex64",
event: roots.Event{
ID: ids["a"], PubKey: ids["b"],
CreatedAt: static.CreatedAt, Kind: static.Kind, Content: static.Content,
Tags: []roots.Tag{{"p", ids["d"]}},
},
expected: func() *Subgraph {
s, eventNode, _ := baseSubgraph(ids["a"], ids["b"])
tagNode := NewTagNode("p", ids["d"])
referencedUser := NewUserNode(ids["d"])
s.AddNode(tagNode)
s.AddNode(referencedUser)
s.AddRel(NewTaggedRel(eventNode, tagNode, nil))
s.AddRel(NewReferencesUserRel(tagNode, referencedUser, nil))
return s
}(),
},
{
name: "p tag with invalid value",
event: roots.Event{
ID: ids["a"], PubKey: ids["b"],
CreatedAt: static.CreatedAt, Kind: static.Kind, Content: static.Content,
Tags: []roots.Tag{{"p", "notvalid"}},
},
expected: func() *Subgraph {
s, eventNode, _ := baseSubgraph(ids["a"], ids["b"])
tagNode := NewTagNode("p", "notvalid")
s.AddNode(tagNode)
s.AddRel(NewTaggedRel(eventNode, tagNode, nil))
return s
}(),
},
} }
expanders := GetDefaultExpanderRegistry()
for _, tc := range cases { for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
got := EventToSubgraph(tc.event) got := EventToSubgraph(tc.event, expanders)
assertSubgraphsEqual(t, tc.expected, got) assertSubgraphsEqual(t, tc.expected, got)
}) })
} }

88
expanders.go Normal file
View File

@@ -0,0 +1,88 @@
package heartwood
import (
roots "git.wisehodl.dev/jay/go-roots/events"
)
type Expander func(e roots.Event, s *Subgraph)
type ExpanderRegistry []Expander
func NewExpanderRegistry() ExpanderRegistry {
return []Expander{}
}
func GetDefaultExpanderRegistry() ExpanderRegistry {
registry := NewExpanderRegistry()
registry.Add(ExpandTaggedEvents)
registry.Add(ExpandTaggedUsers)
return registry
}
func (r *ExpanderRegistry) Add(m Expander) {
*r = append(*r, m)
}
// Default Expander Functions
func ExpandTaggedEvents(e roots.Event, s *Subgraph) {
tagNodes := s.NodesByLabel("Tag")
for _, tag := range e.Tags {
if !isValidTag(tag) {
continue
}
name := tag[0]
value := tag[1]
if name != "e" || !roots.Hex64Pattern.MatchString(value) {
continue
}
tagNode := findTagNode(tagNodes, name, value)
if tagNode == nil {
continue
}
referencedEvent := NewEventNode(value)
s.AddNode(referencedEvent)
s.AddRel(NewReferencesEventRel(tagNode, referencedEvent, nil))
}
}
func ExpandTaggedUsers(e roots.Event, s *Subgraph) {
tagNodes := s.NodesByLabel("Tag")
for _, tag := range e.Tags {
if !isValidTag(tag) {
continue
}
name := tag[0]
value := tag[1]
if name != "p" || !roots.Hex64Pattern.MatchString(value) {
continue
}
tagNode := findTagNode(tagNodes, name, value)
if tagNode == nil {
continue
}
referencedEvent := NewUserNode(value)
s.AddNode(referencedEvent)
s.AddRel(NewReferencesUserRel(tagNode, referencedEvent, nil))
}
}
// Helpers
func findTagNode(nodes []*Node, name, value string) *Node {
for _, node := range nodes {
if node.Props["name"] == name && node.Props["value"] == value {
return node
}
}
return nil
}

View File

@@ -199,6 +199,16 @@ func (s *Subgraph) Rels() []*Relationship {
return s.rels return s.rels
} }
func (s *Subgraph) NodesByLabel(label string) []*Node {
nodes := []*Node{}
for _, node := range s.nodes {
if node.Labels.Contains(label) {
nodes = append(nodes, node)
}
}
return nodes
}
// ======================================== // ========================================
// Structured Subgraph // Structured Subgraph
// ======================================== // ========================================