diff --git a/cmd/ui/main.go b/cmd/ui/main.go index 51308959..a5cbfc98 100644 --- a/cmd/ui/main.go +++ b/cmd/ui/main.go @@ -32,6 +32,7 @@ type config struct { UsersURL string `env:"MG_USERS_URL" envDefault:"http://localhost:9002"` HostURL string `env:"MG_UI_HOST_URL" envDefault:"http://localhost:9095"` BootstrapURL string `env:"MG_BOOTSTRAP_URL" envDefault:"http://localhost:9013"` + DomainsURL string `env:"MG_DOMAINS_URL" envDefault:"http://localhost:8189"` MsgContentType sdk.ContentType `env:"MG_CONTENT-TYPE" envDefault:"application/senml+json"` TLSVerification bool `env:"MG_VERIFICATION_TLS" envDefault:"false"` } @@ -51,6 +52,7 @@ func main() { MsgContentType: cfg.MsgContentType, TLSVerification: cfg.TLSVerification, BootstrapURL: cfg.BootstrapURL, + DomainsURL: cfg.DomainsURL, } logger, err := logger.New(os.Stdout, cfg.LogLevel) diff --git a/docker/.env b/docker/.env index 1ee1f514..ddb339a0 100644 --- a/docker/.env +++ b/docker/.env @@ -10,6 +10,7 @@ MG_HTTP_ADAPTER_URL=http://localhost:8008 MG_READER_URL=http://localhost:9007 MG_THINGS_URL=http://localhost:9000 MG_USERS_URL=http://localhost:9002 +MG_DOMAINS_URL = http://localhost:8189 MG_VERIFICATION_TLS=false MG_BOOTSTRAP_URL=http://localhost:9013 MG_UI_INSTANCE_ID= diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 5689862d..9064edc3 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -24,6 +24,7 @@ services: MG_THINGS_URL: ${MG_THINGS_URL} MG_USERS_URL: ${MG_USERS_URL} MG_BOOTSTRAP_URL: ${MG_BOOTSTRAP_URL} + MG_DOMAINS_HTTP_URL: ${MG_DOMAINS_HTTP_URL} MG_VERIFICATION_TLS: ${MG_VERIFICATION_TLS} MG_UI_INSTANCE_ID: ${MG_UI_INSTANCE_ID} MG_UI_HOST_URL: ${MG_UI_HOST_URL} diff --git a/go.mod b/go.mod index 6067594e..ef16b0a0 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.0 require ( github.com/absmach/agent v0.0.0-20231107115142-c8b509f24d50 - github.com/absmach/magistrala v0.11.1-0.20231102134813-44408395e6a3 + github.com/absmach/magistrala v0.11.1-0.20231130085244-39999ad5ee3a github.com/caarlos0/env/v9 v9.0.0 github.com/eclipse/paho.mqtt.golang v1.4.3 github.com/go-chi/chi/v5 v5.0.10 @@ -39,7 +39,7 @@ require ( github.com/prometheus/procfs v0.12.0 // indirect github.com/x448/float16 v0.8.4 // indirect golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect + golang.org/x/sys v0.14.0 // indirect golang.org/x/text v0.13.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/protobuf v1.31.0 // indirect diff --git a/go.sum b/go.sum index eab60e0e..0ff0f2d9 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,10 @@ github.com/absmach/agent v0.0.0-20231107115142-c8b509f24d50 h1:RyDGAkOtIuN34HEJ/ github.com/absmach/agent v0.0.0-20231107115142-c8b509f24d50/go.mod h1:/S3WufAqHTSU6MEq6cN0g6m21RhPX8dxqubYh08vbU8= github.com/absmach/magistrala v0.11.1-0.20231102134813-44408395e6a3 h1:g5dSaPtjj9mNnz2cMJ076MRKSnrOcMjW8BsJ7Kbzd7s= github.com/absmach/magistrala v0.11.1-0.20231102134813-44408395e6a3/go.mod h1:ebPpg3UNO6ier1Ic2jBHkd8VUDD62707JRacj4UwGkM= +github.com/absmach/magistrala v0.11.1-0.20231117114648-a5fd7b037aea h1:VPlqBO3Z1sLmPGoypvIjitHH2RYoDuyUouLYAQLJxH4= +github.com/absmach/magistrala v0.11.1-0.20231117114648-a5fd7b037aea/go.mod h1:IOlItdz21DSTjm6+Wd5ADlWwVGyePaps/OLU+Yu/1WI= +github.com/absmach/magistrala v0.11.1-0.20231130085244-39999ad5ee3a h1:wbZ8kzLYK404GNIEuq6mF+WjjU99+PCnGefeHBG5pac= +github.com/absmach/magistrala v0.11.1-0.20231130085244-39999ad5ee3a/go.mod h1:LiF4qov0bRFK7eyD79w0IAABjuGa+Ybix24i9q/aiwI= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -916,6 +920,8 @@ golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= diff --git a/package-lock.json b/package-lock.json index 6dd88cd5..9ccca2d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,52 +1,52 @@ { - "name": "magistrala-ui", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "devDependencies": { - "prettier": "^3.0.3", - "prettier-plugin-go-template": "^0.0.15" - } - }, - "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-plugin-go-template": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/prettier-plugin-go-template/-/prettier-plugin-go-template-0.0.15.tgz", - "integrity": "sha512-WqU92E1NokWYNZ9mLE6ijoRg6LtIGdLMePt2C7UBDjXeDH9okcRI3zRqtnWR4s5AloiqyvZ66jNBAa9tmRY5EQ==", - "dev": true, - "dependencies": { - "ulid": "^2.3.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "prettier": "^3.0.0" - } - }, - "node_modules/ulid": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", - "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", - "dev": true, - "bin": { - "ulid": "bin/cli.js" - } - } - } + "name": "magistrala-ui", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "prettier": "3.1.0", + "prettier-plugin-go-template": "^0.0.15" + } + }, + "node_modules/prettier": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-go-template": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/prettier-plugin-go-template/-/prettier-plugin-go-template-0.0.15.tgz", + "integrity": "sha512-WqU92E1NokWYNZ9mLE6ijoRg6LtIGdLMePt2C7UBDjXeDH9okcRI3zRqtnWR4s5AloiqyvZ66jNBAa9tmRY5EQ==", + "dev": true, + "dependencies": { + "ulid": "^2.3.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "prettier": "^3.0.0" + } + }, + "node_modules/ulid": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", + "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", + "dev": true, + "bin": { + "ulid": "bin/cli.js" + } + } + } } diff --git a/package.json b/package.json index 61baadfd..87911017 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "devDependencies": { - "prettier": "^3.0.3", + "prettier": "3.1.0", "prettier-plugin-go-template": "^0.0.15" } } diff --git a/ui/api/endpoint.go b/ui/api/endpoint.go index 9b364dcc..b0cb2da9 100644 --- a/ui/api/endpoint.go +++ b/ui/api/endpoint.go @@ -19,7 +19,7 @@ func indexEndpoint(svc ui.Service) endpoint.Endpoint { if err := req.validate(); err != nil { return nil, err } - res, err := svc.Index(req.token) + res, err := svc.Index(req.token, req.orgID) if err != nil { return nil, err } @@ -66,6 +66,13 @@ func logoutEndpoint(svc ui.Service) endpoint.Endpoint { MaxAge: -1, HttpOnly: true, }, + { + Name: refreshTokenKey, + Value: "", + Path: organizationsAPIEndpoint, + MaxAge: -1, + HttpOnly: true, + }, } return uiRes{ code: http.StatusSeeOther, @@ -171,6 +178,13 @@ func updatePasswordEndpoint(svc ui.Service) endpoint.Endpoint { MaxAge: -1, HttpOnly: true, }, + { + Name: refreshTokenKey, + Value: "", + Path: organizationsAPIEndpoint, + MaxAge: -1, + HttpOnly: true, + }, } return uiRes{ @@ -187,37 +201,32 @@ func tokenEndpoint(svc ui.Service) endpoint.Endpoint { if err := req.validate(); err != nil { return nil, err } - credentials := sdk.Credentials{ - Identity: req.Identity, - Secret: req.Secret, - } - user := sdk.User{ - Credentials: credentials, - } - token, err := svc.Token(user) + token, err := svc.Token( + sdk.Login{ + Identity: req.Identity, + Secret: req.Secret, + }) if err != nil { return nil, err } - cookies := []*http.Cookie{ - { - Name: accessTokenKey, - Value: token.AccessToken, - Path: "/", - HttpOnly: true, - }, - { - Name: refreshTokenKey, - Value: token.RefreshToken, - Path: tokenRefreshAPIEndpoint, - HttpOnly: true, - }, - } - tkr := uiRes{ - code: http.StatusCreated, - cookies: cookies, + code: http.StatusCreated, + cookies: []*http.Cookie{ + { + Name: accessTokenKey, + Value: token.AccessToken, + Path: "/", + HttpOnly: true, + }, + { + Name: refreshTokenKey, + Value: token.RefreshToken, + Path: organizationsAPIEndpoint, + HttpOnly: true, + }, + }, } return tkr, nil @@ -236,31 +245,54 @@ func refreshTokenEndpoint(svc ui.Service) endpoint.Endpoint { return nil, err } - cookies := []*http.Cookie{ - { - Name: accessTokenKey, - Value: token.AccessToken, - Path: "/", - HttpOnly: true, - }, - { - Name: refreshTokenKey, - Value: token.RefreshToken, - Path: tokenRefreshAPIEndpoint, - HttpOnly: true, - }, - } - tkr := uiRes{ code: http.StatusSeeOther, headers: map[string]string{"Location": req.ref}, - cookies: cookies, + cookies: []*http.Cookie{ + { + Name: accessTokenKey, + Value: token.AccessToken, + Path: "/", + HttpOnly: true, + }, + { + Name: refreshTokenKey, + Value: token.RefreshToken, + Path: tokenRefreshAPIEndpoint, + HttpOnly: true, + }, + { + Name: refreshTokenKey, + Value: token.RefreshToken, + Path: organizationsAPIEndpoint, + HttpOnly: true, + }, + }, } return tkr, nil } } +func userProfileEndpoint(svc ui.Service) endpoint.Endpoint { + return func(_ context.Context, request interface{}) (interface{}, error) { + req := request.(listEntityReq) + if err := req.validate(); err != nil { + return nil, err + } + + res, err := svc.UserProfile(req.token, req.page, req.limit) + if err != nil { + return nil, err + } + + return uiRes{ + code: http.StatusOK, + html: res, + }, nil + } +} + func createUserEndpoint(svc ui.Service) endpoint.Endpoint { return func(_ context.Context, request interface{}) (interface{}, error) { req := request.(createUserReq) @@ -437,125 +469,7 @@ func disableUserEndpoint(svc ui.Service) endpoint.Endpoint { } } -func listUserGroupsEndpoint(svc ui.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(listEntityByIDReq) - - res, err := svc.ListUserGroups(req.token, req.id, req.relation, req.page, req.limit) - if err != nil { - return nil, err - } - - return uiRes{ - html: res, - code: http.StatusOK, - }, nil - } -} - -func listUserThingsEndpoint(svc ui.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(listEntityByIDReq) - - res, err := svc.ListUserThings(req.token, req.id, req.page, req.limit) - if err != nil { - return nil, err - } - - return uiRes{ - html: res, - code: http.StatusOK, - }, nil - } -} - -func shareThingEndpoint(svc ui.Service) endpoint.Endpoint { - return func(_ context.Context, request interface{}) (interface{}, error) { - req := request.(shareThingReq) - if err := req.validate(); err != nil { - return nil, err - } - - userRelation := sdk.UsersRelationRequest{ - Relation: req.Relation, - UserIDs: []string{req.UserID}, - } - - if err := svc.ShareThing(req.token, req.ThingID, userRelation); err != nil { - return nil, err - } - - var ret uiRes - - switch req.Item { - case usersItem: - ret = uiRes{ - code: http.StatusSeeOther, - headers: map[string]string{"Location": usersAPIEndpoint + "/" + req.UserID + thingsAPIEndpoint}, - } - case thingsItem: - ret = uiRes{ - code: http.StatusSeeOther, - headers: map[string]string{"Location": thingsAPIEndpoint + "/" + req.ThingID + usersAPIEndpoint}, - } - } - - return ret, nil - } -} - -func unshareThingEndpoint(svc ui.Service) endpoint.Endpoint { - return func(_ context.Context, request interface{}) (interface{}, error) { - req := request.(shareThingReq) - if err := req.validate(); err != nil { - return nil, err - } - - userRelation := sdk.UsersRelationRequest{ - Relation: req.Relation, - UserIDs: []string{req.UserID}, - } - - if err := svc.UnshareThing(req.token, req.ThingID, userRelation); err != nil { - return nil, err - } - - var ret uiRes - - switch req.Item { - case usersItem: - ret = uiRes{ - code: http.StatusSeeOther, - headers: map[string]string{"Location": usersAPIEndpoint + "/" + req.UserID + thingsAPIEndpoint}, - } - case thingsItem: - ret = uiRes{ - code: http.StatusSeeOther, - headers: map[string]string{"Location": thingsAPIEndpoint + "/" + req.ThingID + usersAPIEndpoint}, - } - } - - return ret, nil - } -} - -func listUserChannelsEndpoint(svc ui.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(listEntityByIDReq) - - res, err := svc.ListUserChannels(req.token, req.id, req.relation, req.page, req.limit) - if err != nil { - return nil, err - } - - return uiRes{ - html: res, - code: http.StatusOK, - }, nil - } -} - -func AddChannelToUserEndpoint(svc ui.Service) endpoint.Endpoint { +func AddMemberToChannelEndpoint(svc ui.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(addUserToChannelReq) if err := req.validate(); err != nil { @@ -571,26 +485,14 @@ func AddChannelToUserEndpoint(svc ui.Service) endpoint.Endpoint { return nil, err } - var ret uiRes - - switch req.Item { - case usersItem: - ret = uiRes{ - code: http.StatusSeeOther, - headers: map[string]string{"Location": usersAPIEndpoint + "/" + req.UserID + channelsAPIEndpoint}, - } - case channelsItem: - ret = uiRes{ - code: http.StatusSeeOther, - headers: map[string]string{"Location": channelsAPIEndpoint + "/" + req.ChannelID + usersAPIEndpoint}, - } - } - - return ret, nil + return uiRes{ + code: http.StatusSeeOther, + headers: map[string]string{"Location": channelsAPIEndpoint + "/" + req.ChannelID + usersAPIEndpoint}, + }, nil } } -func RemoveChannelFromUserEndpoint(svc ui.Service) endpoint.Endpoint { +func RemoveMemberFromChannelEndpoint(svc ui.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(addUserToChannelReq) if err := req.validate(); err != nil { @@ -606,22 +508,10 @@ func RemoveChannelFromUserEndpoint(svc ui.Service) endpoint.Endpoint { return nil, err } - var ret uiRes - - switch req.Item { - case usersItem: - ret = uiRes{ - code: http.StatusSeeOther, - headers: map[string]string{"Location": usersAPIEndpoint + "/" + req.UserID + channelsAPIEndpoint}, - } - case channelsItem: - ret = uiRes{ - code: http.StatusSeeOther, - headers: map[string]string{"Location": channelsAPIEndpoint + "/" + req.ChannelID + usersAPIEndpoint}, - } - } - - return ret, nil + return uiRes{ + code: http.StatusSeeOther, + headers: map[string]string{"Location": channelsAPIEndpoint + "/" + req.ChannelID + usersAPIEndpoint}, + }, nil } } @@ -641,22 +531,10 @@ func assignGroupEndpoint(svc ui.Service) endpoint.Endpoint { return nil, err } - var ret uiRes - - switch req.Item { - case usersItem: - ret = uiRes{ - code: http.StatusSeeOther, - headers: map[string]string{"Location": usersAPIEndpoint + "/" + req.UserID + groupsAPIEndpoint}, - } - case groupsItem: - ret = uiRes{ - code: http.StatusSeeOther, - headers: map[string]string{"Location": groupsAPIEndpoint + "/" + req.GroupID + usersAPIEndpoint}, - } - } - - return ret, nil + return uiRes{ + code: http.StatusSeeOther, + headers: map[string]string{"Location": groupsAPIEndpoint + "/" + req.GroupID + usersAPIEndpoint}, + }, nil } } @@ -676,22 +554,10 @@ func unassignGroupEndpoint(svc ui.Service) endpoint.Endpoint { return nil, err } - var ret uiRes - - switch req.Item { - case usersItem: - ret = uiRes{ - code: http.StatusSeeOther, - headers: map[string]string{"Location": usersAPIEndpoint + "/" + req.UserID + groupsAPIEndpoint}, - } - case groupsItem: - ret = uiRes{ - code: http.StatusSeeOther, - headers: map[string]string{"Location": groupsAPIEndpoint + "/" + req.GroupID + usersAPIEndpoint}, - } - } - - return ret, nil + return uiRes{ + code: http.StatusSeeOther, + headers: map[string]string{"Location": groupsAPIEndpoint + "/" + req.GroupID + usersAPIEndpoint}, + }, nil } } @@ -828,27 +694,6 @@ func updateThingSecretEndpoint(svc ui.Service) endpoint.Endpoint { } } -func updateThingOwnerEndpoint(svc ui.Service) endpoint.Endpoint { - return func(_ context.Context, request interface{}) (interface{}, error) { - req := request.(updateThingOwnerReq) - if err := req.validate(); err != nil { - return nil, err - } - - thing := sdk.Thing{ - ID: req.id, - Owner: req.Owner, - } - if err := svc.UpdateThingOwner(req.token, thing); err != nil { - return nil, err - } - - return uiRes{ - code: http.StatusOK, - }, nil - } -} - func enableThingEndpoint(svc ui.Service) endpoint.Endpoint { return func(_ context.Context, request interface{}) (interface{}, error) { req := request.(updateThingStatusReq) @@ -885,11 +730,11 @@ func disableThingEndpoint(svc ui.Service) endpoint.Endpoint { } } -func listThingUsersEndpoint(svc ui.Service) endpoint.Endpoint { +func listThingMembersEndpoint(svc ui.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(listEntityByIDReq) - res, err := svc.ListThingUsers(req.token, req.id, req.page, req.limit) + res, err := svc.ListThingUsers(req.token, req.id, req.relation, req.page, req.limit) if err != nil { return nil, err } @@ -1131,7 +976,7 @@ func disableChannelEndpoint(svc ui.Service) endpoint.Endpoint { } } -func ListChannelUsersEndpoint(svc ui.Service) endpoint.Endpoint { +func ListChannelMembersEndpoint(svc ui.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(listEntityByIDReq) @@ -1147,7 +992,7 @@ func ListChannelUsersEndpoint(svc ui.Service) endpoint.Endpoint { } } -func addUserGroupToChannelEndpoint(svc ui.Service) endpoint.Endpoint { +func addGroupToChannelEndpoint(svc ui.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(addUserGroupToChannelReq) if err := req.validate(); err != nil { @@ -1181,7 +1026,7 @@ func addUserGroupToChannelEndpoint(svc ui.Service) endpoint.Endpoint { } } -func removeUserGroupFromChannelEndpoint(svc ui.Service) endpoint.Endpoint { +func removeGroupFromChannelEndpoint(svc ui.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(addUserGroupToChannelReq) if err := req.validate(); err != nil { @@ -1215,7 +1060,7 @@ func removeUserGroupFromChannelEndpoint(svc ui.Service) endpoint.Endpoint { } } -func ListChannelUserGroupsEndpoint(svc ui.Service) endpoint.Endpoint { +func ListChannelGroupsEndpoint(svc ui.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(listEntityByIDReq) @@ -1265,7 +1110,7 @@ func createGroupsEndpoint(svc ui.Service) endpoint.Endpoint { } } -func listGroupUsersEndpoint(svc ui.Service) endpoint.Endpoint { +func listGroupMembersEndpoint(svc ui.Service) endpoint.Endpoint { return func(_ context.Context, request interface{}) (interface{}, error) { req := request.(listEntityByIDReq) if err := req.validate(); err != nil { @@ -1381,39 +1226,7 @@ func disableGroupEndpoint(svc ui.Service) endpoint.Endpoint { } } -func listParentsEndpoint(svc ui.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(listEntityByIDReq) - - res, err := svc.ListParents(req.token, req.id, req.page, req.limit) - if err != nil { - return nil, err - } - - return uiRes{ - html: res, - code: http.StatusOK, - }, nil - } -} - -func listChildrenEndpoint(svc ui.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(listEntityByIDReq) - - res, err := svc.ListChildren(req.token, req.id, req.page, req.limit) - if err != nil { - return nil, err - } - - return uiRes{ - html: res, - code: http.StatusOK, - }, nil - } -} - -func listUserGroupChannelsEndpoint(svc ui.Service) endpoint.Endpoint { +func listGroupChannelsEndpoint(svc ui.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(listEntityByIDReq) @@ -1652,7 +1465,7 @@ func getEntitiesEndpoint(svc ui.Service) endpoint.Endpoint { return nil, err } - res, err := svc.GetEntities(req.token, req.Item, req.Name, req.Page, req.Limit) + res, err := svc.GetEntities(req.token, req.Item, req.Name, req.OrgID, req.Permission, req.Page, req.Limit) if err != nil { return nil, err } @@ -1681,3 +1494,253 @@ func errorPageEndpoint(svc ui.Service) endpoint.Endpoint { }, nil } } + +func organizationLoginEndpoint(svc ui.Service) endpoint.Endpoint { + return func(_ context.Context, request interface{}) (interface{}, error) { + req := request.(organizationLoginReq) + if err := req.validate(); err != nil { + return nil, err + } + + token, err := svc.OrganizationLogin( + sdk.Login{ + DomainID: req.OrgID, + }, + req.token, + ) + if err != nil { + return nil, err + } + + return uiRes{ + code: http.StatusSeeOther, + cookies: []*http.Cookie{ + { + Name: accessTokenKey, + Value: token.AccessToken, + Path: "/", + HttpOnly: true, + }, + { + Name: refreshTokenKey, + Value: token.RefreshToken, + Path: organizationsAPIEndpoint, + HttpOnly: true, + }, + { + Name: refreshTokenKey, + Value: token.RefreshToken, + Path: tokenRefreshAPIEndpoint, + HttpOnly: true, + }, + }, + headers: map[string]string{"Location": "/?organization=" + req.OrgID}, + }, nil + } +} + +func listOrganizationsEndpoint(svc ui.Service) endpoint.Endpoint { + return func(_ context.Context, request interface{}) (interface{}, error) { + req := request.(listEntityReq) + if err := req.validate(); err != nil { + return nil, err + } + + res, err := svc.ListOrganizations(req.token, req.page, req.limit) + if err != nil { + return nil, err + } + + return uiRes{ + code: http.StatusOK, + html: res, + }, nil + } +} + +func createOrganizationEndpoint(svc ui.Service) endpoint.Endpoint { + return func(_ context.Context, request interface{}) (interface{}, error) { + req := request.(createOrganizationReq) + if err := req.validate(); err != nil { + return nil, err + } + + err := svc.CreateOrganization( + req.token, + sdk.Domain{ + Name: req.Name, + Metadata: req.Metadata, + Tags: req.Tags, + Alias: req.Alias, + }, + ) + if err != nil { + return nil, err + } + + return uiRes{ + code: http.StatusSeeOther, + headers: map[string]string{"Location": organizationsAPIEndpoint}, + }, nil + } +} + +func updateOrganizationEndpoint(svc ui.Service) endpoint.Endpoint { + return func(_ context.Context, request interface{}) (interface{}, error) { + req := request.(updateOrganizationReq) + if err := req.validate(); err != nil { + return nil, err + } + + if err := svc.UpdateOrganization( + req.token, + sdk.Domain{ + ID: req.OrgID, + Name: req.Name, + Metadata: req.Metadata, + Tags: req.Tags, + Alias: req.Alias, + }, + ); err != nil { + return nil, err + } + + return uiRes{ + code: http.StatusOK, + }, nil + } +} + +func organizationEndpoint(svc ui.Service) endpoint.Endpoint { + return func(_ context.Context, request interface{}) (interface{}, error) { + req := request.(listEntityByIDReq) + if err := req.validate(); err != nil { + return nil, err + } + + res, err := svc.Organization(req.token, req.id, req.relation, req.page, req.limit) + if err != nil { + return nil, err + } + + return uiRes{ + code: http.StatusOK, + html: res, + }, nil + } +} + +func assignMemberEndpoint(svc ui.Service) endpoint.Endpoint { + return func(_ context.Context, request interface{}) (interface{}, error) { + req := request.(assignMemberReq) + if err := req.validate(); err != nil { + return nil, err + } + + if err := svc.AssignMember( + req.token, + req.OrgID, + sdk.UsersRelationRequest{ + Relation: req.Relation, + UserIDs: []string{req.UserID}, + }, + ); err != nil { + return nil, err + } + + return uiRes{ + code: http.StatusSeeOther, + headers: map[string]string{"Location": organizationsAPIEndpoint + "/" + req.OrgID + "?relation=members"}, + }, nil + } +} + +func unassignMemberEndpoint(svc ui.Service) endpoint.Endpoint { + return func(_ context.Context, request interface{}) (interface{}, error) { + req := request.(assignMemberReq) + if err := req.validate(); err != nil { + return nil, err + } + + if err := svc.UnassignMember( + req.token, + req.OrgID, + sdk.UsersRelationRequest{ + Relation: req.Relation, + UserIDs: []string{req.UserID}, + }, + ); err != nil { + return nil, err + } + + return uiRes{ + code: http.StatusSeeOther, + headers: map[string]string{"Location": organizationsAPIEndpoint + "/" + req.OrgID + "?relation=members"}, + }, nil + } +} + +func viewMemberEndpoint(svc ui.Service) endpoint.Endpoint { + return func(_ context.Context, request interface{}) (interface{}, error) { + req := request.(viewMemberReq) + if err := req.validate(); err != nil { + return nil, err + } + + res, err := svc.ViewMember(req.token, req.UserIdentity) + if err != nil { + return nil, err + } + + return uiRes{ + code: http.StatusOK, + html: res, + }, nil + } +} + +func shareThingEndpoint(svc ui.Service) endpoint.Endpoint { + return func(_ context.Context, request interface{}) (interface{}, error) { + req := request.(shareThingReq) + if err := req.validate(); err != nil { + return nil, err + } + + userRelation := sdk.UsersRelationRequest{ + Relation: req.Relation, + UserIDs: []string{req.UserID}, + } + + if err := svc.ShareThing(req.token, req.ThingID, userRelation); err != nil { + return nil, err + } + + return uiRes{ + code: http.StatusSeeOther, + headers: map[string]string{"Location": thingsAPIEndpoint + "/" + req.ThingID + usersAPIEndpoint}, + }, nil + } +} + +func unshareThingEndpoint(svc ui.Service) endpoint.Endpoint { + return func(_ context.Context, request interface{}) (interface{}, error) { + req := request.(shareThingReq) + if err := req.validate(); err != nil { + return nil, err + } + + userRelation := sdk.UsersRelationRequest{ + Relation: req.Relation, + UserIDs: []string{req.UserID}, + } + + if err := svc.UnshareThing(req.token, req.ThingID, userRelation); err != nil { + return nil, err + } + + return uiRes{ + code: http.StatusSeeOther, + headers: map[string]string{"Location": thingsAPIEndpoint + "/" + req.ThingID + usersAPIEndpoint}, + }, nil + } +} diff --git a/ui/api/logging.go b/ui/api/logging.go index ef053e56..1ec65f90 100644 --- a/ui/api/logging.go +++ b/ui/api/logging.go @@ -27,7 +27,7 @@ func LoggingMiddleware(svc ui.Service, logger log.Logger) ui.Service { } // Index adds logging middleware to index method. -func (lm *loggingMiddleware) Index(token string) (b []byte, err error) { +func (lm *loggingMiddleware) Index(token, orgID string) (b []byte, err error) { defer func(begin time.Time) { message := fmt.Sprintf("Method index took %s to complete", time.Since(begin)) if err != nil { @@ -37,7 +37,7 @@ func (lm *loggingMiddleware) Index(token string) (b []byte, err error) { lm.logger.Info(fmt.Sprintf("%s without errors.", message)) }(time.Now()) - return lm.svc.Index(token) + return lm.svc.Index(token, orgID) } // Login adds logging middleware to login method. @@ -136,7 +136,7 @@ func (lm *loggingMiddleware) UpdatePassword(token, oldPass, newPass string) (err } // Toke adds logging middleware to token method. -func (lm *loggingMiddleware) Token(user sdk.User) (token sdk.Token, err error) { +func (lm *loggingMiddleware) Token(login sdk.Login) (token sdk.Token, err error) { defer func(begin time.Time) { message := fmt.Sprintf("Method token took %s to complete", time.Since(begin)) if err != nil { @@ -146,7 +146,7 @@ func (lm *loggingMiddleware) Token(user sdk.User) (token sdk.Token, err error) { lm.logger.Info(fmt.Sprintf("%s without errors.", message)) }(time.Now()) - return lm.svc.Token(user) + return lm.svc.Token(login) } // RefreshToken adds logging middleware to refresh token method. @@ -163,6 +163,20 @@ func (lm *loggingMiddleware) RefreshToken(refreshToken string) (token sdk.Token, return lm.svc.RefreshToken(refreshToken) } +// UserProfile adds logging middleware to user profile method. +func (lm *loggingMiddleware) UserProfile(token string, page, limit uint64) (b []byte, err error) { + defer func(begin time.Time) { + message := fmt.Sprintf("Method user_profile took %s to complete", 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.UserProfile(token, page, limit) +} + // CreateUsers adds logging middleware to create users method. func (lm *loggingMiddleware) CreateUsers(token string, users ...sdk.User) (err error) { defer func(begin time.Time) { @@ -289,48 +303,6 @@ func (lm *loggingMiddleware) DisableUser(token, id string) (err error) { return lm.svc.DisableUser(token, id) } -// ListUserGroups adds logging middleware to list user groups method. -func (lm *loggingMiddleware) ListUserGroups(token, userID, relation string, page, limit uint64) (b []byte, err error) { - defer func(begin time.Time) { - message := fmt.Sprintf("Method list_user_groups took %s to complete", 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.ListUserGroups(token, userID, relation, page, limit) -} - -// ListUserThings adds logging middleware to list user things method. -func (lm *loggingMiddleware) ListUserThings(token, userID string, page, limit uint64) (b []byte, err error) { - defer func(begin time.Time) { - message := fmt.Sprintf("Method list_user_things took %s to complete", 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.ListUserThings(token, userID, page, limit) -} - -// ListUserChannels adds logging middleware to list user channels method. -func (lm *loggingMiddleware) ListUserChannels(token, userID, relation string, page, limit uint64) (b []byte, err error) { - defer func(begin time.Time) { - message := fmt.Sprintf("Method list_user_channels took %s to complete", 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.ListUserChannels(token, userID, relation, page, limit) -} - // CreateThing adds logging middleware to create thing method. func (lm *loggingMiddleware) CreateThing(thing sdk.Thing, token string) (err error) { defer func(begin time.Time) { @@ -429,20 +401,6 @@ func (lm *loggingMiddleware) UpdateThingSecret(token, id, secret string) (err er return lm.svc.UpdateThingSecret(token, id, secret) } -// UpdateThingOwner adds logging middleware to update thing owner method. -func (lm *loggingMiddleware) UpdateThingOwner(token string, thing sdk.Thing) (err error) { - defer func(begin time.Time) { - message := fmt.Sprintf("Method update_thing_owner for thing %s took %s to complete", thing.ID, 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.UpdateThingOwner(token, thing) -} - // EnableThing adds logging middleware to enable thing method. func (lm *loggingMiddleware) EnableThing(token, id string) (err error) { defer func(begin time.Time) { @@ -500,7 +458,7 @@ func (lm *loggingMiddleware) UnshareThing(token, thingID string, req sdk.UsersRe } // ListThingUsers adds logging middleware to list thing users method. -func (lm *loggingMiddleware) ListThingUsers(token, thingID string, page, limit uint64) (b []byte, err error) { +func (lm *loggingMiddleware) ListThingUsers(token, thingID, relation string, page, limit uint64) (b []byte, err error) { defer func(begin time.Time) { message := fmt.Sprintf("Method list_thing_users took %s to complete", time.Since(begin)) if err != nil { @@ -510,7 +468,7 @@ func (lm *loggingMiddleware) ListThingUsers(token, thingID string, page, limit u lm.logger.Info(fmt.Sprintf("%s without errors.", message)) }(time.Now()) - return lm.svc.ListThingUsers(token, thingID, page, limit) + return lm.svc.ListThingUsers(token, thingID, relation, page, limit) } // ListChannelsByThing adds logging middleware to list channels by thing method. @@ -905,34 +863,6 @@ func (lm *loggingMiddleware) DisableGroup(token, id string) (err error) { return lm.svc.DisableGroup(token, id) } -// ListParents adds logging middleware to list parents method. -func (lm *loggingMiddleware) ListParents(token, groupID string, page, limit uint64) (b []byte, err error) { - defer func(begin time.Time) { - message := fmt.Sprintf("Method list_parents took %s to complete", 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.ListParents(token, groupID, page, limit) -} - -// ListChildren adds logging middleware to list children method. -func (lm *loggingMiddleware) ListChildren(token, groupID string, page, limit uint64) (b []byte, err error) { - defer func(begin time.Time) { - message := fmt.Sprintf("Method list_children took %s to complete", 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.ListChildren(token, groupID, page, limit) -} - // ListUserGroupChannels adds logging middleware to list usergroup channels method. func (lm *loggingMiddleware) ListUserGroupChannels(token, groupID string, page, limit uint64) (b []byte, err error) { defer func(begin time.Time) { @@ -1105,7 +1035,7 @@ func (lm *loggingMiddleware) ProcessTerminalCommand(ctx context.Context, id, tok } // GetEntities adds logging middleware to get entities method. -func (lm *loggingMiddleware) GetEntities(token, item, name string, page, limit uint64) (b []byte, err error) { +func (lm *loggingMiddleware) GetEntities(token, item, name, orgID, permission string, page, limit uint64) (b []byte, err error) { defer func(begin time.Time) { message := fmt.Sprintf("Method get_entities took %s to complete", time.Since(begin)) if err != nil { @@ -1115,7 +1045,7 @@ func (lm *loggingMiddleware) GetEntities(token, item, name string, page, limit u lm.logger.Info(fmt.Sprintf("%s without errors.", message)) }(time.Now()) - return lm.svc.GetEntities(token, item, name, page, limit) + return lm.svc.GetEntities(token, item, name, orgID, permission, page, limit) } // ErrorPage adds logging middleware to error page method. @@ -1131,3 +1061,112 @@ func (lm *loggingMiddleware) ErrorPage(errMsg string) (b []byte, err error) { return lm.svc.ErrorPage(errMsg) } + +// OrganizationLogin adds logging middleware to organization login method. +func (lm *loggingMiddleware) OrganizationLogin(login sdk.Login, refreshToken string) (token sdk.Token, err error) { + defer func(begin time.Time) { + message := fmt.Sprintf("Method organization_login took %s to complete", 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.OrganizationLogin(login, refreshToken) +} + +// ListOrganizations adds logging middleware to list organizations method. +func (lm *loggingMiddleware) ListOrganizations(token string, page, limit uint64) (b []byte, err error) { + defer func(begin time.Time) { + message := fmt.Sprintf("Method list_organizations took %s to complete", time.Since(begin)) + if err != nil { + lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err)) + } + lm.logger.Info(fmt.Sprintf("%s without errors.", message)) + }(time.Now()) + + return lm.svc.ListOrganizations(token, page, limit) +} + +// CreateOrganization adds logging middleware to create organization method. +func (lm *loggingMiddleware) CreateOrganization(token string, domain sdk.Domain) (err error) { + defer func(begin time.Time) { + message := fmt.Sprintf("Method create_organization took %s to complete", time.Since(begin)) + if err != nil { + lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err)) + } + lm.logger.Info(fmt.Sprintf("%s without errors.", message)) + }(time.Now()) + + return lm.svc.CreateOrganization(token, domain) +} + +// UpdateOrganization adds logging middleware to update organization method. +func (lm *loggingMiddleware) UpdateOrganization(token string, domain sdk.Domain) (err error) { + defer func(begin time.Time) { + message := fmt.Sprintf("Method update_organization for organization %s took %s to complete", domain.ID, time.Since(begin)) + if err != nil { + lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err)) + } + lm.logger.Info(fmt.Sprintf("%s without errors.", message)) + }(time.Now()) + + return lm.svc.UpdateOrganization(token, domain) +} + +// Organization adds logging middleware to organization method. +func (lm *loggingMiddleware) Organization(token, orgID, tabActive string, page, limit uint64) (b []byte, err error) { + defer func(begin time.Time) { + message := fmt.Sprintf("Method organization for organization %s took %s to complete", orgID, time.Since(begin)) + if err != nil { + lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err)) + } + lm.logger.Info(fmt.Sprintf("%s without errors.", message)) + }(time.Now()) + + return lm.svc.Organization(token, orgID, tabActive, page, limit) +} + +// AssignMember adds logging middleware to assign member method. +func (lm *loggingMiddleware) AssignMember(token, orgID string, req sdk.UsersRelationRequest) (err error) { + defer func(begin time.Time) { + message := fmt.Sprintf("Method assign_member for organization %s took %s to complete", orgID, 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.AssignMember(token, orgID, req) +} + +// UnassignMember adds logging middleware to unassign member method. +func (lm *loggingMiddleware) UnassignMember(token, orgID string, req sdk.UsersRelationRequest) (err error) { + defer func(begin time.Time) { + message := fmt.Sprintf("Method unassign_member for organization %s took %s to complete", orgID, 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.UnassignMember(token, orgID, req) +} + +// ViewMember adds logging middleware to view member method. +func (lm *loggingMiddleware) ViewMember(token, identity string) (b []byte, err error) { + defer func(begin time.Time) { + message := fmt.Sprintf("Method view_member for member %s took %s to complete", identity, 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.ViewMember(token, identity) +} diff --git a/ui/api/metrics.go b/ui/api/metrics.go index 317df184..397f3cbf 100644 --- a/ui/api/metrics.go +++ b/ui/api/metrics.go @@ -31,13 +31,13 @@ func MetricsMiddleware(svc ui.Service, counter metrics.Counter, latency metrics. } // Index adds metrics middleware to index method. -func (mm *metricsMiddleware) Index(token string) (b []byte, err error) { +func (mm *metricsMiddleware) Index(token, orgID string) (b []byte, err error) { defer func(begin time.Time) { mm.counter.With("method", "index").Add(1) mm.latency.With("method", "index").Observe(time.Since(begin).Seconds()) }(time.Now()) - return mm.svc.Index(token) + return mm.svc.Index(token, orgID) } // Login adds metrics middleware to login method. @@ -111,13 +111,13 @@ func (mm *metricsMiddleware) UpdatePassword(token, oldPass, newPass string) (err } // Token adds metrics middleware to token method. -func (mm *metricsMiddleware) Token(user sdk.User) (sdk.Token, error) { +func (mm *metricsMiddleware) Token(login sdk.Login) (sdk.Token, error) { defer func(begin time.Time) { mm.counter.With("method", "token").Add(1) mm.latency.With("method", "token").Observe(time.Since(begin).Seconds()) }(time.Now()) - return mm.svc.Token(user) + return mm.svc.Token(login) } // RefreshToken adds metrics middleware to refresh token method. @@ -130,6 +130,16 @@ func (mm *metricsMiddleware) RefreshToken(refreshToken string) (sdk.Token, error return mm.svc.RefreshToken(refreshToken) } +// UserProfile adds metrics middleware to user profile method. +func (mm *metricsMiddleware) UserProfile(token string, page, limit uint64) ([]byte, error) { + defer func(begin time.Time) { + mm.counter.With("method", "user_profile").Add(1) + mm.latency.With("method", "user_profile").Observe(time.Since(begin).Seconds()) + }(time.Now()) + + return mm.svc.UserProfile(token, page, limit) +} + // CreateUsers adds metrics middleware to create users method. func (mm *metricsMiddleware) CreateUsers(token string, users ...sdk.User) (err error) { defer func(begin time.Time) { @@ -220,36 +230,6 @@ func (mm *metricsMiddleware) DisableUser(token, id string) (err error) { return mm.svc.DisableUser(token, id) } -// ListUserGroups adds metrics middleware to list user groups method. -func (mm *metricsMiddleware) ListUserGroups(token, userID, relation string, page, limit uint64) (b []byte, err error) { - defer func(begin time.Time) { - mm.counter.With("method", "list_user_groups").Add(1) - mm.latency.With("method", "list_user_groups").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return mm.svc.ListUserGroups(token, userID, relation, page, limit) -} - -// ListUserThings adds metrics middleware to list user things method. -func (mm *metricsMiddleware) ListUserThings(token, userID string, page, limit uint64) (b []byte, err error) { - defer func(begin time.Time) { - mm.counter.With("method", "list_user_things").Add(1) - mm.latency.With("method", "list_user_things").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return mm.svc.ListUserThings(token, userID, page, limit) -} - -// ListUserChannels adds metrics middleware to list user channels method. -func (mm *metricsMiddleware) ListUserChannels(token, userID, relation string, page, limit uint64) (b []byte, err error) { - defer func(begin time.Time) { - mm.counter.With("method", "list_user_channels").Add(1) - mm.latency.With("method", "list_user_channels").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return mm.svc.ListUserChannels(token, userID, relation, page, limit) -} - // CreateThing adds metrics middleware to create things method. func (mm *metricsMiddleware) CreateThing(thing sdk.Thing, token string) (err error) { defer func(begin time.Time) { @@ -320,16 +300,6 @@ func (mm *metricsMiddleware) UpdateThingSecret(token, id, secret string) (err er return mm.svc.UpdateThingSecret(token, id, secret) } -// UpdateThingOwner adds metrics middleware to update thing owner method. -func (mm *metricsMiddleware) UpdateThingOwner(token string, thing sdk.Thing) (err error) { - defer func(begin time.Time) { - mm.counter.With("method", "update_thing_owner").Add(1) - mm.latency.With("method", "update_thing_owner").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return mm.svc.UpdateThingOwner(token, thing) -} - // EnableThing adds metrics middleware to enable thing method. func (mm *metricsMiddleware) EnableThing(token, id string) (err error) { defer func(begin time.Time) { @@ -371,13 +341,13 @@ func (mm *metricsMiddleware) UnshareThing(token, thingID string, req sdk.UsersRe } // ListThingUsers adds metrics middleware to list thing users method. -func (mm *metricsMiddleware) ListThingUsers(token, thingID string, page, limit uint64) (b []byte, err error) { +func (mm *metricsMiddleware) ListThingUsers(token, thingID, relation string, page, limit uint64) (b []byte, err error) { defer func(begin time.Time) { mm.counter.With("method", "list_thing_users").Add(1) mm.latency.With("method", "list_thing_users").Observe(time.Since(begin).Seconds()) }(time.Now()) - return mm.svc.ListThingUsers(token, thingID, page, limit) + return mm.svc.ListThingUsers(token, thingID, relation, page, limit) } // ListChannelsByThing adds metrics middleware to list channels by thing method. @@ -660,26 +630,6 @@ func (mm *metricsMiddleware) DisableGroup(token, id string) (err error) { return mm.svc.DisableGroup(token, id) } -// ListParents adds metrics middleware to list parents method. -func (mm *metricsMiddleware) ListParents(token, groupID string, page, limit uint64) (b []byte, err error) { - defer func(begin time.Time) { - mm.counter.With("method", "list_parents").Add(1) - mm.latency.With("method", "list_parents").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return mm.svc.ListParents(token, groupID, page, limit) -} - -// ListChildren adds metrics middleware to list children method. -func (mm *metricsMiddleware) ListChildren(token, groupID string, page, limit uint64) (b []byte, err error) { - defer func(begin time.Time) { - mm.counter.With("method", "list_children").Add(1) - mm.latency.With("method", "list_children").Observe(time.Since(begin).Seconds()) - }(time.Now()) - - return mm.svc.ListChildren(token, groupID, page, limit) -} - // ListUSerGroupChannels adds metrics middleware to list usergroup channels method. func (mm *metricsMiddleware) ListUserGroupChannels(token, userID string, page, limit uint64) (b []byte, err error) { defer func(begin time.Time) { @@ -801,13 +751,13 @@ func (mm *metricsMiddleware) ProcessTerminalCommand(ctx context.Context, id, tok } // GetEntities adds metrics middleware to get entities method. -func (mm *metricsMiddleware) GetEntities(token, item, name string, page, limit uint64) ([]byte, error) { +func (mm *metricsMiddleware) GetEntities(token, item, name, orgID, permission string, page, limit uint64) ([]byte, error) { defer func(begin time.Time) { mm.counter.With("method", "get_entities").Add(1) mm.latency.With("method", "get_entities").Observe(time.Since(begin).Seconds()) }(time.Now()) - return mm.svc.GetEntities(token, item, name, page, limit) + return mm.svc.GetEntities(token, item, name, orgID, permission, page, limit) } // ErrorPage adds metrics middleware to error page method. @@ -819,3 +769,83 @@ func (mm *metricsMiddleware) ErrorPage(errMsg string) ([]byte, error) { return mm.svc.ErrorPage(errMsg) } + +// OrganizationLogin adds metrics middleware to organization login method. +func (mm *metricsMiddleware) OrganizationLogin(login sdk.Login, refreshToken string) (sdk.Token, error) { + defer func(begin time.Time) { + mm.counter.With("method", "organization_login").Add(1) + mm.latency.With("method", "organization_login").Observe(time.Since(begin).Seconds()) + }(time.Now()) + + return mm.svc.OrganizationLogin(login, refreshToken) +} + +// ListOrganizations adds metrics middleware to list organizations method. +func (mm *metricsMiddleware) ListOrganizations(token string, page, limit uint64) ([]byte, error) { + defer func(begin time.Time) { + mm.counter.With("method", "list_organizations").Add(1) + mm.latency.With("method", "list_organizations").Observe(time.Since(begin).Seconds()) + }(time.Now()) + + return mm.svc.ListOrganizations(token, page, limit) +} + +// CreateOrganization adds metrics middleware to create organization method. +func (mm *metricsMiddleware) CreateOrganization(token string, domain sdk.Domain) error { + defer func(begin time.Time) { + mm.counter.With("method", "create_organization").Add(1) + mm.latency.With("method", "create_organization").Observe(time.Since(begin).Seconds()) + }(time.Now()) + + return mm.svc.CreateOrganization(token, domain) +} + +// UpdateOrganization adds metrics middleware to update organization method. +func (mm *metricsMiddleware) UpdateOrganization(token string, domain sdk.Domain) error { + defer func(begin time.Time) { + mm.counter.With("method", "update_organization").Add(1) + mm.latency.With("method", "update_organization").Observe(time.Since(begin).Seconds()) + }(time.Now()) + + return mm.svc.UpdateOrganization(token, domain) +} + +// Organization adds metrics middleware to organization method. +func (mm *metricsMiddleware) Organization(token, orgID, tabActive string, page, limit uint64) ([]byte, error) { + defer func(begin time.Time) { + mm.counter.With("method", "organization").Add(1) + mm.latency.With("method", "organization").Observe(time.Since(begin).Seconds()) + }(time.Now()) + + return mm.svc.Organization(token, orgID, tabActive, page, limit) +} + +// AssignMember adds metrics middleware to assign member method. +func (mm *metricsMiddleware) AssignMember(token, orgID string, req sdk.UsersRelationRequest) (err error) { + defer func(begin time.Time) { + mm.counter.With("method", "assign_member").Add(1) + mm.latency.With("method", "assign_member").Observe(float64(time.Since(begin).Seconds())) + }(time.Now()) + + return mm.svc.AssignMember(token, orgID, req) +} + +// UnassignMember adds metrics middleware to unassign member method. +func (mm *metricsMiddleware) UnassignMember(token, orgID string, req sdk.UsersRelationRequest) (err error) { + defer func(begin time.Time) { + mm.counter.With("method", "unassign_member").Add(1) + mm.latency.With("method", "unassign_member").Observe(float64(time.Since(begin).Seconds())) + }(time.Now()) + + return mm.svc.UnassignMember(token, orgID, req) +} + +// ViewMember adds metrics middleware to view member method. +func (mm *metricsMiddleware) ViewMember(token, identity string) (b []byte, err error) { + defer func(begin time.Time) { + mm.counter.With("method", "view_member").Add(1) + mm.latency.With("method", "view_member").Observe(time.Since(begin).Seconds()) + }(time.Now()) + + return mm.svc.ViewMember(token, identity) +} diff --git a/ui/api/requests.go b/ui/api/requests.go index d1c4c572..dd64f738 100644 --- a/ui/api/requests.go +++ b/ui/api/requests.go @@ -12,12 +12,16 @@ const maxNameSize = 1024 type indexReq struct { token string + orgID string } func (req indexReq) validate() error { if req.token == "" { return errAuthorization } + if req.orgID == "" { + return errMalformedEntity + } return nil } @@ -110,6 +114,7 @@ type listEntityByIDReq struct { page uint64 limit uint64 relation string + name string } func (req listEntityByIDReq) validate() error { @@ -372,25 +377,6 @@ func (req updateThingStatusReq) validate() error { return nil } -type updateThingOwnerReq struct { - token string - id string - Owner string `json:"owner,omitempty"` -} - -func (req updateThingOwnerReq) validate() error { - if req.token == "" { - return errAuthorization - } - if req.id == "" { - return errMalformedEntity - } - if req.Owner == "" { - return errMalformedEntity - } - return nil -} - type createThingsReq struct { token string things []sdk.Thing @@ -491,7 +477,6 @@ type shareThingReq struct { ThingID string `json:"thingID,omitempty"` UserID string `json:"userID,omitempty"` Relation string `json:"relation,omitempty"` - Item string `json:"item,omitempty"` } func (req shareThingReq) validate() error { @@ -507,9 +492,6 @@ func (req shareThingReq) validate() error { if req.Relation == "" { return errMalformedEntity } - if req.Item == "" { - return errMalformedEntity - } return nil } @@ -594,7 +576,6 @@ type assignReq struct { GroupID string `json:"groupID"` UserID string `json:"userID"` Relation string `json:"relation"` - Item string `json:"item"` } func (req assignReq) validate() error { @@ -611,9 +592,6 @@ func (req assignReq) validate() error { if req.Relation == "" { return errMalformedEntity } - if req.Item == "" { - return errMalformedEntity - } return nil } @@ -778,11 +756,13 @@ func (req createBootstrapReq) validate() error { } type getEntitiesReq struct { - token string - Page uint64 `json:"page"` - Limit uint64 `json:"limit"` - Item string `json:"item"` - Name string `json:"name"` + token string + Page uint64 `json:"page"` + Limit uint64 `json:"limit"` + Item string `json:"item"` + Name string `json:"name"` + OrgID string `json:"orgID"` + Permission string `json:"permission"` } func (req getEntitiesReq) validate() error { @@ -819,7 +799,6 @@ type addUserToChannelReq struct { ChannelID string `json:"channelID"` UserID string `json:"userID"` Relation string `json:"relation"` - Item string `json:"item"` } func (req addUserToChannelReq) validate() error { @@ -835,9 +814,6 @@ func (req addUserToChannelReq) validate() error { if req.Relation == "" { return errMalformedEntity } - if req.Item == "" { - return errMalformedEntity - } return nil } @@ -863,3 +839,96 @@ func (req addUserGroupToChannelReq) validate() error { } return nil } + +type organizationLoginReq struct { + token string + OrgID string `json:"orgID"` +} + +func (req organizationLoginReq) validate() error { + if req.token == "" { + return errAuthentication + } + if req.OrgID == "" { + return errMalformedEntity + } + return nil +} + +type createOrganizationReq struct { + token string + Name string `json:"name,omitempty"` + Alias string `json:"alias,omitempty"` + Tags []string `json:"tags,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` +} + +func (req createOrganizationReq) validate() error { + if req.token == "" { + return errAuthentication + } + if req.Name == "" { + return errMalformedEntity + } + return nil +} + +type updateOrganizationReq struct { + token string + OrgID string `json:"orgID"` + Name string `json:"name,omitempty"` + Alias string `json:"alias,omitempty"` + Tags []string `json:"tags,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` +} + +func (req updateOrganizationReq) validate() error { + if req.token == "" { + return errAuthentication + } + if req.OrgID == "" { + return errMalformedEntity + } + if req.Name == "" && req.Alias == "" && req.Metadata == nil && len(req.Tags) == 0 { + return errMalformedEntity + } + return nil +} + +type assignMemberReq struct { + token string + OrgID string `json:"orgID"` + UserID string `json:"userID"` + Relation string `json:"relation"` +} + +func (req assignMemberReq) validate() error { + if req.token == "" { + return errAuthentication + } + if req.OrgID == "" { + return errMalformedEntity + } + if req.UserID == "" { + return errMalformedEntity + } + if req.Relation == "" { + return errMalformedEntity + } + return nil +} + +type viewMemberReq struct { + token string + UserIdentity string `json:"userIdentity"` +} + +func (req viewMemberReq) validate() error { + if req.token == "" { + return errAuthentication + } + if req.UserIdentity == "" { + return errMalformedEntity + } + return nil +} diff --git a/ui/api/transport.go b/ui/api/transport.go index bda2d8cf..c9a430ef 100644 --- a/ui/api/transport.go +++ b/ui/api/transport.go @@ -30,36 +30,36 @@ import ( ) const ( - htmContentType = "text/html" - jsonContentType = "application/json" - staticDir = "ui/web/static" - protocol = "http" - pageKey = "page" - limitKey = "limit" - itemKey = "item" - nameKey = "name" - refererKey = "referer_url" - relationKey = "relation" - defPage = 1 - defLimit = 10 - defName = "" - defItem = "" - defRelation = "" - defReferer = "" - usersAPIEndpoint = "/users" - thingsAPIEndpoint = "/things" - channelsAPIEndpoint = "/channels" - groupsAPIEndpoint = "/groups" - bootstrapAPIEndpoint = "/bootstrap" - membersAPIEndpoint = "/members" - loginAPIEndpoint = "/login" - tokenRefreshAPIEndpoint = "/token/refresh" - usersItem = "users" - thingsItem = "things" - channelsItem = "channels" - groupsItem = "groups" - accessTokenKey = "token" - refreshTokenKey = "refresh_token" + htmContentType = "text/html" + jsonContentType = "application/json" + staticDir = "ui/web/static" + protocol = "http" + pageKey = "page" + limitKey = "limit" + itemKey = "item" + nameKey = "name" + refererKey = "referer_url" + relationKey = "relation" + organizationKey = "organization" + permissionKey = "permission" + identityKey = "identity" + defPage = 1 + defLimit = 10 + defKey = "" + usersAPIEndpoint = "/users" + thingsAPIEndpoint = "/things" + channelsAPIEndpoint = "/channels" + groupsAPIEndpoint = "/groups" + bootstrapAPIEndpoint = "/bootstrap" + membersAPIEndpoint = "/organizations/members" + loginAPIEndpoint = "/login" + tokenRefreshAPIEndpoint = "/token/refresh" + organizationsAPIEndpoint = "/organizations" + thingsItem = "things" + channelsItem = "channels" + groupsItem = "groups" + accessTokenKey = "token" + refreshTokenKey = "refresh_token" ) var ( @@ -141,6 +141,20 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler { opts..., ).ServeHTTP) + r.Post("/organizations/login", kithttp.NewServer( + organizationLoginEndpoint(svc), + decodeOrganizationLoginRequest, + encodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/profile", kithttp.NewServer( + userProfileEndpoint(svc), + decodeListEntityRequest, + encodeResponse, + opts..., + ).ServeHTTP) + r.Route("/", func(r chi.Router) { r.Use(TokenMiddleware) r.Get("/", http.HandlerFunc(kithttp.NewServer( @@ -179,16 +193,16 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler { opts..., ).ServeHTTP) - r.Post("/bulk", kithttp.NewServer( - createUsersEndpoint(svc), - decodeUsersCreation, + r.Get("/", kithttp.NewServer( + listUsersEndpoint(svc), + decodeListEntityRequest, encodeResponse, opts..., ).ServeHTTP) - r.Get("/", kithttp.NewServer( - listUsersEndpoint(svc), - decodeListUsersRequest, + r.Post("/bulk", kithttp.NewServer( + createUsersEndpoint(svc), + decodeUsersCreation, encodeResponse, opts..., ).ServeHTTP) @@ -234,69 +248,6 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler { encodeResponse, opts..., ).ServeHTTP) - - r.Get("/{id}/groups", kithttp.NewServer( - listUserGroupsEndpoint(svc), - decodeListUserGroupsRequest, - encodeResponse, - opts..., - ).ServeHTTP) - - r.Post("/{id}/groups/assign", kithttp.NewServer( - assignGroupEndpoint(svc), - decodeAssignGroupRequest, - encodeResponse, - opts..., - ).ServeHTTP) - - r.Post("/{id}/groups/unassign", kithttp.NewServer( - unassignGroupEndpoint(svc), - decodeAssignGroupRequest, - encodeResponse, - opts..., - ).ServeHTTP) - - r.Get("/{id}/channels", kithttp.NewServer( - listUserChannelsEndpoint(svc), - decodeListUserChannelsRequest, - encodeResponse, - opts..., - ).ServeHTTP) - - r.Post("/{id}/channels/assign", kithttp.NewServer( - AddChannelToUserEndpoint(svc), - decodeAddChannelToUserRequest, - encodeResponse, - opts..., - ).ServeHTTP) - - r.Post("/{id}/channels/unassign", kithttp.NewServer( - RemoveChannelFromUserEndpoint(svc), - decodeAddChannelToUserRequest, - encodeResponse, - opts..., - ).ServeHTTP) - - r.Get("/{id}/things", kithttp.NewServer( - listUserThingsEndpoint(svc), - decodeListUserThingsRequest, - encodeResponse, - opts..., - ).ServeHTTP) - - r.Post("/{id}/things/share", kithttp.NewServer( - shareThingEndpoint(svc), - decodeShareThingRequest, - encodeResponse, - opts..., - ).ServeHTTP) - - r.Post("/{id}/things/unshare", kithttp.NewServer( - unshareThingEndpoint(svc), - decodeShareThingRequest, - encodeResponse, - opts..., - ).ServeHTTP) }) r.Route("/things", func(r chi.Router) { @@ -316,7 +267,7 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler { r.Get("/", kithttp.NewServer( listThingsEndpoint(svc), - decodeListThingsRequest, + decodeListEntityRequest, encodeResponse, opts..., ).ServeHTTP) @@ -363,13 +314,6 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler { opts..., ).ServeHTTP) - r.Post("/{id}/owner", kithttp.NewServer( - updateThingOwnerEndpoint(svc), - decodeThingOwnerUpdate, - encodeResponse, - opts..., - ).ServeHTTP) - r.Get("/{id}/channels", kithttp.NewServer( listChannelsByThingEndpoint(svc), decodeListEntityByIDRequest, @@ -406,8 +350,8 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler { ).ServeHTTP) r.Get("/{id}/users", kithttp.NewServer( - listThingUsersEndpoint(svc), - decodeListThingUsersRequest, + listThingMembersEndpoint(svc), + decodeListEntityByIDRequest, encodeResponse, opts..., ).ServeHTTP) @@ -430,7 +374,7 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler { r.Get("/", kithttp.NewServer( listChannelsEndpoint(svc), - decodeListChannelsRequest, + decodeListEntityRequest, encodeResponse, opts..., ).ServeHTTP) @@ -485,43 +429,43 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler { ).ServeHTTP) r.Post("/{id}/users/assign", kithttp.NewServer( - AddChannelToUserEndpoint(svc), - decodeAddChannelToUserRequest, + AddMemberToChannelEndpoint(svc), + decodeAddMemberToChannelRequest, encodeResponse, opts..., ).ServeHTTP) r.Post("/{id}/users/unassign", kithttp.NewServer( - RemoveChannelFromUserEndpoint(svc), - decodeAddChannelToUserRequest, + RemoveMemberFromChannelEndpoint(svc), + decodeAddMemberToChannelRequest, encodeResponse, opts..., ).ServeHTTP) r.Get("/{id}/users", kithttp.NewServer( - ListChannelUsersEndpoint(svc), - decodeListChannelUsersRequest, + ListChannelMembersEndpoint(svc), + decodeListEntityByIDRequest, encodeResponse, opts..., ).ServeHTTP) r.Post("/{id}/groups/assign", kithttp.NewServer( - addUserGroupToChannelEndpoint(svc), - decodeAddUserGroupToChannelRequest, + addGroupToChannelEndpoint(svc), + decodeAddGroupToChannelRequest, encodeResponse, opts..., ).ServeHTTP) r.Post("/{id}/groups/unassign", kithttp.NewServer( - removeUserGroupFromChannelEndpoint(svc), - decodeAddUserGroupToChannelRequest, + removeGroupFromChannelEndpoint(svc), + decodeAddGroupToChannelRequest, encodeResponse, opts..., ).ServeHTTP) r.Get("/{id}/groups", kithttp.NewServer( - ListChannelUserGroupsEndpoint(svc), - decodeListChannelUserGroupsRequest, + ListChannelGroupsEndpoint(svc), + decodeListEntityByIDRequest, encodeResponse, opts..., ).ServeHTTP) @@ -544,7 +488,7 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler { r.Get("/", kithttp.NewServer( listGroupsEndpoint(svc), - decodeListGroupsRequest, + decodeListEntityRequest, encodeResponse, opts..., ).ServeHTTP) @@ -571,7 +515,7 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler { ).ServeHTTP) r.Get("/{id}/users", kithttp.NewServer( - listGroupUsersEndpoint(svc), + listGroupMembersEndpoint(svc), decodeListEntityByIDRequest, encodeResponse, opts..., @@ -598,35 +542,22 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler { opts..., ).ServeHTTP) - r.Get("/{id}/parents", kithttp.NewServer( - listParentsEndpoint(svc), - decodeListParentsRequest, - encodeResponse, - opts..., - ).ServeHTTP) - r.Get("/{id}/children", kithttp.NewServer( - listChildrenEndpoint(svc), - decodeListChildrenRequest, - encodeResponse, - opts..., - ).ServeHTTP) - r.Post("/{id}/channels/assign", kithttp.NewServer( - addUserGroupToChannelEndpoint(svc), - decodeAddUserGroupToChannelRequest, + addGroupToChannelEndpoint(svc), + decodeAddGroupToChannelRequest, encodeResponse, opts..., ).ServeHTTP) r.Post("/{id}/channels/unassign", kithttp.NewServer( - removeUserGroupFromChannelEndpoint(svc), - decodeAddUserGroupToChannelRequest, + removeGroupFromChannelEndpoint(svc), + decodeAddGroupToChannelRequest, encodeResponse, opts..., ).ServeHTTP) r.Get("/{id}/channels", kithttp.NewServer( - listUserGroupChannelsEndpoint(svc), - decodeListUserGroupChannelsRequest, + listGroupChannelsEndpoint(svc), + decodeListEntityByIDRequest, encodeResponse, opts..., ).ServeHTTP) @@ -651,7 +582,7 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler { r.Route("/bootstraps", func(r chi.Router) { r.Get("/", kithttp.NewServer( listBootstrap(svc), - decodeListBoostrapRequest, + decodeListEntityRequest, encodeResponse, opts..., ).ServeHTTP) @@ -705,6 +636,56 @@ func MakeHandler(svc ui.Service, r *chi.Mux, instanceID string) http.Handler { opts..., ).ServeHTTP) }) + r.Route("/organizations", func(r chi.Router) { + r.Post("/", kithttp.NewServer( + createOrganizationEndpoint(svc), + decodeCreateOrganizationRequest, + encodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/", kithttp.NewServer( + listOrganizationsEndpoint(svc), + decodeListEntityRequest, + encodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/{id}", kithttp.NewServer( + organizationEndpoint(svc), + decodeListEntityByIDRequest, + encodeResponse, + opts..., + ).ServeHTTP) + + r.Post("/{id}", kithttp.NewServer( + updateOrganizationEndpoint(svc), + decodeUpdateOrganizationRequest, + encodeResponse, + opts..., + ).ServeHTTP) + + r.Post("/{id}/assign", kithttp.NewServer( + assignMemberEndpoint(svc), + decodeAssignMemberRequest, + encodeResponse, + opts..., + ).ServeHTTP) + + r.Post("/{id}/unassign", kithttp.NewServer( + unassignMemberEndpoint(svc), + decodeAssignMemberRequest, + encodeResponse, + opts..., + ).ServeHTTP) + + r.Get("/members", kithttp.NewServer( + viewMemberEndpoint(svc), + decodeViewMemberRequest, + encodeResponse, + opts..., + ).ServeHTTP) + }) }) r.Get("/health", magistrala.Health("ui", instanceID)) @@ -727,8 +708,15 @@ func decodeIndexRequest(_ context.Context, r *http.Request) (interface{}, error) if err != nil { return nil, err } + + organization, err := readStringQuery(r, organizationKey, defKey) + if err != nil { + return nil, err + } + req := indexReq{ token: token, + orgID: organization, } return req, nil @@ -803,7 +791,7 @@ func decodeRefreshTokenRequest(_ context.Context, r *http.Request) (interface{}, return nil, err } - referer, err := readStringQuery(r, refererKey, defReferer) + referer, err := readStringQuery(r, refererKey, defKey) if err != nil { return nil, err } @@ -919,30 +907,6 @@ func decodeUsersCreation(_ context.Context, r *http.Request) (interface{}, error return req, nil } -func decodeListUsersRequest(_ 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 := listEntityReq{ - token: token, - page: page, - limit: limit, - } - - return req, nil -} - func decodeView(_ context.Context, r *http.Request) (interface{}, error) { token, err := tokenFromCookie(r, "token") if err != nil { @@ -1038,312 +1002,143 @@ func decodeUserStatusUpdate(_ context.Context, r *http.Request) (interface{}, er return req, nil } -func decodeListUserGroupsRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeAssignGroupRequest(_ context.Context, r *http.Request) (interface{}, error) { token, err := tokenFromCookie(r, "token") if err != nil { return nil, err } - relation, err := readStringQuery(r, relationKey, defRelation) - if err != nil { + if err := r.ParseForm(); err != nil { return nil, err } - page, err := readNumQuery[uint64](r, pageKey, defPage) - if err != nil { - return nil, err - } + return assignReq{ + token: token, + GroupID: chi.URLParam(r, "id"), + UserID: r.Form.Get("userID"), + Relation: r.Form.Get("relation"), + }, nil +} - limit, err := readNumQuery[uint64](r, limitKey, defLimit) - if err != nil { +func decodeShareThingRequest(_ context.Context, r *http.Request) (interface{}, error) { + if err := r.ParseForm(); err != nil { return nil, err } - req := listEntityByIDReq{ - token: token, - id: chi.URLParam(r, "id"), - limit: limit, - page: page, - relation: relation, - } - return req, nil -} -func decodeAssignGroupRequest(_ context.Context, r *http.Request) (interface{}, error) { token, err := tokenFromCookie(r, "token") if err != nil { return nil, err } + return shareThingReq{ + token: token, + ThingID: chi.URLParam(r, "id"), + UserID: r.Form.Get("userID"), + Relation: r.Form.Get("relation"), + }, nil +} + +func decodeAddMemberToChannelRequest(_ context.Context, r *http.Request) (interface{}, error) { if err := r.ParseForm(); err != nil { return nil, err } - var req assignReq - - item, err := readStringQuery(r, itemKey, defItem) + token, err := tokenFromCookie(r, "token") if err != nil { return nil, err } - switch item { - case usersItem: - req = assignReq{ - token: token, - UserID: chi.URLParam(r, "id"), - GroupID: r.Form.Get("groupID"), - Relation: r.Form.Get("relation"), - Item: item, - } - case groupsItem: - req = assignReq{ - token: token, - GroupID: chi.URLParam(r, "id"), - UserID: r.Form.Get("userID"), - Relation: r.Form.Get("relation"), - Item: item, - } - } - - return req, nil + return addUserToChannelReq{ + token: token, + ChannelID: chi.URLParam(r, "id"), + Relation: r.Form.Get("relation"), + UserID: r.Form.Get("userID"), + }, nil } -func decodeListUserThingsRequest(_ context.Context, r *http.Request) (interface{}, error) { - token, err := tokenFromCookie(r, "token") - if err != nil { +func decodeThingCreation(_ context.Context, r *http.Request) (interface{}, error) { + var meta map[string]interface{} + if err := json.Unmarshal([]byte(r.PostFormValue("metadata")), &meta); err != nil { return nil, err } - page, err := readNumQuery[uint64](r, pageKey, defPage) - if err != nil { + var tags []string + if err := json.Unmarshal([]byte(r.PostFormValue("tags")), &tags); err != nil { return nil, err } - - limit, err := readNumQuery[uint64](r, limitKey, defLimit) + token, err := tokenFromCookie(r, "token") if err != nil { return nil, err } - req := listEntityByIDReq{ + credentials := sdk.Credentials{ + Identity: r.PostFormValue("identity"), + Secret: r.PostFormValue("secret"), + } + thing := sdk.Thing{ + Name: r.PostFormValue("name"), + ID: r.PostFormValue("thingID"), + Credentials: credentials, + Tags: tags, + Metadata: meta, + } + req := createThingReq{ token: token, - id: chi.URLParam(r, "id"), - limit: limit, - page: page, + Thing: thing, } + return req, nil } -func decodeShareThingRequest(_ context.Context, r *http.Request) (interface{}, error) { - if err := r.ParseForm(); err != nil { - return nil, err - } - +func decodeThingUpdate(_ context.Context, r *http.Request) (interface{}, error) { token, err := tokenFromCookie(r, "token") if err != nil { return nil, err } - var req shareThingReq - - item, err := readStringQuery(r, itemKey, defItem) + var data updateThingReq + err = json.NewDecoder(r.Body).Decode(&data) if err != nil { return nil, err } - switch item { - case usersItem: - req = shareThingReq{ - token: token, - UserID: chi.URLParam(r, "id"), - ThingID: r.Form.Get("thingID"), - Relation: r.Form.Get("relation"), - Item: item, - } - case thingsItem: - req = shareThingReq{ - token: token, - ThingID: chi.URLParam(r, "id"), - UserID: r.Form.Get("userID"), - Relation: r.Form.Get("relation"), - Item: item, - } + req := updateThingReq{ + token: token, + id: chi.URLParam(r, "id"), + Name: data.Name, + Metadata: data.Metadata, } return req, nil } -func decodeAddChannelToUserRequest(_ context.Context, r *http.Request) (interface{}, error) { - if err := r.ParseForm(); err != nil { - return nil, err - } - +func decodeThingTagsUpdate(_ context.Context, r *http.Request) (interface{}, error) { token, err := tokenFromCookie(r, "token") if err != nil { return nil, err } - var req addUserToChannelReq - - item, err := readStringQuery(r, itemKey, defItem) + var data updateThingTagsReq + err = json.NewDecoder(r.Body).Decode(&data) if err != nil { return nil, err } - switch item { - case usersItem: - req = addUserToChannelReq{ - token: token, - UserID: chi.URLParam(r, "id"), - Relation: r.Form.Get("relation"), - ChannelID: r.Form.Get("channelID"), - Item: item, - } - case channelsItem: - req = addUserToChannelReq{ - token: token, - ChannelID: chi.URLParam(r, "id"), - Relation: r.Form.Get("relation"), - UserID: r.Form.Get("userID"), - Item: item, - } + req := updateThingTagsReq{ + token: token, + id: chi.URLParam(r, "id"), + Tags: data.Tags, } return req, nil } -func decodeListUserChannelsRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeThingSecretUpdate(_ context.Context, r *http.Request) (interface{}, error) { token, err := tokenFromCookie(r, "token") if err != nil { return nil, err } - relation, err := readStringQuery(r, relationKey, defRelation) - 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 := listEntityByIDReq{ - token: token, - id: chi.URLParam(r, "id"), - limit: limit, - page: page, - relation: relation, - } - return req, nil -} - -func decodeThingCreation(_ context.Context, r *http.Request) (interface{}, error) { - var meta map[string]interface{} - if err := json.Unmarshal([]byte(r.PostFormValue("metadata")), &meta); err != nil { - return nil, err - } - var tags []string - if err := json.Unmarshal([]byte(r.PostFormValue("tags")), &tags); err != nil { - return nil, err - } - token, err := tokenFromCookie(r, "token") - if err != nil { - return nil, err - } - credentials := sdk.Credentials{ - Identity: r.PostFormValue("identity"), - Secret: r.PostFormValue("secret"), - } - thing := sdk.Thing{ - Name: r.PostFormValue("name"), - ID: r.PostFormValue("thingID"), - Credentials: credentials, - Tags: tags, - Metadata: meta, - } - req := createThingReq{ - token: token, - Thing: thing, - } - - return req, nil -} - -func decodeListThingsRequest(_ 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 := listEntityReq{ - token: token, - page: page, - limit: limit, - } - - return req, nil -} - -func decodeThingUpdate(_ context.Context, r *http.Request) (interface{}, error) { - token, err := tokenFromCookie(r, "token") - if err != nil { - return nil, err - } - - var data updateThingReq - err = json.NewDecoder(r.Body).Decode(&data) - if err != nil { - return nil, err - } - - req := updateThingReq{ - token: token, - id: chi.URLParam(r, "id"), - Name: data.Name, - Metadata: data.Metadata, - } - - return req, nil -} - -func decodeThingTagsUpdate(_ context.Context, r *http.Request) (interface{}, error) { - token, err := tokenFromCookie(r, "token") - if err != nil { - return nil, err - } - - var data updateThingTagsReq - err = json.NewDecoder(r.Body).Decode(&data) - if err != nil { - return nil, err - } - - req := updateThingTagsReq{ - token: token, - id: chi.URLParam(r, "id"), - Tags: data.Tags, - } - - return req, nil -} - -func decodeThingSecretUpdate(_ context.Context, r *http.Request) (interface{}, error) { - token, err := tokenFromCookie(r, "token") - if err != nil { - return nil, err - } - - var data updateThingSecretReq - err = json.NewDecoder(r.Body).Decode(&data) + var data updateThingSecretReq + err = json.NewDecoder(r.Body).Decode(&data) if err != nil { return nil, err } @@ -1371,59 +1166,6 @@ func decodeThingStatusUpdate(_ context.Context, r *http.Request) (interface{}, e return req, nil } -func decodeThingOwnerUpdate(_ context.Context, r *http.Request) (interface{}, error) { - token, err := tokenFromCookie(r, "token") - if err != nil { - return nil, err - } - - var data updateThingOwnerReq - err = json.NewDecoder(r.Body).Decode(&data) - if err != nil { - return nil, err - } - - req := updateThingOwnerReq{ - token: token, - id: chi.URLParam(r, "id"), - Owner: data.Owner, - } - - return req, nil -} - -func decodeListEntityByIDRequest(_ context.Context, r *http.Request) (interface{}, error) { - token, err := tokenFromCookie(r, "token") - if err != nil { - return nil, err - } - - relation, err := readStringQuery(r, relationKey, defRelation) - 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 := listEntityByIDReq{ - token: token, - id: chi.URLParam(r, "id"), - page: page, - limit: limit, - relation: relation, - } - - return req, nil -} - func decodeThingsCreation(_ context.Context, r *http.Request) (interface{}, error) { token, err := tokenFromCookie(r, "token") if err != nil { @@ -1495,29 +1237,6 @@ func decodeThingsCreation(_ context.Context, r *http.Request) (interface{}, erro return req, nil } -func decodeListThingUsersRequest(_ 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 := listEntityByIDReq{ - token: token, - id: chi.URLParam(r, "id"), - limit: limit, - page: page, - } - return req, nil -} - func decodeChannelCreation(_ context.Context, r *http.Request) (interface{}, error) { var meta map[string]interface{} if err := json.Unmarshal([]byte(r.PostFormValue("metadata")), &meta); err != nil { @@ -1626,30 +1345,6 @@ func decodeChannelUpdate(_ context.Context, r *http.Request) (interface{}, error return req, nil } -func decodeListChannelsRequest(_ 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 := listEntityReq{ - token: token, - page: page, - limit: limit, - } - - return req, nil -} - func decodeConnectChannel(_ context.Context, r *http.Request) (interface{}, error) { token, err := tokenFromCookie(r, "token") if err != nil { @@ -1660,7 +1355,7 @@ func decodeConnectChannel(_ context.Context, r *http.Request) (interface{}, erro return nil, err } - item, err := readStringQuery(r, itemKey, defItem) + item, err := readStringQuery(r, itemKey, defKey) if err != nil { return nil, err } @@ -1701,36 +1396,7 @@ func decodeChannelStatusUpdate(_ context.Context, r *http.Request) (interface{}, return req, nil } -func decodeListChannelUsersRequest(_ context.Context, r *http.Request) (interface{}, error) { - token, err := tokenFromCookie(r, "token") - if err != nil { - return nil, err - } - relation, err := readStringQuery(r, relationKey, defRelation) - 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 := listEntityByIDReq{ - token: token, - id: chi.URLParam(r, "id"), - limit: limit, - page: page, - relation: relation, - } - return req, nil -} - -func decodeAddUserGroupToChannelRequest(_ context.Context, r *http.Request) (interface{}, error) { +func decodeAddGroupToChannelRequest(_ context.Context, r *http.Request) (interface{}, error) { if err := r.ParseForm(); err != nil { return nil, err } @@ -1740,53 +1406,25 @@ func decodeAddUserGroupToChannelRequest(_ context.Context, r *http.Request) (int return nil, err } - item, err := readStringQuery(r, itemKey, defItem) + item, err := readStringQuery(r, itemKey, defKey) if err != nil { return nil, err } - var req addUserGroupToChannelReq + req := addUserGroupToChannelReq{ + token: token, + Item: item, + } switch item { case channelsItem: - req = addUserGroupToChannelReq{ - token: token, - ChannelID: chi.URLParam(r, "id"), - GroupID: r.Form.Get("groupID"), - Item: item, - } + req.ChannelID = chi.URLParam(r, "id") + req.GroupID = r.Form.Get("groupID") case groupsItem: - req = addUserGroupToChannelReq{ - token: token, - GroupID: chi.URLParam(r, "id"), - ChannelID: r.Form.Get("channelID"), - Item: item, - } - } - - return req, nil -} - -func decodeListChannelUserGroupsRequest(_ 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 + req.GroupID = chi.URLParam(r, "id") + req.ChannelID = r.Form.Get("channelID") } - limit, err := readNumQuery[uint64](r, limitKey, defLimit) - if err != nil { - return nil, err - } - req := listEntityByIDReq{ - token: token, - id: chi.URLParam(r, "id"), - limit: limit, - page: page, - } return req, nil } @@ -1799,15 +1437,15 @@ func decodeGroupCreation(_ context.Context, r *http.Request) (interface{}, error if err != nil { return nil, err } - group := sdk.Group{ - Name: r.PostFormValue("name"), - Description: r.PostFormValue("description"), - Metadata: meta, - ParentID: r.PostFormValue("parentID"), - } + req := createGroupReq{ token: token, - Group: group, + Group: sdk.Group{ + Name: r.PostFormValue("name"), + Description: r.PostFormValue("description"), + Metadata: meta, + ParentID: r.PostFormValue("parentID"), + }, } return req, nil @@ -1860,37 +1498,13 @@ func decodeGroupsCreation(_ context.Context, r *http.Request) (interface{}, erro if row[2] != "" { group.Description = row[2] - } - - groups = append(groups, group) - } - req := createGroupsReq{ - token: token, - Groups: groups, - } - - return req, nil -} - -func decodeListGroupsRequest(_ 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 + groups = append(groups, group) } - - req := listEntityReq{ - token: token, - page: page, - limit: limit, + req := createGroupsReq{ + token: token, + Groups: groups, } return req, nil @@ -1932,76 +1546,6 @@ func decodeGroupStatusUpdate(_ context.Context, r *http.Request) (interface{}, e return req, nil } -func decodeListParentsRequest(_ 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 := listEntityByIDReq{ - token: token, - id: chi.URLParam(r, "id"), - limit: limit, - page: page, - } - return req, nil -} - -func decodeListChildrenRequest(_ 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 := listEntityByIDReq{ - token: token, - id: chi.URLParam(r, "id"), - limit: limit, - page: page, - } - return req, nil -} - -func decodeListUserGroupChannelsRequest(_ 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 := listEntityByIDReq{ - token: token, - id: chi.URLParam(r, "id"), - limit: limit, - page: page, - } - return req, nil -} - func decodePublishRequest(_ context.Context, r *http.Request) (interface{}, error) { if err := r.ParseForm(); err != nil { return nil, err @@ -2078,29 +1622,6 @@ func decodeTerminalCommandRequest(_ context.Context, r *http.Request) (interface return req, nil } -func decodeListBoostrapRequest(_ 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 := listEntityReq{ - token: token, - page: page, - limit: limit, - } - - return req, nil -} - func decodeCreateBootstrapRequest(_ context.Context, r *http.Request) (interface{}, error) { token, err := tokenFromCookie(r, "token") if err != nil { @@ -2180,20 +1701,92 @@ func decodeUpdateBootstrapConnections(_ context.Context, r *http.Request) (inter return data, nil } +func decodeListEntityRequest(_ 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 := listEntityReq{ + token: token, + page: page, + limit: limit, + } + + return req, nil +} + +func decodeListEntityByIDRequest(_ 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 + } + + relation, err := readStringQuery(r, relationKey, defKey) + if err != nil { + return nil, err + } + + name, err := readStringQuery(r, nameKey, defKey) + if err != nil { + return nil, err + } + + req := listEntityByIDReq{ + token: token, + id: chi.URLParam(r, "id"), + page: page, + limit: limit, + relation: relation, + name: name, + } + + return req, nil +} + func decodeGetEntitiesRequest(_ context.Context, r *http.Request) (interface{}, error) { token, err := tokenFromCookie(r, "token") if err != nil { return nil, err } - item, err := readStringQuery(r, itemKey, defItem) + item, err := readStringQuery(r, itemKey, defKey) + if err != nil { + return nil, err + } + name, err := readStringQuery(r, nameKey, defKey) + if err != nil { + return nil, err + } + + orgID, err := readStringQuery(r, organizationKey, defKey) if err != nil { return nil, err } - name, err := readStringQuery(r, nameKey, defName) + + permission, err := readStringQuery(r, permissionKey, defKey) if err != nil { return nil, err } + page, err := readNumQuery[uint64](r, pageKey, defPage) if err != nil { return nil, err @@ -2205,11 +1798,114 @@ func decodeGetEntitiesRequest(_ context.Context, r *http.Request) (interface{}, } req := getEntitiesReq{ + token: token, + Item: item, + Page: page, + Name: name, + OrgID: orgID, + Limit: limit, + Permission: permission, + } + + return req, nil +} + +func decodeOrganizationLoginRequest(_ context.Context, r *http.Request) (interface{}, error) { + token, err := tokenFromCookie(r, "refresh_token") + if err != nil { + return nil, err + } + + req := organizationLoginReq{ token: token, - Item: item, - Page: page, - Name: name, - Limit: limit, + OrgID: r.FormValue("orgID"), + } + + return req, nil +} + +func decodeCreateOrganizationRequest(_ context.Context, r *http.Request) (interface{}, error) { + token, err := tokenFromCookie(r, "token") + if err != nil { + return nil, err + } + + meta, err := parseMetadata(r) + if err != nil { + return nil, err + } + + tags, err := parseTags(r) + if err != nil { + return nil, err + } + + req := createOrganizationReq{ + token: token, + Name: r.FormValue("name"), + Alias: r.FormValue("alias"), + Tags: tags, + Metadata: meta, + } + + if err != nil { + return nil, err + } + + return req, nil +} + +func decodeUpdateOrganizationRequest(_ context.Context, r *http.Request) (interface{}, error) { + token, err := tokenFromCookie(r, "token") + if err != nil { + return nil, err + } + + req := updateOrganizationReq{ + token: token, + OrgID: chi.URLParam(r, "id"), + } + err = json.NewDecoder(r.Body).Decode(&req) + if err != nil { + return nil, err + } + + return req, nil +} + +func decodeAssignMemberRequest(_ context.Context, r *http.Request) (interface{}, error) { + if err := r.ParseForm(); err != nil { + return nil, err + } + token, err := tokenFromCookie(r, "token") + if err != nil { + return nil, err + } + + req := assignMemberReq{ + token: token, + OrgID: chi.URLParam(r, "id"), + UserID: r.Form.Get("userID"), + Relation: r.Form.Get("relation"), + } + + return req, nil +} + +func decodeViewMemberRequest(_ context.Context, r *http.Request) (interface{}, error) { + token, err := tokenFromCookie(r, "token") + if err != nil { + return nil, err + } + + identity, err := readStringQuery(r, identityKey, defKey) + if err != nil { + return nil, err + } + + req := viewMemberReq{ + token: token, + UserIdentity: identity, } return req, nil @@ -2325,6 +2021,32 @@ func handleStaticFiles(m *chi.Mux) { } } +func parseMetadata(r *http.Request) (map[string]interface{}, error) { + metadataStr := r.PostFormValue("metadata") + + metadata := make(map[string]interface{}) + if len(metadataStr) > 0 { + if err := json.Unmarshal([]byte(metadataStr), &metadata); err != nil { + return nil, err + } + } + + return metadata, nil +} + +func parseTags(r *http.Request) ([]string, error) { + tagsStr := r.PostFormValue("tags") + + tags := make([]string, 0) + if len(tagsStr) > 0 { + if err := json.Unmarshal([]byte(tagsStr), &tags); err != nil { + return nil, err + } + } + + return tags, nil +} + func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { w.Header().Set("Content-Type", htmContentType) ar, _ := response.(uiRes) diff --git a/ui/service.go b/ui/service.go index bbc72478..f72bacc1 100644 --- a/ui/service.go +++ b/ui/service.go @@ -38,6 +38,7 @@ const ( channelsActive = "channels" readMessagesActive = "readmessages" bootstrapsActive = "bootstraps" + organizationActive = "organization" ) type dataSummary struct { @@ -59,7 +60,6 @@ var ( templates = []string{ "header", "navbar", - "footer", "tableheader", "tablefooter", "error", @@ -78,8 +78,6 @@ var ( "groupusers", "groups", "groupchannels", - // "parents", - // "children", "index", @@ -94,11 +92,12 @@ var ( "things", "thingusers", - "user", "users", - "usergroups", - "userchannels", - "userthings", + "user", + + "organizations", + "organization", + "member", } ErrToken = errors.New("failed to create token") ErrTokenRefresh = errors.New("failed to refresh token") @@ -123,14 +122,14 @@ var ( ErrFailedShare = errors.New("failed to share entity") ErrFailedUnshare = errors.New("failed to unshare entity") emptyData = struct{}{} - groupRelations = []string{"owner", "admin", "editor", "viewer"} - thingRelations = []string{"owner"} + groupRelations = []string{"administrator", "editor", "viewer", "member"} + thingRelations = []string{"administrator"} ) // Service specifies service API. type Service interface { // Index displays the landing page of the UI. - Index(token string) ([]byte, error) + Index(token, orgID string) ([]byte, error) // Login displays the login page. Login() ([]byte, error) // Logout deletes the access token and refresh token from the cookies and logs the user out of the UI. @@ -146,9 +145,13 @@ type Service interface { // UpdatePassword updates the user's old password to the new password. UpdatePassword(token, oldPass, newPass string) error // Token provides a user with an access token and a refresh token. - Token(user sdk.User) (sdk.Token, error) + Token(login sdk.Login) (sdk.Token, error) // RefreshToken retrieves a new access token and refresh token from the provided refresh token. RefreshToken(refreshToken string) (sdk.Token, error) + // OrganizationLogin provides a user with an organization level access token and a refresh token. + OrganizationLogin(login sdk.Login, refreshToken string) (sdk.Token, error) + // UserProfile displays the user profile page. + UserProfile(token string, page, limit uint64) ([]byte, error) // CreateUsers creates new users. CreateUsers(token string, users ...sdk.User) error @@ -168,12 +171,6 @@ type Service interface { EnableUser(token, userID string) error // DisableUser updates the status of a user with the given ID to disabled. DisableUser(token, userID string) error - // ListUserGroups retrieves the groups a user belongs to. - ListUserGroups(token, userID, relation string, page, limit uint64) (b []byte, err error) - // ListUserThings retrieves the things shared to a user. - ListUserThings(token, userID string, page, limit uint64) (b []byte, err error) - // ListUserChannels retrievs a list of channels that a user is connected to. - ListUserChannels(token, userID, relation string, page, limit uint64) (b []byte, err error) // CreateThing creates a new thing. CreateThing(thing sdk.Thing, token string) error @@ -189,8 +186,6 @@ type Service interface { UpdateThingTags(token, id string, thing sdk.Thing) error // UpdateThingSecret updates the secret of the thing with the given ID. UpdateThingSecret(token, id, secret string) error - // UpdateThingOwner updates the owner of the thing with the given ID. - UpdateThingOwner(token string, thing sdk.Thing) error // EnableThing updates the status of the thing with the given ID to enabled. EnableThing(token, id string) error // DisableThing updates the status of the thing with the given ID to disabled. @@ -200,7 +195,7 @@ 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 string, page, limit uint64) (b []byte, err error) + ListThingUsers(token, thingID, relation string, page, limit uint64) (b []byte, err error) // ListChannelsByThing retrieves a list of channels based on the given thing ID. ListChannelsByThing(token, thingID string, page, limit uint64) ([]byte, error) @@ -236,7 +231,7 @@ type Service interface { ListChannelUsers(token, channelID, relation string, page, limit uint64) (b []byte, err error) // AddUserGroupToChannel adds a userGroup to a channel. AddUserGroupToChannel(token, channelID string, req sdk.UserGroupsRequest) error - // RemoveUserGroupFromChannel removes a userGroup from a channel. + // 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) @@ -259,10 +254,6 @@ type Service interface { EnableGroup(token, id string) error // DisableGroup updates the status of the group to disabled. DisableGroup(token, id string) error - // ListParents retrieves the parents of a group. - ListParents(token, groupID string, page, limit uint64) (b []byte, err error) - // ListChildren retrieves the children of a group. - ListChildren(token, groupID string, page, limit uint64) (b []byte, err error) // ListUserGroupChannels retrieves a list of channels that a userGroup is connected to. ListUserGroupChannels(token, groupID string, page, limit uint64) (b []byte, err error) @@ -290,9 +281,24 @@ type Service interface { ProcessTerminalCommand(ctx context.Context, id, token, command string, res chan string) error // GetEntities retrieves all entities. - GetEntities(token, item, name string, page, limit uint64) ([]byte, error) + GetEntities(token, item, name, orgID, permission string, page, limit uint64) ([]byte, error) // ErrorPage displays an error page. ErrorPage(errMsg string) ([]byte, error) + + // ListOrganizations retrieves organizations owned/shared by a user. + ListOrganizations(token string, page, limit uint64) ([]byte, error) + // CreateOrganization creates a new organization. + CreateOrganization(token string, domain sdk.Domain) error + // UpdateOrganization updates the organization with the given ID. + UpdateOrganization(token string, domain sdk.Domain) error + // Organization displays the organization page. + Organization(token, orgID, tabActive string, page, limit uint64) ([]byte, error) + // AssignMember adds a member to an organization. + AssignMember(token, orgID string, req sdk.UsersRelationRequest) error + // UnassignMember removes a member from an organization. + UnassignMember(token, orgID string, req sdk.UsersRelationRequest) error + // View Member retrieves information about the organization Member with the given ID. + ViewMember(token, userIdentity string) ([]byte, error) } var _ Service = (*uiService)(nil) @@ -314,7 +320,7 @@ func New(sdk sdk.SDK) (Service, error) { }, nil } -func (us *uiService) Index(token string) (b []byte, err error) { +func (us *uiService) Index(token, orgID string) (b []byte, err error) { pgm := sdk.PageMetadata{ Offset: uint64(0), Visibility: statusAll, @@ -327,7 +333,7 @@ func (us *uiService) Index(token string) (b []byte, err error) { Status: enabled, } - users, err := us.sdk.Users(pgm, token) + users, err := us.sdk.ListDomainUsers(orgID, pgm, token) if err != nil { return []byte{}, errors.Wrap(err, ErrFailedRetreive) } @@ -347,7 +353,7 @@ func (us *uiService) Index(token string) (b []byte, err error) { return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - enabledUsers, err := us.sdk.Users(enabledPgm, token) + enabledUsers, err := us.sdk.ListDomainUsers(orgID, enabledPgm, token) if err != nil { return []byte{}, errors.Wrap(err, ErrFailedRetreive) } @@ -382,18 +388,11 @@ func (us *uiService) Index(token string) (b []byte, err error) { DisabledChannels: int(channels.Total - enabledChannels.Total), } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - data := struct { NavbarActive string - User sdk.User Summary dataSummary }{ dashboardActive, - user, summary, } @@ -443,17 +442,10 @@ func (us *uiService) ShowPasswordReset() ([]byte, error) { } func (us *uiService) PasswordUpdate(token string) ([]byte, error) { - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - data := struct { NavbarActive string - User sdk.User }{ "password", - user, } var btpl bytes.Buffer @@ -472,8 +464,8 @@ func (us *uiService) UpdatePassword(token, oldPass, newPass string) error { return nil } -func (us *uiService) Token(user sdk.User) (sdk.Token, error) { - token, err := us.sdk.CreateToken(user) +func (us *uiService) Token(login sdk.Login) (sdk.Token, error) { + token, err := us.sdk.CreateToken(login) if err != nil { return sdk.Token{}, errors.Wrap(err, ErrToken) } @@ -481,7 +473,7 @@ func (us *uiService) Token(user sdk.User) (sdk.Token, error) { } func (us *uiService) RefreshToken(refreshToken string) (sdk.Token, error) { - token, err := us.sdk.RefreshToken(refreshToken) + token, err := us.sdk.RefreshToken(sdk.Login{}, refreshToken) if err != nil { return sdk.Token{}, errors.Wrap(err, ErrTokenRefresh) } @@ -489,6 +481,59 @@ func (us *uiService) RefreshToken(refreshToken string) (sdk.Token, error) { return token, nil } +func (us *uiService) OrganizationLogin(login sdk.Login, refreshToken string) (sdk.Token, error) { + token, err := us.sdk.RefreshToken(login, refreshToken) + if err != nil { + return sdk.Token{}, err + } + + return token, nil +} + +func (us *uiService) UserProfile(token string, page, limit uint64) ([]byte, error) { + user, err := us.sdk.UserProfile(token) + if err != nil { + return nil, err + } + + offset := (page - 1) * limit + + pgm := sdk.PageMetadata{ + Offset: offset, + Limit: limit, + } + + domainsPage, err := us.sdk.Domains(pgm, token) + if err != nil { + return []byte{}, err + } + + noOfPages := int(math.Ceil(float64(domainsPage.Total) / float64(limit))) + + data := struct { + NavbarActive string + User sdk.User + Domains []sdk.Domain + CurrentPage int + Pages int + Limit int + }{ + "profile", + user, + domainsPage.Domains, + int(page), + noOfPages, + int(limit), + } + + var btpl bytes.Buffer + if err := us.tpls.ExecuteTemplate(&btpl, "profile", data); err != nil { + return []byte{}, err + } + + return btpl.Bytes(), nil +} + func (us *uiService) CreateUsers(token string, users ...sdk.User) error { for i := range users { _, err := us.sdk.CreateUser(users[i], token) @@ -504,32 +549,25 @@ func (us *uiService) ListUsers(token string, page, limit uint64) ([]byte, error) offset := (page - 1) * limit pgm := sdk.PageMetadata{ - Offset: offset, - Limit: limit, - Visibility: statusAll, + Offset: offset, + Limit: limit, } users, err := us.sdk.Users(pgm, token) if err != nil { return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } noOfPages := int(math.Ceil(float64(users.Total) / float64(limit))) data := struct { NavbarActive string Users []sdk.User - User sdk.User CurrentPage int Pages int Limit int }{ usersActive, users.Users, - user, int(page), noOfPages, int(limit), @@ -548,20 +586,12 @@ func (us *uiService) ViewUser(token, userID string) (b []byte, err error) { if err != nil { return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - loggedUser, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } data := struct { NavbarActive string - UserID string User sdk.User - ViewedUser sdk.User }{ usersActive, - userID, - loggedUser, user, } @@ -621,149 +651,6 @@ func (us *uiService) DisableUser(token, userID string) error { return nil } -func (us *uiService) ListUserGroups(token, userID, relation string, page, limit uint64) (b []byte, err error) { - offset := (page - 1) * limit - pgm := sdk.PageMetadata{ - Offset: offset, - Limit: limit, - Visibility: statusAll, - Permission: relation, - } - - groupsPage, err := us.sdk.ListUserGroups(userID, pgm, token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - - noOfPages := int(math.Ceil(float64(groupsPage.Total) / float64(limit))) - - data := struct { - NavbarActive string - Groups []sdk.Group - User sdk.User - UserID string - Relations []string - CurrentPage int - Pages int - Limit int - TabActive string - }{ - usersActive, - groupsPage.Groups, - user, - userID, - groupRelations, - int(page), - noOfPages, - int(limit), - relation, - } - - var btpl bytes.Buffer - if err := us.tpls.ExecuteTemplate(&btpl, "usergroups", data); err != nil { - return []byte{}, errors.Wrap(err, ErrExecTemplate) - } - - return btpl.Bytes(), nil -} - -func (us *uiService) ListUserThings(token, userID string, page, limit uint64) (b []byte, err error) { - offset := (page - 1) * limit - pgm := sdk.PageMetadata{ - Offset: offset, - Limit: limit, - } - thingsPage, err := us.sdk.ListUserThings(userID, pgm, token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - - noOfPages := int(math.Ceil(float64(thingsPage.Total) / float64(limit))) - - data := struct { - NavbarActive string - Things []sdk.Thing - User sdk.User - UserID string - Relations []string - CurrentPage int - Pages int - Limit int - }{ - usersActive, - thingsPage.Things, - user, - userID, - thingRelations, - int(page), - noOfPages, - int(limit), - } - - var btpl bytes.Buffer - if err := us.tpls.ExecuteTemplate(&btpl, "userthings", data); err != nil { - return []byte{}, errors.Wrap(err, ErrExecTemplate) - } - - return btpl.Bytes(), nil -} - -func (us *uiService) ListUserChannels(token, userID, relation string, page, limit uint64) (b []byte, err error) { - offset := (page - 1) * limit - pgm := sdk.PageMetadata{ - Offset: offset, - Limit: limit, - Permission: relation, - } - channelsPage, err := us.sdk.ListUserChannels(userID, pgm, token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - - noOfPages := int(math.Ceil(float64(channelsPage.Total) / float64(limit))) - - data := struct { - NavbarActive string - Channels []sdk.Channel - User sdk.User - UserID string - Relations []string - CurrentPage int - Pages int - Limit int - TabActive string - }{ - usersActive, - channelsPage.Channels, - user, - userID, - groupRelations, - int(page), - noOfPages, - int(limit), - relation, - } - - var btpl bytes.Buffer - if err := us.tpls.ExecuteTemplate(&btpl, "userchannels", data); err != nil { - return []byte{}, errors.Wrap(err, ErrExecTemplate) - } - - return btpl.Bytes(), nil -} - func (us *uiService) CreateThing(thing sdk.Thing, token string) error { _, err := us.sdk.CreateThing(thing, token) if err != nil { @@ -797,23 +684,17 @@ func (us *uiService) ListThings(token string, page, limit uint64) ([]byte, error return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } noOfPages := int(math.Ceil(float64(things.Total) / float64(limit))) data := struct { NavbarActive string Things []sdk.Thing - User sdk.User CurrentPage int Pages int Limit int }{ thingsActive, things.Things, - user, int(page), noOfPages, int(limit), @@ -831,21 +712,14 @@ func (us *uiService) ViewThing(token, id string) (b []byte, err error) { return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - data := struct { NavbarActive string ID string Thing sdk.Thing - User sdk.User }{ thingsActive, id, thing, - user, } var btpl bytes.Buffer @@ -880,14 +754,6 @@ func (us *uiService) UpdateThingSecret(token, id, secret string) error { return nil } -func (us *uiService) UpdateThingOwner(token string, thing sdk.Thing) error { - if _, err := us.sdk.UpdateThingOwner(thing, token); err != nil { - return errors.Wrap(err, ErrFailedUpdate) - } - - return nil -} - func (us *uiService) EnableThing(token, id string) error { if _, err := us.sdk.EnableThing(id, token); err != nil { return errors.Wrap(err, ErrFailedEnable) @@ -920,20 +786,17 @@ func (us *uiService) UnshareThing(token, thingID string, req sdk.UsersRelationRe return nil } -func (us *uiService) ListThingUsers(token, thingID string, page, limit uint64) (b []byte, err error) { +func (us *uiService) ListThingUsers(token, thingID, relation string, page, limit uint64) (b []byte, err error) { offset := (page - 1) * limit pgm := sdk.PageMetadata{ - Offset: offset, - Limit: limit, + Offset: offset, + Limit: limit, + Permission: relation, } usersPage, err := us.sdk.ListThingUsers(thingID, pgm, token) if err != nil { return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } noOfPages := int(math.Ceil(float64(usersPage.Total) / float64(limit))) @@ -941,20 +804,20 @@ func (us *uiService) ListThingUsers(token, thingID string, page, limit uint64) ( NavbarActive string ThingID string Users []sdk.User - User sdk.User Relations []string CurrentPage int Pages int Limit int + TabActive string }{ thingsActive, thingID, usersPage.Users, - user, thingRelations, int(page), noOfPages, int(limit), + relation, } var btpl bytes.Buffer @@ -983,17 +846,12 @@ func (us *uiService) ListChannelsByThing(token, thingID string, page, limit uint return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } noOfPages := int(math.Ceil(float64(chsPage.Total) / float64(limit))) data := struct { NavbarActive string Thing sdk.Thing Channels []sdk.Channel - User sdk.User CurrentPage int Pages int Limit int @@ -1001,7 +859,6 @@ func (us *uiService) ListChannelsByThing(token, thingID string, page, limit uint thingsActive, thing, chsPage.Channels, - user, int(page), noOfPages, int(limit), @@ -1046,23 +903,17 @@ func (us *uiService) ListChannels(token string, page, limit uint64) ([]byte, err return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } noOfPages := int(math.Ceil(float64(chsPage.Total) / float64(limit))) data := struct { NavbarActive string Channels []sdk.Channel - User sdk.User CurrentPage int Pages int Limit int }{ channelsActive, chsPage.Channels, - user, int(page), noOfPages, int(limit), @@ -1082,21 +933,14 @@ func (us *uiService) ViewChannel(token, id string) (b []byte, err error) { return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - data := struct { NavbarActive string ID string Channel sdk.Channel - User sdk.User }{ channelsActive, id, channel, - user, } var btpl bytes.Buffer @@ -1128,18 +972,12 @@ func (us *uiService) ListThingsByChannel(token, channelID string, page, limit ui return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - noOfPages := int(math.Ceil(float64(thsPage.Total) / float64(limit))) data := struct { NavbarActive string ChannelID string Things []sdk.Thing - User sdk.User CurrentPage int Pages int Limit int @@ -1147,7 +985,6 @@ func (us *uiService) ListThingsByChannel(token, channelID string, page, limit ui channelsActive, channelID, thsPage.Things, - user, int(page), noOfPages, int(limit), @@ -1237,10 +1074,6 @@ func (us *uiService) ListChannelUsers(token, channelID, relation string, page, l if err != nil { return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } noOfPages := int(math.Ceil(float64(usersPage.Total) / float64(limit))) @@ -1248,7 +1081,6 @@ func (us *uiService) ListChannelUsers(token, channelID, relation string, page, l NavbarActive string ChannelID string Users []sdk.User - User sdk.User Relations []string CurrentPage int Pages int @@ -1258,7 +1090,6 @@ func (us *uiService) ListChannelUsers(token, channelID, relation string, page, l channelsActive, channelID, usersPage.Users, - user, groupRelations, int(page), noOfPages, @@ -1300,17 +1131,11 @@ func (us *uiService) ListChannelUserGroups(token, channelID string, page, limit return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - noOfPages := int(math.Ceil(float64(groupsPage.Total) / float64(limit))) data := struct { NavbarActive string Groups []sdk.Group - User sdk.User ChannelID string Relations []string CurrentPage int @@ -1319,7 +1144,6 @@ func (us *uiService) ListChannelUserGroups(token, channelID string, page, limit }{ channelsActive, groupsPage.Groups, - user, channelID, groupRelations, int(page), @@ -1361,18 +1185,12 @@ func (us *uiService) ListGroupUsers(token, id, relation string, page, limit uint return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - noOfPages := int(math.Ceil(float64(usersPage.Total) / float64(limit))) data := struct { NavbarActive string GroupID string Users []sdk.User - User sdk.User Relations []string CurrentPage int Pages int @@ -1382,7 +1200,6 @@ func (us *uiService) ListGroupUsers(token, id, relation string, page, limit uint groupsActive, id, usersPage.Users, - user, groupRelations, int(page), noOfPages, @@ -1419,21 +1236,14 @@ func (us *uiService) ViewGroup(token, id string) (b []byte, err error) { return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - data := struct { NavbarActive string ID string Group sdk.Group - User sdk.User }{ groupsActive, id, group, - user, } var btpl bytes.Buffer @@ -1465,23 +1275,17 @@ func (us *uiService) ListGroups(token string, page, limit uint64) ([]byte, error return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } noOfPages := int(math.Ceil(float64(grpPage.Total) / float64(limit))) data := struct { NavbarActive string Groups []sdk.Group - User sdk.User CurrentPage int Pages int Limit int }{ groupsActive, grpPage.Groups, - user, int(page), noOfPages, int(limit), @@ -1511,92 +1315,6 @@ func (us *uiService) DisableGroup(token, id string) error { return nil } -func (us *uiService) ListParents(token, groupID string, page, limit uint64) (b []byte, err error) { - offset := (page - 1) * limit - pgm := sdk.PageMetadata{ - Offset: offset, - Limit: limit, - } - groupsPage, err := us.sdk.Parents(groupID, pgm, token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - - noOfPages := int(math.Ceil(float64(groupsPage.Total) / float64(limit))) - - data := struct { - NavbarActive string - Groups []sdk.Group - User sdk.User - GroupID string - CurrentPage int - Pages int - Limit int - }{ - groupsActive, - groupsPage.Groups, - user, - groupID, - int(page), - noOfPages, - int(limit), - } - - var btpl bytes.Buffer - if err := us.tpls.ExecuteTemplate(&btpl, "parents", data); err != nil { - return []byte{}, errors.Wrap(err, ErrExecTemplate) - } - - return btpl.Bytes(), nil -} - -func (us *uiService) ListChildren(token, groupID string, page, limit uint64) (b []byte, err error) { - offset := (page - 1) * limit - pgm := sdk.PageMetadata{ - Offset: offset, - Limit: limit, - } - groupsPage, err := us.sdk.Children(groupID, pgm, token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - - noOfPages := int(math.Ceil(float64(groupsPage.Total) / float64(limit))) - - data := struct { - NavbarActive string - Groups []sdk.Group - User sdk.User - GroupID string - CurrentPage int - Pages int - Limit int - }{ - groupsActive, - groupsPage.Groups, - user, - groupID, - int(page), - noOfPages, - int(limit), - } - - var btpl bytes.Buffer - if err := us.tpls.ExecuteTemplate(&btpl, "children", data); err != nil { - return []byte{}, errors.Wrap(err, ErrExecTemplate) - } - - return btpl.Bytes(), nil -} - func (us *uiService) ListUserGroupChannels(token, groupID string, page, limit uint64) (b []byte, err error) { offset := (page - 1) * limit pgm := sdk.PageMetadata{ @@ -1607,17 +1325,12 @@ func (us *uiService) ListUserGroupChannels(token, groupID string, page, limit ui if err != nil { return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } noOfPages := int(math.Ceil(float64(channelsPage.Total) / float64(limit))) data := struct { NavbarActive string Channels []sdk.Group - User sdk.User GroupID string Relations []string CurrentPage int @@ -1626,7 +1339,6 @@ func (us *uiService) ListUserGroupChannels(token, groupID string, page, limit ui }{ groupsActive, channelsPage.Groups, - user, groupID, groupRelations, int(page), @@ -1728,18 +1440,12 @@ func (us *uiService) ListBootstrap(token string, page, limit uint64) ([]byte, er return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - noOfPages := int(math.Ceil(float64(bootstraps.Total) / float64(limit))) data := struct { NavbarActive string Bootstraps []sdk.BootstrapConfig Things []sdk.Thing - User sdk.User CurrentPage int Pages int Limit int @@ -1747,7 +1453,6 @@ func (us *uiService) ListBootstrap(token string, page, limit uint64) ([]byte, er bootstrapsActive, bootstraps.Configs, things.Things, - user, int(page), noOfPages, int(limit), @@ -1800,11 +1505,6 @@ func (us *uiService) ViewBootstrap(token, id string) ([]byte, error) { return []byte{}, errors.Wrap(err, ErrFailedRetreive) } - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - switch channels := bootstrap.Channels.(type) { case []sdk.Channel: var strChannels []string @@ -1823,11 +1523,9 @@ func (us *uiService) ViewBootstrap(token, id string) ([]byte, error) { data := struct { NavbarActive string Bootstrap sdk.BootstrapConfig - User sdk.User }{ bootstrapsActive, bootstrap, - user, } var btpl bytes.Buffer @@ -1839,19 +1537,12 @@ func (us *uiService) ViewBootstrap(token, id string) ([]byte, error) { } func (us *uiService) GetRemoteTerminal(id, token string) ([]byte, error) { - user, err := us.sdk.UserProfile(token) - if err != nil { - return []byte{}, errors.Wrap(err, ErrFailedRetreive) - } - data := struct { NavbarActive string ThingID string - User sdk.User }{ NavbarActive: bootstrapsActive, ThingID: id, - User: user, } var btpl bytes.Buffer if err := us.tpls.ExecuteTemplate(&btpl, "remoteTerminal", data); err != nil { @@ -1943,14 +1634,15 @@ func (us *uiService) ProcessTerminalCommand(ctx context.Context, id, tkn, comman return nil } -func (us *uiService) GetEntities(token, item, name string, page, limit uint64) ([]byte, error) { +func (us *uiService) GetEntities(token, item, name, orgID, permission string, page, limit uint64) ([]byte, error) { offset := (page - 1) * limit pgm := sdk.PageMetadata{ Offset: offset, Limit: limit, Name: name, - Visibility: statusAll, + Permission: permission, } + items := make(map[string]interface{}) switch item { case "groups": @@ -1977,6 +1669,12 @@ func (us *uiService) GetEntities(token, item, name string, page, limit uint64) ( return []byte{}, errors.Wrap(err, ErrFailedRetreive) } items["data"] = channels.Channels + case "members": + members, err := us.sdk.ListDomainUsers(orgID, pgm, token) + if err != nil { + return []byte{}, errors.Wrap(err, ErrFailedRetreive) + } + items["data"] = members.Users } jsonData, err := json.Marshal(items) @@ -2001,6 +1699,137 @@ func (us *uiService) ErrorPage(errMsg string) ([]byte, error) { return btpl.Bytes(), nil } +func (us *uiService) ListOrganizations(token string, page, limit uint64) ([]byte, error) { + offset := (page - 1) * limit + + pgm := sdk.PageMetadata{ + Offset: offset, + Limit: limit, + } + + domainsPage, err := us.sdk.Domains(pgm, token) + if err != nil { + return []byte{}, err + } + + user, err := us.sdk.UserProfile(token) + if err != nil { + return []byte{}, errors.Wrap(err, ErrFailedRetreive) + } + + noOfPages := int(math.Ceil(float64(domainsPage.Total) / float64(limit))) + + data := struct { + Domains []sdk.Domain + User sdk.User + CurrentPage int + Pages int + Limit int + }{ + domainsPage.Domains, + user, + int(page), + noOfPages, + int(limit), + } + + var btpl bytes.Buffer + if err := us.tpls.ExecuteTemplate(&btpl, "organizations", data); err != nil { + return []byte{}, err + } + + return btpl.Bytes(), nil +} + +func (us *uiService) CreateOrganization(token string, domain sdk.Domain) error { + _, err := us.sdk.CreateDomain(domain, token) + return err +} + +func (us *uiService) UpdateOrganization(token string, domain sdk.Domain) error { + _, err := us.sdk.UpdateDomain(domain, token) + return err +} + +func (us *uiService) Organization(token, orgID, tabActive string, page, limit uint64) ([]byte, error) { + offset := (page - 1) * limit + + pgm := sdk.PageMetadata{ + Offset: offset, + Limit: limit, + } + domain, err := us.sdk.Domain(orgID, token) + if err != nil { + return []byte{}, err + } + + membersPage, err := us.sdk.ListDomainUsers(orgID, pgm, token) + if err != nil { + return []byte{}, err + } + + noOfPages := int(math.Ceil(float64(membersPage.Total) / float64(limit))) + + data := struct { + NavbarActive string + Organization sdk.Domain + Members []sdk.User + Relations []string + TabActive string + CurrentPage int + Pages int + Limit int + }{ + organizationActive, + domain, + membersPage.Users, + groupRelations, + tabActive, + int(page), + noOfPages, + int(limit), + } + + var btpl bytes.Buffer + if err := us.tpls.ExecuteTemplate(&btpl, "organization", data); err != nil { + return []byte{}, err + } + + return btpl.Bytes(), nil +} + +func (us *uiService) AssignMember(token, orgID string, req sdk.UsersRelationRequest) error { + return us.sdk.AddUserToDomain(orgID, req, token) +} + +func (us *uiService) UnassignMember(token, orgID string, req sdk.UsersRelationRequest) error { + return us.sdk.RemoveUserFromDomain(orgID, req, token) +} + +func (us *uiService) ViewMember(token, userIdentity string) (b []byte, err error) { + pgm := sdk.PageMetadata{ + Identity: userIdentity, + } + usersPage, err := us.sdk.Users(pgm, token) + if err != nil { + return []byte{}, errors.Wrap(err, ErrFailedRetreive) + } + data := struct { + NavbarActive string + User sdk.User + }{ + organizationActive, + usersPage.Users[0], + } + + var btpl bytes.Buffer + if err := us.tpls.ExecuteTemplate(&btpl, "member", 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{ diff --git a/ui/web/static/css/styles.css b/ui/web/static/css/styles.css index 70f121f5..8e226c60 100644 --- a/ui/web/static/css/styles.css +++ b/ui/web/static/css/styles.css @@ -208,11 +208,6 @@ body.sidebar-toggled .main-content { font-weight: 700; } -.doc-button { - border: 1px solid var(--main-color) !important; - border-radius: var(--border-radius) !important; -} - .doc-button:hover { border: 1px solid var(--main-color) !important; border-radius: var(--border-radius) !important; @@ -247,7 +242,7 @@ body.sidebar-toggled .main-content { color: var(--main-color); } -.active .page-link { +.page-item.active .page-link { background: var(--main-color) !important; color: #fff !important; } @@ -286,10 +281,6 @@ body.sidebar-toggled .main-content { border-radius: 1rem; } -.login-card h1 { - font-family: "Montserrat"; -} - .input-field { background-color: #eaeaea; border-radius: 0.5rem; @@ -483,6 +474,55 @@ button.edit-btn { position: relative; } + +.organizations-body { + background-color: #d5d8dd; +} + +.organizations-card { + border-radius: 1rem; +} + +.organizations-card h1 { + color: var(--main-color); + font-family: "Montserrat"; +} + +.organizations-table h2 { + color: var(--main-color); +} + +.organizations-table .body-button { + background: var(--main-color); + border-radius: var(--border-radius); + color: #fff; + font-size: 1rem; + padding: 0.5rem 1rem; + border: 1px solid var(--main-color); +} + +.organizations-table .body-button:hover { + color: #fff; + background: var(--main-color); + transform: scale(1.03); + font-weight: 700; +} + +.org-col { + border-radius: var(--border-radius); + background-color: #fff; +} + +.org-nav { + font-weight: 700 !important; + font-size: 1.2rem !important; +} + +.table tbody tr.clickable-row:hover td, +.table tbody tr.clickable-row:hover td i { + cursor: pointer; +} + @media (max-width: 768px) { .sidebar { width: var(--sidebar-min); @@ -562,4 +602,4 @@ button.edit-btn { .table-container .desc-col { display: none; } -} +} \ No newline at end of file diff --git a/ui/web/static/js/infinitescroll.js b/ui/web/static/js/infinitescroll.js index ab5c5e4a..28d07c07 100644 --- a/ui/web/static/js/infinitescroll.js +++ b/ui/web/static/js/infinitescroll.js @@ -2,9 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 export function fetchIndividualEntity(config) { + if (config.item === "members") { + config.permission = "member"; + } else { + config.permission = ""; + } document.addEventListener("DOMContentLoaded", function () { - getEntities(config.item, ""); - infiniteScroll(config.item); + getEntities(config, ""); + infiniteScroll(config); }); const input = document.getElementById(config.input); @@ -13,21 +18,27 @@ export function fetchIndividualEntity(config) { const itemSelect = document.getElementById(config.itemSelect); if (event.target.value === "") { itemSelect.innerHTML = ``; - getEntities(config.item, ""); - infiniteScroll(config.item); + getEntities(config, ""); + infiniteScroll(config); } else { itemSelect.innerHTML = ""; - getEntities(config.item, event.target.value); + getEntities(config, event.target.value); } }); } -function getEntities(item, name) { - fetchData(item, name, 1); +function getEntities(config, name) { + fetchData({ + item: config.item, + organization: config.organization, + permission: config.permission, + name: name, + page: 1, + }); } -function infiniteScroll(item) { - var selectElement = document.getElementById("infiniteScroll"); +function infiniteScroll(config) { + var selectElement = document.getElementById(config.itemSelect); var singleOptionHeight = selectElement.querySelector("option").offsetHeight; var selectBoxHeight = selectElement.offsetHeight; var numOptionsBeforeLoad = 2; @@ -43,7 +54,14 @@ function infiniteScroll(item) { currentScroll = st + selectBoxHeight; if (currentScroll + numOptionsBeforeLoad * singleOptionHeight >= totalHeight) { currentPageNo++; - fetchData(item, "", currentPageNo); + fetchData({ + item: config.item, + item: config.item, + name: "", + organization: config.organization, + permission: config.permission, + page: currentPageNo, + }); } } @@ -52,10 +70,13 @@ function infiniteScroll(item) { } let limit = 5; -function fetchData(item, name, page) { - fetch(`/entities?item=${item}&limit=${limit}&name=${name}&page=${page}`, { - method: "GET", - }) +function fetchData(config) { + fetch( + `/entities?item=${config.item}&limit=${limit}&name=${config.name}&page=${config.page}&organization=${config.organization}&permission=${config.permission}`, + { + method: "GET", + }, + ) .then((response) => response.json()) .then((data) => { const selectElement = document.getElementById("infiniteScroll"); diff --git a/ui/web/static/js/update.js b/ui/web/static/js/update.js index 21d9f5a4..99d7649e 100644 --- a/ui/web/static/js/update.js +++ b/ui/web/static/js/update.js @@ -10,6 +10,8 @@ import { validatePassword, } from "./validation.js"; +import { removeErrorMessage } from "./errors.js"; + function updateName(config) { const button = document.getElementById(config.button); @@ -100,22 +102,6 @@ function updateSecret(config) { }); } -function updateOwner(config) { - const button = document.getElementById(config.button); - - button.addEventListener("click", function () { - const updatedValue = config.cell.textContent.trim(); - const url = `/${config.entity}/${config.id}/owner`; - const data = { [config.field]: updatedValue }; - - submitUpdateForm({ - url: url, - data: data, - alertDiv: config.alertDiv, - }); - }); -} - function updateDescription(config) { const button = document.getElementById(config.button); @@ -269,7 +255,6 @@ export { updateMetadata, updateTags, updateSecret, - updateOwner, updateDescription, updateContent, updateClientCerts, diff --git a/ui/web/template/bootstrap.html b/ui/web/template/bootstrap.html index a821146e..02b5d6aa 100644 --- a/ui/web/template/bootstrap.html +++ b/ui/web/template/bootstrap.html @@ -7,7 +7,6 @@ Bootstrap {{ template "header" }} - {{ template "navbar" . }} @@ -132,7 +131,6 @@ - {{ template "footer" }} - - {{ template "navbar" . }} @@ -169,7 +166,7 @@