diff --git a/context.go b/context.go index 59e409b..b4320d0 100644 --- a/context.go +++ b/context.go @@ -1294,6 +1294,16 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) { return nil, err } + err = ctx.RegisterSignal(reflect.TypeOf(RemoveMemberSignal{}), RemoveMemberSignalType) + if err != nil { + return nil, err + } + + err = ctx.RegisterSignal(reflect.TypeOf(AddMemberSignal{}), AddMemberSignalType) + if err != nil { + return nil, err + } + err = ctx.RegisterSignal(reflect.TypeOf(StopSignal{}), StopSignalType) if err != nil { return nil, err diff --git a/gql.go b/gql.go index 46387d4..45a4f0d 100644 --- a/gql.go +++ b/gql.go @@ -517,9 +517,7 @@ func GQLHandler(ctx *Context, server *Node, gql_ext *GQLExt) func(http.ResponseW params.VariableValues = query.Variables } - ctx.Log.Logf("gql", "PARAMS: %+v", params) result := graphql.Do(params) - ctx.Log.Logf("gql", "RESULT: %+v", result) if len(result.Errors) > 0 { extra_fields := map[string]interface{}{} extra_fields["body"] = string(str) diff --git a/group.go b/group.go index e958158..3dfa572 100644 --- a/group.go +++ b/group.go @@ -1,8 +1,45 @@ package graphvent import ( + "slices" ) +type AddMemberSignal struct { + SignalHeader + MemberID NodeID `gv:"member_id"` +} + +func (signal AddMemberSignal) Permission() Tree { + return Tree{ + SerializedType(AddMemberSignalType): nil, + } +} + +func NewAddMemberSignal(member_id NodeID) *AddMemberSignal { + return &AddMemberSignal{ + NewSignalHeader(Direct), + member_id, + } +} + +type RemoveMemberSignal struct { + SignalHeader + MemberID NodeID `gv:"member_id"` +} + +func (signal RemoveMemberSignal) Permission() Tree { + return Tree{ + SerializedType(RemoveMemberSignalType): nil, + } +} + +func NewRemoveMemberSignal(member_id NodeID) *RemoveMemberSignal { + return &RemoveMemberSignal{ + NewSignalHeader(Direct), + member_id, + } +} + var GroupReadPolicy = NewAllNodesPolicy(Tree{ SerializedType(ReadSignalType): { SerializedType(GroupExtType): { @@ -22,6 +59,29 @@ func NewGroupExt(members []NodeID) *GroupExt { } func (ext *GroupExt) Process(ctx *Context, node *Node, source NodeID, signal Signal) (Messages, Changes) { - return nil, nil + var messages Messages = nil + var changes Changes = nil + + switch sig := signal.(type) { + case *AddMemberSignal: + if slices.Contains(ext.Members, sig.MemberID) == true { + messages = messages.Add(ctx, node.ID, node.Key, NewErrorSignal(sig.Id, "already_member"), source) + } else { + ext.Members = append(ext.Members, sig.MemberID) + messages = messages.Add(ctx, node.ID, node.Key, NewSuccessSignal(sig.Id), source) + changes = changes.Add("member_added") + } + case *RemoveMemberSignal: + idx := slices.Index(ext.Members, sig.MemberID) + if idx == -1 { + messages = messages.Add(ctx, node.ID, node.Key, NewErrorSignal(sig.Id, "not_member"), source) + } else { + ext.Members = slices.Delete(ext.Members, idx, idx+1) + messages = messages.Add(ctx, node.ID, node.Key, NewSuccessSignal(sig.Id), source) + changes = changes.Add("member_removed") + } + } + + return messages, changes } diff --git a/group_test.go b/group_test.go new file mode 100644 index 0000000..9b7b623 --- /dev/null +++ b/group_test.go @@ -0,0 +1,61 @@ +package graphvent + +import ( + "testing" + "time" +) + +func TestGroupAdd(t *testing.T) { + ctx := logTestContext(t, []string{"listener", "test"}) + + group_listener := NewListenerExt(10) + group, err := NewNode(ctx, nil, GroupNodeType, 10, nil, group_listener, NewGroupExt(nil)) + fatalErr(t, err) + + user_id := RandID() + add_member_signal := NewAddMemberSignal(user_id) + + messages := Messages{} + messages = messages.Add(ctx, group.ID, group.Key, add_member_signal, group.ID) + fatalErr(t, ctx.Send(messages)) + + _, err = WaitForResponse(group_listener.Chan, 10*time.Millisecond, add_member_signal.Id) + fatalErr(t, err) + + read_signal := NewReadSignal(map[ExtType][]string{ + GroupExtType: {"members"}, + }) + + messages = Messages{} + messages = messages.Add(ctx, group.ID, group.Key, read_signal, group.ID) + fatalErr(t, ctx.Send(messages)) + + response, err := WaitForResponse(group_listener.Chan, 10*time.Millisecond, read_signal.Id) + fatalErr(t, err) + + read_response := response.(*ReadResultSignal) + + members_serialized := read_response.Extensions[GroupExtType]["members"] + + _, member_value, remaining, err := DeserializeValue(ctx, members_serialized) + + if len(remaining.Data) > 0 { + t.Fatalf("Data remaining after deserializing member list: %d", len(remaining.Data)) + } + + member_list, ok := member_value.Interface().([]NodeID) + + if ok != true { + t.Fatalf("member_list wrong type %s", member_value.Type()) + } + + if len(member_list) != 1 { + t.Fatalf("member_list wrong length %d", len(member_list)) + } + + if member_list[0] != user_id { + t.Fatalf("member_list wrong value %s", member_list[0]) + } + + ctx.Log.Logf("test", "Read Response: %+v", read_response) +} diff --git a/serialize.go b/serialize.go index b2de952..9c17076 100644 --- a/serialize.go +++ b/serialize.go @@ -84,22 +84,24 @@ var ( GQLExtType = NewExtType("GQL") GroupExtType = NewExtType("GROUP") - GQLNodeType = NewNodeType("GQL") - BaseNodeType = NewNodeType("BASE") + GQLNodeType = NewNodeType("GQL") + BaseNodeType = NewNodeType("BASE") GroupNodeType = NewNodeType("GROUP") - StopSignalType = NewSignalType("STOP") - CreateSignalType = NewSignalType("CREATE") - StartSignalType = NewSignalType("START") - StatusSignalType = NewSignalType("STATUS") - LinkSignalType = NewSignalType("LINK") - LockSignalType = NewSignalType("LOCK") - TimeoutSignalType = NewSignalType("TIMEOUT") - ReadSignalType = NewSignalType("READ") - ACLTimeoutSignalType = NewSignalType("ACL_TIMEOUT") - ErrorSignalType = NewSignalType("ERROR") - SuccessSignalType = NewSignalType("SUCCESS") - ReadResultSignalType = NewSignalType("READ_RESULT") + StopSignalType = NewSignalType("STOP") + CreateSignalType = NewSignalType("CREATE") + StartSignalType = NewSignalType("START") + StatusSignalType = NewSignalType("STATUS") + LinkSignalType = NewSignalType("LINK") + LockSignalType = NewSignalType("LOCK") + TimeoutSignalType = NewSignalType("TIMEOUT") + ReadSignalType = NewSignalType("READ") + ACLTimeoutSignalType = NewSignalType("ACL_TIMEOUT") + ErrorSignalType = NewSignalType("ERROR") + SuccessSignalType = NewSignalType("SUCCESS") + ReadResultSignalType = NewSignalType("READ_RESULT") + RemoveMemberSignalType = NewSignalType("REMOVE_MEMBER") + AddMemberSignalType = NewSignalType("ADD_MEMBER") MemberOfPolicyType = NewPolicyType("USER_OF") RequirementOfPolicyType = NewPolicyType("REQUIEMENT_OF")