1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-05-02 22:17:10 +08:00
Mirko Teodorovic 7195cad0f6
MF-397 - Introduce Thing Groups (#1259)
* add things group

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add things group repository

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add things group repository

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add things group repository

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add parents and children methods

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add parents and children methods

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix logging message and temporary test fix

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix bootstrap test fail

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* move groups to pkg

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* move groups to pkg

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* move groups to pkg

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* move groups to internal

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* move groups to internal

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix import

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix linter errors

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix comments

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* resolve comments, add hierarchy info when retrieving groups

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* code refactor, separate http into multiple files

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* code refactor, separate http into multiple files

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix group update

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* use user id when saving

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* use user id when saving

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* rename methods

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* move code

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* move code

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* remove temporary test

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add groups test

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix comments, fix responses in api for groups

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* revert changes

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

add checks for name length

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

fix validation

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

small change to response

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

add comment for Level

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

add level to limit hierarchy retrieval

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

tidy vendor

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

add level to limit hierarchy retrieval

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

mod tidy

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

revert

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

resolve comments

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

small naming and code organize refactor

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

revert Member type

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

fix typo

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

use ltree

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

use ltree

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

add level for retrieving

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

add level

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

add ltre

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

upgrade postgres version in test

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

add ltre

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

remove test for now

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

minor fixes

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

fix id setting

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

fix tree endpoint

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

minor style changes

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* small changes, adding new lines

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* minor changes

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* change function signature

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* change primary key

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* change function signature

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* simplufy code

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* remove groups test for now

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix tabulation

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix whitespace

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* remove white space, fix grammar

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix sqls so that not additional retrieve is needed

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix sqls so that not additional retrieve is needed

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

fix primary key

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

remove retrive by name

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* remove name and parent update

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix cound sql

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add line

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* simplify sql, fix table name

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add date

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add date

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add timestamp to api

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix var name

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* fix var name

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>
2020-11-23 11:34:29 +01:00

338 lines
8.5 KiB
Go

package groups
import (
"context"
"github.com/go-kit/kit/endpoint"
"github.com/mainflux/mainflux/internal/groups"
"github.com/mainflux/mainflux/pkg/errors"
)
func CreateGroupEndpoint(svc groups.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(createGroupReq)
if err := req.validate(); err != nil {
return groupRes{}, err
}
group := groups.Group{
Name: req.Name,
Description: req.Description,
ParentID: req.ParentID,
Metadata: req.Metadata,
}
id, err := svc.CreateGroup(ctx, req.token, group)
if err != nil {
return groupRes{}, errors.Wrap(groups.ErrCreateGroup, err)
}
return groupRes{created: true, id: id}, nil
}
}
func ViewGroupEndpoint(svc groups.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(groupReq)
if err := req.validate(); err != nil {
return viewGroupRes{}, errors.Wrap(groups.ErrMalformedEntity, err)
}
group, err := svc.ViewGroup(ctx, req.token, req.groupID)
if err != nil {
return viewGroupRes{}, errors.Wrap(groups.ErrFetchGroups, err)
}
res := viewGroupRes{
ID: group.ID,
Name: group.Name,
Description: group.Description,
Metadata: group.Metadata,
ParentID: group.ParentID,
OwnerID: group.OwnerID,
}
return res, nil
}
}
func UpdateGroupEndpoint(svc groups.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(updateGroupReq)
if err := req.validate(); err != nil {
return groupRes{}, errors.Wrap(groups.ErrMalformedEntity, err)
}
group := groups.Group{
ID: req.id,
Name: req.Name,
Description: req.Description,
ParentID: req.ParentID,
Metadata: req.Metadata,
}
_, err := svc.UpdateGroup(ctx, req.token, group)
if err != nil {
return groupRes{}, errors.Wrap(groups.ErrUpdateGroup, err)
}
res := groupRes{created: false}
return res, nil
}
}
func DeleteGroupEndpoint(svc groups.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(groupReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(groups.ErrMalformedEntity, err)
}
if err := svc.RemoveGroup(ctx, req.token, req.groupID); err != nil {
return nil, errors.Wrap(groups.ErrDeleteGroup, err)
}
return groupDeleteRes{}, nil
}
}
func ListGroupsEndpoint(svc groups.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(listGroupsReq)
if err := req.validate(); err != nil {
return groupPageRes{}, errors.Wrap(groups.ErrMalformedEntity, err)
}
page, err := svc.ListGroups(ctx, req.token, req.level, req.metadata)
if err != nil {
return groupPageRes{}, errors.Wrap(groups.ErrFetchGroups, err)
}
if req.tree {
return buildGroupsResponseTree(page), nil
}
return buildGroupsResponse(page), nil
}
}
func ListMembership(svc groups.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(listMemberGroupReq)
if err := req.validate(); err != nil {
return memberPageRes{}, err
}
page, err := svc.ListMemberships(ctx, req.token, req.memberID, req.offset, req.limit, req.metadata)
if err != nil {
return memberPageRes{}, err
}
if req.tree {
return buildGroupsResponseTree(page), nil
}
return buildGroupsResponse(page), nil
}
}
func ListGroupChildrenEndpoint(svc groups.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(listGroupsReq)
if err := req.validate(); err != nil {
return groupPageRes{}, errors.Wrap(groups.ErrMalformedEntity, err)
}
page, err := svc.ListChildren(ctx, req.token, req.groupID, req.level, req.metadata)
if err != nil {
return groupPageRes{}, errors.Wrap(groups.ErrFetchGroups, err)
}
if req.tree {
return buildGroupsResponseTree(page), nil
}
return buildGroupsResponse(page), nil
}
}
func ListGroupParentsEndpoint(svc groups.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(listGroupsReq)
if err := req.validate(); err != nil {
return groupPageRes{}, errors.Wrap(groups.ErrMalformedEntity, err)
}
page, err := svc.ListParents(ctx, req.token, req.groupID, req.level, req.metadata)
if err != nil {
return groupPageRes{}, errors.Wrap(groups.ErrFetchGroups, err)
}
if req.tree {
return buildGroupsResponseTree(page), nil
}
return buildGroupsResponse(page), nil
}
}
func AssignEndpoint(svc groups.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(memberGroupReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(groups.ErrMalformedEntity, err)
}
if err := svc.Assign(ctx, req.token, req.memberID, req.groupID); err != nil {
return nil, errors.Wrap(groups.ErrAssignToGroup, err)
}
return assignMemberToGroupRes{}, nil
}
}
func UnassignEndpoint(svc groups.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(memberGroupReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(groups.ErrMalformedEntity, err)
}
if err := svc.Unassign(ctx, req.token, req.memberID, req.groupID); err != nil {
return nil, errors.Wrap(groups.ErrUnassignFromGroup, err)
}
return removeMemberFromGroupRes{}, nil
}
}
func ListMembersEndpoint(svc groups.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(listMemberGroupReq)
if err := req.validate(); err != nil {
return memberPageRes{}, errors.Wrap(groups.ErrMalformedEntity, err)
}
page, err := svc.ListMembers(ctx, req.token, req.groupID, req.offset, req.limit, req.metadata)
if err != nil {
return memberPageRes{}, err
}
return buildUsersResponse(page), nil
}
}
func buildGroupsResponseTree(page groups.GroupPage) groupPageRes {
groupsMap := map[string]*groups.Group{}
// Parents map keeps its array of children.
parentsMap := map[string][]*groups.Group{}
for i := range page.Groups {
if _, ok := groupsMap[page.Groups[i].ID]; !ok {
groupsMap[page.Groups[i].ID] = &page.Groups[i]
parentsMap[page.Groups[i].ID] = make([]*groups.Group, 0)
}
}
for _, group := range groupsMap {
if ch, ok := parentsMap[group.ParentID]; ok {
ch = append(ch, group)
parentsMap[group.ParentID] = ch
}
}
res := groupPageRes{
pageRes: pageRes{
Total: page.Total,
Offset: page.Offset,
Limit: page.Limit,
},
Groups: []viewGroupRes{},
}
for _, group := range groupsMap {
if children, ok := parentsMap[group.ID]; ok {
group.Children = children
}
}
for _, group := range groupsMap {
view := toViewGroupRes(*group)
if children, ok := parentsMap[group.ParentID]; len(children) == 0 || !ok {
res.Groups = append(res.Groups, view)
}
}
return res
}
func toViewGroupRes(g groups.Group) viewGroupRes {
view := viewGroupRes{
ID: g.ID,
ParentID: g.ParentID,
OwnerID: g.OwnerID,
Name: g.Name,
Description: g.Description,
Metadata: g.Metadata,
Level: g.Level,
Path: g.Path,
Children: make([]*viewGroupRes, 0),
CreatedAt: g.CreatedAt,
UpdatedAt: g.UpdatedAt,
}
for _, ch := range g.Children {
child := toViewGroupRes(*ch)
view.Children = append(view.Children, &child)
}
return view
}
func buildGroupsResponse(gp groups.GroupPage) groupPageRes {
res := groupPageRes{
pageRes: pageRes{
Total: gp.Total,
Offset: gp.Offset,
Limit: gp.Limit,
},
Groups: []viewGroupRes{},
}
for _, group := range gp.Groups {
view := viewGroupRes{
ID: group.ID,
ParentID: group.ParentID,
OwnerID: group.OwnerID,
Name: group.Name,
Description: group.Description,
Metadata: group.Metadata,
Level: group.Level,
Path: group.Path,
CreatedAt: group.CreatedAt,
UpdatedAt: group.UpdatedAt,
}
res.Groups = append(res.Groups, view)
}
return res
}
func buildUsersResponse(mp groups.MemberPage) memberPageRes {
res := memberPageRes{
pageRes: pageRes{
Total: mp.Total,
Offset: mp.Offset,
Limit: mp.Limit,
Name: mp.Name,
},
Members: []interface{}{},
}
for _, m := range mp.Members {
res.Members = append(res.Members, m)
}
return res
}