gql_cataclysm
noah metz 2023-07-30 10:09:04 -06:00
parent 79e40bf3f3
commit 96c2b84b6f
5 changed files with 94 additions and 108 deletions

@ -844,17 +844,19 @@ func NewGQLExtContext() *GQLExtContext {
"requirements", "requirements",
LockableExtType, LockableExtType,
func(p graphql.ResolveParams, val interface{}) ([]NodeID, error) { func(p graphql.ResolveParams, val interface{}) ([]NodeID, error) {
id_strs, ok := val.(map[NodeID]ReqState) id_strs, ok := val.(LinkMap)
if ok == false { if ok == false {
return nil, fmt.Errorf("can't parse requirements %+v as string, %s", val, reflect.TypeOf(val)) return nil, fmt.Errorf("can't parse requirements %+v as string, %s", val, reflect.TypeOf(val))
} }
ids := make([]NodeID, len(id_strs)) ids := make([]NodeID, len(id_strs))
i := 0 i := 0
for id, _ := range(id_strs) { for id, state := range(id_strs) {
if state.Link == "linked" {
ids[i] = id ids[i] = id
i++ i++
} }
}
return ids, nil return ids, nil
}, },
}, },
@ -862,17 +864,19 @@ func NewGQLExtContext() *GQLExtContext {
"dependencies", "dependencies",
LockableExtType, LockableExtType,
func(p graphql.ResolveParams, val interface{}) ([]NodeID, error) { func(p graphql.ResolveParams, val interface{}) ([]NodeID, error) {
id_strs, ok := val.(map[NodeID]string) id_strs, ok := val.(LinkMap)
if ok == false { if ok == false {
return nil, fmt.Errorf("can't parse dependencies %+v as string, %s", val, reflect.TypeOf(val)) return nil, fmt.Errorf("can't parse dependencies %+v as string, %s", val, reflect.TypeOf(val))
} }
ids := make([]NodeID, len(id_strs)) ids := make([]NodeID, len(id_strs))
i := 0 i := 0
for id, _ := range(id_strs) { for id, state := range(id_strs) {
if state.Link == "linked" {
ids[i] = id ids[i] = id
i++ i++
} }
}
return ids, nil return ids, nil
}, },
}, },

@ -56,24 +56,46 @@ func (ext *ListenerExt) Process(ctx *Context, princ_id NodeID, node *Node, signa
} }
// ReqState holds the multiple states of a requirement // ReqState holds the multiple states of a requirement
type ReqState struct { type LinkState struct {
Link string `json:"link"` Link string `json:"link"`
Lock string `json:"lock"` Lock string `json:"lock"`
Initiator NodeID `json:"initiator"`
} }
// A LockableExt allows a node to be linked to other nodes(via LinkSignal) and locked/unlocked(via LockSignal) // A LockableExt allows a node to be linked to other nodes(via LinkSignal) and locked/unlocked(via LockSignal)
type LockableExt struct { type LinkMap map[NodeID]LinkState
Owner *NodeID func (m LinkMap) MarshalJSON() ([]byte, error) {
PendingOwner *NodeID tmp := map[string]LinkState{}
Requirements map[NodeID]ReqState for id, state := range(m) {
Dependencies map[NodeID]string tmp[id.String()] = state
}
return json.Marshal(tmp)
} }
type LockableExtJSON struct { func (m LinkMap) UnmarshalJSON(data []byte) error {
tmp := map[string]LinkState{}
err := json.Unmarshal(data, &tmp)
if err != nil {
return err
}
for id_str, state := range(tmp) {
id, err := ParseID(id_str)
if err != nil {
return err
}
m[id] = state
}
return nil
}
type LockableExt struct {
Owner *NodeID `json:"owner"` Owner *NodeID `json:"owner"`
PendingOwner *NodeID `json:"pending_owner"` PendingOwner *NodeID `json:"pending_owner"`
Requirements map[string]ReqState `json:"requirements"` Requirements LinkMap `json:"requirements"`
Dependencies map[string]string `json:"dependencies"` Dependencies LinkMap `json:"dependencies"`
} }
func (ext *LockableExt) Field(name string) interface{} { func (ext *LockableExt) Field(name string) interface{} {
@ -94,28 +116,13 @@ func (ext *LockableExt) Field(name string) interface{} {
} }
func LoadLockableExt(ctx *Context, data []byte) (Extension, error) { func LoadLockableExt(ctx *Context, data []byte) (Extension, error) {
var j LockableExtJSON var ext LockableExt
err := json.Unmarshal(data, &j) err := json.Unmarshal(data, &ext)
if err != nil {
return nil, err
}
requirements, err := LoadIDMap(j.Requirements)
if err != nil {
return nil, err
}
dependencies, err := LoadIDMap(j.Dependencies)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &LockableExt{ return &ext, nil
Owner: j.Owner,
PendingOwner: j.PendingOwner,
Requirements: requirements,
Dependencies: dependencies,
}, nil
} }
func (ext *ListenerExt) Serialize() ([]byte, error) { func (ext *ListenerExt) Serialize() ([]byte, error) {
@ -127,20 +134,15 @@ func (ext *LockableExt) Type() ExtType {
} }
func (ext *LockableExt) Serialize() ([]byte, error) { func (ext *LockableExt) Serialize() ([]byte, error) {
return json.MarshalIndent(&LockableExtJSON{ return json.MarshalIndent(ext, "", " ")
Owner: ext.Owner,
PendingOwner: ext.PendingOwner,
Requirements: IDMap(ext.Requirements),
Dependencies: IDMap(ext.Dependencies),
}, "", " ")
} }
func NewLockableExt() *LockableExt { func NewLockableExt() *LockableExt {
return &LockableExt{ return &LockableExt{
Owner: nil, Owner: nil,
PendingOwner: nil, PendingOwner: nil,
Requirements: map[NodeID]ReqState{}, Requirements: map[NodeID]LinkState{},
Dependencies: map[NodeID]string{}, Dependencies: map[NodeID]LinkState{},
} }
} }
@ -315,9 +317,9 @@ func (ext *LockableExt) HandleLockSignal(ctx *Context, source NodeID, node *Node
} }
} }
func (ext *LockableExt) HandleLinkStartSignal(ctx *Context, source NodeID, node *Node, signal LinkStartSignal) { func (ext *LockableExt) HandleLinkStartSignal(ctx *Context, source NodeID, node *Node, signal IDStateSignal) {
ctx.Log.Logf("lockable", "LINK__START_SIGNAL: %s->%s %+v", source, node.ID, signal) ctx.Log.Logf("lockable", "LINK__START_SIGNAL: %s->%s %+v", source, node.ID, signal)
link_type := signal.LinkType link_type := signal.State
target := signal.ID target := signal.ID
switch link_type { switch link_type {
case "req": case "req":
@ -340,8 +342,8 @@ func (ext *LockableExt) HandleLinkStartSignal(ctx *Context, source NodeID, node
} else if dep_exists == true { } else if dep_exists == true {
ctx.Send(node.ID, source, NewLinkStartSignal("already_dep", target)) ctx.Send(node.ID, source, NewLinkStartSignal("already_dep", target))
} else { } else {
ext.Requirements[target] = ReqState{"linking", "unlocked"} ext.Requirements[target] = LinkState{"linking", "unlocked", source}
ctx.Send(node.ID, target, NewLinkSignal("link_as_req")) ctx.Send(node.ID, target, NewLinkSignal("linked_as_req"))
ctx.Send(node.ID, source, NewLinkStartSignal("linking_req", target)) ctx.Send(node.ID, source, NewLinkStartSignal("linking_req", target))
} }
} }
@ -353,14 +355,14 @@ func (ext *LockableExt) HandleLinkSignal(ctx *Context, source NodeID, node *Node
ctx.Log.Logf("lockable", "LINK_SIGNAL: %s->%s %+v", source, node.ID, signal) ctx.Log.Logf("lockable", "LINK_SIGNAL: %s->%s %+v", source, node.ID, signal)
state := signal.State state := signal.State
switch state { switch state {
case "link_as_dep": case "linked_as_dep":
state, exists := ext.Requirements[source] state, exists := ext.Requirements[source]
if exists == true && state.Link == "linked" { if exists == true && state.Link == "linked" {
ctx.Send(node.ID, source, NewLinkSignal("already_req")) ctx.Send(node.ID, state.Initiator, NewLinkStartSignal("linked_as_req", source))
} else if state.Link == "linking" { } else if state.Link == "linking" {
state.Link = "linked" state.Link = "linked"
ext.Requirements[source] = state ext.Requirements[source] = state
ctx.Send(node.ID, source, NewLinkSignal("linked_as_dep")) ctx.Send(node.ID, source, NewLinkSignal("linked_as_req"))
} else if ext.PendingOwner != ext.Owner { } else if ext.PendingOwner != ext.Owner {
if ext.Owner == nil { if ext.Owner == nil {
ctx.Send(node.ID, source, NewLinkSignal("locking")) ctx.Send(node.ID, source, NewLinkSignal("locking"))
@ -368,17 +370,19 @@ func (ext *LockableExt) HandleLinkSignal(ctx *Context, source NodeID, node *Node
ctx.Send(node.ID, source, NewLinkSignal("unlocking")) ctx.Send(node.ID, source, NewLinkSignal("unlocking"))
} }
} else { } else {
ext.Requirements[source] = ReqState{"linking", "unlocked"} ext.Requirements[source] = LinkState{"linking", "unlocked", source}
ctx.Send(node.ID, source, NewLinkSignal("link_as_req")) ctx.Send(node.ID, source, NewLinkSignal("linked_as_req"))
} }
ctx.Log.Logf("lockable", "%s is a dependency of %s", node.ID, source)
case "link_as_req": case "linked_as_req":
state, exists := ext.Dependencies[source] state, exists := ext.Dependencies[source]
if exists == true && state == "linked" { if exists == true && state.Link == "linked" {
ctx.Send(node.ID, source, NewLinkSignal("already_dep")) ctx.Send(node.ID, state.Initiator, NewLinkStartSignal("linked_as_dep", source))
} else if state == "linking" { } else if state.Link == "linking" {
ext.Dependencies[source] = "linked" state.Link = "linked"
ctx.Send(node.ID, source, NewLinkSignal("linked_as_req")) ext.Dependencies[source] = state
ctx.Send(node.ID, source, NewLinkSignal("linked_as_dep"))
} else if ext.PendingOwner != ext.Owner { } else if ext.PendingOwner != ext.Owner {
if ext.Owner == nil { if ext.Owner == nil {
ctx.Send(node.ID, source, NewLinkSignal("locking")) ctx.Send(node.ID, source, NewLinkSignal("locking"))
@ -386,28 +390,7 @@ func (ext *LockableExt) HandleLinkSignal(ctx *Context, source NodeID, node *Node
ctx.Send(node.ID, source, NewLinkSignal("unlocking")) ctx.Send(node.ID, source, NewLinkSignal("unlocking"))
} }
} else { } else {
ext.Dependencies[source] = "linking" ext.Dependencies[source] = LinkState{"linking", "unlocked", source}
ctx.Send(node.ID, source, NewLinkSignal("link_as_dep"))
}
case "linked_as_dep":
state, exists := ext.Dependencies[source]
if exists == false {
ctx.Send(node.ID, source, NewLinkSignal("not_linking"))
} else if state == "linked" {
} else if state == "linking" {
ext.Dependencies[source] = "linked"
ctx.Send(node.ID, source, NewLinkSignal("linked_as_req"))
}
ctx.Log.Logf("lockable", "%s is a dependency of %s", node.ID, source)
case "linked_as_req":
state, exists := ext.Requirements[source]
if exists == false {
ctx.Send(node.ID, source, NewLinkSignal("not_linking"))
} else if state.Link == "linked" {
} else if state.Link == "linking" {
state.Link = "linked"
ext.Requirements[source] = state
ctx.Send(node.ID, source, NewLinkSignal("linked_as_dep")) ctx.Send(node.ID, source, NewLinkSignal("linked_as_dep"))
} }
ctx.Log.Logf("lockable", "%s is a requirement of %s", node.ID, source) ctx.Log.Logf("lockable", "%s is a requirement of %s", node.ID, source)
@ -424,7 +407,7 @@ func (ext *LockableExt) Process(ctx *Context, source NodeID, node *Node, signal
case Up: case Up:
owner_sent := false owner_sent := false
for dependency, state := range(ext.Dependencies) { for dependency, state := range(ext.Dependencies) {
if state == "linked" { if state.Link == "linked" {
err := ctx.Send(node.ID, dependency, signal) err := ctx.Send(node.ID, dependency, signal)
if err != nil { if err != nil {
ctx.Log.Logf("signal", "LOCKABLE_SIGNAL_ERR: %s->%s - %e", node.ID, dependency, err) ctx.Log.Logf("signal", "LOCKABLE_SIGNAL_ERR: %s->%s - %e", node.ID, dependency, err)
@ -462,7 +445,7 @@ func (ext *LockableExt) Process(ctx *Context, source NodeID, node *Node, signal
case LockSignalType: case LockSignalType:
ext.HandleLockSignal(ctx, source, node, signal.(StateSignal)) ext.HandleLockSignal(ctx, source, node, signal.(StateSignal))
case LinkStartSignalType: case LinkStartSignalType:
ext.HandleLinkStartSignal(ctx, source, node, signal.(LinkStartSignal)) ext.HandleLinkStartSignal(ctx, source, node, signal.(IDStateSignal))
default: default:
} }
default: default:

@ -21,7 +21,7 @@ var link_policy = NewAllNodesPolicy(Actions{MakeAction(LinkSignalType, "*"), Mak
var lock_policy = NewAllNodesPolicy(Actions{MakeAction(LockSignalType, "*")}) var lock_policy = NewAllNodesPolicy(Actions{MakeAction(LockSignalType, "*")})
func TestLink(t *testing.T) { func TestLink(t *testing.T) {
ctx := lockableTestContext(t, []string{}) ctx := lockableTestContext(t, []string{"lockable"})
l1_listener := NewListenerExt(10) l1_listener := NewListenerExt(10)
l1 := NewNode(ctx, nil, TestLockableType, 10, nil, l1 := NewNode(ctx, nil, TestLockableType, 10, nil,
@ -40,14 +40,10 @@ func TestLink(t *testing.T) {
err := LinkRequirement(ctx, l1.ID, l2.ID) err := LinkRequirement(ctx, l1.ID, l2.ID)
fatalErr(t, err) fatalErr(t, err)
_, err = WaitForSignal(ctx, l1_listener, time.Millisecond*10, LinkSignalType, func(sig StateSignal) bool { _, err = WaitForSignal(ctx, l1_listener, time.Millisecond*10, LinkStartSignalType, func(sig IDStateSignal) bool {
return sig.State == "linked_as_req" return sig.State == "linked_as_req"
}) })
fatalErr(t, err) fatalErr(t, err)
_, err = WaitForSignal(ctx, l2_listener, time.Millisecond*10, LinkSignalType, func(sig StateSignal) bool {
return sig.State == "linked_as_dep"
})
fatalErr(t, err)
err = ctx.Send(l2.ID, l2.ID, NewStatusSignal("TEST", l2.ID)) err = ctx.Send(l2.ID, l2.ID, NewStatusSignal("TEST", l2.ID))
fatalErr(t, err) fatalErr(t, err)
@ -95,7 +91,7 @@ func TestLink10K(t *testing.T) {
for range(lockables) { for range(lockables) {
_, err := WaitForSignal(ctx, l0_listener, time.Millisecond*10, LinkSignalType, func(sig StateSignal) bool { _, err := WaitForSignal(ctx, l0_listener, time.Millisecond*10, LinkStartSignalType, func(sig IDStateSignal) bool {
return sig.State == "linked_as_req" return sig.State == "linked_as_req"
}) })
fatalErr(t, err) fatalErr(t, err)
@ -144,7 +140,7 @@ func TestLock(t *testing.T) {
err = LinkRequirement(ctx, l0.ID, l5.ID) err = LinkRequirement(ctx, l0.ID, l5.ID)
fatalErr(t, err) fatalErr(t, err)
linked_as_req := func(sig StateSignal) bool { linked_as_req := func(sig IDStateSignal) bool {
return sig.State == "linked_as_req" return sig.State == "linked_as_req"
} }
@ -152,22 +148,22 @@ func TestLock(t *testing.T) {
return sig.State == "locked" return sig.State == "locked"
} }
_, err = WaitForSignal(ctx, l1_listener, time.Millisecond*10, LinkSignalType, linked_as_req) _, err = WaitForSignal(ctx, l1_listener, time.Millisecond*10, LinkStartSignalType, linked_as_req)
fatalErr(t, err) fatalErr(t, err)
_, err = WaitForSignal(ctx, l1_listener, time.Millisecond*10, LinkSignalType, linked_as_req) _, err = WaitForSignal(ctx, l1_listener, time.Millisecond*10, LinkStartSignalType, linked_as_req)
fatalErr(t, err) fatalErr(t, err)
_, err = WaitForSignal(ctx, l1_listener, time.Millisecond*10, LinkSignalType, linked_as_req) _, err = WaitForSignal(ctx, l1_listener, time.Millisecond*10, LinkStartSignalType, linked_as_req)
fatalErr(t, err) fatalErr(t, err)
_, err = WaitForSignal(ctx, l1_listener, time.Millisecond*10, LinkSignalType, linked_as_req) _, err = WaitForSignal(ctx, l1_listener, time.Millisecond*10, LinkStartSignalType, linked_as_req)
fatalErr(t, err) fatalErr(t, err)
_, err = WaitForSignal(ctx, l0_listener, time.Millisecond*10, LinkSignalType, linked_as_req) _, err = WaitForSignal(ctx, l0_listener, time.Millisecond*10, LinkStartSignalType, linked_as_req)
fatalErr(t, err) fatalErr(t, err)
_, err = WaitForSignal(ctx, l0_listener, time.Millisecond*10, LinkSignalType, linked_as_req) _, err = WaitForSignal(ctx, l0_listener, time.Millisecond*10, LinkStartSignalType, linked_as_req)
fatalErr(t, err) fatalErr(t, err)
_, err = WaitForSignal(ctx, l0_listener, time.Millisecond*10, LinkSignalType, linked_as_req) _, err = WaitForSignal(ctx, l0_listener, time.Millisecond*10, LinkStartSignalType, linked_as_req)
fatalErr(t, err) fatalErr(t, err)
_, err = WaitForSignal(ctx, l0_listener, time.Millisecond*10, LinkSignalType, linked_as_req) _, err = WaitForSignal(ctx, l0_listener, time.Millisecond*10, LinkStartSignalType, linked_as_req)
fatalErr(t, err) fatalErr(t, err)
err = LockLockable(ctx, l1) err = LockLockable(ctx, l1)

@ -33,8 +33,11 @@ var (
// A NodeID uniquely identifies a Node // A NodeID uniquely identifies a Node
type NodeID uuid.UUID type NodeID uuid.UUID
func (id NodeID) MarshalJSON() ([]byte, error) { func (id *NodeID) MarshalJSON() ([]byte, error) {
str := id.String() str := ""
if id != nil {
str = id.String()
}
return json.Marshal(&str) return json.Marshal(&str)
} }

@ -189,16 +189,16 @@ func NewLinkSignal(state string) StateSignal {
} }
} }
type LinkStartSignal struct { func NewIDStateSignal(signal_type SignalType, direction SignalDirection, state string, id NodeID) IDStateSignal {
IDSignal return IDStateSignal{
LinkType string `json:"link_type"` BaseSignal: NewBaseSignal(signal_type, direction),
ID: id,
State: state,
}
} }
func NewLinkStartSignal(link_type string, target NodeID) LinkStartSignal { func NewLinkStartSignal(link_type string, target NodeID) IDStateSignal {
return LinkStartSignal{ return NewIDStateSignal(LinkStartSignalType, Direct, link_type, target)
IDSignal: NewIDSignal(LinkStartSignalType, Direct, target),
LinkType: link_type,
}
} }
func NewLockSignal(state string) StateSignal { func NewLockSignal(state string) StateSignal {