Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MG-71 - Add Events pages #85

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 2 additions & 0 deletions cmd/ui/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type config struct {
BootstrapURL string `env:"MG_BOOTSTRAP_URL" envDefault:"http://localhost:9013"`
DomainsURL string `env:"MG_DOMAINS_URL" envDefault:"http://localhost:8189"`
InvitationsURL string `env:"MG_INVITATIONS_URL" envDefault:"http://localhost:9020"`
EventsURL string `env:"MG_EVENTS_URL" envDefault:"http://localhost:9021"`
MsgContentType sdk.ContentType `env:"MG_UI_CONTENT_TYPE" envDefault:"application/senml+json"`
TLSVerification bool `env:"MG_UI_VERIFICATION_TLS" envDefault:"false"`
}
Expand All @@ -55,6 +56,7 @@ func main() {
BootstrapURL: cfg.BootstrapURL,
DomainsURL: cfg.DomainsURL,
InvitationsURL: cfg.InvitationsURL,
EventsURL: cfg.EventsURL,
}

logger, err := logger.New(os.Stdout, cfg.LogLevel)
Expand Down
1 change: 1 addition & 0 deletions docker/.env
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ MG_INVITATIONS_URL=http://localhost:9020
MG_DOMAINS_URL = http://localhost:8189
MG_BOOTSTRAP_URL=http://localhost:9013
MG_UI_HOST_URL=http://localhost:9095
MG_EVENTS_URL=http://localhost:9021
MG_UI_VERIFICATION_TLS=false
MG_UI_CONTENT_TYPE = application/senml+json
MG_UI_INSTANCE_ID=
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)

replace github.com/absmach/magistrala => /home/ian/work/Rodney/magistrala
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/absmach/agent v0.0.0-20231107115142-c8b509f24d50 h1:RyDGAkOtIuN34HEJ/UDTc0r7qZFO5jBx5cB3wmUtPJE=
github.com/absmach/agent v0.0.0-20231107115142-c8b509f24d50/go.mod h1:/S3WufAqHTSU6MEq6cN0g6m21RhPX8dxqubYh08vbU8=
github.com/absmach/magistrala v0.11.1-0.20240103120656-b3e206321c9d h1:Q0HIBP9SSnp41CgGT1cmkd63mvf2/uqGCbaOZbIlSvI=
github.com/absmach/magistrala v0.11.1-0.20240103120656-b3e206321c9d/go.mod h1:7D+27TxXQwMqTmlerdulb0QtvXpMcSgeGoyvzhxr8MQ=
github.com/absmach/mproxy v0.3.1-0.20231221215510-0ffbc4fc2337 h1:OW2WIn094hQCwrkXZ2KHgoOzsKAwqPaxZvRZ94VTc5U=
github.com/absmach/mproxy v0.3.1-0.20231221215510-0ffbc4fc2337/go.mod h1:HmXsnuSWIN0OKrcscIxBzDO/GRjvqYxUTnd6vpuo+MQ=
github.com/absmach/senml v1.0.5 h1:zNPRYpGr2Wsb8brAusz8DIfFqemy1a2dNbmMnegY3GE=
Expand Down
19 changes: 19 additions & 0 deletions ui/api/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,25 @@ func listThingMembersEndpoint(svc ui.Service) endpoint.Endpoint {
}
}

func listEntityEventsEndpoint(svc ui.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(listEntityEventsReq)
if err := req.validate(); err != nil {
return nil, err
}

res, err := svc.ListEntityEvents(req.token, req.entityType, req.entityID, req.page, req.limit)
if err != nil {
return nil, err
}

return uiRes{
html: res,
code: http.StatusOK,
}, nil
}
}

func listChannelsByThingEndpoint(svc ui.Service) endpoint.Endpoint {
return func(_ context.Context, request interface{}) (interface{}, error) {
req := request.(listEntityByIDReq)
Expand Down
2 changes: 2 additions & 0 deletions ui/api/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@ var (
errMissingDomainID = errors.New("missing domain id")
errMissingRole = errors.New("missing role")
errMissingValue = errors.New("missing value")
errMissingType = errors.New("missing type")
errMissingID = errors.New("missing id")
)
13 changes: 13 additions & 0 deletions ui/api/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,19 @@ func (lm *loggingMiddleware) ListChannelsByThing(token, thingID string, page, li
return lm.svc.ListChannelsByThing(token, thingID, page, limit)
}

// ListEntityEvents adds logging middleware to list entity events method.
func (lm *loggingMiddleware) ListEntityEvents(token, entityType, entityID string, page, limit uint64) (b []byte, err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method list_entity_events for thing %s took %s to complete", entityID, time.Since(begin))
if err != nil {
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
return
}
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
}(time.Now())
return lm.svc.ListEntityEvents(token, entityType, entityID, page, limit)
}

// CreateChannel adds logging middleware to create channel method.
func (lm *loggingMiddleware) CreateChannel(channel sdk.Channel, token string) (err error) {
defer func(begin time.Time) {
Expand Down
10 changes: 10 additions & 0 deletions ui/api/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,16 @@ func (mm *metricsMiddleware) ListChannelsByThing(token, thingID string, page, li
return mm.svc.ListChannelsByThing(token, thingID, page, limit)
}

// ListEntityEvents adds metrics middleware to list entity events method.
func (lm *metricsMiddleware) ListEntityEvents(token, entityType, entityID string, page, limit uint64) (b []byte, err error) {
defer func(begin time.Time) {
lm.counter.With("method", "list_entity_events").Add(1)
lm.latency.With("method", "list_entity_events").Observe(time.Since(begin).Seconds())
}(time.Now())

return lm.svc.ListEntityEvents(token, entityType, entityID, page, limit)
}

// CreateChannel adds metrics middleware to create channel method.
func (mm *metricsMiddleware) CreateChannel(channel sdk.Channel, token string) (err error) {
defer func(begin time.Time) {
Expand Down
27 changes: 27 additions & 0 deletions ui/api/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,33 @@ func (req listEntityByIDReq) validate() error {
return nil
}

type listEntityEventsReq struct {
token string
entityID string
entityType string
page uint64
limit uint64
}

func (req listEntityEventsReq) validate() error {
if req.token == "" {
return errAuthorization
}
if req.entityID == "" {
return errMissingID
}
if req.entityType == "" {
return errMissingType
}
if req.page == 0 {
return errPageSize
}
if req.limit == 0 {
return errLimitSize
}
return nil
}

type viewResourceReq struct {
token string
id string
Expand Down
33 changes: 33 additions & 0 deletions ui/api/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,13 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler {
opts...,
).ServeHTTP)
})

r.Get("/events/{id}/{entity}", kithttp.NewServer(
listEntityEventsEndpoint(svc),
decodeListEntityEventsRequest,
encodeResponse,
opts...,
).ServeHTTP)
})

r.Get("/health", magistrala.Health("ui", instanceID))
Expand Down Expand Up @@ -1829,6 +1836,32 @@ func decodeListEntityByIDRequest(_ context.Context, r *http.Request) (interface{
return req, nil
}

func decodeListEntityEventsRequest(_ context.Context, r *http.Request) (interface{}, error) {
token, err := tokenFromCookie(r, "token")
if err != nil {
return nil, err
}
page, err := readNumQuery[uint64](r, pageKey, defPage)
if err != nil {
return nil, err
}

limit, err := readNumQuery[uint64](r, limitKey, defLimit)
if err != nil {
return nil, err
}

req := listEntityEventsReq{
token: token,
entityID: chi.URLParam(r, "id"),
entityType: chi.URLParam(r, "entity"),
page: page,
limit: limit,
}

return req, nil
}

func decodeGetEntitiesRequest(_ context.Context, r *http.Request) (interface{}, error) {
token, err := tokenFromCookie(r, "token")
if err != nil {
Expand Down
86 changes: 81 additions & 5 deletions ui/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ var (
"breadcrumb",
"metadatamodal",
"statusupdate",
"events",

"bootstrap",
"bootstraps",
Expand Down Expand Up @@ -223,9 +224,11 @@ type Service interface {
// UnshareThing unshares a thing with a user.
UnshareThing(token, thingID string, req sdk.UsersRelationRequest) error
// ListThingUsers retrieves users that share a thing.
ListThingUsers(token, thingID, relation string, page, limit uint64) (b []byte, err error)
ListThingUsers(token, thingID, relation string, page, limit uint64) ([]byte, error)
// ListChannelsByThing retrieves a list of channels based on the given thing ID.
ListChannelsByThing(token, thingID string, page, limit uint64) ([]byte, error)
// ListEvents retrieves a list of events based on the given ID.
ListEntityEvents(token, entityType, entityID string, page, limit uint64) ([]byte, error)

// CreateChannel creates a new channel.
CreateChannel(channel sdk.Channel, token string) error
Expand Down Expand Up @@ -256,13 +259,13 @@ type Service interface {
// RemoveUserFromChannel removes a user from a channel.
RemoveUserFromChannel(token, channelID string, req sdk.UsersRelationRequest) error
// ListChannelUsers retrieves a list of users that are connected to a channel.
ListChannelUsers(token, channelID, relation string, page, limit uint64) (b []byte, err error)
ListChannelUsers(token, channelID, relation string, page, limit uint64) ([]byte, error)
// AddUserGroupToChannel adds a userGroup to a channel.
AddUserGroupToChannel(token, channelID string, req sdk.UserGroupsRequest) error
// RemoveGroupFromChannel removes a userGroup from a channel.
RemoveUserGroupFromChannel(token, channelID string, req sdk.UserGroupsRequest) error
// ListChannelUserGroups retrieves a list of userGroups connected to a channel.
ListChannelUserGroups(token, channelID string, page, limit uint64) (b []byte, err error)
ListChannelUserGroups(token, channelID string, page, limit uint64) ([]byte, error)

// CreateGroups creates new groups.
CreateGroups(token string, groups ...sdk.Group) error
Expand All @@ -283,7 +286,7 @@ type Service interface {
// DisableGroup updates the status of the group to disabled.
DisableGroup(token, id string) error
// ListUserGroupChannels retrieves a list of channels that a userGroup is connected to.
ListUserGroupChannels(token, groupID string, page, limit uint64) (b []byte, err error)
ListUserGroupChannels(token, groupID string, page, limit uint64) ([]byte, error)

// Publish facilitates a thing publishin messages to a channel.
Publish(token, thKey string, msg *messaging.Message) error
Expand Down Expand Up @@ -1621,13 +1624,15 @@ func (gs *uiService) Publish(token, thKey string, msg *messaging.Message) error
func (us *uiService) ReadMessage(token, chID, thKey string, page, limit uint64) ([]byte, error) {
var msg sdk.MessagesPage

pgm := sdk.PageMetadata{}

user, err := us.sdk.UserProfile(token)
if err != nil {
return []byte{}, err
}

if chID != "" {
msg, err = us.sdk.ReadMessages(chID, thKey)
msg, err = us.sdk.ReadMessages(pgm, chID, thKey)
if err != nil {
return []byte{}, err
}
Expand Down Expand Up @@ -2289,6 +2294,77 @@ func (us *uiService) DeleteInvitation(token, userID, domainID string) error {
return us.sdk.DeleteInvitation(userID, domainID, token)
}

func (us *uiService) ListEntityEvents(token, entityType, entityID string, page, limit uint64) ([]byte, error) {
offset := (page - 1) * limit
pgm := sdk.PageMetadata{
Offset: offset,
Limit: limit,
Direction: "desc",
WithPayload: true,
}

var crumbs []breadcrumb
switch entityType {
case "thing":
crumbs = []breadcrumb{
{Name: thingsActive, URL: thingsEndpoint},
{Name: entityID, URL: thingsEndpoint + "/" + entityID},
{Name: "Events"},
}
case "channel":
crumbs = []breadcrumb{
{Name: channelsActive, URL: channelsEndpoint},
{Name: entityID, URL: channelsEndpoint + "/" + entityID},
{Name: "Events"},
}
entityType = "group"
case "user":
crumbs = []breadcrumb{
{Name: usersActive, URL: usersEndpoint},
{Name: entityID, URL: usersEndpoint + "/" + entityID},
{Name: "Events"},
}
case "group":
crumbs = []breadcrumb{
{Name: groupsActive, URL: groupsEndpoint},
{Name: entityID, URL: groupsEndpoint + "/" + entityID},
{Name: "Events"},
}
}

eventsPage, err := us.sdk.Events(pgm, entityID, entityType, token)
if err != nil {
return []byte{}, errors.Wrap(err, ErrFailedRetreive)
}

noOfPages := int(math.Ceil(float64(eventsPage.Total) / float64(limit)))

data := struct {
NavbarActive string
CollapseActive string
Events []sdk.Event
CurrentPage int
Pages int
Limit int
Breadcrumbs []breadcrumb
}{
thingsActive,
thingsActive,
eventsPage.Events,
int(page),
noOfPages,
int(limit),
crumbs,
}

var btpl bytes.Buffer
if err := us.tpls.ExecuteTemplate(&btpl, "events", data); err != nil {
return []byte{}, errors.Wrap(err, ErrExecTemplate)
}

return btpl.Bytes(), nil
}

func parseTemplates(mfsdk sdk.SDK, templates []string) (tpl *template.Template, err error) {
tpl = template.New("mainflux")
tpl = tpl.Funcs(template.FuncMap{
Expand Down
Loading
Loading