remove old services

Signed-off-by: Arvindh <arvindh91@gmail.com>
This commit is contained in:
Arvindh
2026-06-19 17:09:26 +05:30
parent a410217c6f
commit 002e3c7fe7
64 changed files with 4 additions and 38518 deletions
-379
View File
@@ -1,379 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package auth_test
import (
"testing"
"time"
apiutil "github.com/absmach/magistrala/api/http/util"
"github.com/absmach/magistrala/auth"
channelsOps "github.com/absmach/magistrala/channels/operations"
clientsOps "github.com/absmach/magistrala/clients/operations"
groupsOps "github.com/absmach/magistrala/groups/operations"
"github.com/stretchr/testify/assert"
)
func TestScopeAuthorized(t *testing.T) {
cases := []struct {
desc string
scope *auth.Scope
entityType auth.EntityType
domainID string
operation string
entityID string
expected bool
}{
{
desc: "Authorized with matching entity type, domain, operation and entity ID",
scope: &auth.Scope{
EntityType: auth.GroupsType,
DomainID: "domain1",
Operation: "view",
EntityID: "entity1",
},
entityType: auth.GroupsType,
domainID: "domain1",
operation: "view",
entityID: "entity1",
expected: true,
},
{
desc: "Authorized with wildcard entity ID",
scope: &auth.Scope{
EntityType: auth.GroupsType,
DomainID: "domain1",
Operation: "view",
EntityID: "*",
},
entityType: auth.GroupsType,
domainID: "domain1",
operation: "view",
entityID: "any-entity",
expected: true,
},
{
desc: "Authorized without domain ID",
scope: &auth.Scope{
EntityType: auth.ClientsType,
DomainID: "",
Operation: "view",
EntityID: "client1",
},
entityType: auth.ClientsType,
domainID: "domain1",
operation: "view",
entityID: "client1",
expected: true,
},
{
desc: "Not authorized with different entity type",
scope: &auth.Scope{
EntityType: auth.GroupsType,
DomainID: "domain1",
Operation: "view",
EntityID: "entity1",
},
entityType: auth.ChannelsType,
domainID: "domain1",
operation: "view",
entityID: "entity1",
expected: false,
},
{
desc: "Not authorized with different domain ID",
scope: &auth.Scope{
EntityType: auth.GroupsType,
DomainID: "domain1",
Operation: "view",
EntityID: "entity1",
},
entityType: auth.GroupsType,
domainID: "domain2",
operation: "view",
entityID: "entity1",
expected: false,
},
{
desc: "Not authorized with different operation",
scope: &auth.Scope{
EntityType: auth.GroupsType,
DomainID: "domain1",
Operation: "view",
EntityID: "entity1",
},
entityType: auth.GroupsType,
domainID: "domain1",
operation: "delete",
entityID: "entity1",
expected: false,
},
{
desc: "Not authorized with different entity ID",
scope: &auth.Scope{
EntityType: auth.GroupsType,
DomainID: "domain1",
Operation: "view",
EntityID: "entity1",
},
entityType: auth.GroupsType,
domainID: "domain1",
operation: "view",
entityID: "entity2",
expected: false,
},
{
desc: "Not authorized with nil scope",
scope: nil,
entityType: auth.GroupsType,
domainID: "domain1",
operation: "view",
entityID: "entity1",
expected: false,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
result := tc.scope.Authorized(tc.entityType, tc.domainID, tc.operation, tc.entityID)
assert.Equal(t, tc.expected, result, "Authorized() = %v, expected %v", result, tc.expected)
})
}
}
func TestScopeValidate(t *testing.T) {
cases := []struct {
desc string
scope *auth.Scope
err error
}{
{
desc: "Valid scope for groups with domain ID",
scope: &auth.Scope{
EntityType: auth.GroupsType,
DomainID: "domain1",
Operation: "view",
EntityID: "entity1",
},
err: nil,
},
{
desc: "Valid scope for channels with domain ID",
scope: &auth.Scope{
EntityType: auth.ChannelsType,
DomainID: "domain1",
Operation: "view",
EntityID: "channel1",
},
err: nil,
},
{
desc: "Valid scope for clients with domain ID",
scope: &auth.Scope{
EntityType: auth.ClientsType,
DomainID: "domain1",
Operation: "update",
EntityID: "client1",
},
err: nil,
},
{
desc: "Valid scope for messages with domain ID",
scope: &auth.Scope{
EntityType: auth.MessagesType,
DomainID: "domain1",
Operation: "message_publish",
EntityID: "message1",
},
err: nil,
},
{
desc: "Valid scope for dashboard with domain ID",
scope: &auth.Scope{
EntityType: auth.DashboardType,
DomainID: "domain1",
Operation: "dashboard_share",
EntityID: "dashboard1",
},
err: nil,
},
{
desc: "Valid scope with wildcard entity ID",
scope: &auth.Scope{
EntityType: auth.GroupsType,
DomainID: "domain1",
Operation: "view",
EntityID: "*",
},
err: nil,
},
{
desc: "Invalid nil scope",
scope: nil,
err: assert.AnError, // Will be checked with Contains
},
{
desc: "Invalid scope without entity ID",
scope: &auth.Scope{
EntityType: auth.GroupsType,
DomainID: "domain1",
Operation: groupsOps.OperationDetails()[groupsOps.OpViewGroup].Name,
EntityID: "",
},
err: apiutil.ErrMissingEntityID,
},
{
desc: "Invalid scope for groups without domain ID",
scope: &auth.Scope{
EntityType: auth.GroupsType,
DomainID: "",
Operation: groupsOps.OperationDetails()[groupsOps.OpViewGroup].Name,
EntityID: "entity1",
},
err: apiutil.ErrMissingDomainID,
},
{
desc: "Invalid scope for channels without domain ID",
scope: &auth.Scope{
EntityType: auth.ChannelsType,
DomainID: "",
Operation: channelsOps.OperationDetails()[channelsOps.OpViewChannel].Name,
EntityID: "channel1",
},
err: apiutil.ErrMissingDomainID,
},
{
desc: "Invalid scope for clients without domain ID",
scope: &auth.Scope{
EntityType: auth.ClientsType,
DomainID: "",
Operation: clientsOps.OperationDetails()[clientsOps.OpViewClient].Name,
EntityID: "client1",
},
err: apiutil.ErrMissingDomainID,
},
{
desc: "Invalid scope for dashboard without domain ID",
scope: &auth.Scope{
EntityType: auth.DashboardType,
DomainID: "",
Operation: auth.OpShare,
EntityID: "dashboard1",
},
err: apiutil.ErrMissingDomainID,
},
{
desc: "Invalid scope for messages without domain ID",
scope: &auth.Scope{
EntityType: auth.MessagesType,
DomainID: "",
Operation: auth.OpPublish,
EntityID: "message1",
},
err: apiutil.ErrMissingDomainID,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
err := tc.scope.Validate()
if tc.err != nil {
assert.Error(t, err, "Validate() should return error")
if tc.err != assert.AnError {
assert.Equal(t, tc.err, err, "Validate() error = %v, expected %v", err, tc.err)
}
} else {
assert.NoError(t, err, "Validate() should not return error")
}
})
}
}
func TestPATValidate(t *testing.T) {
cases := []struct {
desc string
pat *auth.PAT
err bool
}{
{
desc: "Valid PAT",
pat: &auth.PAT{
ID: "pat-id",
User: "user-id",
Name: "test-pat",
Description: "test description",
},
err: false,
},
{
desc: "Invalid nil PAT",
pat: nil,
err: true,
},
{
desc: "Invalid PAT without name",
pat: &auth.PAT{
ID: "pat-id",
User: "user-id",
Name: "",
Description: "test description",
},
err: true,
},
{
desc: "Invalid PAT without user",
pat: &auth.PAT{
ID: "pat-id",
User: "",
Name: "test-pat",
Description: "test description",
},
err: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
err := tc.pat.Validate()
if tc.err {
assert.Error(t, err, "Validate() should return error")
} else {
assert.NoError(t, err, "Validate() should not return error")
}
})
}
}
func TestPATMarshalUnmarshalBinary(t *testing.T) {
pat := auth.PAT{
ID: "pat-id",
User: "user-id",
Name: "test-pat",
Description: "test description",
Secret: "secret",
IssuedAt: time.Now().UTC().Round(time.Second),
ExpiresAt: time.Now().UTC().Add(24 * time.Hour).Round(time.Second),
Status: auth.ActiveStatus,
}
// Marshal
data, err := pat.MarshalBinary()
assert.NoError(t, err, "MarshalBinary() should not return error")
assert.NotNil(t, data, "MarshalBinary() should return data")
// Unmarshal
var newPAT auth.PAT
err = newPAT.UnmarshalBinary(data)
assert.NoError(t, err, "UnmarshalBinary() should not return error")
assert.Equal(t, pat.ID, newPAT.ID, "ID mismatch")
assert.Equal(t, pat.User, newPAT.User, "User mismatch")
assert.Equal(t, pat.Name, newPAT.Name, "Name mismatch")
assert.Equal(t, pat.Description, newPAT.Description, "Description mismatch")
assert.Equal(t, pat.Secret, newPAT.Secret, "Secret mismatch")
assert.Equal(t, pat.Status, newPAT.Status, "Status mismatch")
}
-875
View File
@@ -1,875 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli_test
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"testing"
"github.com/absmach/magistrala/cli"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
mgsdk "github.com/absmach/magistrala/pkg/sdk"
sdkmocks "github.com/absmach/magistrala/pkg/sdk/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
var (
clientID = testsutil.GenerateUUID(&testing.T{})
channelID = testsutil.GenerateUUID(&testing.T{})
domainID = testsutil.GenerateUUID(&testing.T{})
profileID = testsutil.GenerateUUID(&testing.T{})
bootConfig = mgsdk.BootstrapConfig{
ID: clientID,
Name: "Test Bootstrap",
ExternalID: "09:6:0:sb:sa",
ExternalKey: "key",
}
bootProfile = mgsdk.BootstrapProfile{
ID: profileID,
Name: "Test Profile",
Description: "Test profile",
ContentFormat: "go-template",
ContentTemplate: "{\"device_id\":\"{{ .Device.ID }}\"}",
Version: 1,
}
validToken = "validToken"
invalidToken = "invalidToken"
extraArg = "extra-arg"
invalidID = "invalidID"
all = "all"
)
func TestCreateBootstrapConfigCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
bootCmd := cli.NewBootstrapCmd()
rootCmd := setFlags(bootCmd)
jsonConfig := fmt.Sprintf("{\"external_id\":\"09:6:0:sb:sa\", \"external_key\":\"key\", \"name\": \"%s\"}", "Test Bootstrap")
invalidJson := fmt.Sprintf("{\"external_id\":\"09:6:0:sb:sa\", \"external_key\":\"key\", \"name\": \"%s\"", "Test Bootstrap")
cases := []struct {
desc string
args []string
logType outputLog
response string
sdkErr errors.SDKError
errLogMessage string
id string
}{
{
desc: "create bootstrap config successfully",
args: []string{
jsonConfig,
domainID,
validToken,
},
logType: createLog,
id: clientID,
response: fmt.Sprintf("\ncreated: %s\n\n", clientID),
},
{
desc: "create bootstrap config with invald args",
args: []string{
jsonConfig,
domainID,
validToken,
extraArg,
},
logType: usageLog,
},
{
desc: "create bootstrap config with invald json",
args: []string{
invalidJson,
domainID,
validToken,
},
sdkErr: errors.NewSDKError(errors.New("unexpected end of JSON input")),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.New("unexpected end of JSON input")),
logType: errLog,
},
{
desc: "create bootstrap config with invald token",
args: []string{
jsonConfig,
domainID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("AddBootstrap", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.id, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{createCmd}, tc.args...)...)
switch tc.logType {
case createLog:
assert.Equal(t, tc.response, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.response, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
func TestGetBootstrapConfigCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
bootCmd := cli.NewBootstrapCmd()
rootCmd := setFlags(bootCmd)
var boot mgsdk.BootstrapConfig
var page mgsdk.BootstrapPage
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
page mgsdk.BootstrapPage
boot mgsdk.BootstrapConfig
logType outputLog
errLogMessage string
}{
{
desc: "get all bootstrap config successfully",
args: []string{
all,
domainID,
validToken,
},
page: mgsdk.BootstrapPage{
PageRes: mgsdk.PageRes{
Total: 1,
Offset: 0,
Limit: 10,
},
Configs: []mgsdk.BootstrapConfig{bootConfig},
},
logType: entityLog,
},
{
desc: "get bootstrap config with id",
args: []string{
channelID,
domainID,
validToken,
},
logType: entityLog,
boot: bootConfig,
},
{
desc: "get bootstrap config with invalid args",
args: []string{
all,
domainID,
validToken,
extraArg,
},
logType: usageLog,
},
{
desc: "get all bootstrap config with invalid token",
args: []string{
all,
domainID,
invalidToken,
},
logType: errLog,
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
},
{
desc: "get bootstrap config with invalid id",
args: []string{
invalidID,
domainID,
validToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("ViewBootstrap", mock.Anything, tc.args[0], tc.args[1], tc.args[2]).Return(tc.boot, tc.sdkErr)
sdkCall1 := sdkMock.On("Bootstraps", mock.Anything, mock.Anything, tc.args[1], tc.args[2]).Return(tc.page, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{getCmd}, tc.args...)...)
switch tc.logType {
case entityLog:
if tc.args[0] == all {
err := json.Unmarshal([]byte(out), &page)
assert.Nil(t, err)
assert.Equal(t, tc.page, page, fmt.Sprintf("%v unexpected response, expected: %v, got: %v", tc.desc, tc.page, page))
} else {
err := json.Unmarshal([]byte(out), &boot)
assert.Nil(t, err)
assert.Equal(t, tc.boot, boot, fmt.Sprintf("%v unexpected response, expected: %v, got: %v", tc.desc, tc.boot, boot))
}
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
sdkCall1.Unset()
})
}
}
func TestRemoveBootstrapConfigCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
bootCmd := cli.NewBootstrapCmd()
rootCmd := setFlags(bootCmd)
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
logType outputLog
errLogMessage string
}{
{
desc: "remove bootstrap config successfully",
args: []string{
clientID,
domainID,
validToken,
},
logType: okLog,
},
{
desc: "remove bootstrap config with invalid args",
args: []string{
clientID,
domainID,
validToken,
extraArg,
},
logType: usageLog,
},
{
desc: "remove bootstrap config with invalid client id",
args: []string{
invalidID,
domainID,
validToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
{
desc: "remove bootstrap config with invalid token",
args: []string{
clientID,
domainID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("RemoveBootstrap", mock.Anything, tc.args[0], tc.args[1], tc.args[2]).Return(tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{rmCmd}, tc.args...)...)
switch tc.logType {
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
func TestUpdateBootstrapConfigCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
bootCmd := cli.NewBootstrapCmd()
rootCmd := setFlags(bootCmd)
config := "config"
connection := "connection"
newConfigJson := "{\"name\" : \"New Bootstrap\"}"
chanIDsJson := fmt.Sprintf("[\"%s\"]", channelID)
cases := []struct {
desc string
args []string
boot mgsdk.BootstrapConfig
sdkErr errors.SDKError
errLogMessage string
logType outputLog
}{
{
desc: "update bootstrap config successfully",
args: []string{
config,
newConfigJson,
domainID,
validToken,
},
logType: okLog,
},
{
desc: "update bootstrap config with invalid token",
args: []string{
config,
newConfigJson,
domainID,
invalidToken,
},
logType: errLog,
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
},
{
desc: "update bootstrap connections successfully",
args: []string{
connection,
clientID,
chanIDsJson,
domainID,
validToken,
},
logType: okLog,
},
{
desc: "update bootstrap connections with invalid json",
args: []string{
connection,
clientID,
fmt.Sprintf("[\"%s\"", clientID),
domainID,
validToken,
},
sdkErr: errors.NewSDKError(errors.New("unexpected end of JSON input")),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.New("unexpected end of JSON input")),
logType: errLog,
},
{
desc: "update bootstrap connections with invalid token",
args: []string{
connection,
clientID,
chanIDsJson,
domainID,
invalidToken,
},
logType: errLog,
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
},
{
desc: "update bootstrap certs successfully",
args: []string{
"certs",
clientID,
"client cert",
"client key",
"ca",
domainID,
validToken,
},
boot: bootConfig,
logType: entityLog,
},
{
desc: "update bootstrap certs with invalid token",
args: []string{
"certs",
clientID,
"client cert",
"client key",
"ca",
domainID,
invalidToken,
},
logType: errLog,
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
},
{
desc: "update bootstrap config with invalid args",
args: []string{
newConfigJson,
domainID,
validToken,
},
logType: usageLog,
},
{
desc: "update bootstrap config with invalid json",
args: []string{
config,
"{\"name\" : \"New Bootstrap\"",
domainID,
validToken,
},
sdkErr: errors.NewSDKError(errors.New("unexpected end of JSON input")),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.New("unexpected end of JSON input")),
logType: errLog,
},
{
desc: "update bootstrap with invalid args",
args: []string{
extraArg,
extraArg,
extraArg,
extraArg,
extraArg,
},
logType: usageLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
var boot mgsdk.BootstrapConfig
sdkCall := sdkMock.On("UpdateBootstrap", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.sdkErr)
sdkCall1 := sdkMock.On("UpdateBootstrapConnection", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.sdkErr)
sdkCall2 := sdkMock.On("UpdateBootstrapCerts", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.boot, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{updCmd}, tc.args...)...)
switch tc.logType {
case entityLog:
err := json.Unmarshal([]byte(out), &boot)
assert.Nil(t, err)
assert.Equal(t, tc.boot, boot, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.boot, boot))
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
}
sdkCall.Unset()
sdkCall1.Unset()
sdkCall2.Unset()
})
}
}
func TestWhitelistConfigCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
bootCmd := cli.NewBootstrapCmd()
rootCmd := setFlags(bootCmd)
jsonConfig := fmt.Sprintf("{\"client_id\": \"%s\", \"status\":%d}", clientID, 1)
cases := []struct {
desc string
args []string
logType outputLog
errLogMessage string
sdkErr errors.SDKError
}{
{
desc: "whitelist config successfully",
args: []string{
jsonConfig,
domainID,
validToken,
},
logType: okLog,
},
{
desc: "whitelist config with invalid args",
args: []string{
jsonConfig,
domainID,
validToken,
extraArg,
},
logType: usageLog,
},
{
desc: "whitelist config with invalid json",
args: []string{
fmt.Sprintf("{\"client_id\": \"%s\", \"status\":%d", clientID, 1),
domainID,
validToken,
},
sdkErr: errors.NewSDKError(errors.New("unexpected end of JSON input")),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.New("unexpected end of JSON input")),
logType: errLog,
},
{
desc: "whitelist config with invalid token",
args: []string{
jsonConfig,
domainID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("Whitelist", mock.Anything, mock.Anything, mock.Anything, tc.args[1], tc.args[2]).Return(tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{whitelistCmd}, tc.args...)...)
switch tc.logType {
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
}
sdkCall.Unset()
})
}
}
func TestBootstrapConfigCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
bootCmd := cli.NewBootstrapCmd()
rootCmd := setFlags(bootCmd)
var boot mgsdk.BootstrapConfig
cryptoKey := "v7aT0HGxJxt2gULzr3RHwf4WIf6DusPp"
invalidKey := "invalid key"
cases := []struct {
desc string
args []string
logType outputLog
errLogMessage string
sdkErr errors.SDKError
boot mgsdk.BootstrapConfig
}{
{
desc: "bootstrap secure config successfully",
args: []string{
"secure",
bootConfig.ExternalID,
bootConfig.ExternalKey,
cryptoKey,
},
boot: bootConfig,
logType: entityLog,
},
{
desc: "bootstrap config successfully",
args: []string{
bootConfig.ExternalID,
bootConfig.ExternalKey,
},
boot: bootConfig,
logType: entityLog,
},
{
desc: "bootstrap secure config with invalid args",
args: []string{
cryptoKey,
},
logType: usageLog,
},
{
desc: "bootstrap secure config with invalid key",
args: []string{
"secure",
bootConfig.ExternalID,
invalidKey,
cryptoKey,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized)),
logType: errLog,
},
{
desc: "bootstrap config with invalid key",
args: []string{
bootConfig.ExternalID,
invalidKey,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("BootstrapSecure", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.boot, tc.sdkErr)
sdkCall1 := sdkMock.On("Bootstrap", mock.Anything, mock.Anything, mock.Anything).Return(tc.boot, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{bootStrapCmd}, tc.args...)...)
switch tc.logType {
case entityLog:
err := json.Unmarshal([]byte(out), &boot)
assert.Nil(t, err)
assert.Equal(t, tc.boot, boot, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.boot, boot))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
}
sdkCall.Unset()
sdkCall1.Unset()
})
}
}
func TestBootstrapProfilesCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
bootCmd := cli.NewBootstrapCmd()
rootCmd := setFlags(bootCmd)
profilePayload, err := json.Marshal(bootProfile)
assert.Nil(t, err)
jsonProfile := string(profilePayload)
cases := []struct {
desc string
args []string
profile mgsdk.BootstrapProfile
page mgsdk.BootstrapProfilesPage
sdkErr errors.SDKError
logType outputLog
errLogMessage string
}{
{
desc: "create bootstrap profile successfully",
args: []string{
"create",
jsonProfile,
domainID,
validToken,
},
profile: bootProfile,
logType: entityLog,
},
{
desc: "get all bootstrap profiles successfully",
args: []string{
"get",
all,
domainID,
validToken,
},
page: mgsdk.BootstrapProfilesPage{
PageRes: mgsdk.PageRes{
Total: 1,
Offset: 0,
Limit: 10,
},
Profiles: []mgsdk.BootstrapProfile{bootProfile},
},
logType: entityLog,
},
{
desc: "view bootstrap profile successfully",
args: []string{
"get",
profileID,
domainID,
validToken,
},
profile: bootProfile,
logType: entityLog,
},
{
desc: "update bootstrap profile successfully",
args: []string{
"update",
jsonProfile,
domainID,
validToken,
},
profile: bootProfile,
logType: entityLog,
},
{
desc: "remove bootstrap profile successfully",
args: []string{
"remove",
profileID,
domainID,
validToken,
},
logType: okLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
var gotProfile mgsdk.BootstrapProfile
var gotPage mgsdk.BootstrapProfilesPage
createCall := sdkMock.On("CreateBootstrapProfile", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.profile, tc.sdkErr)
listCall := sdkMock.On("BootstrapProfiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.page, tc.sdkErr)
viewCall := sdkMock.On("ViewBootstrapProfile", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.profile, tc.sdkErr)
updateCall := sdkMock.On("UpdateBootstrapProfile", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.profile, tc.sdkErr)
removeCall := sdkMock.On("RemoveBootstrapProfile", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{"profiles"}, tc.args...)...)
switch tc.logType {
case entityLog:
if tc.args[0] == "get" && tc.args[1] == all {
err := json.Unmarshal([]byte(out), &gotPage)
assert.Nil(t, err)
assert.Equal(t, tc.page, gotPage, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.page, gotPage))
} else {
err := json.Unmarshal([]byte(out), &gotProfile)
assert.Nil(t, err)
assert.Equal(t, tc.profile, gotProfile, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.profile, gotProfile))
}
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
createCall.Unset()
listCall.Unset()
viewCall.Unset()
updateCall.Unset()
removeCall.Unset()
})
}
}
func TestBootstrapEnrollmentsCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
bootCmd := cli.NewBootstrapCmd()
rootCmd := setFlags(bootCmd)
bindings := []mgsdk.BootstrapBindingRequest{
{
Slot: "mqtt_client",
Type: "client",
ResourceID: clientID,
},
}
snapshots := []mgsdk.BootstrapBindingSnapshot{
{
ConfigID: clientID,
Slot: "mqtt_client",
Type: "client",
ResourceID: clientID,
},
}
jsonBindings := fmt.Sprintf("[{\"slot\":\"%s\",\"type\":\"%s\",\"resource_id\":\"%s\"}]", bindings[0].Slot, bindings[0].Type, bindings[0].ResourceID)
cases := []struct {
desc string
args []string
snapshots []mgsdk.BootstrapBindingSnapshot
sdkErr errors.SDKError
logType outputLog
errLogMessage string
}{
{
desc: "assign bootstrap profile successfully",
args: []string{
"assign-profile",
clientID,
profileID,
domainID,
validToken,
},
logType: okLog,
},
{
desc: "bind bootstrap resources successfully",
args: []string{
"bind",
clientID,
jsonBindings,
domainID,
validToken,
},
logType: okLog,
},
{
desc: "get bootstrap bindings successfully",
args: []string{
"get-bindings",
clientID,
domainID,
validToken,
},
snapshots: snapshots,
logType: entityLog,
},
{
desc: "refresh bootstrap bindings successfully",
args: []string{
"refresh-bindings",
clientID,
domainID,
validToken,
},
logType: okLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
var gotSnapshots []mgsdk.BootstrapBindingSnapshot
assignCall := sdkMock.On("AssignBootstrapProfile", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.sdkErr)
bindCall := sdkMock.On("BindBootstrapResources", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.sdkErr)
listCall := sdkMock.On("BootstrapBindings", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.snapshots, tc.sdkErr)
refreshCall := sdkMock.On("RefreshBootstrapBindings", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{"enrollments"}, tc.args...)...)
switch tc.logType {
case entityLog:
err := json.Unmarshal([]byte(out), &gotSnapshots)
assert.Nil(t, err)
assert.Equal(t, tc.snapshots, gotSnapshots, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.snapshots, gotSnapshots))
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
assignCall.Unset()
bindCall.Unset()
listCall.Unset()
refreshCall.Unset()
})
}
}
-908
View File
@@ -1,908 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli_test
import (
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"testing"
"github.com/absmach/magistrala/certs"
"github.com/absmach/magistrala/cli"
"github.com/absmach/magistrala/pkg/errors"
"github.com/absmach/magistrala/pkg/sdk"
sdkmocks "github.com/absmach/magistrala/pkg/sdk/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
const (
revokeCmd = "revoke"
deleteCmd = "delete"
issueCmd = "issue"
renewCmd = "renew"
certsListCmd = "get"
downloadCACmd = "download-ca"
CATokenCmd = "certsToken-ca"
viewCACmd = "view-ca"
filePermission = 0o644
)
var (
serialNumber = "39054620502613157373429341617471746606"
id = "5b4c9ee3-e719-4a0a-9ee5-354932c5e6a4"
commonName = "test-name"
certsToken = "certsToken"
certsDomainID = "domain-id"
)
func TestIssueCertCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
certCmd := cli.NewCertsCmd()
rootCmd := setFlags(certCmd)
ipAddrs := "[\"192.168.100.22\"]"
var cert sdk.Certificate
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
logType outputLog
cert sdk.Certificate
}{
{
desc: "issue cert successfully",
args: []string{
id,
commonName,
ipAddrs,
certsDomainID,
certsToken,
},
logType: entityLog,
cert: sdk.Certificate{SerialNumber: serialNumber},
},
{
desc: "issue cert with invalid args",
args: []string{
id,
ipAddrs,
},
logType: usageLog,
},
{
desc: "issue cert failed",
args: []string{
id,
commonName,
ipAddrs,
certsDomainID,
certsToken,
},
sdkErr: errors.NewSDKErrorWithStatus(certs.ErrCreateEntity, http.StatusUnprocessableEntity),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(certs.ErrCreateEntity, http.StatusUnprocessableEntity)),
logType: errLog,
},
{
desc: "issue cert with 6 args",
args: []string{
id,
commonName,
ipAddrs,
"{\"organization\":[\"organization_name\"]}",
certsDomainID,
certsToken,
},
logType: entityLog,
cert: sdk.Certificate{SerialNumber: serialNumber},
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
defer func() {
cleanupFiles(t, []string{"cert.pem", "key.pem"})
}()
sdkCall := sdkMock.On("IssueCert", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.cert, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{issueCmd}, tc.args...)...)
switch tc.logType {
case entityLog:
lines := strings.Split(out, "\n")
var jsonLines []string
var inJSON bool
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "{") {
inJSON = true
jsonLines = append(jsonLines, line)
} else if inJSON && strings.HasSuffix(line, "}") {
jsonLines = append(jsonLines, line)
break
} else if inJSON {
jsonLines = append(jsonLines, line)
}
}
if len(jsonLines) == 0 {
t.Fatalf("No JSON found in output: %s", out)
}
jsonPart := strings.Join(jsonLines, "")
err := json.Unmarshal([]byte(jsonPart), &cert)
assert.Nil(t, err)
assert.Equal(t, tc.cert, cert, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.cert, cert))
assert.True(t, strings.Contains(out, "All certificate files have been saved successfully"), fmt.Sprintf("%s should save files", tc.desc))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
func TestRevokeCertCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
certCmd := cli.NewCertsCmd()
rootCmd := setFlags(certCmd)
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
logType outputLog
}{
{
desc: "revoke cert successfully",
args: []string{
serialNumber,
certsDomainID,
certsToken,
},
logType: okLog,
},
{
desc: "revoke cert with invalid args",
args: []string{
serialNumber,
extraArg,
},
logType: usageLog,
},
{
desc: "revoke cert failed",
args: []string{
serialNumber,
certsDomainID,
certsToken,
},
sdkErr: errors.NewSDKErrorWithStatus(certs.ErrUpdateEntity, http.StatusUnprocessableEntity),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(certs.ErrUpdateEntity, http.StatusUnprocessableEntity)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("RevokeCert", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{revokeCmd}, tc.args...)...)
switch tc.logType {
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
func TestDeleteCertCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
certCmd := cli.NewCertsCmd()
rootCmd := setFlags(certCmd)
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
logType outputLog
}{
{
desc: "delete certs successfully",
args: []string{
id,
certsDomainID,
certsToken,
},
logType: okLog,
},
{
desc: "delete certs with invalid args",
args: []string{
id,
extraArg,
},
logType: usageLog,
},
{
desc: "delete certs failed",
args: []string{
id,
certsDomainID,
certsToken,
},
sdkErr: errors.NewSDKErrorWithStatus(certs.ErrUpdateEntity, http.StatusUnprocessableEntity),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(certs.ErrUpdateEntity, http.StatusUnprocessableEntity)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("DeleteCert", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{deleteCmd}, tc.args...)...)
switch tc.logType {
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
func TestRenewCertCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
certCmd := cli.NewCertsCmd()
rootCmd := setFlags(certCmd)
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
logType outputLog
}{
{
desc: "renew cert successfully",
args: []string{
serialNumber,
certsDomainID,
certsToken,
},
logType: okLog,
},
{
desc: "renew cert with invalid args",
args: []string{
serialNumber,
extraArg,
},
logType: usageLog,
},
{
desc: "renew cert failed",
args: []string{
serialNumber,
certsDomainID,
certsToken,
},
sdkErr: errors.NewSDKErrorWithStatus(certs.ErrUpdateEntity, http.StatusUnprocessableEntity),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(certs.ErrUpdateEntity, http.StatusUnprocessableEntity)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("RenewCert", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(sdk.Certificate{}, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{renewCmd}, tc.args...)...)
switch tc.logType {
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
func TestListCertsCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
certCmd := cli.NewCertsCmd()
rootCmd := setFlags(certCmd)
var page sdk.CertificatePage
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
logType outputLog
page sdk.CertificatePage
}{
{
desc: "list certs successfully",
args: []string{
all,
certsDomainID,
certsToken,
},
logType: entityLog,
page: sdk.CertificatePage{
Total: 1,
Offset: 0,
Limit: 10,
Certificates: []sdk.Certificate{
{SerialNumber: serialNumber},
},
},
},
{
desc: "list certs successfully with entity ID",
args: []string{
id,
certsDomainID,
certsToken,
},
logType: entityLog,
page: sdk.CertificatePage{
Total: 1,
Offset: 0,
Limit: 10,
Certificates: []sdk.Certificate{
{SerialNumber: serialNumber},
},
},
},
{
desc: "list certs with invalid args",
args: []string{
all,
extraArg,
},
logType: usageLog,
},
{
desc: "failed list certs with all",
args: []string{
all,
certsDomainID,
certsToken,
},
sdkErr: errors.NewSDKErrorWithStatus(certs.ErrViewEntity, http.StatusUnprocessableEntity),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(certs.ErrViewEntity, http.StatusUnprocessableEntity)),
logType: errLog,
},
{
desc: "failed list certs with entity ID",
args: []string{
id,
certsDomainID,
certsToken,
},
sdkErr: errors.NewSDKErrorWithStatus(certs.ErrViewEntity, http.StatusUnprocessableEntity),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(certs.ErrViewEntity, http.StatusUnprocessableEntity)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("ListCerts", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.page, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{certsListCmd}, tc.args...)...)
switch tc.logType {
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
case entityLog:
err := json.Unmarshal([]byte(out), &page)
if err != nil {
t.Fatalf("Failed to unmarshal JSON: %v", err)
}
assert.Equal(t, tc.page, page, fmt.Sprintf("%v unexpected response, expected: %v, got: %v", tc.desc, tc.page, page))
}
sdkCall.Unset()
})
}
}
func TestDownloadCACmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
certCmd := cli.NewCertsCmd()
rootCmd := setFlags(certCmd)
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
logMessage string
logType outputLog
certBundle sdk.CertificateBundle
}{
{
desc: "download CA successfully",
args: []string{},
logType: entityLog,
certBundle: sdk.CertificateBundle{
Certificate: []byte("certificate"),
},
logMessage: "Saved ca.crt\n\nAll certificate files have been saved successfully.\n",
},
{
desc: "download CA with invalid args",
args: []string{
extraArg,
},
logType: usageLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
defer func() {
cleanupFiles(t, []string{"ca.crt"})
}()
sdkCall := sdkMock.On("DownloadCA", mock.Anything).Return(tc.certBundle, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{downloadCACmd}, tc.args...)...)
switch tc.logType {
case entityLog:
assert.True(t, strings.Contains(out, "Saved ca.crt"), fmt.Sprintf("%s invalid output: %s", tc.desc, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
}
sdkCall.Unset()
})
}
}
func TestViewCACmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
certCmd := cli.NewCertsCmd()
rootCmd := setFlags(certCmd)
var cert sdk.Certificate
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
logType outputLog
cert sdk.Certificate
}{
{
desc: "view cert successfully",
args: []string{},
logType: entityLog,
cert: sdk.Certificate{
Certificate: "certificate",
Key: "privatekey",
},
},
{
desc: "view cert failed",
args: []string{},
sdkErr: errors.NewSDKErrorWithStatus(certs.ErrUpdateEntity, http.StatusUnprocessableEntity),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(certs.ErrUpdateEntity, http.StatusUnprocessableEntity)),
logType: errLog,
cert: sdk.Certificate{},
},
{
desc: "view cert with invalid args",
args: []string{extraArg},
logType: usageLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("ViewCA", mock.Anything).Return(tc.cert, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{viewCACmd}, tc.args...)...)
switch tc.logType {
case entityLog:
err := json.Unmarshal([]byte(out), &cert)
assert.Nil(t, err)
assert.Equal(t, tc.cert, cert, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.cert, cert))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
}
sdkCall.Unset()
})
}
}
func TestGenerateCRLCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
certCmd := cli.NewCertsCmd()
rootCmd := setFlags(certCmd)
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
logType outputLog
crlBytes []byte
}{
{
desc: "generate CRL successfully",
args: []string{},
logType: entityLog,
crlBytes: []byte("crl-data"),
},
{
desc: "generate CRL failed",
args: []string{},
sdkErr: errors.NewSDKErrorWithStatus(certs.ErrFailedCertCreation, http.StatusUnprocessableEntity),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(certs.ErrFailedCertCreation, http.StatusUnprocessableEntity)),
logType: errLog,
},
{
desc: "generate CRL with invalid args",
args: []string{"invalid"},
logType: usageLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
defer func() {
cleanupFiles(t, []string{"ca.crl"})
}()
sdkCall := sdkMock.On("GenerateCRL", mock.Anything).Return(tc.crlBytes, tc.sdkErr)
defer sdkCall.Unset()
out := executeCommand(t, rootCmd, append([]string{"crl"}, tc.args...)...)
switch tc.logType {
case entityLog:
assert.True(t, strings.Contains(out, "CRL file has been saved successfully"), fmt.Sprintf("%s invalid output: %s", tc.desc, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
}
})
}
}
func TestGetEntityIDCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
certCmd := cli.NewCertsCmd()
rootCmd := setFlags(certCmd)
entityID := "test-entity-id"
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
logType outputLog
entityID string
}{
{
desc: "get entity ID successfully",
args: []string{serialNumber, certsDomainID, certsToken},
logType: entityLog,
entityID: entityID,
},
{
desc: "get entity ID with invalid args",
args: []string{serialNumber, extraArg},
logType: usageLog,
},
{
desc: "get entity ID failed",
args: []string{serialNumber, certsDomainID, certsToken},
sdkErr: errors.NewSDKErrorWithStatus(certs.ErrViewEntity, http.StatusUnprocessableEntity),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(certs.ErrViewEntity, http.StatusUnprocessableEntity)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("EntityID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.entityID, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{"entity-id"}, tc.args...)...)
switch tc.logType {
case entityLog:
assert.True(t, strings.Contains(out, tc.entityID), fmt.Sprintf("%s invalid output: %s", tc.desc, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
}
sdkCall.Unset()
})
}
}
func cleanupFiles(t *testing.T, filenames []string) {
for _, filename := range filenames {
err := os.Remove(filename)
if err != nil && !os.IsNotExist(err) {
t.Logf("Failed to remove file %s: %v", filename, err)
}
}
}
func TestIssueFromCSRInternalCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
certCmd := cli.NewCertsCmd()
rootCmd := setFlags(certCmd)
agentToken := "agent-certsToken-123"
csrPath := "test.csr"
bytes := []byte("-----BEGIN CERTIFICATE REQUEST-----\n-csr-content\n-----END CERTIFICATE REQUEST-----")
err := os.WriteFile(csrPath, bytes, filePermission)
if err != nil {
t.Fatalf("Failed to create test CSR file: %v", err)
}
defer os.Remove(csrPath)
var cert sdk.Certificate
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
logType outputLog
cert sdk.Certificate
}{
{
desc: "issue cert from CSR internal successfully",
args: []string{
id,
"10h",
csrPath,
agentToken,
},
logType: entityLog,
cert: sdk.Certificate{SerialNumber: serialNumber},
},
{
desc: "issue cert from CSR internal with invalid args",
args: []string{
id,
extraArg,
},
logType: usageLog,
},
{
desc: "issue cert from CSR internal failed",
args: []string{
id,
"10h",
csrPath,
agentToken,
},
sdkErr: errors.NewSDKErrorWithStatus(certs.ErrFailedCertCreation, http.StatusUnprocessableEntity),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(certs.ErrFailedCertCreation, http.StatusUnprocessableEntity)),
logType: errLog,
},
{
desc: "issue cert from CSR internal with non-existent file",
args: []string{
id,
"10h",
"non-existent.csr",
agentToken,
},
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
defer func() {
cleanupFiles(t, []string{"cert.pem", "key.pem"})
}()
sdkCall := sdkMock.On("IssueFromCSRInternal", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.cert, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{"issue-csr-internal"}, tc.args...)...)
switch tc.logType {
case entityLog:
lines := strings.Split(out, "\n")
var jsonLines []string
var inJSON bool
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "{") {
inJSON = true
jsonLines = append(jsonLines, line)
} else if inJSON && strings.HasSuffix(line, "}") {
jsonLines = append(jsonLines, line)
break
} else if inJSON {
jsonLines = append(jsonLines, line)
}
}
if len(jsonLines) == 0 {
t.Fatalf("No JSON found in output: %s", out)
}
jsonPart := strings.Join(jsonLines, "")
err := json.Unmarshal([]byte(jsonPart), &cert)
assert.Nil(t, err)
assert.Equal(t, tc.cert, cert, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.cert, cert))
assert.True(t, strings.Contains(out, "All certificate files have been saved successfully"), fmt.Sprintf("%s should save files", tc.desc))
case errLog:
if tc.errLogMessage != "" {
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
} else {
assert.True(t, strings.Contains(out, "error"), fmt.Sprintf("%s should contain error message: %s", tc.desc, out))
}
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
func TestIssueFromCSRCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
certCmd := cli.NewCertsCmd()
rootCmd := setFlags(certCmd)
csrPath := "test.csr"
bytes := []byte("-----BEGIN CERTIFICATE REQUEST-----\n-csr-content\n-----END CERTIFICATE REQUEST-----")
err := os.WriteFile(csrPath, bytes, filePermission)
if err != nil {
t.Fatalf("Failed to create test CSR file: %v", err)
}
defer os.Remove(csrPath)
var cert sdk.Certificate
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
logType outputLog
cert sdk.Certificate
}{
{
desc: "issue cert from CSR successfully",
args: []string{
id,
"10h",
csrPath,
certsDomainID,
certsToken,
},
logType: entityLog,
cert: sdk.Certificate{SerialNumber: serialNumber},
},
{
desc: "issue cert from CSR with invalid args",
args: []string{
id,
extraArg,
},
logType: usageLog,
},
{
desc: "issue cert from CSR failed",
args: []string{
id,
"10h",
csrPath,
certsDomainID,
certsToken,
},
sdkErr: errors.NewSDKErrorWithStatus(certs.ErrFailedCertCreation, http.StatusUnprocessableEntity),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(certs.ErrFailedCertCreation, http.StatusUnprocessableEntity)),
logType: errLog,
},
{
desc: "issue cert from CSR with non-existent file",
args: []string{
id,
"10h",
"non-existent.csr",
certsDomainID,
certsToken,
},
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
defer func() {
cleanupFiles(t, []string{"cert.pem", "key.pem"})
}()
sdkCall := sdkMock.On("IssueFromCSR", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.cert, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{"issue-csr"}, tc.args...)...)
switch tc.logType {
case entityLog:
lines := strings.Split(out, "\n")
var jsonLines []string
var inJSON bool
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "{") {
inJSON = true
jsonLines = append(jsonLines, line)
} else if inJSON && strings.HasSuffix(line, "}") {
jsonLines = append(jsonLines, line)
break
} else if inJSON {
jsonLines = append(jsonLines, line)
}
}
if len(jsonLines) == 0 {
t.Fatalf("No JSON found in output: %s", out)
}
jsonPart := strings.Join(jsonLines, "")
err := json.Unmarshal([]byte(jsonPart), &cert)
assert.Nil(t, err)
assert.Equal(t, tc.cert, cert, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.cert, cert))
assert.True(t, strings.Contains(out, "All certificate files have been saved successfully"), fmt.Sprintf("%s should save files", tc.desc))
case errLog:
if tc.errLogMessage != "" {
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
} else {
assert.True(t, strings.Contains(out, "error"), fmt.Sprintf("%s should contain error message: %s", tc.desc, out))
}
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
-660
View File
@@ -1,660 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli_test
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"testing"
"github.com/absmach/magistrala/cli"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
mgsdk "github.com/absmach/magistrala/pkg/sdk"
sdkmocks "github.com/absmach/magistrala/pkg/sdk/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
var channel = mgsdk.Channel{
ID: testsutil.GenerateUUID(&testing.T{}),
Name: "testchannel",
}
func TestCreateChannelCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
channelJson := "{\"name\":\"testchannel\", \"metadata\":{\"key1\":\"value1\"}}"
channelCmd := cli.NewChannelsCmd()
rootCmd := setFlags(channelCmd)
cp := mgsdk.Channel{}
cases := []struct {
desc string
args []string
logType outputLog
channel mgsdk.Channel
sdkErr errors.SDKError
errLogMessage string
}{
{
desc: "create channel successfully",
args: []string{
createCmd,
channelJson,
domainID,
token,
},
channel: channel,
logType: entityLog,
},
{
desc: "create channel with invalid args",
args: []string{
createCmd,
channelJson,
domainID,
token,
extraArg,
},
logType: usageLog,
},
{
desc: "create channel with invalid json",
args: []string{
createCmd,
"{\"name\":\"testchannel\", \"metadata\":{\"key1\":\"value1\"}",
domainID,
token,
},
sdkErr: errors.NewSDKError(errors.New("unexpected end of JSON input")),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.New("unexpected end of JSON input")),
logType: errLog,
},
{
desc: "create channel with invalid token",
args: []string{
createCmd,
channelJson,
domainID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
var sdkCall *mock.Call
if len(tc.args) >= 4 {
sdkCall = sdkMock.On("CreateChannel", mock.Anything, mock.Anything, tc.args[2], tc.args[3]).Return(tc.channel, tc.sdkErr)
}
out := executeCommand(t, rootCmd, tc.args...)
switch tc.logType {
case entityLog:
err := json.Unmarshal([]byte(out), &cp)
assert.Nil(t, err)
assert.Equal(t, tc.channel, cp, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.channel, cp))
case usageLog:
assert.True(t, strings.Contains(out, "cli channels create"), fmt.Sprintf("%s invalid usage: expected to contain create usage, got: %s", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
}
if sdkCall != nil {
sdkCall.Unset()
}
})
}
}
func TestGetChannelsCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
channelCmd := cli.NewChannelsCmd()
rootCmd := setFlags(channelCmd)
var ch mgsdk.Channel
var page mgsdk.ChannelsPage
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
page mgsdk.ChannelsPage
channel mgsdk.Channel
logType outputLog
errLogMessage string
}{
{
desc: "get all channels successfully",
args: []string{
all,
getCmd,
domainID,
token,
},
page: mgsdk.ChannelsPage{
Channels: []mgsdk.Channel{channel},
},
logType: entityLog,
},
{
desc: "get channel with id",
args: []string{
channel.ID,
getCmd,
domainID,
token,
},
logType: entityLog,
channel: channel,
},
{
desc: "get channels with invalid args",
args: []string{
all,
getCmd,
domainID,
token,
extraArg,
},
logType: usageLog,
},
{
desc: "get all channels with invalid token",
args: []string{
all,
getCmd,
domainID,
invalidToken,
},
logType: errLog,
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
},
{
desc: "get channel with invalid id",
args: []string{
invalidID,
getCmd,
domainID,
token,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("Channel", mock.Anything, tc.args[0], tc.args[2], tc.args[3]).Return(tc.channel, tc.sdkErr)
sdkCall1 := sdkMock.On("Channels", mock.Anything, mock.Anything, tc.args[2], tc.args[3]).Return(tc.page, tc.sdkErr)
out := executeCommand(t, rootCmd, tc.args...)
switch tc.logType {
case entityLog:
if tc.args[0] == all {
err := json.Unmarshal([]byte(out), &page)
assert.Nil(t, err)
assert.Equal(t, tc.page, page, fmt.Sprintf("%v unexpected response, expected: %v, got: %v", tc.desc, tc.page, page))
} else {
err := json.Unmarshal([]byte(out), &ch)
assert.Nil(t, err)
assert.Equal(t, tc.channel, ch, fmt.Sprintf("%v unexpected response, expected: %v, got: %v", tc.desc, tc.channel, ch))
}
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
sdkCall1.Unset()
})
}
}
func TestDeleteChannelCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
channelCmd := cli.NewChannelsCmd()
rootCmd := setFlags(channelCmd)
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
logType outputLog
errLogMessage string
}{
{
desc: "delete channel successfully",
args: []string{
channel.ID,
delCmd,
domainID,
token,
},
logType: okLog,
},
{
desc: "delete channel with invalid args",
args: []string{
channel.ID,
delCmd,
domainID,
token,
extraArg,
},
logType: usageLog,
},
{
desc: "delete channel with invalid channel id",
args: []string{
invalidID,
delCmd,
domainID,
token,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
{
desc: "delete channel with invalid token",
args: []string{
channel.ID,
delCmd,
domainID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("DeleteChannel", mock.Anything, tc.args[0], tc.args[2], tc.args[3]).Return(tc.sdkErr)
out := executeCommand(t, rootCmd, tc.args...)
switch tc.logType {
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
func TestUpdateChannelCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
channelCmd := cli.NewChannelsCmd()
rootCmd := setFlags(channelCmd)
newChannelJson := "{\"name\" : \"channel1\"}"
cases := []struct {
desc string
args []string
channel mgsdk.Channel
sdkErr errors.SDKError
errLogMessage string
logType outputLog
}{
{
desc: "update channel successfully",
args: []string{
channel.ID,
updateCmd,
newChannelJson,
domainID,
token,
},
channel: mgsdk.Channel{
Name: "newchannel1",
ID: channel.ID,
},
logType: entityLog,
},
{
desc: "update channel with invalid args",
args: []string{
channel.ID,
updateCmd,
newChannelJson,
domainID,
token,
extraArg,
},
logType: usageLog,
},
{
desc: "update channel with invalid channel id",
args: []string{
invalidID,
updateCmd,
newChannelJson,
domainID,
token,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
{
desc: "update channel with invalid json syntax",
args: []string{
channel.ID,
updateCmd,
"{\"name\" : \"channel1\"",
domainID,
token,
},
sdkErr: errors.NewSDKError(errors.New("unexpected end of JSON input")),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.New("unexpected end of JSON input")),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
var ch mgsdk.Channel
sdkCall := sdkMock.On("UpdateChannel", mock.Anything, mock.Anything, tc.args[3], tc.args[4]).Return(tc.channel, tc.sdkErr)
out := executeCommand(t, rootCmd, tc.args...)
switch tc.logType {
case entityLog:
err := json.Unmarshal([]byte(out), &ch)
assert.Nil(t, err)
assert.Equal(t, tc.channel, ch, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.channel, ch))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
}
sdkCall.Unset()
})
}
}
func TestEnableChannelCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
channelCmd := cli.NewChannelsCmd()
rootCmd := setFlags(channelCmd)
var ch mgsdk.Channel
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
channel mgsdk.Channel
logType outputLog
}{
{
desc: "enable channel successfully",
args: []string{
channel.ID,
enableCmd,
domainID,
validToken,
},
channel: channel,
logType: entityLog,
},
{
desc: "delete channel with invalid token",
args: []string{
channel.ID,
enableCmd,
domainID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
{
desc: "delete channel with invalid channel ID",
args: []string{
invalidID,
enableCmd,
domainID,
token,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
{
desc: "enable channel with invalid args",
args: []string{
channel.ID,
enableCmd,
domainID,
validToken,
extraArg,
},
logType: usageLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("EnableChannel", mock.Anything, tc.args[0], tc.args[2], tc.args[3]).Return(tc.channel, tc.sdkErr)
out := executeCommand(t, rootCmd, tc.args...)
switch tc.logType {
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
case entityLog:
err := json.Unmarshal([]byte(out), &ch)
assert.Nil(t, err)
assert.Equal(t, tc.channel, ch, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.channel, ch))
}
sdkCall.Unset()
})
}
}
func TestDisableChannelCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
channelsCmd := cli.NewChannelsCmd()
rootCmd := setFlags(channelsCmd)
var ch mgsdk.Channel
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
channel mgsdk.Channel
logType outputLog
}{
{
desc: "disable channel successfully",
args: []string{
channel.ID,
disableCmd,
domainID,
validToken,
},
logType: entityLog,
channel: channel,
},
{
desc: "disable channel with invalid token",
args: []string{
channel.ID,
disableCmd,
domainID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
{
desc: "disable channel with invalid id",
args: []string{
invalidID,
disableCmd,
domainID,
token,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
{
desc: "disable client with invalid args",
args: []string{
channel.ID,
disableCmd,
domainID,
validToken,
extraArg,
},
logType: usageLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("DisableChannel", mock.Anything, tc.args[0], tc.args[2], tc.args[3]).Return(tc.channel, tc.sdkErr)
out := executeCommand(t, rootCmd, tc.args...)
switch tc.logType {
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
case entityLog:
err := json.Unmarshal([]byte(out), &ch)
if err != nil {
t.Fatalf("json.Unmarshal failed: %v", err)
}
assert.Equal(t, tc.channel, ch, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.channel, ch))
}
sdkCall.Unset()
})
}
}
func TestChannelUsersCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
channelsCmd := cli.NewChannelsCmd()
rootCmd := setFlags(channelsCmd)
var mp mgsdk.EntityMembersPage
memberRole := mgsdk.MemberRoles{
MemberID: testsutil.GenerateUUID(t),
Roles: []mgsdk.MemberRole{},
}
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
errLogMessage string
usersPage mgsdk.EntityMembersPage
logType outputLog
}{
{
desc: "list channel users successfully",
args: []string{
channel.ID,
usersCmd,
domainID,
validToken,
},
usersPage: mgsdk.EntityMembersPage{
Members: []mgsdk.MemberRoles{memberRole},
},
logType: entityLog,
},
{
desc: "list channel users with invalid token",
args: []string{
channel.ID,
usersCmd,
domainID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
{
desc: "list channel users with invalid channel id",
args: []string{
invalidID,
usersCmd,
domainID,
token,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
{
desc: "list channel users with invalid args",
args: []string{
channel.ID,
usersCmd,
domainID,
validToken,
extraArg,
},
errLogMessage: rootCmd.Use,
logType: usageLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("ListChannelMembers", mock.Anything, tc.args[0], tc.args[2], mock.Anything, tc.args[3]).Return(tc.usersPage, tc.sdkErr)
out := executeCommand(t, rootCmd, tc.args...)
switch tc.logType {
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
case entityLog:
err := json.Unmarshal([]byte(out), &mp)
if err != nil {
t.Fatalf("json.Unmarshal failed: %v", err)
}
assert.Equal(t, tc.usersPage, mp, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.usersPage, mp))
}
sdkCall.Unset()
})
}
}
-701
View File
@@ -1,701 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli
import (
"encoding/json"
"fmt"
"github.com/absmach/magistrala/clients"
smqsdk "github.com/absmach/magistrala/pkg/sdk"
"github.com/spf13/cobra"
)
const (
connect = "connect"
disconnect = "disconnect"
roles = "roles"
actions = "actions"
members = "members"
secret = "secret"
// Usage strings for client operations.
usageClientCreate = "cli clients create <JSON_client> <domain_id> <user_auth_token>"
usageClientGet = "cli clients <client_id|all> get <domain_id> <user_auth_token>"
usageClientDelete = "cli clients <client_id> delete <domain_id> <user_auth_token>"
usageClientUpdate = "cli clients <client_id> update <JSON_string> <domain_id> <user_auth_token>"
usageClientUpdateTags = "cli clients <client_id> update tags <tags> <domain_id> <user_auth_token>"
usageClientUpdateSecret = "cli clients <client_id> update secret <secret> <domain_id> <user_auth_token>"
usageClientEnable = "cli clients <client_id> enable <domain_id> <user_auth_token>"
usageClientDisable = "cli clients <client_id> disable <domain_id> <user_auth_token>"
usageClientConnect = "cli clients <client_id> connect <channel_id> <conn_types_json_list> <domain_id> <user_auth_token>"
usageClientDisconnect = "cli clients <client_id> disconnect <channel_id> <conn_types_json_list> <domain_id> <user_auth_token>"
usageClientUsers = "cli clients <client_id> users <domain_id> <user_auth_token>"
// Usage strings for client roles operations.
usageClientRolesCreate = "cli clients <client_id> roles create <JSON_role> <domain_id> <user_auth_token>"
usageClientRolesGet = "cli clients <client_id> roles get <role_id|all> <domain_id> <user_auth_token>"
usageClientRolesUpdate = "cli clients <client_id> roles update <role_id> <new_name> <domain_id> <user_auth_token>"
usageClientRolesDelete = "cli clients <client_id> roles delete <role_id> <domain_id> <user_auth_token>"
// Usage strings for client role actions operations.
usageClientRoleActionsAdd = "cli clients <client_id> roles actions add <role_id> <JSON_actions> <domain_id> <user_auth_token>"
usageClientRoleActionsList = "cli clients <client_id> roles actions list <role_id> <domain_id> <user_auth_token>"
usageClientRoleActionsDelete = "cli clients <client_id> roles actions delete <role_id> <JSON_actions|all> <domain_id> <user_auth_token>"
usageClientRoleActionsAvailable = "cli clients roles actions available-actions <domain_id> <user_auth_token>"
// Usage strings for client role members operations.
usageClientRoleMembersAdd = "cli clients <client_id> roles members add <role_id> <JSON_members> <domain_id> <user_auth_token>"
usageClientRoleMembersList = "cli clients <client_id> roles members list <role_id> <domain_id> <user_auth_token>"
usageClientRoleMembersDelete = "cli clients <client_id> roles members delete <role_id> <JSON_members|all> <domain_id> <user_auth_token>"
)
func NewClientsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "clients <client_id|all|create> [operation] [args...]",
Short: "Clients management",
Long: `Format:
clients create [args...]
clients <client_id|all> <operation> [args...]
Operations (require client_id/all): get, update, delete, enable, disable, connect, disconnect, users, roles
Examples:
clients create <JSON_client> <domain_id> <user_auth_token>
clients all get <domain_id> <user_auth_token>
clients <client_id> get <domain_id> <user_auth_token>
clients <client_id> update <JSON_string> <domain_id> <user_auth_token>
clients <client_id> delete <domain_id> <user_auth_token>
clients <client_id> enable <domain_id> <user_auth_token>
clients <client_id> disable <domain_id> <user_auth_token>
clients <client_id> connect <channel_id> <conn_types_json_list> <domain_id> <user_auth_token>
clients <client_id> users <domain_id> <user_auth_token>`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
logUsageCmd(*cmd, cmd.Use)
return
}
if args[0] == create {
handleClientCreate(cmd, args[1:])
return
}
if len(args) < 2 {
logUsageCmd(*cmd, "clients <client_id|all> <get|update|delete|enable|disable|connect|disconnect|users|roles> [args...]")
return
}
clientParams := args[0]
operation := args[1]
opArgs := args[2:]
switch operation {
case get:
handleClientGet(cmd, clientParams, opArgs)
case update:
handleClientUpdate(cmd, clientParams, opArgs)
case delete:
handleClientDelete(cmd, clientParams, opArgs)
case enable:
handleClientEnable(cmd, clientParams, opArgs)
case disable:
handleClientDisable(cmd, clientParams, opArgs)
case connect:
handleClientConnect(cmd, clientParams, opArgs)
case disconnect:
handleClientDisconnect(cmd, clientParams, opArgs)
case users:
handleClientUsers(cmd, clientParams, opArgs)
case roles:
handleClientRoles(cmd, clientParams, opArgs)
default:
logErrorCmd(*cmd, fmt.Errorf("unknown operation: %s", operation))
}
},
}
return cmd
}
func handleClientCreate(cmd *cobra.Command, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageClientCreate)
return
}
var client smqsdk.Client
if err := json.Unmarshal([]byte(args[0]), &client); err != nil {
logErrorCmd(*cmd, err)
return
}
client.Status = clients.EnabledStatus.String()
client, err := sdk.CreateClient(cmd.Context(), client, args[1], args[2])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, client)
}
func handleClientGet(cmd *cobra.Command, clientParams string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageClientGet)
return
}
if clientParams == all {
metadata, err := convertMetadata(Metadata)
if err != nil {
logErrorCmd(*cmd, err)
return
}
pageMetadata := smqsdk.PageMetadata{
Name: Name,
Offset: Offset,
Limit: Limit,
Metadata: metadata,
}
l, err := sdk.Clients(cmd.Context(), pageMetadata, args[0], args[1])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, l)
return
}
t, err := sdk.Client(cmd.Context(), clientParams, args[0], args[1])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, t)
}
func handleClientUpdate(cmd *cobra.Command, clientID string, args []string) {
if len(args) < 3 || len(args) > 4 {
if args[0] == tags {
logUsageCmd(*cmd, usageClientUpdateTags)
return
}
if args[0] == secret {
logUsageCmd(*cmd, usageClientUpdateSecret)
return
}
logUsageCmd(*cmd, usageClientUpdate)
return
}
if len(args) == 4 && args[0] == "tags" {
var client smqsdk.Client
if err := json.Unmarshal([]byte(args[1]), &client.Tags); err != nil {
logErrorCmd(*cmd, err)
return
}
client.ID = clientID
client, err := sdk.UpdateClientTags(cmd.Context(), client, args[2], args[3])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, client)
return
}
if len(args) == 4 && args[0] == "secret" {
client, err := sdk.UpdateClientSecret(cmd.Context(), clientID, args[1], args[2], args[3])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, client)
return
}
if len(args) != 3 {
logUsageCmd(*cmd, usageClientUpdate)
return
}
var client smqsdk.Client
if err := json.Unmarshal([]byte(args[0]), &client); err != nil {
logErrorCmd(*cmd, err)
return
}
client.ID = clientID
client, err := sdk.UpdateClient(cmd.Context(), client, args[1], args[2])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, client)
}
func handleClientDelete(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageClientDelete)
return
}
if err := sdk.DeleteClient(cmd.Context(), clientID, args[0], args[1]); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleClientEnable(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageClientEnable)
return
}
client, err := sdk.EnableClient(cmd.Context(), clientID, args[0], args[1])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, client)
}
func handleClientDisable(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageClientDisable)
return
}
client, err := sdk.DisableClient(cmd.Context(), clientID, args[0], args[1])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, client)
}
func handleClientConnect(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 4 {
logUsageCmd(*cmd, usageClientConnect)
return
}
var conn_types []string
err := json.Unmarshal([]byte(args[1]), &conn_types)
if err != nil {
logErrorCmd(*cmd, err)
return
}
connIDs := smqsdk.Connection{
ChannelIDs: []string{args[0]},
ClientIDs: []string{clientID},
Types: conn_types,
}
if err := sdk.Connect(cmd.Context(), connIDs, args[2], args[3]); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleClientDisconnect(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 4 {
logUsageCmd(*cmd, usageClientDisconnect)
return
}
var conn_types []string
err := json.Unmarshal([]byte(args[1]), &conn_types)
if err != nil {
logErrorCmd(*cmd, err)
return
}
connIDs := smqsdk.Connection{
ClientIDs: []string{clientID},
ChannelIDs: []string{args[0]},
Types: conn_types,
}
if err := sdk.Disconnect(cmd.Context(), connIDs, args[2], args[3]); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleClientUsers(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageClientUsers)
return
}
pm := smqsdk.PageMetadata{
Offset: Offset,
Limit: Limit,
}
ul, err := sdk.ListClientMembers(cmd.Context(), clientID, args[0], pm, args[1])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, ul)
}
func handleClientRoles(cmd *cobra.Command, clientID string, args []string) {
if len(args) < 1 {
logUsageCmd(*cmd, "cli clients <client_id> roles <operation> [args...]")
return
}
operation := args[0]
opArgs := args[1:]
switch operation {
case create:
handleClientRoleCreate(cmd, clientID, opArgs)
case get:
handleClientRoleGet(cmd, clientID, opArgs)
case update:
handleClientRoleUpdate(cmd, clientID, opArgs)
case delete:
handleClientRoleDelete(cmd, clientID, opArgs)
case actions:
handleClientRoleActions(cmd, clientID, opArgs)
case members:
handleClientRoleMembers(cmd, clientID, opArgs)
default:
logErrorCmd(*cmd, fmt.Errorf("unknown roles operation: %s", operation))
}
}
func handleClientRoleCreate(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageClientRolesCreate)
return
}
var roleReq smqsdk.RoleReq
if err := json.Unmarshal([]byte(args[0]), &roleReq); err != nil {
logErrorCmd(*cmd, err)
return
}
r, err := sdk.CreateClientRole(cmd.Context(), clientID, args[1], roleReq, args[2])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, r)
}
func handleClientRoleGet(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageClientRolesGet)
return
}
roleID := args[0]
domainID := args[1]
token := args[2]
if roleID == all {
pageMetadata := smqsdk.PageMetadata{
Offset: Offset,
Limit: Limit,
}
rs, err := sdk.ClientRoles(cmd.Context(), clientID, domainID, pageMetadata, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, rs)
return
}
r, err := sdk.ClientRole(cmd.Context(), clientID, roleID, domainID, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, r)
}
func handleClientRoleUpdate(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 4 {
logUsageCmd(*cmd, usageClientRolesUpdate)
return
}
roleID := args[0]
newName := args[1]
domainID := args[2]
token := args[3]
r, err := sdk.UpdateClientRole(cmd.Context(), clientID, roleID, newName, domainID, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, r)
}
func handleClientRoleDelete(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageClientRolesDelete)
return
}
roleID := args[0]
domainID := args[1]
token := args[2]
if err := sdk.DeleteClientRole(cmd.Context(), clientID, roleID, domainID, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleClientRoleActions(cmd *cobra.Command, clientID string, args []string) {
if len(args) < 1 {
logUsageCmd(*cmd, "cli clients <client_id> roles actions <operation> [args...]")
return
}
operation := args[0]
opArgs := args[1:]
switch operation {
case add:
handleClientRoleActionsAdd(cmd, clientID, opArgs)
case list:
handleClientRoleActionsList(cmd, clientID, opArgs)
case delete:
handleClientRoleActionsDelete(cmd, clientID, opArgs)
case availableActions:
handleClientRoleActionsAvailable(cmd, opArgs)
default:
logErrorCmd(*cmd, fmt.Errorf("unknown actions operation: %s", operation))
}
}
func handleClientRoleActionsAdd(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 4 {
logUsageCmd(*cmd, usageClientRoleActionsAdd)
return
}
roleID := args[0]
actionsJSON := args[1]
domainID := args[2]
token := args[3]
actions := struct {
Actions []string `json:"actions"`
}{}
if err := json.Unmarshal([]byte(actionsJSON), &actions); err != nil {
logErrorCmd(*cmd, err)
return
}
acts, err := sdk.AddClientRoleActions(cmd.Context(), clientID, roleID, domainID, actions.Actions, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, acts)
}
func handleClientRoleActionsList(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageClientRoleActionsList)
return
}
roleID := args[0]
domainID := args[1]
token := args[2]
l, err := sdk.ClientRoleActions(cmd.Context(), clientID, roleID, domainID, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, l)
}
func handleClientRoleActionsDelete(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 4 {
logUsageCmd(*cmd, usageClientRoleActionsDelete)
return
}
roleID := args[0]
actionsJSON := args[1]
domainID := args[2]
token := args[3]
if actionsJSON == all {
if err := sdk.RemoveAllClientRoleActions(cmd.Context(), clientID, roleID, domainID, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
return
}
actions := struct {
Actions []string `json:"actions"`
}{}
if err := json.Unmarshal([]byte(actionsJSON), &actions); err != nil {
logErrorCmd(*cmd, err)
return
}
if err := sdk.RemoveClientRoleActions(cmd.Context(), clientID, roleID, domainID, actions.Actions, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleClientRoleActionsAvailable(cmd *cobra.Command, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageClientRoleActionsAvailable)
return
}
domainID := args[0]
token := args[1]
acts, err := sdk.AvailableClientRoleActions(cmd.Context(), domainID, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, acts)
}
func handleClientRoleMembers(cmd *cobra.Command, clientID string, args []string) {
if len(args) < 1 {
logUsageCmd(*cmd, "cli clients <client_id> roles members <operation> [args...]")
return
}
operation := args[0]
opArgs := args[1:]
switch operation {
case add:
handleClientRoleMembersAdd(cmd, clientID, opArgs)
case list:
handleClientRoleMembersList(cmd, clientID, opArgs)
case delete:
handleClientRoleMembersDelete(cmd, clientID, opArgs)
default:
logErrorCmd(*cmd, fmt.Errorf("unknown members operation: %s", operation))
}
}
func handleClientRoleMembersAdd(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 4 {
logUsageCmd(*cmd, usageClientRoleMembersAdd)
return
}
roleID := args[0]
membersJSON := args[1]
domainID := args[2]
token := args[3]
members := struct {
Members []string `json:"members"`
}{}
if err := json.Unmarshal([]byte(membersJSON), &members); err != nil {
logErrorCmd(*cmd, err)
return
}
memb, err := sdk.AddClientRoleMembers(cmd.Context(), clientID, roleID, domainID, members.Members, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, memb)
}
func handleClientRoleMembersList(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageClientRoleMembersList)
return
}
roleID := args[0]
domainID := args[1]
token := args[2]
pageMetadata := smqsdk.PageMetadata{
Offset: Offset,
Limit: Limit,
}
l, err := sdk.ClientRoleMembers(cmd.Context(), clientID, roleID, domainID, pageMetadata, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, l)
}
func handleClientRoleMembersDelete(cmd *cobra.Command, clientID string, args []string) {
if len(args) != 4 {
logUsageCmd(*cmd, usageClientRoleMembersDelete)
return
}
roleID := args[0]
membersJSON := args[1]
domainID := args[2]
token := args[3]
if membersJSON == all {
if err := sdk.RemoveAllClientRoleMembers(cmd.Context(), clientID, roleID, domainID, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
return
}
members := struct {
Members []string `json:"members"`
}{}
if err := json.Unmarshal([]byte(membersJSON), &members); err != nil {
logErrorCmd(*cmd, err)
return
}
if err := sdk.RemoveClientRoleMembers(cmd.Context(), clientID, roleID, domainID, members.Members, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
-1943
View File
File diff suppressed because it is too large Load Diff
-64
View File
@@ -1,64 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli_test
// CRUD and common commands
const (
createCmd = "create"
updateCmd = "update"
getCmd = "get"
enableCmd = "enable"
disableCmd = "disable"
freezeCmd = "freeze"
delCmd = "delete"
)
// Users commands
const (
tokCmd = "token"
refTokCmd = "refreshtoken"
profCmd = "profile"
resPassReqCmd = "resetpasswordrequest"
resPassCmd = "resetpassword"
passCmd = "password"
)
// Clients commands
const (
connCmd = "connect"
disconnCmd = "disconnect"
usersCmd = "users"
)
// Messages commands
const sendCmd = "send"
// Invitations commands
const (
acceptCmd = "accept"
rejectCmd = "reject"
userCmd = "user"
domainCmd = "domain"
)
// Role commands
const (
rolesCmd = "roles"
actionsCmd = "actions"
availableActionsCmd = "available-actions"
addCmd = "add"
listCmd = "list"
membersCmd = "members"
)
// Bootstrap commands
const (
updCmd = "update"
rmCmd = "remove"
whitelistCmd = "whitelist"
bootStrapCmd = "bootstrap"
)
-269
View File
@@ -1,269 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli_test
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"testing"
"github.com/absmach/magistrala/cli"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
mgsdk "github.com/absmach/magistrala/pkg/sdk"
sdkmocks "github.com/absmach/magistrala/pkg/sdk/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
var (
userID = testsutil.GenerateUUID(&testing.T{})
subscription = mgsdk.Subscription{
ID: testsutil.GenerateUUID(&testing.T{}),
OwnerID: userID,
Topic: "topic",
Contact: "identity@example.com",
}
)
func TestCreateSubscriptionCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
subCmd := cli.NewSubscriptionCmd()
rootCmd := setFlags(subCmd)
cases := []struct {
desc string
args []string
logType outputLog
errLogMessage string
sdkErr errors.SDKError
response string
id string
}{
{
desc: "create subscription successfully",
args: []string{
subscription.Topic,
subscription.Contact,
validToken,
},
id: userID,
response: fmt.Sprintf("\ncreated: %s\n\n", userID),
logType: createLog,
},
{
desc: "create subscription with invalid args",
args: []string{
subscription.Topic,
subscription.Contact,
validToken,
extraArg,
},
logType: usageLog,
},
{
desc: "create subscription with invalid token",
args: []string{
subscription.Topic,
subscription.Contact,
invalidToken,
},
logType: errLog,
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("CreateSubscription", mock.Anything, tc.args[0], tc.args[1], tc.args[2]).Return(tc.id, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{createCmd}, tc.args...)...)
switch tc.logType {
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case createLog:
assert.Equal(t, tc.response, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.response, out))
}
sdkCall.Unset()
})
}
}
func TestGetSubscriptionsCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
subCmd := cli.NewSubscriptionCmd()
rootCmd := setFlags(subCmd)
var sub mgsdk.Subscription
var page mgsdk.SubscriptionPage
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
page mgsdk.SubscriptionPage
subscription mgsdk.Subscription
logType outputLog
errLogMessage string
}{
{
desc: "get all subscriptions successfully",
args: []string{
all,
validToken,
},
page: mgsdk.SubscriptionPage{
Subscriptions: []mgsdk.Subscription{subscription},
},
logType: entityLog,
},
{
desc: "get subscription with id",
args: []string{
subscription.ID,
validToken,
},
logType: entityLog,
subscription: subscription,
},
{
desc: "get subscriptions with invalid args",
args: []string{
all,
validToken,
extraArg,
},
logType: usageLog,
},
{
desc: "get all subscriptions with invalid token",
args: []string{
all,
invalidToken,
},
logType: errLog,
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
},
{
desc: "get subscription with invalid id",
args: []string{
invalidID,
validToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("ViewSubscription", mock.Anything, tc.args[0], tc.args[1]).Return(tc.subscription, tc.sdkErr)
sdkCall1 := sdkMock.On("ListSubscriptions", mock.Anything, mock.Anything, tc.args[1]).Return(tc.page, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{getCmd}, tc.args...)...)
switch tc.logType {
case entityLog:
if tc.args[1] == all {
err := json.Unmarshal([]byte(out), &page)
assert.Nil(t, err)
assert.Equal(t, tc.page, page, fmt.Sprintf("%v unexpected response, expected: %v, got: %v", tc.desc, tc.page, page))
} else {
err := json.Unmarshal([]byte(out), &sub)
assert.Nil(t, err)
assert.Equal(t, tc.subscription, sub, fmt.Sprintf("%v unexpected response, expected: %v, got: %v", tc.desc, tc.subscription, sub))
}
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
sdkCall1.Unset()
})
}
}
func TestRemoveSubscriptionCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
subCmd := cli.NewSubscriptionCmd()
rootCmd := setFlags(subCmd)
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
logType outputLog
errLogMessage string
}{
{
desc: "remove subscription successfully",
args: []string{
subscription.ID,
validToken,
},
logType: okLog,
},
{
desc: "remove subscription with invalid args",
args: []string{
subscription.ID,
validToken,
extraArg,
},
logType: usageLog,
},
{
desc: "remove subscription with invalid subscription id",
args: []string{
invalidID,
validToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
{
desc: "remove subscription with invalid token",
args: []string{
subscription.ID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("DeleteSubscription", mock.Anything, tc.args[0], tc.args[1]).Return(tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{rmCmd}, tc.args...)...)
switch tc.logType {
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
-583
View File
@@ -1,583 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli
import (
"encoding/json"
"fmt"
smqsdk "github.com/absmach/magistrala/pkg/sdk"
"github.com/spf13/cobra"
)
const (
freeze = "freeze"
// Usage strings for domain operations.
usageDomainCreate = "cli domains create <domain_name> <route> <user_auth_token>"
usageDomainGet = "cli domains <domain_id|all> get <user_auth_token>"
usageDomainUpdate = "cli domains <domain_id> update <JSON_string> <user_auth_token>"
usageDomainEnable = "cli domains <domain_id> enable <user_auth_token>"
usageDomainDisable = "cli domains <domain_id> disable <user_auth_token>"
usageDomainFreeze = "cli domains <domain_id> freeze <user_auth_token>"
usageDomainUsers = "cli domains <domain_id> users <user_auth_token>"
// Usage strings for domain roles operations.
usageDomainRolesCreate = "cli domains <domain_id> roles create <JSON_role> <user_auth_token>"
usageDomainRolesGet = "cli domains <domain_id> roles get <role_id|all> <user_auth_token>"
usageDomainRolesUpdate = "cli domains <domain_id> roles update <role_id> <new_name> <user_auth_token>"
usageDomainRolesDelete = "cli domains <domain_id> roles delete <role_id> <user_auth_token>"
// Usage strings for domain role actions operations.
usageDomainRoleActionsAdd = "cli domains <domain_id> roles actions add <role_id> <JSON_actions> <user_auth_token>"
usageDomainRoleActionsList = "cli domains <domain_id> roles actions list <role_id> <user_auth_token>"
usageDomainRoleActionsDelete = "cli domains <domain_id> roles actions delete <role_id> <JSON_actions|all> <user_auth_token>"
usageDomainRoleActionsAvailable = "cli domains roles actions available-actions <user_auth_token>"
// Usage strings for domain role members operations.
usageDomainRoleMembersAdd = "cli domains <domain_id> roles members add <role_id> <JSON_members> <user_auth_token>"
usageDomainRoleMembersList = "cli domains <domain_id> roles members list <role_id> <user_auth_token>"
usageDomainRoleMembersDelete = "cli domains <domain_id> roles members delete <role_id> <JSON_members|all> <user_auth_token>"
)
func NewDomainsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "domains <domain_id|all|create> [operation] [args...]",
Short: "Domains management",
Long: `Format:
domains create [args...]
domains <domain_id|all> <operation> [args...]
Operations (require domain_id/all): get, update, enable, disable, freeze, users, roles
Examples:
domains create <domain_name> <route> <user_auth_token>
domains all get <user_auth_token>
domains <domain_id> get <user_auth_token>
domains <domain_id> update <JSON_string> <user_auth_token>
domains <domain_id> enable <user_auth_token>
domains <domain_id> disable <user_auth_token>
domains <domain_id> freeze <user_auth_token>
domains <domain_id> users <user_auth_token>`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
logUsageCmd(*cmd, cmd.Use)
return
}
if args[0] == create {
handleDomainCreate(cmd, args[1:])
return
}
if len(args) < 2 {
logUsageCmd(*cmd, "domains <domain_id|all> <get|update|enable|disable|freeze|users|roles> [args...]")
return
}
domainParams := args[0]
operation := args[1]
opArgs := args[2:]
switch operation {
case get:
handleDomainGet(cmd, domainParams, opArgs)
case update:
handleDomainUpdate(cmd, domainParams, opArgs)
case enable:
handleDomainEnable(cmd, domainParams, opArgs)
case disable:
handleDomainDisable(cmd, domainParams, opArgs)
case freeze:
handleDomainFreeze(cmd, domainParams, opArgs)
case users:
handleDomainUsers(cmd, domainParams, opArgs)
case roles:
handleDomainRoles(cmd, domainParams, opArgs)
default:
logErrorCmd(*cmd, fmt.Errorf("unknown operation: %s", operation))
}
},
}
return cmd
}
func handleDomainCreate(cmd *cobra.Command, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageDomainCreate)
return
}
dom := smqsdk.Domain{
Name: args[0],
Route: args[1],
}
d, err := sdk.CreateDomain(cmd.Context(), dom, args[2])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, d)
}
func handleDomainGet(cmd *cobra.Command, domainParams string, args []string) {
if len(args) != 1 {
logUsageCmd(*cmd, usageDomainGet)
return
}
if domainParams == all {
metadata, err := convertMetadata(Metadata)
if err != nil {
logErrorCmd(*cmd, err)
return
}
pageMetadata := smqsdk.PageMetadata{
Name: Name,
Offset: Offset,
Limit: Limit,
Metadata: metadata,
Status: Status,
}
l, err := sdk.Domains(cmd.Context(), pageMetadata, args[0])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, l)
return
}
d, err := sdk.Domain(cmd.Context(), domainParams, args[0])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, d)
}
func handleDomainUpdate(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageDomainUpdate)
return
}
var d smqsdk.Domain
if err := json.Unmarshal([]byte(args[0]), &d); err != nil {
logErrorCmd(*cmd, err)
return
}
d.ID = domainID
d, err := sdk.UpdateDomain(cmd.Context(), d, args[1])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, d)
}
func handleDomainEnable(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 1 {
logUsageCmd(*cmd, usageDomainEnable)
return
}
if err := sdk.EnableDomain(cmd.Context(), domainID, args[0]); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleDomainDisable(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 1 {
logUsageCmd(*cmd, usageDomainDisable)
return
}
if err := sdk.DisableDomain(cmd.Context(), domainID, args[0]); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleDomainFreeze(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 1 {
logUsageCmd(*cmd, usageDomainFreeze)
return
}
if err := sdk.FreezeDomain(cmd.Context(), domainID, args[0]); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleDomainUsers(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 1 {
logUsageCmd(*cmd, usageDomainUsers)
return
}
metadata, err := convertMetadata(Metadata)
if err != nil {
logErrorCmd(*cmd, err)
return
}
pageMetadata := smqsdk.PageMetadata{
Offset: Offset,
Limit: Limit,
Metadata: metadata,
Status: Status,
}
l, err := sdk.ListDomainMembers(cmd.Context(), domainID, pageMetadata, args[0])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, l)
}
func handleDomainRoles(cmd *cobra.Command, domainID string, args []string) {
if len(args) < 1 {
logUsageCmd(*cmd, "cli domains <domain_id> roles <operation> [args...]")
return
}
operation := args[0]
opArgs := args[1:]
switch operation {
case create:
handleDomainRoleCreate(cmd, domainID, opArgs)
case get:
handleDomainRoleGet(cmd, domainID, opArgs)
case update:
handleDomainRoleUpdate(cmd, domainID, opArgs)
case delete:
handleDomainRoleDelete(cmd, domainID, opArgs)
case actions:
handleDomainRoleActions(cmd, domainID, opArgs)
case members:
handleDomainRoleMembers(cmd, domainID, opArgs)
default:
logErrorCmd(*cmd, fmt.Errorf("unknown roles operation: %s", operation))
}
}
func handleDomainRoleCreate(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageDomainRolesCreate)
return
}
var roleReq smqsdk.RoleReq
if err := json.Unmarshal([]byte(args[0]), &roleReq); err != nil {
logErrorCmd(*cmd, err)
return
}
r, err := sdk.CreateDomainRole(cmd.Context(), domainID, roleReq, args[1])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, r)
}
func handleDomainRoleGet(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageDomainRolesGet)
return
}
roleID := args[0]
token := args[1]
if roleID == all {
pageMetadata := smqsdk.PageMetadata{
Offset: Offset,
Limit: Limit,
}
rs, err := sdk.DomainRoles(cmd.Context(), domainID, pageMetadata, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, rs)
return
}
r, err := sdk.DomainRole(cmd.Context(), domainID, roleID, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, r)
}
func handleDomainRoleUpdate(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageDomainRolesUpdate)
return
}
roleID := args[0]
newName := args[1]
token := args[2]
r, err := sdk.UpdateDomainRole(cmd.Context(), domainID, roleID, newName, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, r)
}
func handleDomainRoleDelete(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageDomainRolesDelete)
return
}
roleID := args[0]
token := args[1]
if err := sdk.DeleteDomainRole(cmd.Context(), domainID, roleID, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleDomainRoleActions(cmd *cobra.Command, domainID string, args []string) {
if len(args) < 1 {
logUsageCmd(*cmd, "cli domains <domain_id> roles actions <operation> [args...]")
return
}
operation := args[0]
opArgs := args[1:]
switch operation {
case add:
handleDomainRoleActionsAdd(cmd, domainID, opArgs)
case list:
handleDomainRoleActionsList(cmd, domainID, opArgs)
case delete:
handleDomainRoleActionsDelete(cmd, domainID, opArgs)
case availableActions:
handleDomainRoleActionsAvailable(cmd, opArgs)
default:
logErrorCmd(*cmd, fmt.Errorf("unknown actions operation: %s", operation))
}
}
func handleDomainRoleActionsAdd(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageDomainRoleActionsAdd)
return
}
roleID := args[0]
actionsJSON := args[1]
token := args[2]
actions := struct {
Actions []string `json:"actions"`
}{}
if err := json.Unmarshal([]byte(actionsJSON), &actions); err != nil {
logErrorCmd(*cmd, err)
return
}
acts, err := sdk.AddDomainRoleActions(cmd.Context(), domainID, roleID, actions.Actions, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, acts)
}
func handleDomainRoleActionsList(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageDomainRoleActionsList)
return
}
roleID := args[0]
token := args[1]
l, err := sdk.DomainRoleActions(cmd.Context(), domainID, roleID, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, l)
}
func handleDomainRoleActionsDelete(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageDomainRoleActionsDelete)
return
}
roleID := args[0]
actionsJSON := args[1]
token := args[2]
if actionsJSON == all {
if err := sdk.RemoveAllDomainRoleActions(cmd.Context(), domainID, roleID, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
return
}
actions := struct {
Actions []string `json:"actions"`
}{}
if err := json.Unmarshal([]byte(actionsJSON), &actions); err != nil {
logErrorCmd(*cmd, err)
return
}
if err := sdk.RemoveDomainRoleActions(cmd.Context(), domainID, roleID, actions.Actions, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleDomainRoleActionsAvailable(cmd *cobra.Command, args []string) {
if len(args) != 1 {
logUsageCmd(*cmd, usageDomainRoleActionsAvailable)
return
}
token := args[0]
acts, err := sdk.AvailableDomainRoleActions(cmd.Context(), token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, acts)
}
func handleDomainRoleMembers(cmd *cobra.Command, domainID string, args []string) {
if len(args) < 1 {
logUsageCmd(*cmd, "cli domains <domain_id> roles members <operation> [args...]")
return
}
operation := args[0]
opArgs := args[1:]
switch operation {
case add:
handleDomainRoleMembersAdd(cmd, domainID, opArgs)
case list:
handleDomainRoleMembersList(cmd, domainID, opArgs)
case delete:
handleDomainRoleMembersDelete(cmd, domainID, opArgs)
default:
logErrorCmd(*cmd, fmt.Errorf("unknown members operation: %s", operation))
}
}
func handleDomainRoleMembersAdd(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageDomainRoleMembersAdd)
return
}
roleID := args[0]
membersJSON := args[1]
token := args[2]
members := struct {
Members []string `json:"members"`
}{}
if err := json.Unmarshal([]byte(membersJSON), &members); err != nil {
logErrorCmd(*cmd, err)
return
}
memb, err := sdk.AddDomainRoleMembers(cmd.Context(), domainID, roleID, members.Members, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, memb)
}
func handleDomainRoleMembersList(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageDomainRoleMembersList)
return
}
roleID := args[0]
token := args[1]
pageMetadata := smqsdk.PageMetadata{
Offset: Offset,
Limit: Limit,
}
l, err := sdk.DomainRoleMembers(cmd.Context(), domainID, roleID, pageMetadata, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, l)
}
func handleDomainRoleMembersDelete(cmd *cobra.Command, domainID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageDomainRoleMembersDelete)
return
}
roleID := args[0]
membersJSON := args[1]
token := args[2]
if membersJSON == all {
if err := sdk.RemoveAllDomainRoleMembers(cmd.Context(), domainID, roleID, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
return
}
members := struct {
Members []string `json:"members"`
}{}
if err := json.Unmarshal([]byte(membersJSON), &members); err != nil {
logErrorCmd(*cmd, err)
return
}
if err := sdk.RemoveDomainRoleMembers(cmd.Context(), domainID, roleID, members.Members, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
-1435
View File
File diff suppressed because it is too large Load Diff
-601
View File
@@ -1,601 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli
import (
"encoding/json"
"fmt"
"github.com/absmach/magistrala/groups"
smqsdk "github.com/absmach/magistrala/pkg/sdk"
"github.com/spf13/cobra"
)
const (
tags = "tags"
add = "add"
list = "list"
availableActions = "available-actions"
// Usage strings for group operations.
usageGroupCreate = "cli groups create <JSON_group> <domain_id> <user_auth_token>"
usageGroupGet = "cli groups <group_id|all> get <domain_id> <user_auth_token>"
usageGroupUpdate = "cli groups <group_id> update <JSON_string> <domain_id> <user_auth_token>"
usageGroupUpdateTags = "cli groups <group_id> update tags <tags> <domain_id> <user_auth_token>"
usageGroupDelete = "cli groups <group_id> delete <domain_id> <user_auth_token>"
usageGroupEnable = "cli groups <group_id> enable <domain_id> <user_auth_token>"
usageGroupDisable = "cli groups <group_id> disable <domain_id> <user_auth_token>"
// Usage strings for group roles operations.
usageGroupRolesCreate = "cli groups <group_id> roles create <JSON_role> <domain_id> <user_auth_token>"
usageGroupRolesGet = "cli groups <group_id> roles get <role_id|all> <domain_id> <user_auth_token>"
usageGroupRolesUpdate = "cli groups <group_id> roles update <role_id> <new_name> <domain_id> <user_auth_token>"
usageGroupRolesDelete = "cli groups <group_id> roles delete <role_id> <domain_id> <user_auth_token>"
// Usage strings for group role actions operations.
usageGroupRoleActionsAdd = "cli groups <group_id> roles actions add <role_id> <JSON_actions> <domain_id> <user_auth_token>"
usageGroupRoleActionsList = "cli groups <group_id> roles actions list <role_id> <domain_id> <user_auth_token>"
usageGroupRoleActionsDelete = "cli groups <group_id> roles actions delete <role_id> <JSON_actions|all> <domain_id> <user_auth_token>"
usageGroupRoleActionsAvailable = "cli groups roles actions available-actions <domain_id> <user_auth_token>"
// Usage strings for group role members operations.
usageGroupRoleMembersAdd = "cli groups <group_id> roles members add <role_id> <JSON_members> <domain_id> <user_auth_token>"
usageGroupRoleMembersList = "cli groups <group_id> roles members list <role_id> <domain_id> <user_auth_token>"
usageGroupRoleMembersDelete = "cli groups <group_id> roles members delete <role_id> <JSON_members|all> <domain_id> <user_auth_token>"
)
func NewGroupsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "groups <group_id|all|create> [operation] [args...]",
Short: "Groups management",
Long: `Format:
groups create [args...]
groups <group_id|all> <operation> [args...]
Operations (require group_id/all): get, update, delete, enable, disable, roles
Examples:
groups create <JSON_group> <domain_id> <user_auth_token>
groups all get <domain_id> <user_auth_token>
groups <group_id> get <domain_id> <user_auth_token>
groups <group_id> update <JSON_string> <domain_id> <user_auth_token>
groups <group_id> update tags <tags> <domain_id> <user_auth_token>
groups <group_id> delete <domain_id> <user_auth_token>
groups <group_id> enable <domain_id> <user_auth_token>
groups <group_id> disable <domain_id> <user_auth_token>`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
logUsageCmd(*cmd, cmd.Use)
return
}
if args[0] == create {
handleGroupCreate(cmd, args[1:])
return
}
if len(args) < 2 {
logUsageCmd(*cmd, "groups <group_id|all> <get|update|delete|enable|disable|roles> [args...]")
return
}
groupParams := args[0]
operation := args[1]
opArgs := args[2:]
switch operation {
case get:
handleGroupGet(cmd, groupParams, opArgs)
case update:
handleGroupUpdate(cmd, groupParams, opArgs)
case delete:
handleGroupDelete(cmd, groupParams, opArgs)
case enable:
handleGroupEnable(cmd, groupParams, opArgs)
case disable:
handleGroupDisable(cmd, groupParams, opArgs)
case roles:
handleGroupRoles(cmd, groupParams, opArgs)
default:
logErrorCmd(*cmd, fmt.Errorf("unknown operation: %s", operation))
}
},
}
return cmd
}
func handleGroupCreate(cmd *cobra.Command, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageGroupCreate)
return
}
var group smqsdk.Group
if err := json.Unmarshal([]byte(args[0]), &group); err != nil {
logErrorCmd(*cmd, err)
return
}
group.Status = groups.EnabledStatus.String()
group, err := sdk.CreateGroup(cmd.Context(), group, args[1], args[2])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, group)
}
func handleGroupGet(cmd *cobra.Command, groupParams string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageGroupGet)
return
}
if groupParams == all {
metadata, err := convertMetadata(Metadata)
if err != nil {
logErrorCmd(*cmd, err)
return
}
pageMetadata := smqsdk.PageMetadata{
Name: Name,
Offset: Offset,
Limit: Limit,
Metadata: metadata,
}
l, err := sdk.Groups(cmd.Context(), pageMetadata, args[0], args[1])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, l)
return
}
g, err := sdk.Group(cmd.Context(), groupParams, args[0], args[1])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, g)
}
func handleGroupUpdate(cmd *cobra.Command, groupID string, args []string) {
if len(args) < 3 || len(args) > 4 {
if args[0] == tags {
logUsageCmd(*cmd, usageGroupUpdateTags)
return
}
logUsageCmd(*cmd, usageGroupUpdate)
return
}
if len(args) == 4 && args[0] == tags {
var group smqsdk.Group
if err := json.Unmarshal([]byte(args[1]), &group.Tags); err != nil {
logErrorCmd(*cmd, err)
return
}
group.ID = groupID
group, err := sdk.UpdateGroupTags(cmd.Context(), group, args[2], args[3])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, group)
return
}
if len(args) != 3 {
logUsageCmd(*cmd, usageGroupUpdate)
return
}
var group smqsdk.Group
if err := json.Unmarshal([]byte(args[0]), &group); err != nil {
logErrorCmd(*cmd, err)
return
}
group.ID = groupID
group, err := sdk.UpdateGroup(cmd.Context(), group, args[1], args[2])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, group)
}
func handleGroupDelete(cmd *cobra.Command, groupID string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageGroupDelete)
return
}
if err := sdk.DeleteGroup(cmd.Context(), groupID, args[0], args[1]); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleGroupEnable(cmd *cobra.Command, groupID string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageGroupEnable)
return
}
group, err := sdk.EnableGroup(cmd.Context(), groupID, args[0], args[1])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, group)
}
func handleGroupDisable(cmd *cobra.Command, groupID string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageGroupDisable)
return
}
group, err := sdk.DisableGroup(cmd.Context(), groupID, args[0], args[1])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, group)
}
func handleGroupRoles(cmd *cobra.Command, groupID string, args []string) {
if len(args) < 1 {
logUsageCmd(*cmd, "cli groups <group_id> roles <operation> [args...]")
return
}
operation := args[0]
opArgs := args[1:]
switch operation {
case create:
handleGroupRoleCreate(cmd, groupID, opArgs)
case get:
handleGroupRoleGet(cmd, groupID, opArgs)
case update:
handleGroupRoleUpdate(cmd, groupID, opArgs)
case delete:
handleGroupRoleDelete(cmd, groupID, opArgs)
case actions:
handleGroupRoleActions(cmd, groupID, opArgs)
case members:
handleGroupRoleMembers(cmd, groupID, opArgs)
default:
logErrorCmd(*cmd, fmt.Errorf("unknown roles operation: %s", operation))
}
}
func handleGroupRoleCreate(cmd *cobra.Command, groupID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageGroupRolesCreate)
return
}
var roleReq smqsdk.RoleReq
if err := json.Unmarshal([]byte(args[0]), &roleReq); err != nil {
logErrorCmd(*cmd, err)
return
}
r, err := sdk.CreateGroupRole(cmd.Context(), groupID, args[1], roleReq, args[2])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, r)
}
func handleGroupRoleGet(cmd *cobra.Command, groupID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageGroupRolesGet)
return
}
roleID := args[0]
domainID := args[1]
token := args[2]
if roleID == all {
pageMetadata := smqsdk.PageMetadata{
Offset: Offset,
Limit: Limit,
}
rs, err := sdk.GroupRoles(cmd.Context(), groupID, domainID, pageMetadata, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, rs)
return
}
r, err := sdk.GroupRole(cmd.Context(), groupID, roleID, domainID, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, r)
}
func handleGroupRoleUpdate(cmd *cobra.Command, groupID string, args []string) {
if len(args) != 4 {
logUsageCmd(*cmd, usageGroupRolesUpdate)
return
}
roleID := args[0]
newName := args[1]
domainID := args[2]
token := args[3]
r, err := sdk.UpdateGroupRole(cmd.Context(), groupID, roleID, newName, domainID, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, r)
}
func handleGroupRoleDelete(cmd *cobra.Command, groupID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageGroupRolesDelete)
return
}
roleID := args[0]
domainID := args[1]
token := args[2]
if err := sdk.DeleteGroupRole(cmd.Context(), groupID, roleID, domainID, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleGroupRoleActions(cmd *cobra.Command, groupID string, args []string) {
if len(args) < 1 {
logUsageCmd(*cmd, "cli groups <group_id> roles actions <operation> [args...]")
return
}
operation := args[0]
opArgs := args[1:]
switch operation {
case add:
handleGroupRoleActionsAdd(cmd, groupID, opArgs)
case list:
handleGroupRoleActionsList(cmd, groupID, opArgs)
case delete:
handleGroupRoleActionsDelete(cmd, groupID, opArgs)
case availableActions:
handleGroupRoleActionsAvailable(cmd, opArgs)
default:
logErrorCmd(*cmd, fmt.Errorf("unknown actions operation: %s", operation))
}
}
func handleGroupRoleActionsAdd(cmd *cobra.Command, groupID string, args []string) {
if len(args) != 4 {
logUsageCmd(*cmd, usageGroupRoleActionsAdd)
return
}
roleID := args[0]
actionsJSON := args[1]
domainID := args[2]
token := args[3]
actions := struct {
Actions []string `json:"actions"`
}{}
if err := json.Unmarshal([]byte(actionsJSON), &actions); err != nil {
logErrorCmd(*cmd, err)
return
}
acts, err := sdk.AddGroupRoleActions(cmd.Context(), groupID, roleID, domainID, actions.Actions, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, acts)
}
func handleGroupRoleActionsList(cmd *cobra.Command, groupID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageGroupRoleActionsList)
return
}
roleID := args[0]
domainID := args[1]
token := args[2]
l, err := sdk.GroupRoleActions(cmd.Context(), groupID, roleID, domainID, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, l)
}
func handleGroupRoleActionsDelete(cmd *cobra.Command, groupID string, args []string) {
if len(args) != 4 {
logUsageCmd(*cmd, usageGroupRoleActionsDelete)
return
}
roleID := args[0]
actionsJSON := args[1]
domainID := args[2]
token := args[3]
if actionsJSON == all {
if err := sdk.RemoveAllGroupRoleActions(cmd.Context(), groupID, roleID, domainID, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
return
}
actions := struct {
Actions []string `json:"actions"`
}{}
if err := json.Unmarshal([]byte(actionsJSON), &actions); err != nil {
logErrorCmd(*cmd, err)
return
}
if err := sdk.RemoveGroupRoleActions(cmd.Context(), groupID, roleID, domainID, actions.Actions, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleGroupRoleActionsAvailable(cmd *cobra.Command, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageGroupRoleActionsAvailable)
return
}
domainID := args[0]
token := args[1]
acts, err := sdk.AvailableGroupRoleActions(cmd.Context(), domainID, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, acts)
}
func handleGroupRoleMembers(cmd *cobra.Command, groupID string, args []string) {
if len(args) < 1 {
logUsageCmd(*cmd, "cli groups <group_id> roles members <operation> [args...]")
return
}
operation := args[0]
opArgs := args[1:]
switch operation {
case add:
handleGroupRoleMembersAdd(cmd, groupID, opArgs)
case list:
handleGroupRoleMembersList(cmd, groupID, opArgs)
case delete:
handleGroupRoleMembersDelete(cmd, groupID, opArgs)
default:
logErrorCmd(*cmd, fmt.Errorf("unknown members operation: %s", operation))
}
}
func handleGroupRoleMembersAdd(cmd *cobra.Command, groupID string, args []string) {
if len(args) != 4 {
logUsageCmd(*cmd, usageGroupRoleMembersAdd)
return
}
roleID := args[0]
membersJSON := args[1]
domainID := args[2]
token := args[3]
members := struct {
Members []string `json:"members"`
}{}
if err := json.Unmarshal([]byte(membersJSON), &members); err != nil {
logErrorCmd(*cmd, err)
return
}
memb, err := sdk.AddGroupRoleMembers(cmd.Context(), groupID, roleID, domainID, members.Members, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, memb)
}
func handleGroupRoleMembersList(cmd *cobra.Command, groupID string, args []string) {
if len(args) != 3 {
logUsageCmd(*cmd, usageGroupRoleMembersList)
return
}
roleID := args[0]
domainID := args[1]
token := args[2]
pageMetadata := smqsdk.PageMetadata{
Offset: Offset,
Limit: Limit,
}
l, err := sdk.GroupRoleMembers(cmd.Context(), groupID, roleID, domainID, pageMetadata, token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, l)
}
func handleGroupRoleMembersDelete(cmd *cobra.Command, groupID string, args []string) {
if len(args) != 4 {
logUsageCmd(*cmd, usageGroupRoleMembersDelete)
return
}
roleID := args[0]
membersJSON := args[1]
domainID := args[2]
token := args[3]
if membersJSON == all {
if err := sdk.RemoveAllGroupRoleMembers(cmd.Context(), groupID, roleID, domainID, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
return
}
members := struct {
Members []string `json:"members"`
}{}
if err := json.Unmarshal([]byte(membersJSON), &members); err != nil {
logErrorCmd(*cmd, err)
return
}
if err := sdk.RemoveGroupRoleMembers(cmd.Context(), groupID, roleID, domainID, members.Members, token); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
-1325
View File
File diff suppressed because it is too large Load Diff
-87
View File
@@ -1,87 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli_test
import (
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/absmach/magistrala/cli"
"github.com/absmach/magistrala/pkg/errors"
mgsdk "github.com/absmach/magistrala/pkg/sdk"
sdkmocks "github.com/absmach/magistrala/pkg/sdk/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func TestHealthCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
healthCmd := cli.NewHealthCmd()
rootCmd := setFlags(healthCmd)
service := "users"
var health mgsdk.HealthInfo
cases := []struct {
desc string
args []string
logType outputLog
errLogMessage string
health mgsdk.HealthInfo
sdkErr errors.SDKError
}{
{
desc: "Check health successfully",
args: []string{
service,
},
logType: entityLog,
health: mgsdk.HealthInfo{
Status: "pass",
Description: "users service",
},
},
{
desc: "Check health with invalid args",
args: []string{
service,
extraArg,
},
logType: usageLog,
},
{
desc: "Check health with invalid service",
args: []string{
"invalid",
},
sdkErr: errors.NewSDKErrorWithStatus(errors.New("unsupported protocol scheme"), 306),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(errors.New("unsupported protocol scheme"), 306)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("Health", mock.Anything).Return(tc.health, tc.sdkErr)
out := executeCommand(t, rootCmd, tc.args...)
switch tc.logType {
case entityLog:
err := json.Unmarshal([]byte(out), &health)
assert.Nil(t, err)
assert.Equal(t, tc.health, health, fmt.Sprintf("%s unexpected response: expected: %v, got: %v", tc.desc, tc.health, health))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.True(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
-416
View File
@@ -1,416 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli_test
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"testing"
"github.com/absmach/magistrala/cli"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
mgsdk "github.com/absmach/magistrala/pkg/sdk"
sdkmocks "github.com/absmach/magistrala/pkg/sdk/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
var invitation = mgsdk.Invitation{
InvitedBy: testsutil.GenerateUUID(&testing.T{}),
InviteeUserID: user.ID,
DomainID: domain.ID,
}
func TestSendDomainInvitationCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
invCmd := cli.NewInvitationsCmd()
rootCmd := setFlags(invCmd)
cases := []struct {
desc string
args []string
logType outputLog
errLogMessage string
sdkErr errors.SDKError
}{
{
desc: "send domain invitation successfully",
args: []string{
user.ID,
domain.ID,
relation,
validToken,
},
logType: okLog,
},
{
desc: "send domain invitation with invalid args",
args: []string{
user.ID,
domain.ID,
relation,
validToken,
extraArg,
},
logType: usageLog,
},
{
desc: "send domain invitation with invalid token",
args: []string{
user.ID,
domain.ID,
relation,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("SendInvitation", mock.Anything, mock.Anything, mock.Anything).Return(tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{domainCmd, sendCmd}, tc.args...)...)
switch tc.logType {
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
func TestGetUserInvitationsCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
invCmd := cli.NewInvitationsCmd()
rootCmd := setFlags(invCmd)
var page mgsdk.InvitationPage
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
page mgsdk.InvitationPage
logType outputLog
errLogMessage string
}{
{
desc: "get user invitations successfully",
args: []string{
token,
},
page: mgsdk.InvitationPage{
Total: 1,
Offset: 0,
Limit: 10,
Invitations: []mgsdk.Invitation{invitation},
},
logType: entityLog,
},
{
desc: "get user invitations with invalid args",
args: []string{
token,
extraArg,
},
logType: usageLog,
},
{
desc: "get user invitations with invalid token",
args: []string{
invalidToken,
},
logType: errLog,
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("Invitations", mock.Anything, mock.Anything, tc.args[0]).Return(tc.page, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{userCmd, getCmd}, tc.args...)...)
switch tc.logType {
case entityLog:
err := json.Unmarshal([]byte(out), &page)
assert.Nil(t, err)
assert.Equal(t, tc.page, page, fmt.Sprintf("%v unexpected response, expected: %v, got: %v", tc.desc, tc.page, page))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
func TestGetDomainInvitationsCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
invCmd := cli.NewInvitationsCmd()
rootCmd := setFlags(invCmd)
var page mgsdk.InvitationPage
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
page mgsdk.InvitationPage
logType outputLog
errLogMessage string
}{
{
desc: "get domain invitations successfully",
args: []string{
domain.ID,
token,
},
page: mgsdk.InvitationPage{
Total: 1,
Offset: 0,
Limit: 10,
Invitations: []mgsdk.Invitation{invitation},
},
logType: entityLog,
},
{
desc: "get domain invitations with invalid args",
args: []string{
domain.ID,
token,
extraArg,
},
logType: usageLog,
},
{
desc: "get domain invitations with invalid token",
args: []string{
domain.ID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("DomainInvitations", mock.Anything, mock.Anything, tc.args[1], tc.args[0]).Return(tc.page, tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{domainCmd, getCmd}, tc.args...)...)
switch tc.logType {
case entityLog:
err := json.Unmarshal([]byte(out), &page)
assert.Nil(t, err)
assert.Equal(t, tc.page, page, fmt.Sprintf("%v unexpected response, expected: %v, got: %v", tc.desc, tc.page, page))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
func TestAcceptUserInvitationCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
invCmd := cli.NewInvitationsCmd()
rootCmd := setFlags(invCmd)
cases := []struct {
desc string
args []string
logType outputLog
errLogMessage string
sdkErr errors.SDKError
}{
{
desc: "accept user invitation successfully",
args: []string{
domain.ID,
validToken,
},
logType: okLog,
},
{
desc: "accept user invitation with invalid args",
args: []string{
domain.ID,
validToken,
extraArg,
},
logType: usageLog,
},
{
desc: "accept user invitation with invalid token",
args: []string{
domain.ID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("AcceptInvitation", mock.Anything, mock.Anything, mock.Anything).Return(tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{userCmd, acceptCmd}, tc.args...)...)
switch tc.logType {
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
func TestRejectUserInvitationCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
invCmd := cli.NewInvitationsCmd()
rootCmd := setFlags(invCmd)
cases := []struct {
desc string
args []string
logType outputLog
errLogMessage string
sdkErr errors.SDKError
}{
{
desc: "reject user invitation successfully",
args: []string{
domain.ID,
validToken,
},
logType: okLog,
},
{
desc: "reject user invitation with invalid args",
args: []string{
domain.ID,
validToken,
extraArg,
},
logType: usageLog,
},
{
desc: "reject user invitation with invalid token",
args: []string{
domain.ID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("RejectInvitation", mock.Anything, mock.Anything, mock.Anything).Return(tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{userCmd, rejectCmd}, tc.args...)...)
switch tc.logType {
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
func TestDeleteDomainInvitationCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
invCmd := cli.NewInvitationsCmd()
rootCmd := setFlags(invCmd)
cases := []struct {
desc string
args []string
logType outputLog
errLogMessage string
sdkErr errors.SDKError
}{
{
desc: "delete domain invitation successfully",
args: []string{
user.ID,
domain.ID,
validToken,
},
logType: okLog,
},
{
desc: "delete domain invitation with invalid args",
args: []string{
user.ID,
domain.ID,
validToken,
extraArg,
},
logType: usageLog,
},
{
desc: "delete domain invitation with invalid token",
args: []string{
user.ID,
domain.ID,
invalidToken,
},
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusUnauthorized)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("DeleteInvitation", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{domainCmd, delCmd}, tc.args...)...)
switch tc.logType {
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
-126
View File
@@ -1,126 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli_test
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"testing"
"github.com/absmach/magistrala/cli"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
mgsdk "github.com/absmach/magistrala/pkg/sdk"
sdkmocks "github.com/absmach/magistrala/pkg/sdk/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
var journal = mgsdk.Journal{
ID: testsutil.GenerateUUID(&testing.T{}),
}
func TestGetJournalCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
invCmd := cli.NewJournalCmd()
rootCmd := setFlags(invCmd)
var page mgsdk.JournalsPage
entityType := "group"
entityId := testsutil.GenerateUUID(t)
domainId := testsutil.GenerateUUID(t)
cases := []struct {
desc string
args []string
sdkErr errors.SDKError
page mgsdk.JournalsPage
logType outputLog
errLogMessage string
}{
{
desc: "get user journal",
args: []string{
"user",
entityId,
token,
},
logType: entityLog,
page: mgsdk.JournalsPage{
Total: 1,
Offset: 0,
Limit: 10,
Journals: []mgsdk.Journal{journal},
},
},
{
desc: "get group journal",
args: []string{
entityType,
entityId,
domainId,
token,
},
logType: entityLog,
page: mgsdk.JournalsPage{
Total: 1,
Offset: 0,
Limit: 10,
Journals: []mgsdk.Journal{journal},
},
},
{
desc: "get journal with invalid args",
args: []string{
entityType,
entityId,
token,
domainId,
extraArg,
},
logType: usageLog,
},
{
desc: "get journal with invalid token",
args: []string{
entityType,
entityId,
domainId,
invalidToken,
},
logType: errLog,
sdkErr: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden)),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("Journal", mock.Anything, tc.args[0], tc.args[1], "", mock.Anything, tc.args[2]).Return(tc.page, tc.sdkErr)
if tc.args[0] != "user" {
sdkCall = sdkMock.On("Journal", mock.Anything, tc.args[0], tc.args[1], tc.args[2], mock.Anything, tc.args[3]).Return(tc.page, tc.sdkErr)
}
out := executeCommand(t, rootCmd, append([]string{getCmd}, tc.args...)...)
switch tc.logType {
case entityLog:
err := json.Unmarshal([]byte(out), &page)
assert.Nil(t, err)
assert.Equal(t, tc.page, page, fmt.Sprintf("%v unexpected response, expected: %v, got: %v", tc.desc, tc.page, page))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
-89
View File
@@ -1,89 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli_test
import (
"fmt"
"net/http"
"strings"
"testing"
"github.com/absmach/magistrala/cli"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
sdkmocks "github.com/absmach/magistrala/pkg/sdk/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func TestSendMesageCmd(t *testing.T) {
sdkMock := new(sdkmocks.SDK)
cli.SetSDK(sdkMock)
messageCmd := cli.NewMessagesCmd()
rootCmd := setFlags(messageCmd)
message := "[{\"bn\":\"Dev1\",\"n\":\"temp\",\"v\":20}, {\"n\":\"hum\",\"v\":40}, {\"bn\":\"Dev2\", \"n\":\"temp\",\"v\":20}, {\"n\":\"hum\",\"v\":40}]"
cases := []struct {
desc string
args []string
logType outputLog
errLogMessage string
sdkErr errors.SDKError
}{
{
desc: "send message successfully",
args: []string{
domainID,
channel.ID,
message,
client.Credentials.Secret,
},
logType: okLog,
},
{
desc: "send message with invalid args",
args: []string{
domainID,
channel.ID,
message,
client.Credentials.Secret,
extraArg,
},
logType: usageLog,
},
{
desc: "send message with invalid client secret",
args: []string{
domainID,
channel.ID,
message,
"invalid_secret",
},
sdkErr: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrAuthentication, errors.Wrap(svcerr.ErrAuthorization, svcerr.ErrNotFound)), http.StatusBadRequest),
errLogMessage: fmt.Sprintf("\nerror: %s\n\n", errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrAuthentication, errors.Wrap(svcerr.ErrAuthorization, svcerr.ErrNotFound)), http.StatusBadRequest)),
logType: errLog,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
sdkCall := sdkMock.On("SendMessage", mock.Anything, tc.args[0], tc.args[1], tc.args[2], tc.args[3]).Return(tc.sdkErr)
out := executeCommand(t, rootCmd, append([]string{sendCmd}, tc.args...)...)
switch tc.logType {
case okLog:
assert.True(t, strings.Contains(out, "ok"), fmt.Sprintf("%s unexpected response: expected success message, got: %v", tc.desc, out))
case errLog:
assert.Equal(t, tc.errLogMessage, out, fmt.Sprintf("%s unexpected error response: expected %s got errLogMessage:%s", tc.desc, tc.errLogMessage, out))
case usageLog:
assert.False(t, strings.Contains(out, rootCmd.Use), fmt.Sprintf("%s invalid usage: %s", tc.desc, out))
}
sdkCall.Unset()
})
}
}
-115
View File
@@ -1,115 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli_test
import (
"bytes"
"testing"
"github.com/absmach/magistrala/cli"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
)
type outputLog uint8
const (
usageLog outputLog = iota
errLog
entityLog
okLog
createLog
revokeLog
)
func executeCommand(t *testing.T, root *cobra.Command, args ...string) string {
buffer := new(bytes.Buffer)
root.SetOut(buffer)
root.SetErr(buffer)
root.SetArgs(args)
err := root.Execute()
assert.NoError(t, err, "Error executing command")
return buffer.String()
}
func setFlags(rootCmd *cobra.Command) *cobra.Command {
// Root Flags
rootCmd.PersistentFlags().BoolVarP(
&cli.RawOutput,
"raw",
"r",
cli.RawOutput,
"Enables raw output mode for easier parsing of output",
)
// Client and Channels Flags
rootCmd.PersistentFlags().Uint64VarP(
&cli.Limit,
"limit",
"l",
10,
"Limit query parameter",
)
rootCmd.PersistentFlags().Uint64VarP(
&cli.Offset,
"offset",
"o",
0,
"Offset query parameter",
)
rootCmd.PersistentFlags().StringVarP(
&cli.Name,
"name",
"n",
"",
"Name query parameter",
)
rootCmd.PersistentFlags().StringVarP(
&cli.Identity,
"identity",
"I",
"",
"User identity query parameter",
)
rootCmd.PersistentFlags().StringVarP(
&cli.Metadata,
"metadata",
"m",
"",
"Metadata query parameter",
)
rootCmd.PersistentFlags().StringVarP(
&cli.Status,
"status",
"S",
"",
"User status query parameter",
)
rootCmd.PersistentFlags().StringVarP(
&cli.Topic,
"topic",
"T",
"",
"Subscription topic query parameter",
)
rootCmd.PersistentFlags().StringVarP(
&cli.Contact,
"contact",
"C",
"",
"Subscription contact query parameter",
)
return rootCmd
}
-595
View File
@@ -1,595 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cli
import (
"encoding/json"
"fmt"
"net/url"
"strconv"
smqsdk "github.com/absmach/magistrala/pkg/sdk"
smqusers "github.com/absmach/magistrala/users"
"github.com/spf13/cobra"
)
const (
token = "token"
refreshToken = "refreshtoken"
profile = "profile"
resetPasswordRequest = "resetpasswordrequest"
resetPassword = "resetpassword"
password = "password"
search = "search"
username = "username"
email = "email"
role = "role"
// Usage strings for user operations.
usageUserCreate = "cli users create <first_name> <last_name> <email> <username> <password> [user_auth_token]"
usageUserGet = "cli users <user_id|all> get <user_auth_token>"
usageUserToken = "cli users token <username> <password>"
usageUserRefreshToken = "cli users refreshtoken <token>"
usageUserUpdate = "cli users <user_id> update <JSON_string> <user_auth_token>"
usageUserUpdateTags = "cli users <user_id> update tags <tags> <user_auth_token>"
usageUserUpdateUsername = "cli users <user_id> update username <username> <user_auth_token>"
usageUserUpdateEmail = "cli users <user_id> update email <email> <user_auth_token>"
usageUserUpdateRole = "cli users <user_id> update role <role> <user_auth_token>"
usageUserUpdateAll = `cli users <user_id> update <JSON_string|tags|username|email|role> [args...]
Available update options:
cli users <user_id> update <JSON_string> <user_auth_token>
cli users <user_id> update tags <tags> <user_auth_token>
cli users <user_id> update username <username> <user_auth_token>
cli users <user_id> update email <email> <user_auth_token>
cli users <user_id> update role <role> <user_auth_token>`
usageUserProfile = "cli users profile <user_auth_token>"
usageUserResetPasswordReq = "cli users resetpasswordrequest <email>"
usageUserResetPassword = "cli users resetpassword <password> <confpass> <password_request_token>"
usageUserPassword = "cli users password <old_password> <password> <user_auth_token>"
usageUserEnable = "cli users <user_id> enable <user_auth_token>"
usageUserDisable = "cli users <user_id> disable <user_auth_token>"
usageUserDelete = "cli users <user_id> delete <user_auth_token>"
usageUserSearch = "cli users search <query> <user_auth_token>\nQuery format: username=<value>|firstname=<value>|lastname=<value>|id=<value>[&offset=<value>][&limit=<value>]\nExample: cli users search \"username=john_doe\" <user_auth_token>"
usageUserSendVerification = "cli users sendverification <user_auth_token>"
usageUserVerifyEmail = "cli users verifyemail <verification_token>"
)
func NewUsersCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "users <user_id|all|create|token|refreshtoken|profile|resetpasswordrequest|resetpassword|password|search|sendverification|verifyemail> [operation] [args...]",
Short: "Users management",
Long: `Format:
users <create|token|refreshtoken|profile|resetpasswordrequest|resetpassword|password|search|sendverification|verifyemail> [args...]
users <user_id|all> <operation> [args...]
Operations (require user_id/all): get, update, enable, disable, delete
Examples:
users create <first_name> <last_name> <email> <username> <password> [user_auth_token]
users token <username> <password>
users refreshtoken <refresh_token>
users profile <user_auth_token>
users resetpasswordrequest <email>
users resetpassword <password> <confpass> <password_request_token>
users password <old_password> <new_password> <user_auth_token>
users search "username=john_doe" <user_auth_token>
users search "firstname=john&limit=10" <user_auth_token>
users sendverification <user_auth_token>
users verifyemail <verification_token>
users all get <user_auth_token>
users <user_id> get <user_auth_token>
users <user_id> update <JSON_string> <user_auth_token>
users <user_id> update tags <tags> <user_auth_token>
users <user_id> update username <username> <user_auth_token>
users <user_id> update email <email> <user_auth_token>
users <user_id> enable <user_auth_token>
users <user_id> disable <user_auth_token>
users <user_id> delete <user_auth_token>`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
logUsageCmd(*cmd, cmd.Use)
return
}
switch args[0] {
case create:
handleUserCreate(cmd, args[1:])
return
case sendVerification:
if len(args) < 2 {
logUsageCmd(*cmd, usageUserSendVerification)
return
}
handleSendVerification(cmd, args[1])
return
case verifyEmail:
if len(args) < 2 {
logUsageCmd(*cmd, usageUserVerifyEmail)
return
}
handleVerify(cmd, args[1])
return
case token:
if len(args) < 2 {
logUsageCmd(*cmd, usageUserToken)
return
}
if len(args) < 3 {
logUsageCmd(*cmd, usageUserToken)
return
}
handleUserToken(cmd, args[1], args[2:])
return
case refreshToken:
if len(args) < 2 {
logUsageCmd(*cmd, usageUserRefreshToken)
return
}
handleUserRefreshToken(cmd, args[1], args[2:])
return
case profile:
if len(args) < 2 {
logUsageCmd(*cmd, usageUserProfile)
return
}
handleUserProfile(cmd, args[1], args[2:])
return
case resetPasswordRequest:
if len(args) < 2 {
logUsageCmd(*cmd, usageUserResetPasswordReq)
return
}
handleUserResetPasswordRequest(cmd, args[1], args[2:])
return
case resetPassword:
if len(args) < 2 {
logUsageCmd(*cmd, usageUserResetPassword)
return
}
if len(args) < 4 {
logUsageCmd(*cmd, usageUserResetPassword)
return
}
handleUserResetPassword(cmd, args[1], args[2:])
return
case password:
if len(args) < 2 {
logUsageCmd(*cmd, usageUserPassword)
return
}
if len(args) < 4 {
logUsageCmd(*cmd, usageUserPassword)
return
}
handleUserPassword(cmd, args[1], args[2:])
return
case search:
if len(args) < 2 {
logUsageCmd(*cmd, usageUserSearch)
return
}
if len(args) < 3 {
logUsageCmd(*cmd, usageUserSearch)
return
}
handleUserSearch(cmd, args[1], args[2:])
return
}
if len(args) < 2 {
logUsageCmd(*cmd, "users <user_id|all> <get|update|enable|disable|delete> [args...]")
return
}
userParams := args[0]
operation := args[1]
opArgs := args[2:]
switch operation {
case get:
handleUserGet(cmd, userParams, opArgs)
case update:
handleUserUpdate(cmd, userParams, opArgs)
case enable:
handleUserEnable(cmd, userParams, opArgs)
case disable:
handleUserDisable(cmd, userParams, opArgs)
case delete:
handleUserDelete(cmd, userParams, opArgs)
default:
logErrorCmd(*cmd, fmt.Errorf("unknown operation: %s", operation))
}
},
}
return cmd
}
func handleUserCreate(cmd *cobra.Command, args []string) {
if len(args) < 5 || len(args) > 6 {
logUsageCmd(*cmd, usageUserCreate)
return
}
if len(args) == 5 {
args = append(args, "")
}
user := smqsdk.User{
FirstName: args[0],
LastName: args[1],
Email: args[2],
Credentials: smqsdk.Credentials{
Username: args[3],
Secret: args[4],
},
Status: smqusers.EnabledStatus.String(),
}
user, err := sdk.CreateUser(cmd.Context(), user, args[5])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, user)
}
func handleSendVerification(cmd *cobra.Command, token string) {
if token == "" {
logUsageCmd(*cmd, usageUserToken)
return
}
if err := sdk.SendVerification(cmd.Context(), token); err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, "sent verification successfully")
}
func handleVerify(cmd *cobra.Command, token string) {
if token == "" {
logUsageCmd(*cmd, usageUserToken)
return
}
if err := sdk.VerifyEmail(cmd.Context(), token); err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, "verified successfully")
}
func handleUserGet(cmd *cobra.Command, userParams string, args []string) {
if len(args) != 1 {
logUsageCmd(*cmd, usageUserGet)
return
}
if userParams == all {
metadata, err := convertMetadata(Metadata)
if err != nil {
logErrorCmd(*cmd, err)
return
}
pageMetadata := smqsdk.PageMetadata{
Username: Username,
Identity: Identity,
Offset: Offset,
Limit: Limit,
Metadata: metadata,
Status: Status,
}
l, err := sdk.Users(cmd.Context(), pageMetadata, args[0])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, l)
return
}
u, err := sdk.User(cmd.Context(), userParams, args[0])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, u)
}
func handleUserUpdate(cmd *cobra.Command, userID string, args []string) {
if len(args) < 1 {
logUsageCmd(*cmd, usageUserUpdateAll)
return
}
if len(args) < 2 || len(args) > 3 {
if len(args) >= 1 {
switch args[0] {
case tags:
logUsageCmd(*cmd, usageUserUpdateTags)
return
case username:
logUsageCmd(*cmd, usageUserUpdateUsername)
return
case email:
logUsageCmd(*cmd, usageUserUpdateEmail)
return
case role:
logUsageCmd(*cmd, usageUserUpdateRole)
return
}
}
logUsageCmd(*cmd, usageUserUpdateAll)
return
}
var user smqsdk.User
if args[0] == "tags" {
if len(args) != 3 {
logUsageCmd(*cmd, usageUserUpdateTags)
return
}
if err := json.Unmarshal([]byte(args[1]), &user.Tags); err != nil {
logErrorCmd(*cmd, err)
return
}
user.ID = userID
user, err := sdk.UpdateUserTags(cmd.Context(), user, args[2])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, user)
return
}
if args[0] == "email" {
if len(args) != 3 {
logUsageCmd(*cmd, usageUserUpdateEmail)
return
}
user.ID = userID
user.Email = args[1]
user, err := sdk.UpdateUserEmail(cmd.Context(), user, args[2])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, user)
return
}
if args[0] == "username" {
if len(args) != 3 {
logUsageCmd(*cmd, usageUserUpdateUsername)
return
}
user.ID = userID
user.Credentials.Username = args[1]
user, err := sdk.UpdateUsername(cmd.Context(), user, args[2])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, user)
return
}
if args[0] == "role" {
if len(args) != 3 {
logUsageCmd(*cmd, usageUserUpdateRole)
return
}
user.ID = userID
user.Role = args[1]
user, err := sdk.UpdateUserRole(cmd.Context(), user, args[2])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, user)
return
}
if len(args) != 2 {
logUsageCmd(*cmd, usageUserUpdate)
return
}
if err := json.Unmarshal([]byte(args[0]), &user); err != nil {
logErrorCmd(*cmd, err)
return
}
user.ID = userID
user, err := sdk.UpdateUser(cmd.Context(), user, args[1])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, user)
}
func handleUserEnable(cmd *cobra.Command, userID string, args []string) {
if len(args) != 1 {
logUsageCmd(*cmd, usageUserEnable)
return
}
user, err := sdk.EnableUser(cmd.Context(), userID, args[0])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, user)
}
func handleUserDisable(cmd *cobra.Command, userID string, args []string) {
if len(args) != 1 {
logUsageCmd(*cmd, usageUserDisable)
return
}
user, err := sdk.DisableUser(cmd.Context(), userID, args[0])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, user)
}
func handleUserDelete(cmd *cobra.Command, userID string, args []string) {
if len(args) != 1 {
logUsageCmd(*cmd, usageUserDelete)
return
}
if err := sdk.DeleteUser(cmd.Context(), userID, args[0]); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleUserToken(cmd *cobra.Command, username string, args []string) {
if len(args) != 1 {
logUsageCmd(*cmd, usageUserToken)
return
}
loginReq := smqsdk.Login{
Username: username,
Password: args[0],
}
token, err := sdk.CreateToken(cmd.Context(), loginReq)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, token)
}
func handleUserRefreshToken(cmd *cobra.Command, refreshToken string, args []string) {
if len(args) != 0 {
logUsageCmd(*cmd, usageUserRefreshToken)
return
}
token, err := sdk.RefreshToken(cmd.Context(), refreshToken)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, token)
}
func handleUserProfile(cmd *cobra.Command, token string, args []string) {
if len(args) != 0 {
logUsageCmd(*cmd, usageUserProfile)
return
}
user, err := sdk.UserProfile(cmd.Context(), token)
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, user)
}
func handleUserResetPasswordRequest(cmd *cobra.Command, email string, args []string) {
if len(args) != 0 {
logUsageCmd(*cmd, usageUserResetPasswordReq)
return
}
if err := sdk.ResetPasswordRequest(cmd.Context(), email); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleUserResetPassword(cmd *cobra.Command, password string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageUserResetPassword)
return
}
if err := sdk.ResetPassword(cmd.Context(), password, args[0], args[1]); err != nil {
logErrorCmd(*cmd, err)
return
}
logOKCmd(*cmd)
}
func handleUserPassword(cmd *cobra.Command, oldPassword string, args []string) {
if len(args) != 2 {
logUsageCmd(*cmd, usageUserPassword)
return
}
user, err := sdk.UpdatePassword(cmd.Context(), oldPassword, args[0], args[1])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, user)
}
func handleUserSearch(cmd *cobra.Command, query string, args []string) {
if len(args) != 1 {
logUsageCmd(*cmd, usageUserSearch)
return
}
values, err := url.ParseQuery(query)
if err != nil {
logErrorCmd(*cmd, fmt.Errorf("failed to parse query: %s", err))
return
}
pm := smqsdk.PageMetadata{
Offset: Offset,
Limit: Limit,
ID: values.Get("id"),
Username: values.Get("username"),
FirstName: values.Get("firstname"),
LastName: values.Get("lastname"),
}
if off, err := strconv.Atoi(values.Get("offset")); err == nil {
pm.Offset = uint64(off)
}
if lim, err := strconv.Atoi(values.Get("limit")); err == nil {
pm.Limit = uint64(lim)
}
users, err := sdk.SearchUsers(cmd.Context(), pm, args[0])
if err != nil {
logErrorCmd(*cmd, err)
return
}
logJSONCmd(*cmd, users)
}
-1406
View File
File diff suppressed because it is too large Load Diff
+4 -25
View File
@@ -8,9 +8,6 @@ require (
github.com/absmach/callhome v0.18.2
github.com/absmach/fluxmq v0.30.0
github.com/absmach/senml v1.0.8
github.com/authzed/authzed-go v1.10.0
github.com/authzed/grpcutil v0.0.0-20260105210157-e237581949c2
github.com/authzed/spicedb v1.54.0
github.com/caarlos0/env/v10 v10.0.0
github.com/caarlos0/env/v11 v11.4.1
github.com/dgraph-io/ristretto/v2 v2.4.0
@@ -30,7 +27,6 @@ require (
github.com/jackc/pgx/v5 v5.10.0
github.com/jmoiron/sqlx v1.4.0
github.com/lestrrat-go/jwx/v2 v2.1.6
github.com/lib/pq v1.12.3
github.com/mitchellh/mapstructure v1.5.0
github.com/nats-io/nats.go v1.52.0
github.com/oklog/ulid/v2 v2.1.1
@@ -57,10 +53,8 @@ require (
go.opentelemetry.io/otel/sdk v1.44.0
go.opentelemetry.io/otel/trace v1.44.0
golang.org/x/crypto v0.53.0
golang.org/x/oauth2 v0.36.0
golang.org/x/sync v0.21.0
gonum.org/v1/gonum v0.17.0
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad
google.golang.org/grpc v1.81.1
google.golang.org/protobuf v1.36.11
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
@@ -69,28 +63,22 @@ require (
)
require (
github.com/lib/pq v1.12.3 // indirect
github.com/smarty/assertions v1.16.0 // indirect
github.com/smartystreets/goconvey v1.8.1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad // indirect
)
require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1 // indirect
buf.build/go/protovalidate v1.2.0 // indirect
cel.dev/expr v0.25.2 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
connectrpc.com/connect v1.20.0
dario.cat/mergo v1.0.2 // indirect
filippo.io/edwards25519 v1.2.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/authzed/cel-go v0.20.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/ccoveille/go-safecast/v2 v2.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/continuity v0.5.0 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
@@ -103,12 +91,9 @@ require (
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/golib/memfile v1.0.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect
github.com/felixge/httpsnoop v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.10.1 // indirect
github.com/fxamacker/cbor/v2 v2.9.2 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/go-kit/log v0.2.1 // indirect
@@ -118,9 +103,7 @@ require (
github.com/go-sql-driver/mysql v1.10.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/goccy/go-json v0.10.6 // indirect
github.com/google/cel-go v0.28.1 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@@ -135,7 +118,6 @@ require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jzelinskie/stringz v0.0.3 // indirect
github.com/klauspost/compress v1.18.6 // indirect
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
@@ -156,17 +138,15 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runc v1.2.8 // indirect
github.com/pelletier/go-toml/v2 v2.4.0
github.com/pelletier/go-toml/v2 v2.3.1 // indirect
github.com/pion/dtls/v3 v3.1.4 // indirect
github.com/pion/logging v0.2.4 // indirect
github.com/pion/transport/v4 v4.0.2 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.68.1 // indirect
github.com/prometheus/procfs v0.20.1 // indirect
github.com/rabbitmq/amqp091-go v1.12.0
github.com/rs/zerolog v1.35.1 // indirect
github.com/rabbitmq/amqp091-go v1.11.0
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/segmentio/asm v1.2.1 // indirect
@@ -174,7 +154,6 @@ require (
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/stoewer/go-strcase v1.3.1 // indirect
github.com/stretchr/objx v0.5.3 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
-104
View File
@@ -1,14 +1,5 @@
al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1 h1:s6hzCXtND/ICdGPTMGk7C+/BFlr2Jg5GyH0NKf4XGXg=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM=
buf.build/go/protovalidate v1.2.0 h1:DQVrUWkmGTBij+kOYv/x2LLxwcLaGKMdzShj1/6/3H0=
buf.build/go/protovalidate v1.2.0/go.mod h1:7rYiQEhqvAipoazpVNBBH2S2f8bjG4huMVy1V2Yofn4=
cel.dev/expr v0.25.2 h1:K6j46C81hXtZQfuX60cVWQFBJahKSE2gfRbNuvr5bFs=
cel.dev/expr v0.25.2/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ=
connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4=
connectrpc.com/otelconnect v0.9.0 h1:NggB3pzRC3pukQWaYbRHJulxuXvmCKCKkQ9hbrHAWoA=
@@ -42,26 +33,13 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/authzed/authzed-go v1.10.0 h1:GUPzYFnStk1PIBZOkQzqcYA3iHaOlVYVl1UnHIRofKU=
github.com/authzed/authzed-go v1.10.0/go.mod h1:2DL7pg4iqMltwWOSw+wvbEzAK7uRt3545+bkcGYD8D8=
github.com/authzed/cel-go v0.20.2 h1:GlmLecGry7Z8HU0k+hmaHHUV05ZHrsFxduXHtIePvck=
github.com/authzed/cel-go v0.20.2/go.mod h1:pJHVFWbqUHV1J+klQoZubdKswlbxcsbojda3mye9kiU=
github.com/authzed/grpcutil v0.0.0-20260105210157-e237581949c2 h1:ymPD1ugBsXVUpLIG/lnRn1ndgOrsrki/0ZX7uP/S1GI=
github.com/authzed/grpcutil v0.0.0-20260105210157-e237581949c2/go.mod h1:FLssYBs1DrwuItfI411kzqcV8QSqGb/B7PC6snNhjvU=
github.com/authzed/spicedb v1.54.0 h1:XQiFm/G/YpQktYmcLCVrQRWkkLpAjUBZa1SBoEbmd0A=
github.com/authzed/spicedb v1.54.0/go.mod h1:9McK3CNkb831IzWZ1pVsswVoqjZTyDPy3fUF7GcvvOY=
github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.40.45 h1:QN1nsY27ssD/JmW4s83qmSb+uL6DG4GmCDzjmJB4xUI=
github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4=
github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
@@ -72,15 +50,10 @@ github.com/caarlos0/env/v11 v11.4.1 h1:fYwH0sWEsBSMPG7t4e/PEfTFzrWrpjyygXyUnWiSw
github.com/caarlos0/env/v11 v11.4.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
github.com/cbroglie/mustache v1.0.1 h1:ivMg8MguXq/rrz2eu3tw6g3b16+PQhoTn6EZAhst2mw=
github.com/cbroglie/mustache v1.0.1/go.mod h1:R/RUa+SobQ14qkP4jtx5Vke5sDytONDQXNLPY/PO69g=
github.com/ccoveille/go-safecast/v2 v2.0.1 h1:2+mIu3gXtwmWelBia2kkxfB8eP4orTHDH7ClSlWkd6I=
github.com/ccoveille/go-safecast/v2 v2.0.1/go.mod h1:JIYA4CAR33blIDuE6fSwCp2sz1oOBahXnvmdBhOAABs=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s=
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -88,8 +61,6 @@ github.com/cheggaaa/pb/v3 v3.0.5/go.mod h1:X1L61/+36nz9bjIsrDU52qHKOQukUQe2Ge+Yv
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/containerd/continuity v0.5.0 h1:7a85HZpCSs+1Zps0Ee3DPSuAWY+0SJM1JNM51nlEVDg=
github.com/containerd/continuity v0.5.0/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
@@ -130,14 +101,6 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2ISgCl2W7nE=
github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds=
github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
@@ -154,8 +117,6 @@ github.com/fxamacker/cbor/v2 v2.9.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh
github.com/fxamacker/cbor/v2 v2.9.2/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-chi/chi/v5 v5.3.0 h1:halUjDxhshgXHMrao5bB8eNBXo/rnzwr8m5m36glehM=
github.com/go-chi/chi/v5 v5.3.0/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw=
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
@@ -192,13 +153,9 @@ github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx
github.com/gofrs/uuid/v5 v5.4.0 h1:EfbpCTjqMuGyq5ZJwxqzn3Cbr2d0rUZU7v5ycAk/e/0=
github.com/gofrs/uuid/v5 v5.4.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
@@ -208,9 +165,6 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/cel-go v0.28.1 h1:YWIwi77J4xIsYUwAF/iIuS6haffzIHS8yWI8glSbLWM=
github.com/google/cel-go v0.28.1/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -232,8 +186,6 @@ github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25d
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -332,9 +284,6 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jzelinskie/stringz v0.0.3 h1:0GhG3lVMYrYtIvRbxvQI6zqRTT1P1xyQlpa0FhfUXas=
github.com/jzelinskie/stringz v0.0.3/go.mod h1:hHYbgxJuNLRw91CmpuFsYEOyQqpDVFg8pvEh23vy4P0=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
@@ -430,7 +379,6 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/opencontainers/runc v1.2.8 h1:RnEICeDReapbZ5lZEgHvj7E9Q3Eex9toYmaGBsbvU5Q=
github.com/opencontainers/runc v1.2.8/go.mod h1:cC0YkmZcuvr+rtBZ6T7NBoVbMGNAdLa/21vIElJDOzI=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/ory/dockertest/v3 v3.12.0 h1:3oV9d0sDzlSQfHtIaB5k6ghUCVMVLpAY8hwrqoCyRCw=
github.com/ory/dockertest/v3 v3.12.0/go.mod h1:aKNDTva3cp8dwOWwb9cWuX84aH5akkxXRvO7KCwWVjE=
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
@@ -447,8 +395,6 @@ github.com/pion/transport/v4 v4.0.2/go.mod h1:06hFI+jCFcok2X2MekVufNZ/uzNZXivGBP
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587 h1:xzZOeCMQLA/W198ZkdVdt4EKFKJtS26B773zNU377ZY=
github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/plgd-dev/go-coap/v3 v3.5.1 h1:jRJXJAIQ2/YmjNycM+P+oabrqjpWQGl2HHuImvF3OJ8=
github.com/plgd-dev/go-coap/v3 v3.5.1/go.mod h1:kgdxil4mi3Bi9s5av/NbQeVwRJ+8N6zGHFEPy7qTRWI=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -464,7 +410,6 @@ github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
@@ -483,16 +428,12 @@ github.com/rabbitmq/amqp091-go v1.12.0 h1:V0v14Iqfs+MwHWihJt/nGS5Ulu0vw572b2Co3m
github.com/rabbitmq/amqp091-go v1.12.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
github.com/redis/go-redis/v9 v9.20.1 h1:sfCU6A8P3dXbKyWes02uxA2baehGux9dZHfEKtsTB1w=
github.com/redis/go-redis/v9 v9.20.1/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
github.com/rodaine/protogofakeit v0.1.1 h1:ZKouljuRM3A+TArppfBqnH8tGZHOwM/pjvtXe9DaXH8=
github.com/rodaine/protogofakeit v0.1.1/go.mod h1:pXn/AstBYMaSfc1/RqH3N82pBuxtWgejz1AlYpY1mI0=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI=
github.com/rs/zerolog v1.35.1/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
github.com/rubenv/sql-migrate v1.8.1 h1:EPNwCvjAowHI3TnZ+4fQu3a915OpnQoPAjTXCGOy2U0=
github.com/rubenv/sql-migrate v1.8.1/go.mod h1:BTIKBORjzyxZDS6dzoiw6eAFYJ1iNlGAtjn4LGeVjS8=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -534,8 +475,6 @@ github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/sqids/sqids-go v0.4.1 h1:eQKYzmAZbLlRwHeHYPF35QhgxwZHLnlmVj9AkIj/rrw=
github.com/sqids/sqids-go v0.4.1/go.mod h1:EMwHuPQgSNFS0A49jESTfIQS+066XQTVhukrzEPScl8=
github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs=
github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -576,8 +515,6 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 h1:noHsffKZsNfU38DwcXWEPldrTjIZ8FPNKx8mYMGnqjs=
github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7/go.mod h1:bbMEM6aU1WDF1ErA5YJ0p91652pGv140gGw4Ww3RGp8=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
github.com/yuin/gopher-lua v1.1.2 h1:yF/FjE3hD65tBbt0VXLE13HWS9h34fdzJmrWRXwobGA=
@@ -611,21 +548,17 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
@@ -645,33 +578,23 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20260611194520-c48552f49976 h1:X8Hz2ImujgbmetVuW+w2YkyZChE3cBpZi2P158rTG9M=
golang.org/x/exp v0.0.0-20260611194520-c48552f49976/go.mod h1:vnf4pv9iKZXY58sQE1L86zmNWJ4159e1RkcWiLCkeEY=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
@@ -679,22 +602,16 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -711,14 +628,12 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -748,20 +663,14 @@ golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -772,20 +681,11 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto/googleapis/api v0.0.0-20260610212136-7ab31c22f7ad h1:3iLyITS/sySRwbUKoC7ogfj2Yr1Cjs0pfaRKj5U5HEw=
google.golang.org/genproto/googleapis/api v0.0.0-20260610212136-7ab31c22f7ad/go.mod h1:KdNqO+rCIWgFumrNBSEDlDNrkrQnpkax7Tv1WxNY8V4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad h1:45WmJvIV6C2+O/jjLkPUH+F3aOj/1miDoU2DD0+NWbg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
@@ -815,18 +715,14 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=
moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE=
-300
View File
@@ -1,300 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package consumer
import (
"time"
"github.com/absmach/magistrala/channels"
"github.com/absmach/magistrala/pkg/connections"
"github.com/absmach/magistrala/pkg/errors"
"github.com/absmach/magistrala/pkg/roles"
rconsumer "github.com/absmach/magistrala/pkg/roles/rolemanager/events/consumer"
)
const layout = "2006-01-02T15:04:05.999999Z"
var (
errDecodeCreateChannelEvent = errors.New("failed to decode channel create event")
errDecodeUpdateChannelEvent = errors.New("failed to decode channel update event")
errDecodeChangeStatusChannelEvent = errors.New("failed to decode channel change status event")
errDecodeRemoveChannelEvent = errors.New("failed to decode channel remove event")
errDecodeSetParentGroupEvent = errors.New("failed to decode channel set parent event")
errDecodeRemoveParentGroupEvent = errors.New("failed to decode channel remove parent event")
errDecodeConnectEvent = errors.New("failed to decode channel connect event")
errDeocodeDisconnectEvent = errors.New("failed to decode channel disconnect event")
errID = errors.New("missing or invalid 'id'")
errDomain = errors.New("missing or invalid 'domain'")
errStatus = errors.New("missing or invalid 'status'")
errTags = errors.New("invalid 'tags'")
errConvertStatus = errors.New("failed to convert status")
errChannelIDs = errors.New("missing or invalid 'channel_ids' in connection")
errClientIDs = errors.New("missing or invalid 'client_ids' in connection")
errConnType = errors.New("missing or invalid 'type' in connection")
errCreatedAt = errors.New("failed to parse 'created_at' time")
errUpdatedAt = errors.New("failed to parse 'updated_at' time")
)
func ToChannel(data map[string]any) (channels.Channel, error) {
var c channels.Channel
id, ok := data["id"].(string)
if !ok {
return channels.Channel{}, errID
}
c.ID = id
dom, ok := data["domain"].(string)
if !ok {
return channels.Channel{}, errDomain
}
c.Domain = dom
st, ok := data["status"].(string)
if !ok {
return channels.Channel{}, errStatus
}
status, err := channels.ToStatus(st)
if err != nil {
return channels.Channel{}, errConvertStatus
}
c.Status = status
cat, ok := data["created_at"].(string)
if !ok {
return channels.Channel{}, errCreatedAt
}
ct, err := time.Parse(layout, cat)
if err != nil {
return channels.Channel{}, errors.Wrap(errCreatedAt, err)
}
c.CreatedAt = ct
// Following fields of channels are allowed to be empty.
name, ok := data["name"].(string)
if ok {
c.Name = name
}
parent, ok := data["parent_group_id"].(string)
if ok {
c.ParentGroup = parent
}
itags, ok := data["tags"].([]any)
if ok {
tags, err := rconsumer.ToStrings(itags)
if err != nil {
return channels.Channel{}, errors.Wrap(errTags, err)
}
c.Tags = tags
}
meta, ok := data["metadata"].(map[string]any)
if ok {
c.Metadata = meta
}
uby, ok := data["updated_by"].(string)
if ok {
c.UpdatedBy = uby
}
uat, ok := data["updated_at"].(string)
if ok {
ut, err := time.Parse(layout, uat)
if err != nil {
return channels.Channel{}, errors.Wrap(errUpdatedAt, err)
}
c.UpdatedAt = ut
}
return c, nil
}
func ToConnections(data map[string]any) ([]channels.Connection, error) {
var connTypes []connections.ConnType
domain, ok := data["domain"].(string)
if !ok {
return nil, errDomain
}
ityp, ok := data["types"].([]any)
if !ok {
return nil, errConnType
}
typs, err := rconsumer.ToStrings(ityp)
if err != nil {
return nil, errors.Wrap(errConnType, err)
}
for _, typ := range typs {
connType, err := connections.ParseConnType(typ)
if err != nil {
return nil, errors.Wrap(errConnType, err)
}
connTypes = append(connTypes, connType)
}
ichanIDs, ok := data["channel_ids"].([]any)
if !ok {
return []channels.Connection{}, errChannelIDs
}
channelIDs, err := rconsumer.ToStrings(ichanIDs)
if err != nil {
return []channels.Connection{}, errors.Wrap(errChannelIDs, err)
}
iclIDs, ok := data["client_ids"].([]any)
if !ok {
return []channels.Connection{}, errClientIDs
}
clientIDs, err := rconsumer.ToStrings(iclIDs)
if err != nil {
return []channels.Connection{}, errors.Wrap(errClientIDs, err)
}
var conns []channels.Connection
for _, chanID := range channelIDs {
for _, clientID := range clientIDs {
for _, connType := range connTypes {
conns = append(conns, channels.Connection{
ChannelID: chanID,
ClientID: clientID,
Type: connType,
DomainID: domain,
})
}
}
}
return conns, nil
}
func decodeCreateChannelEvent(data map[string]any) (channels.Channel, []roles.RoleProvision, error) {
c, err := ToChannel(data)
if err != nil {
return channels.Channel{}, []roles.RoleProvision{}, errors.Wrap(errDecodeCreateChannelEvent, err)
}
irps, ok := data["roles_provisioned"].([]any)
if !ok {
return channels.Channel{}, []roles.RoleProvision{}, errors.Wrap(errDecodeCreateChannelEvent, errors.New("missing or invalid 'roles_provisioned'"))
}
rps, err := rconsumer.ToRoleProvisions(irps)
if err != nil {
return channels.Channel{}, []roles.RoleProvision{}, errors.Wrap(errDecodeCreateChannelEvent, err)
}
return c, rps, nil
}
func decodeUpdateChannelEvent(data map[string]any) (channels.Channel, error) {
c, err := ToChannel(data)
if err != nil {
return channels.Channel{}, errors.Wrap(errDecodeUpdateChannelEvent, err)
}
return c, nil
}
func decodeChangeStatusChannelEvent(data map[string]any) (channels.Channel, error) {
c, err := ToChannelStatus(data)
if err != nil {
return channels.Channel{}, errors.Wrap(errDecodeChangeStatusChannelEvent, err)
}
return c, nil
}
func ToChannelStatus(data map[string]any) (channels.Channel, error) {
var c channels.Channel
id, ok := data["id"].(string)
if !ok {
return channels.Channel{}, errID
}
c.ID = id
stat, ok := data["status"].(string)
if !ok {
return channels.Channel{}, errStatus
}
st, err := channels.ToStatus(stat)
if err != nil {
return channels.Channel{}, errors.Wrap(errConvertStatus, err)
}
c.Status = st
uat, ok := data["updated_at"].(string)
if ok {
ut, err := time.Parse(layout, uat)
if err != nil {
return channels.Channel{}, errors.Wrap(errUpdatedAt, err)
}
c.UpdatedAt = ut
}
uby, ok := data["updated_by"].(string)
if ok {
c.UpdatedBy = uby
}
return c, nil
}
func decodeRemoveChannelEvent(data map[string]any) (channels.Channel, error) {
var c channels.Channel
id, ok := data["id"].(string)
if !ok {
return channels.Channel{}, errors.Wrap(errDecodeRemoveChannelEvent, errID)
}
c.ID = id
return c, nil
}
func decodeConnectEvent(data map[string]any) ([]channels.Connection, error) {
conns, err := ToConnections(data)
if err != nil {
return []channels.Connection{}, errors.Wrap(errDecodeConnectEvent, err)
}
return conns, nil
}
func decodeDisconnectEvent(data map[string]any) ([]channels.Connection, error) {
conns, err := ToConnections(data)
if err != nil {
return []channels.Connection{}, errors.Wrap(errDeocodeDisconnectEvent, err)
}
return conns, nil
}
func decodeSetParentGroupEvent(data map[string]any) (channels.Channel, error) {
id, ok := data["id"].(string)
if !ok {
return channels.Channel{}, errors.Wrap(errDecodeSetParentGroupEvent, errID)
}
parent, ok := data["parent_group_id"].(string)
if !ok {
return channels.Channel{}, errors.Wrap(errDecodeSetParentGroupEvent, errID)
}
return channels.Channel{
ID: id,
ParentGroup: parent,
}, nil
}
func decodeRemoveParentGroupEvent(data map[string]any) (channels.Channel, error) {
id, ok := data["id"].(string)
if !ok {
return channels.Channel{}, errors.Wrap(errDecodeRemoveParentGroupEvent, errID)
}
return channels.Channel{
ID: id,
}, nil
}
-9
View File
@@ -1,9 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Package consumer contains events consumer for events
// published by channels service.
package consumer
-220
View File
@@ -1,220 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package consumer
import (
"context"
"log/slog"
"github.com/absmach/magistrala/channels"
"github.com/absmach/magistrala/pkg/errors"
"github.com/absmach/magistrala/pkg/events"
"github.com/absmach/magistrala/pkg/events/store"
rconsumer "github.com/absmach/magistrala/pkg/roles/rolemanager/events/consumer"
)
const (
stream = "events.magistrala.channel.*"
create = "channel.create"
update = "channel.update"
updateTags = "channel.update_tags"
enable = "channel.enable"
disable = "channel.disable"
remove = "channel.remove"
connect = "channel.connect"
disconnect = "channel.disconnect"
setParentGroup = "channel.set_parent"
removeParentGroup = "channel.remove_parent"
)
var (
errNoOperationKey = errors.New("operation key is not found in event message")
errCreateChannelEvent = errors.New("failed to consume channel create event")
errUpdateChannelEvent = errors.New("failed to consume channel update event")
errChangeStatusChannelEvent = errors.New("failed to consume channel change status event")
errRemoveChannelEvent = errors.New("failed to consume channel remove event")
errConnectEvent = errors.New("failed to consume channel connect event")
errDisconnectEvent = errors.New("failed to consume channel disconnect event")
errSetParentGroupEvent = errors.New("failed to consume channel add parent group event")
errRemoveParentGroupEvent = errors.New("failed to consume channel remove parent group event")
)
type eventHandler struct {
repo channels.Repository
rolesEventHandler rconsumer.EventHandler
}
func ChannelsEventsSubscribe(ctx context.Context, repo channels.Repository, esURL, esConsumerName string, logger *slog.Logger) error {
subscriber, err := store.NewSubscriber(ctx, esURL, "channels-es-sub", logger)
if err != nil {
return err
}
subConfig := events.SubscriberConfig{
Stream: stream,
Consumer: esConsumerName,
Handler: NewEventHandler(repo),
Ordered: true,
}
return subscriber.Subscribe(ctx, subConfig)
}
// NewEventHandler returns new event store handler.
func NewEventHandler(repo channels.Repository) events.EventHandler {
reh := rconsumer.NewEventHandler("channel", repo)
return &eventHandler{
repo: repo,
rolesEventHandler: reh,
}
}
func (es *eventHandler) Handle(ctx context.Context, event events.Event) error {
msg, err := event.Encode()
if err != nil {
return err
}
op, ok := msg["operation"]
if !ok {
return errNoOperationKey
}
switch op {
case create:
return es.createChannelHandler(ctx, msg)
case update:
return es.updateChannelHandler(ctx, msg)
case updateTags:
return es.updateChannelTagsHandler(ctx, msg)
case enable, disable:
return es.changeStatusChannelHandler(ctx, msg)
case remove:
return es.removeChannelHandler(ctx, msg)
case connect:
return es.connectChannelHandler(ctx, msg)
case disconnect:
return es.disconnectChannelHandler(ctx, msg)
case setParentGroup:
return es.setParentGroupHandler(ctx, msg)
case removeParentGroup:
return es.removeParentGroupHandler(ctx, msg)
}
return es.rolesEventHandler.Handle(ctx, op, msg)
}
func (es *eventHandler) createChannelHandler(ctx context.Context, data map[string]any) error {
c, rps, err := decodeCreateChannelEvent(data)
if err != nil {
return errors.Wrap(errCreateChannelEvent, err)
}
if _, err := es.repo.Save(ctx, c); err != nil {
return errors.Wrap(errCreateChannelEvent, err)
}
if _, err := es.repo.AddRoles(ctx, rps); err != nil {
return errors.Wrap(errCreateChannelEvent, err)
}
return nil
}
func (es *eventHandler) updateChannelHandler(ctx context.Context, data map[string]any) error {
c, err := decodeUpdateChannelEvent(data)
if err != nil {
return errors.Wrap(errUpdateChannelEvent, err)
}
if _, err := es.repo.Update(ctx, c); err != nil {
return errors.Wrap(errUpdateChannelEvent, err)
}
return nil
}
func (es *eventHandler) updateChannelTagsHandler(ctx context.Context, data map[string]any) error {
c, err := decodeUpdateChannelEvent(data)
if err != nil {
return errors.Wrap(errUpdateChannelEvent, err)
}
if _, err := es.repo.UpdateTags(ctx, c); err != nil {
return errors.Wrap(errUpdateChannelEvent, err)
}
return nil
}
func (es *eventHandler) changeStatusChannelHandler(ctx context.Context, data map[string]any) error {
c, err := decodeChangeStatusChannelEvent(data)
if err != nil {
return errors.Wrap(errChangeStatusChannelEvent, err)
}
if _, err := es.repo.ChangeStatus(ctx, c); err != nil {
return errors.Wrap(errChangeStatusChannelEvent, err)
}
return nil
}
func (es *eventHandler) removeChannelHandler(ctx context.Context, data map[string]any) error {
c, err := decodeRemoveChannelEvent(data)
if err != nil {
return errors.Wrap(errRemoveChannelEvent, err)
}
if err := es.repo.Remove(ctx, c.ID); err != nil {
return errors.Wrap(errRemoveChannelEvent, err)
}
return nil
}
func (es *eventHandler) connectChannelHandler(ctx context.Context, data map[string]any) error {
c, err := decodeConnectEvent(data)
if err != nil {
return errors.Wrap(errConnectEvent, err)
}
if err := es.repo.AddConnections(ctx, c); err != nil {
return errors.Wrap(errConnectEvent, err)
}
return nil
}
func (es *eventHandler) disconnectChannelHandler(ctx context.Context, data map[string]any) error {
c, err := decodeDisconnectEvent(data)
if err != nil {
return errors.Wrap(errDisconnectEvent, err)
}
if err := es.repo.RemoveConnections(ctx, c); err != nil {
return errors.Wrap(errDisconnectEvent, err)
}
return nil
}
func (es *eventHandler) setParentGroupHandler(ctx context.Context, data map[string]any) error {
c, err := decodeSetParentGroupEvent(data)
if err != nil {
return errors.Wrap(errSetParentGroupEvent, err)
}
if err := es.repo.SetParentGroup(ctx, c); err != nil {
return errors.Wrap(errSetParentGroupEvent, err)
}
return nil
}
func (es *eventHandler) removeParentGroupHandler(ctx context.Context, data map[string]any) error {
c, err := decodeRemoveParentGroupEvent(data)
if err != nil {
return errors.Wrap(errRemoveParentGroupEvent, err)
}
if err := es.repo.RemoveParentGroup(ctx, c); err != nil {
return errors.Wrap(errRemoveParentGroupEvent, err)
}
return nil
}
-228
View File
@@ -1,228 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package consumer
import (
"time"
"github.com/absmach/magistrala/clients"
"github.com/absmach/magistrala/pkg/errors"
"github.com/absmach/magistrala/pkg/roles"
rconsumer "github.com/absmach/magistrala/pkg/roles/rolemanager/events/consumer"
)
const layout = "2006-01-02T15:04:05.999999Z"
var (
errDecodeCreateClientEvent = errors.New("failed to decode client create event")
errDecodeUpdateClientEvent = errors.New("failed to decode client update event")
errDecodeChangeStatusClientEvent = errors.New("failed to decode client change status event")
errDecodeRemoveClientEvent = errors.New("failed to decode client remove event")
errDecodeSetParentGroupEvent = errors.New("failed to decode client set parent event")
errDecodeRemoveParentGroupEvent = errors.New("failed to decode client remove parent event")
errID = errors.New("missing or invalid 'id'")
errDomain = errors.New("missing or invalid 'domain'")
errStatus = errors.New("missing or invalid 'status'")
errTags = errors.New("invalid 'tags'")
errConvertStatus = errors.New("failed to convert status")
errCreatedAt = errors.New("failed to parse 'created_at' time")
errUpdatedAt = errors.New("failed to parse 'updated_at' time")
)
func ToClient(data map[string]any) (clients.Client, error) {
var c clients.Client
id, ok := data["id"].(string)
if !ok {
return clients.Client{}, errID
}
c.ID = id
dom, ok := data["domain"].(string)
if !ok {
return clients.Client{}, errDomain
}
c.Domain = dom
st, ok := data["status"].(string)
if !ok {
return clients.Client{}, errStatus
}
status, err := clients.ToStatus(st)
if err != nil {
return clients.Client{}, errConvertStatus
}
c.Status = status
cat, ok := data["created_at"].(string)
if !ok {
return clients.Client{}, errCreatedAt
}
ct, err := time.Parse(layout, cat)
if err != nil {
return clients.Client{}, errors.Wrap(errCreatedAt, err)
}
c.CreatedAt = ct
// Following fields of clients are allowed to be empty.
name, ok := data["name"].(string)
if ok {
c.Name = name
}
identity, ok := data["identity"].(string)
if ok {
c.Identity = identity
}
parent, ok := data["parent_group_id"].(string)
if ok {
c.ParentGroup = parent
}
itags, ok := data["tags"].([]any)
if ok {
tags, err := rconsumer.ToStrings(itags)
if err != nil {
return clients.Client{}, errors.Wrap(errTags, err)
}
c.Tags = tags
}
meta, ok := data["metadata"].(map[string]any)
if ok {
c.Metadata = meta
}
pmeta, ok := data["private_metadata"].(map[string]any)
if ok {
c.PrivateMetadata = pmeta
}
uby, ok := data["updated_by"].(string)
if ok {
c.UpdatedBy = uby
}
uat, ok := data["updated_at"].(string)
if ok {
ut, err := time.Parse(layout, uat)
if err != nil {
return clients.Client{}, errors.Wrap(errUpdatedAt, err)
}
c.UpdatedAt = ut
}
return c, nil
}
func decodeCreateClientEvent(data map[string]any) (clients.Client, []roles.RoleProvision, error) {
c, err := ToClient(data)
if err != nil {
return clients.Client{}, []roles.RoleProvision{}, errors.Wrap(errDecodeCreateClientEvent, err)
}
irps, ok := data["roles_provisioned"].([]any)
if !ok {
return clients.Client{}, []roles.RoleProvision{}, errors.Wrap(errDecodeCreateClientEvent, errors.New("missing or invalid 'roles_provisioned'"))
}
rps, err := rconsumer.ToRoleProvisions(irps)
if err != nil {
return clients.Client{}, []roles.RoleProvision{}, errors.Wrap(errDecodeCreateClientEvent, err)
}
return c, rps, nil
}
func decodeUpdateClientEvent(data map[string]any) (clients.Client, error) {
c, err := ToClient(data)
if err != nil {
return clients.Client{}, errors.Wrap(errDecodeUpdateClientEvent, err)
}
return c, nil
}
func decodeChangeStatusClientEvent(data map[string]any) (clients.Client, error) {
c, err := ToClientStatus(data)
if err != nil {
return clients.Client{}, errors.Wrap(errDecodeChangeStatusClientEvent, err)
}
return c, nil
}
func ToClientStatus(data map[string]any) (clients.Client, error) {
var c clients.Client
id, ok := data["id"].(string)
if !ok {
return clients.Client{}, errID
}
c.ID = id
stat, ok := data["status"].(string)
if !ok {
return clients.Client{}, errStatus
}
st, err := clients.ToStatus(stat)
if err != nil {
return clients.Client{}, errors.Wrap(errConvertStatus, err)
}
c.Status = st
uat, ok := data["updated_at"].(string)
if ok {
ut, err := time.Parse(layout, uat)
if err != nil {
return clients.Client{}, errors.Wrap(errUpdatedAt, err)
}
c.UpdatedAt = ut
}
uby, ok := data["updated_by"].(string)
if ok {
c.UpdatedBy = uby
}
return c, nil
}
func decodeRemoveClientEvent(data map[string]any) (clients.Client, error) {
var c clients.Client
id, ok := data["id"].(string)
if !ok {
return clients.Client{}, errors.Wrap(errDecodeRemoveClientEvent, errID)
}
c.ID = id
return c, nil
}
func decodeSetParentGroupEvent(data map[string]any) (clients.Client, error) {
id, ok := data["id"].(string)
if !ok {
return clients.Client{}, errors.Wrap(errDecodeSetParentGroupEvent, errID)
}
parent, ok := data["parent_group_id"].(string)
if !ok {
return clients.Client{}, errors.Wrap(errDecodeSetParentGroupEvent, errID)
}
return clients.Client{
ID: id,
ParentGroup: parent,
}, nil
}
func decodeRemoveParentGroupEvent(data map[string]any) (clients.Client, error) {
id, ok := data["id"].(string)
if !ok {
return clients.Client{}, errors.Wrap(errDecodeRemoveParentGroupEvent, errID)
}
return clients.Client{
ID: id,
}, nil
}
-9
View File
@@ -1,9 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Package consumer contains events consumer for events
// published by clients service.
package consumer
-190
View File
@@ -1,190 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package consumer
import (
"context"
"log/slog"
"github.com/absmach/magistrala/clients"
"github.com/absmach/magistrala/pkg/errors"
"github.com/absmach/magistrala/pkg/events"
"github.com/absmach/magistrala/pkg/events/store"
rconsumer "github.com/absmach/magistrala/pkg/roles/rolemanager/events/consumer"
)
const (
stream = "events.magistrala.client.*"
create = "client.create"
update = "client.update"
updateTags = "client.update_tags"
enable = "client.enable"
disable = "client.disable"
remove = "client.remove"
setParentGroup = "client.set_parent"
removeParentGroup = "client.remove_parent"
)
var (
errNoOperationKey = errors.New("operation key is not found in event message")
errCreateClientEvent = errors.New("failed to consume client create event")
errUpdateClientEvent = errors.New("failed to consume client update event")
errChangeStatusClientEvent = errors.New("failed to consume client change status event")
errRemoveClientEvent = errors.New("failed to consume client remove event")
errSetParentGroupEvent = errors.New("failed to consume client add parent group event")
errRemoveParentGroupEvent = errors.New("failed to consume client remove parent group event")
)
type eventHandler struct {
repo clients.Repository
rolesEventHandler rconsumer.EventHandler
}
func ClientsEventsSubscribe(ctx context.Context, repo clients.Repository, esURL, esConsumerName string, logger *slog.Logger) error {
subscriber, err := store.NewSubscriber(ctx, esURL, "clients-es-sub", logger)
if err != nil {
return err
}
subConfig := events.SubscriberConfig{
Stream: stream,
Consumer: esConsumerName,
Handler: NewEventHandler(repo),
Ordered: true,
}
return subscriber.Subscribe(ctx, subConfig)
}
// NewEventHandler returns new event store handler.
func NewEventHandler(repo clients.Repository) events.EventHandler {
reh := rconsumer.NewEventHandler("client", repo)
return &eventHandler{
repo: repo,
rolesEventHandler: reh,
}
}
func (es *eventHandler) Handle(ctx context.Context, event events.Event) error {
msg, err := event.Encode()
if err != nil {
return err
}
op, ok := msg["operation"]
if !ok {
return errNoOperationKey
}
switch op {
case create:
return es.createClientHandler(ctx, msg)
case update:
return es.updateClientHandler(ctx, msg)
case updateTags:
return es.updateClientTagsHandler(ctx, msg)
case enable, disable:
return es.changeStatusClientHandler(ctx, msg)
case remove:
return es.removeClientHandler(ctx, msg)
case setParentGroup:
return es.setParentGroupHandler(ctx, msg)
case removeParentGroup:
return es.removeParentGroupHandler(ctx, msg)
}
return es.rolesEventHandler.Handle(ctx, op, msg)
}
func (es *eventHandler) createClientHandler(ctx context.Context, data map[string]any) error {
c, rps, err := decodeCreateClientEvent(data)
if err != nil {
return errors.Wrap(errCreateClientEvent, err)
}
if _, err := es.repo.Save(ctx, c); err != nil {
return errors.Wrap(errCreateClientEvent, err)
}
if _, err := es.repo.AddRoles(ctx, rps); err != nil {
return errors.Wrap(errCreateClientEvent, err)
}
return nil
}
func (es *eventHandler) updateClientHandler(ctx context.Context, data map[string]any) error {
c, err := decodeUpdateClientEvent(data)
if err != nil {
return errors.Wrap(errUpdateClientEvent, err)
}
if _, err := es.repo.Update(ctx, c); err != nil {
return errors.Wrap(errUpdateClientEvent, err)
}
return nil
}
func (es *eventHandler) updateClientTagsHandler(ctx context.Context, data map[string]any) error {
c, err := decodeUpdateClientEvent(data)
if err != nil {
return errors.Wrap(errUpdateClientEvent, err)
}
if _, err := es.repo.UpdateTags(ctx, c); err != nil {
return errors.Wrap(errUpdateClientEvent, err)
}
return nil
}
func (es *eventHandler) changeStatusClientHandler(ctx context.Context, data map[string]any) error {
c, err := decodeChangeStatusClientEvent(data)
if err != nil {
return errors.Wrap(errChangeStatusClientEvent, err)
}
if _, err := es.repo.ChangeStatus(ctx, c); err != nil {
return errors.Wrap(errChangeStatusClientEvent, err)
}
return nil
}
func (es *eventHandler) removeClientHandler(ctx context.Context, data map[string]any) error {
c, err := decodeRemoveClientEvent(data)
if err != nil {
return errors.Wrap(errRemoveClientEvent, err)
}
if err := es.repo.Delete(ctx, c.ID); err != nil {
return errors.Wrap(errRemoveClientEvent, err)
}
return nil
}
func (es *eventHandler) setParentGroupHandler(ctx context.Context, data map[string]any) error {
c, err := decodeSetParentGroupEvent(data)
if err != nil {
return errors.Wrap(errSetParentGroupEvent, err)
}
if err := es.repo.SetParentGroup(ctx, c); err != nil {
return errors.Wrap(errSetParentGroupEvent, err)
}
return nil
}
func (es *eventHandler) removeParentGroupHandler(ctx context.Context, data map[string]any) error {
c, err := decodeRemoveParentGroupEvent(data)
if err != nil {
return errors.Wrap(errRemoveParentGroupEvent, err)
}
if err := es.repo.RemoveParentGroup(ctx, c); err != nil {
return errors.Wrap(errRemoveParentGroupEvent, err)
}
return nil
}
-274
View File
@@ -1,274 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package consumer
import (
"fmt"
"time"
"github.com/absmach/magistrala/domains"
"github.com/absmach/magistrala/pkg/errors"
"github.com/absmach/magistrala/pkg/roles"
rconsumer "github.com/absmach/magistrala/pkg/roles/rolemanager/events/consumer"
)
const (
layout = "2006-01-02T15:04:05.999999Z"
)
var (
errDecodeCreateDomainEvent = errors.New("failed to decode domain create event")
errDecodeUpdateDomainEvent = errors.New("failed to decode domain update event")
errDecodeEnableDomainEvent = errors.New("failed to decode domain enable event")
errDecodeDisableDomainEvent = errors.New("failed to decode domain disable event")
errDecodeFreezeDomainEvent = errors.New("failed to decode domain freeze event")
errDecodeRemoveDomainsEvent = errors.New("failed to decode domain remove event")
errID = errors.New("missing or invalid 'id'")
errName = errors.New("missing or invalid 'name'")
errRoute = errors.New("missing or invalid 'route'")
errTags = errors.New("invalid 'tags'")
errStatus = errors.New("missing or invalid 'status'")
errConvertStatus = errors.New("failed to convert status")
errCreatedBy = errors.New("missing or invalid 'created_by'")
errCreatedAt = errors.New("failed to parse 'created_at' time")
errUpdatedAt = errors.New("failed to parse 'updated_at' time")
)
func ToDomains(data map[string]any) (domains.Domain, error) {
var d domains.Domain
id, ok := data["id"].(string)
if !ok {
return domains.Domain{}, errID
}
d.ID = id
name, ok := data["name"].(string)
if !ok {
return domains.Domain{}, errName
}
d.Name = name
stat, ok := data["status"].(string)
if !ok {
return domains.Domain{}, errStatus
}
st, err := domains.ToStatus(stat)
if err != nil {
return domains.Domain{}, errors.Wrap(errConvertStatus, err)
}
d.Status = st
route, ok := data["route"].(string)
if !ok {
return domains.Domain{}, errRoute
}
d.Route = route
cby, ok := data["created_by"].(string)
if !ok {
return domains.Domain{}, errCreatedBy
}
d.CreatedBy = cby
cat, ok := data["created_at"].(string)
if !ok {
return domains.Domain{}, errCreatedAt
}
ct, err := time.Parse(layout, cat)
if err != nil {
return domains.Domain{}, errors.Wrap(errCreatedAt, err)
}
d.CreatedAt = ct
// Following fields of groups are allowed to be empty.
itags, ok := data["tags"].([]any)
if ok {
tags, err := rconsumer.ToStrings(itags)
if err != nil {
return domains.Domain{}, errors.Wrap(errTags, err)
}
d.Tags = tags
}
meta, ok := data["metadata"].(map[string]any)
if ok {
d.Metadata = meta
}
uby, ok := data["updated_by"].(string)
if ok {
d.UpdatedBy = uby
}
uat, ok := data["updated_at"].(string)
if ok {
ut, err := time.Parse(layout, uat)
if err != nil {
return domains.Domain{}, errors.Wrap(errUpdatedAt, err)
}
d.UpdatedAt = ut
}
return d, nil
}
func decodeCreateDomainEvent(data map[string]any) (domains.Domain, []roles.RoleProvision, error) {
d, err := ToDomains(data)
if err != nil {
return domains.Domain{}, []roles.RoleProvision{}, errors.Wrap(errDecodeCreateDomainEvent, err)
}
irps, ok := data["roles_provisioned"].([]any)
if !ok {
return domains.Domain{}, []roles.RoleProvision{}, errors.Wrap(errDecodeCreateDomainEvent, errors.New("missing or invalid 'roles_provisioned'"))
}
rps, err := rconsumer.ToRoleProvisions(irps)
if err != nil {
return domains.Domain{}, []roles.RoleProvision{}, errors.Wrap(errDecodeCreateDomainEvent, err)
}
return d, rps, nil
}
func decodeUpdateDomainEvent(data map[string]any) (domains.Domain, error) {
var d domains.Domain
id, ok := data["id"].(string)
if !ok {
return domains.Domain{}, errors.Wrap(errDecodeUpdateDomainEvent, errID)
}
d.ID = id
name, ok := data["name"].(string)
if ok {
d.Name = name
}
route, ok := data["route"].(string)
if ok {
d.Route = route
}
itags, ok := data["tags"].([]any)
if ok {
tags, err := rconsumer.ToStrings(itags)
if err != nil {
return domains.Domain{}, errors.Wrap(errDecodeUpdateDomainEvent, err)
}
d.Tags = tags
}
meta, ok := data["metadata"].(map[string]any)
if ok {
d.Metadata = meta
}
uby, ok := data["updated_by"].(string)
if ok {
d.UpdatedBy = uby
}
uat, ok := data["updated_at"].(string)
if ok {
ut, err := time.Parse(layout, uat)
if err != nil {
return domains.Domain{}, errors.Wrap(errDecodeUpdateDomainEvent, errors.Wrap(errUpdatedAt, err))
}
d.UpdatedAt = ut
}
return d, nil
}
func decodeEnableDomainEvent(data map[string]any) (domains.Domain, error) {
var d domains.Domain
id, ok := data["id"].(string)
if !ok {
return domains.Domain{}, errors.Wrap(errDecodeEnableDomainEvent, errID)
}
d.ID = id
uby, ok := data["updated_by"].(string)
if ok {
d.UpdatedBy = uby
}
uat, ok := data["updated_at"].(string)
if ok {
ut, err := time.Parse(layout, uat)
if err != nil {
return domains.Domain{}, errors.Wrap(errDecodeEnableDomainEvent, errors.Wrap(errUpdatedAt, err))
}
d.UpdatedAt = ut
}
return d, nil
}
func decodeDisableDomainEvent(data map[string]any) (domains.Domain, error) {
var d domains.Domain
id, ok := data["id"].(string)
if !ok {
return domains.Domain{}, errors.Wrap(errDecodeDisableDomainEvent, errID)
}
d.ID = id
uby, ok := data["updated_by"].(string)
if ok {
d.UpdatedBy = uby
}
uat, ok := data["updated_at"].(string)
if ok {
ut, err := time.Parse(layout, uat)
if err != nil {
return domains.Domain{}, errors.Wrap(errDecodeDisableDomainEvent, errors.Wrap(errUpdatedAt, err))
}
d.UpdatedAt = ut
}
return d, nil
}
func decodeFreezeDomainEvent(data map[string]any) (domains.Domain, error) {
var d domains.Domain
id, ok := data["id"].(string)
if !ok {
return domains.Domain{}, errors.Wrap(errDecodeFreezeDomainEvent, errID)
}
d.ID = id
uby, ok := data["updated_by"].(string)
if ok {
d.UpdatedBy = uby
}
uat, ok := data["updated_at"].(string)
if ok {
ut, err := time.Parse(layout, uat)
if err != nil {
return domains.Domain{}, errors.Wrap(errDecodeFreezeDomainEvent, errors.Wrap(errUpdatedAt, err))
}
d.UpdatedAt = ut
}
return d, nil
}
func decodeUserDeleteDomainEvent(_ map[string]any) (domains.Domain, error) {
return domains.Domain{}, fmt.Errorf("not implemented decode domain user delete event ")
}
func decodeDeleteDomainEvent(data map[string]any) (domains.Domain, error) {
var d domains.Domain
id, ok := data["id"].(string)
if !ok {
return domains.Domain{}, errors.Wrap(errDecodeRemoveDomainsEvent, errID)
}
d.ID = id
return d, nil
}
-7
View File
@@ -1,7 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package consumer
-207
View File
@@ -1,207 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package consumer
import (
"context"
"fmt"
"log/slog"
"github.com/absmach/magistrala/domains"
"github.com/absmach/magistrala/pkg/errors"
"github.com/absmach/magistrala/pkg/events"
"github.com/absmach/magistrala/pkg/events/store"
"github.com/absmach/magistrala/pkg/messaging"
rconsumer "github.com/absmach/magistrala/pkg/roles/rolemanager/events/consumer"
)
const (
stream = "events.magistrala.domain.*"
create = "domain.create"
update = "domain.update"
enable = "domain.enable"
disable = "domain.disable"
freeze = "domain.freeze"
delete = "domain.delete"
userDelete = "domain.user_delete"
)
var (
errNoOperationKey = errors.New("operation key is not found in event message")
errCreateDomainEvent = errors.New("failed to consume domain create event")
errUpdateDomainEvent = errors.New("failed to consume domain update event")
errEnableDomainGroupEvent = errors.New("failed to consume domain enable event")
errDisableDomainGroupEvent = errors.New("failed to consume domain disable event")
errFreezeDomainGroupEvent = errors.New("failed to consume domain freeze event")
errUserDeleteDomainEvent = errors.New("failed to consume domain user delete event")
errDeleteDomainEvent = errors.New("failed to consume domain delete event")
)
type eventHandler struct {
repo domains.Repository
rolesEventHandler rconsumer.EventHandler
}
func DomainsEventsSubscribe(ctx context.Context, repo domains.Repository, esURL, esConsumerName string, logger *slog.Logger) error {
subscriber, err := store.NewSubscriber(ctx, esURL, "domains-es-sub", logger)
if err != nil {
return err
}
subConfig := events.SubscriberConfig{
Stream: stream,
Consumer: esConsumerName,
Handler: NewEventHandler(repo),
DeliveryPolicy: messaging.DeliverNewPolicy,
Ordered: true,
}
return subscriber.Subscribe(ctx, subConfig)
}
// NewEventHandler returns new event store handler.
func NewEventHandler(repo domains.Repository) events.EventHandler {
reh := rconsumer.NewEventHandler("domain", repo)
return &eventHandler{
repo: repo,
rolesEventHandler: reh,
}
}
func (es *eventHandler) Handle(ctx context.Context, event events.Event) error {
msg, err := event.Encode()
if err != nil {
return err
}
op, ok := msg["operation"]
if !ok {
return errNoOperationKey
}
switch op {
case create:
return es.createDomainHandler(ctx, msg)
case update:
return es.updateDomainHandler(ctx, msg)
case enable:
return es.enableDomainHandler(ctx, msg)
case disable:
return es.disableDomainHandler(ctx, msg)
case freeze:
return es.freezeDomainHandler(ctx, msg)
case userDelete:
return es.userDeleteDomainHandler(ctx, msg)
case delete:
return es.deleteDomainHandler(ctx, msg)
}
return es.rolesEventHandler.Handle(ctx, op, msg)
}
func (es *eventHandler) createDomainHandler(ctx context.Context, data map[string]any) error {
d, rps, err := decodeCreateDomainEvent(data)
if err != nil {
return errors.Wrap(errCreateDomainEvent, err)
}
if _, err := es.repo.SaveDomain(ctx, d); err != nil {
return errors.Wrap(errCreateDomainEvent, err)
}
if _, err := es.repo.AddRoles(ctx, rps); err != nil {
return errors.Wrap(errCreateDomainEvent, err)
}
return nil
}
func (es *eventHandler) updateDomainHandler(ctx context.Context, data map[string]any) error {
d, err := decodeUpdateDomainEvent(data)
if err != nil {
return errors.Wrap(errUpdateDomainEvent, err)
}
if _, err := es.repo.UpdateDomain(
ctx,
d.ID,
domains.DomainReq{
Name: &d.Name,
Metadata: &d.Metadata,
Tags: &d.Tags,
UpdatedBy: &d.UpdatedBy,
UpdatedAt: &d.UpdatedAt,
},
); err != nil {
return errors.Wrap(errUpdateDomainEvent, err)
}
return nil
}
func (es *eventHandler) enableDomainHandler(ctx context.Context, data map[string]any) error {
d, err := decodeEnableDomainEvent(data)
if err != nil {
return errors.Wrap(errEnableDomainGroupEvent, err)
}
enabled := domains.EnabledStatus
if _, err := es.repo.UpdateDomain(ctx, d.ID, domains.DomainReq{Status: &enabled, UpdatedBy: &d.UpdatedBy, UpdatedAt: &d.UpdatedAt}); err != nil {
return errors.Wrap(errEnableDomainGroupEvent, err)
}
return nil
}
func (es *eventHandler) disableDomainHandler(ctx context.Context, data map[string]any) error {
d, err := decodeDisableDomainEvent(data)
if err != nil {
return errors.Wrap(errDisableDomainGroupEvent, err)
}
disabled := domains.DisabledStatus
if _, err := es.repo.UpdateDomain(ctx, d.ID, domains.DomainReq{Status: &disabled, UpdatedBy: &d.UpdatedBy, UpdatedAt: &d.UpdatedAt}); err != nil {
return errors.Wrap(errDisableDomainGroupEvent, err)
}
return nil
}
func (es *eventHandler) freezeDomainHandler(ctx context.Context, data map[string]any) error {
d, err := decodeFreezeDomainEvent(data)
if err != nil {
return errors.Wrap(errFreezeDomainGroupEvent, err)
}
freeze := domains.FreezeStatus
if _, err := es.repo.UpdateDomain(ctx, d.ID, domains.DomainReq{Status: &freeze, UpdatedBy: &d.UpdatedBy, UpdatedAt: &d.UpdatedAt}); err != nil {
return errors.Wrap(errFreezeDomainGroupEvent, err)
}
return nil
}
func (es *eventHandler) userDeleteDomainHandler(_ context.Context, data map[string]any) error {
_, err := decodeUserDeleteDomainEvent(data)
if err != nil {
return errors.Wrap(errUserDeleteDomainEvent, err)
}
return fmt.Errorf("not implemented user delete domain handler")
}
func (es *eventHandler) deleteDomainHandler(ctx context.Context, data map[string]any) error {
d, err := decodeDeleteDomainEvent(data)
if err != nil {
return errors.Wrap(errDeleteDomainEvent, err)
}
if err := es.repo.DeleteDomain(ctx, d.ID); err != nil {
return errors.Wrap(errDeleteDomainEvent, err)
}
return nil
}
-38
View File
@@ -1,38 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package domainscache
import (
"context"
"github.com/absmach/magistrala/domains"
"github.com/absmach/magistrala/domains/private"
pkgDomains "github.com/absmach/magistrala/pkg/domains"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
)
type authorization struct {
psvc private.Service
}
var _ pkgDomains.Authorization = (*authorization)(nil)
func NewAuthorization(psvc private.Service) pkgDomains.Authorization {
return authorization{
psvc: psvc,
}
}
func (a authorization) RetrieveStatus(ctx context.Context, id string) (domains.Status, error) {
status, err := a.psvc.RetrieveStatus(ctx, id)
if err != nil {
return domains.AllStatus, errors.Wrap(svcerr.ErrViewEntity, err)
}
return status, nil
}
-257
View File
@@ -1,257 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package consumer
import (
"time"
"github.com/absmach/magistrala/groups"
"github.com/absmach/magistrala/internal/nullable"
"github.com/absmach/magistrala/pkg/errors"
"github.com/absmach/magistrala/pkg/roles"
rconsumer "github.com/absmach/magistrala/pkg/roles/rolemanager/events/consumer"
)
var (
errDecodeCreateGroupEvent = errors.New("failed to decode group create event")
errDecodeUpdateGroupEvent = errors.New("failed to decode group update event")
errDecodeChangeStatusGroupEvent = errors.New("failed to decode group change status event")
errDecodeRemoveGroupEvent = errors.New("failed to decode group remove event")
errDecodeAddParentGroupEvent = errors.New("failed to decode group add parent event")
errDecodeRemoveParentGroupEvent = errors.New("failed to decode group remove parent event")
errDecodeAddChildrenGroupsEvent = errors.New("failed to decode group add children groups event")
errDecodeRemoveChildrenGroupsEvent = errors.New("failed to decode group remove children groups event")
errID = errors.New("missing or invalid 'id'")
errName = errors.New("missing or invalid 'name'")
errDomain = errors.New("missing or invalid 'domain'")
errParent = errors.New("missing or invalid 'parent'")
errChildrenIDs = errors.New("missing or invalid 'children_ids'")
errStatus = errors.New("missing or invalid 'status'")
errConvertStatus = errors.New("failed to convert status")
errCreatedAt = errors.New("failed to parse 'created_at' time")
errUpdatedAt = errors.New("failed to parse 'updated_at' time")
)
const layout = "2006-01-02T15:04:05.999999Z"
func ToGroups(data map[string]any) (groups.Group, error) {
var g groups.Group
id, ok := data["id"].(string)
if !ok {
return groups.Group{}, errID
}
g.ID = id
name, ok := data["name"].(string)
if !ok {
return groups.Group{}, errName
}
g.Name = name
dom, ok := data["domain"].(string)
if !ok {
return groups.Group{}, errDomain
}
g.Domain = dom
stat, ok := data["status"].(string)
if !ok {
return groups.Group{}, errStatus
}
st, err := groups.ToStatus(stat)
if err != nil {
return groups.Group{}, errors.Wrap(errConvertStatus, err)
}
g.Status = st
cat, ok := data["created_at"].(string)
if !ok {
return groups.Group{}, errCreatedAt
}
ct, err := time.Parse(layout, cat)
if err != nil {
return groups.Group{}, errors.Wrap(errCreatedAt, err)
}
g.CreatedAt = ct
// Following fields of groups are allowed to be empty.
desc, ok := data["description"].(string)
if ok {
g.Description = nullable.New(desc)
}
parent, ok := data["parent"].(string)
if ok {
g.Parent = parent
}
meta, ok := data["metadata"].(map[string]any)
if ok {
g.Metadata = meta
}
uby, ok := data["updated_by"].(string)
if ok {
g.UpdatedBy = uby
}
uat, ok := data["updated_at"].(string)
if ok {
ut, err := time.Parse(layout, uat)
if err != nil {
return groups.Group{}, errors.Wrap(errUpdatedAt, err)
}
g.UpdatedAt = ut
}
return g, nil
}
func decodeCreateGroupEvent(data map[string]any) (groups.Group, []roles.RoleProvision, error) {
g, err := ToGroups(data)
if err != nil {
return groups.Group{}, []roles.RoleProvision{}, errors.Wrap(errDecodeCreateGroupEvent, err)
}
irps, ok := data["roles_provisioned"].([]any)
if !ok {
return groups.Group{}, []roles.RoleProvision{}, errors.Wrap(errDecodeCreateGroupEvent, errors.New("missing or invalid 'roles_provisioned'"))
}
rps, err := rconsumer.ToRoleProvisions(irps)
if err != nil {
return groups.Group{}, []roles.RoleProvision{}, errors.Wrap(errDecodeCreateGroupEvent, err)
}
return g, rps, nil
}
func decodeUpdateGroupEvent(data map[string]any) (groups.Group, error) {
g, err := ToGroups(data)
if err != nil {
return groups.Group{}, errors.Wrap(errDecodeUpdateGroupEvent, err)
}
return g, nil
}
func ToGroupStatus(data map[string]any) (groups.Group, error) {
var g groups.Group
id, ok := data["id"].(string)
if !ok {
return groups.Group{}, errID
}
g.ID = id
stat, ok := data["status"].(string)
if !ok {
return groups.Group{}, errStatus
}
st, err := groups.ToStatus(stat)
if err != nil {
return groups.Group{}, errors.Wrap(errConvertStatus, err)
}
g.Status = st
uat, ok := data["updated_at"].(string)
if ok {
ut, err := time.Parse(layout, uat)
if err != nil {
return groups.Group{}, errors.Wrap(errUpdatedAt, err)
}
g.UpdatedAt = ut
}
uby, ok := data["updated_by"].(string)
if ok {
g.UpdatedBy = uby
}
return g, nil
}
func decodeChangeStatusGroupEvent(data map[string]any) (groups.Group, error) {
g, err := ToGroupStatus(data)
if err != nil {
return groups.Group{}, errors.Wrap(errDecodeChangeStatusGroupEvent, err)
}
return g, nil
}
func decodeRemoveGroupEvent(data map[string]any) (groups.Group, error) {
var g groups.Group
id, ok := data["id"].(string)
if !ok {
return groups.Group{}, errors.Wrap(errDecodeRemoveGroupEvent, errID)
}
g.ID = id
return g, nil
}
func decodeAddParentGroupEvent(data map[string]any) (id string, parent string, err error) {
id, ok := data["id"].(string)
if !ok {
return "", "", errors.Wrap(errAddParentGroupEvent, errID)
}
parent, ok = data["parent_id"].(string)
if !ok {
return "", "", errors.Wrap(errDecodeAddParentGroupEvent, errParent)
}
return id, parent, nil
}
func decodeRemoveParentGroupEvent(data map[string]any) (id string, err error) {
id, ok := data["id"].(string)
if !ok {
return "", errors.Wrap(errDecodeRemoveParentGroupEvent, errID)
}
return id, nil
}
func decodeAddChildrenGroupEvent(data map[string]any) (id string, childrenIDs []string, err error) {
id, ok := data["id"].(string)
if !ok {
return "", []string{}, errors.Wrap(errDecodeAddChildrenGroupsEvent, errID)
}
chIDs, ok := data["children_ids"].([]any)
if !ok {
return "", []string{}, errors.Wrap(errDecodeAddChildrenGroupsEvent, errChildrenIDs)
}
cids, err := rconsumer.ToStrings(chIDs)
if err != nil {
return "", []string{}, errors.Wrap(errDecodeAddChildrenGroupsEvent, errors.Wrap(errChildrenIDs, err))
}
return id, cids, nil
}
func decodeRemoveChildrenGroupEvent(data map[string]any) (id string, childrenIDs []string, err error) {
id, ok := data["id"].(string)
if !ok {
return "", []string{}, errors.Wrap(errDecodeRemoveChildrenGroupsEvent, errID)
}
chIDs, ok := data["children_ids"].([]any)
if !ok {
return "", []string{}, errors.Wrap(errDecodeRemoveChildrenGroupsEvent, errChildrenIDs)
}
cids, err := rconsumer.ToStrings(chIDs)
if err != nil {
return "", []string{}, errors.Wrap(errDecodeRemoveChildrenGroupsEvent, errors.Wrap(errChildrenIDs, err))
}
return id, cids, nil
}
func decodeRemoveAllChildrenGroupEvent(data map[string]any) (id string, err error) {
id, ok := data["id"].(string)
if !ok {
return "", errors.Wrap(errDecodeRemoveChildrenGroupsEvent, errID)
}
return id, nil
}
-9
View File
@@ -1,9 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Package consumer contains events consumer for events
// published by Bootstrap service.
package consumer
-226
View File
@@ -1,226 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package consumer
import (
"context"
"log/slog"
"github.com/absmach/magistrala/groups"
"github.com/absmach/magistrala/pkg/errors"
repoerr "github.com/absmach/magistrala/pkg/errors/repository"
"github.com/absmach/magistrala/pkg/events"
"github.com/absmach/magistrala/pkg/events/store"
rconsumer "github.com/absmach/magistrala/pkg/roles/rolemanager/events/consumer"
)
const (
stream = "events.magistrala.group.*"
create = "group.create"
update = "group.update"
enable = "group.enable"
disable = "group.disable"
remove = "group.remove"
addParentGroup = "group.add_parent_group"
removeParentGroup = "group.remove_parent_group"
addChildrenGroups = "group.add_children_groups"
removeChildrenGroups = "group.remove_children_groups"
removeAllChildrenGroups = "group.remove_all_children_groups"
)
var (
errNoOperationKey = errors.New("operation key is not found in event message")
errCreateGroupEvent = errors.New("failed to consume group create event")
errUpdateGroupEvent = errors.New("failed to consume group update event")
errChangeStatusGroupEvent = errors.New("failed to consume group change status event")
errRemoveGroupEvent = errors.New("failed to consume group remove event")
errAddParentGroupEvent = errors.New("failed to consume group add parent group event")
errRemoveParentGroupEvent = errors.New("failed to consume group remove parent group event")
errAddChildrenGroupEvent = errors.New("failed to consume group add children groups event")
errRemoveChildrenGroupEvent = errors.New("failed to consume group remove children groups event")
errRemoveAllChildrenGroupEvent = errors.New("failed to consume group remove all children groups event")
)
type eventHandler struct {
repo groups.Repository
rolesEventHandler rconsumer.EventHandler
}
func GroupsEventsSubscribe(ctx context.Context, repo groups.Repository, esURL, esConsumerName string, logger *slog.Logger) error {
subscriber, err := store.NewSubscriber(ctx, esURL, "groups-es-sub", logger)
if err != nil {
return err
}
subConfig := events.SubscriberConfig{
Stream: stream,
Consumer: esConsumerName,
Handler: NewEventHandler(repo),
Ordered: true,
}
return subscriber.Subscribe(ctx, subConfig)
}
// NewEventHandler returns new event store handler.
func NewEventHandler(repo groups.Repository) events.EventHandler {
reh := rconsumer.NewEventHandler("group", repo)
return &eventHandler{
repo: repo,
rolesEventHandler: reh,
}
}
func (es *eventHandler) Handle(ctx context.Context, event events.Event) error {
msg, err := event.Encode()
if err != nil {
return err
}
op, ok := msg["operation"]
if !ok {
return errNoOperationKey
}
switch op {
case create:
return es.createGroupHandler(ctx, msg)
case update:
return es.updateGroupHandler(ctx, msg)
case enable, disable:
return es.changeStatusGroupHandler(ctx, msg)
case remove:
return es.removeGroupHandler(ctx, msg)
case addParentGroup:
return es.addParentGroupHandler(ctx, msg)
case removeParentGroup:
return es.removeParentGroupHandler(ctx, msg)
case addChildrenGroups:
return es.addChildrenGroupsHandler(ctx, msg)
case removeChildrenGroups:
return es.removeChildrenGroupsHandler(ctx, msg)
case removeAllChildrenGroups:
return es.removeAllChildrenGroupsHandler(ctx, msg)
}
return es.rolesEventHandler.Handle(ctx, op, msg)
}
func (es *eventHandler) createGroupHandler(ctx context.Context, data map[string]any) error {
g, rps, err := decodeCreateGroupEvent(data)
if err != nil {
return errors.Wrap(errCreateGroupEvent, err)
}
if _, err := es.repo.Save(ctx, g); err != nil {
return errors.Wrap(errCreateGroupEvent, err)
}
if _, err := es.repo.AddRoles(ctx, rps); err != nil {
return errors.Wrap(errCreateGroupEvent, err)
}
return nil
}
func (es *eventHandler) updateGroupHandler(ctx context.Context, data map[string]any) error {
g, err := decodeUpdateGroupEvent(data)
if err != nil {
return errors.Wrap(errUpdateGroupEvent, err)
}
if _, err := es.repo.Update(ctx, g); err != nil {
return errors.Wrap(errUpdateGroupEvent, err)
}
return nil
}
func (es *eventHandler) changeStatusGroupHandler(ctx context.Context, data map[string]any) error {
g, err := decodeChangeStatusGroupEvent(data)
if err != nil {
return errors.Wrap(errChangeStatusGroupEvent, err)
}
if _, err := es.repo.ChangeStatus(ctx, g); err != nil {
return errors.Wrap(errChangeStatusGroupEvent, err)
}
return nil
}
func (es *eventHandler) removeGroupHandler(ctx context.Context, data map[string]any) error {
g, err := decodeRemoveGroupEvent(data)
if err != nil {
return errors.Wrap(errRemoveGroupEvent, err)
}
if err := es.repo.Delete(ctx, g.ID); err != nil {
return errors.Wrap(errRemoveGroupEvent, err)
}
return nil
}
func (es *eventHandler) addParentGroupHandler(ctx context.Context, data map[string]any) error {
id, parent, err := decodeAddParentGroupEvent(data)
if err != nil {
return errors.Wrap(errAddParentGroupEvent, err)
}
if err := es.repo.AssignParentGroup(ctx, parent, id); err != nil {
return errors.Wrap(errAddParentGroupEvent, err)
}
return nil
}
func (es *eventHandler) removeParentGroupHandler(ctx context.Context, data map[string]any) error {
id, err := decodeRemoveParentGroupEvent(data)
if err != nil {
return errors.Wrap(errRemoveParentGroupEvent, err)
}
g, err := es.repo.RetrieveByID(ctx, id)
if err != nil {
return errors.Wrap(errRemoveParentGroupEvent, err)
}
if err := es.repo.UnassignParentGroup(ctx, g.Parent, id); err != nil {
return errors.Wrap(errRemoveParentGroupEvent, err)
}
return nil
}
func (es *eventHandler) addChildrenGroupsHandler(ctx context.Context, data map[string]any) error {
id, cids, err := decodeAddChildrenGroupEvent(data)
if err != nil {
return errors.Wrap(errAddChildrenGroupEvent, err)
}
if err := es.repo.AssignParentGroup(ctx, id, cids...); err != nil {
return errors.Wrap(errAddChildrenGroupEvent, err)
}
return nil
}
func (es *eventHandler) removeChildrenGroupsHandler(ctx context.Context, data map[string]any) error {
id, cids, err := decodeRemoveChildrenGroupEvent(data)
if err != nil {
return errors.Wrap(errRemoveChildrenGroupEvent, err)
}
if err := es.repo.UnassignParentGroup(ctx, id, cids...); err != nil {
return errors.Wrap(errRemoveChildrenGroupEvent, err)
}
return nil
}
func (es *eventHandler) removeAllChildrenGroupsHandler(ctx context.Context, data map[string]any) error {
id, err := decodeRemoveAllChildrenGroupEvent(data)
if err != nil {
return errors.Wrap(errRemoveAllChildrenGroupEvent, err)
}
if err := es.repo.UnassignAllChildrenGroups(ctx, id); err != nil && err != repoerr.ErrNotFound {
return errors.Wrap(errRemoveAllChildrenGroupEvent, err)
}
return nil
}
-171
View File
@@ -1,171 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package grpcclient_test
import (
"context"
"fmt"
"testing"
"time"
grpcClientsV1 "github.com/absmach/magistrala/api/grpc/clients/v1"
grpcDomainsV1 "github.com/absmach/magistrala/api/grpc/domains/v1"
grpcTokenV1 "github.com/absmach/magistrala/api/grpc/token/v1"
tokengrpcapi "github.com/absmach/magistrala/auth/api/grpc/token"
"github.com/absmach/magistrala/auth/mocks"
clientsgrpcapi "github.com/absmach/magistrala/clients/api/grpc"
climocks "github.com/absmach/magistrala/clients/private/mocks"
domainsgrpcapi "github.com/absmach/magistrala/domains/api/grpc"
domainsMocks "github.com/absmach/magistrala/domains/private/mocks"
mglog "github.com/absmach/magistrala/logger"
"github.com/absmach/magistrala/pkg/errors"
"github.com/absmach/magistrala/pkg/grpcclient"
"github.com/absmach/magistrala/pkg/server"
grpcserver "github.com/absmach/magistrala/pkg/server/grpc"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc"
)
func TestSetupToken(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
registerAuthServiceServer := func(srv *grpc.Server) {
grpcTokenV1.RegisterTokenServiceServer(srv, tokengrpcapi.NewTokenServer(new(mocks.Service)))
}
gs := grpcserver.NewServer(ctx, cancel, "auth", server.Config{Port: "12345"}, registerAuthServiceServer, mglog.NewMock())
go func() {
err := gs.Start()
assert.Nil(t, err, fmt.Sprintf(`"Unexpected error creating server %s"`, err))
}()
defer func() {
err := gs.Stop()
assert.Nil(t, err, fmt.Sprintf(`"Unexpected error stopping server %s"`, err))
}()
cases := []struct {
desc string
config grpcclient.Config
err error
}{
{
desc: "successful",
config: grpcclient.Config{
URL: "localhost:12345",
Timeout: time.Second,
},
err: nil,
},
{
desc: "failed with empty URL",
config: grpcclient.Config{
URL: "",
Timeout: time.Second,
},
err: errors.New("service is not serving"),
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
client, handler, err := grpcclient.SetupTokenClient(context.Background(), c.config)
assert.True(t, errors.Contains(err, c.err), fmt.Sprintf("expected %s to contain %s", err, c.err))
if err == nil {
assert.NotNil(t, client)
assert.NotNil(t, handler)
}
})
}
}
func TestSetupClientsClient(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
registerClientsServiceServer := func(srv *grpc.Server) {
grpcClientsV1.RegisterClientsServiceServer(srv, clientsgrpcapi.NewServer(new(climocks.Service)))
}
gs := grpcserver.NewServer(ctx, cancel, "clients", server.Config{Port: "12345"}, registerClientsServiceServer, mglog.NewMock())
go func() {
err := gs.Start()
assert.Nil(t, err, fmt.Sprintf(`"Unexpected error creating server %s"`, err))
}()
time.Sleep(time.Second)
defer func() {
err := gs.Stop()
assert.Nil(t, err, fmt.Sprintf(`"Unexpected error stopping server %s"`, err))
}()
cases := []struct {
desc string
config grpcclient.Config
err error
}{
{
desc: "successful",
config: grpcclient.Config{
URL: "localhost:12345",
Timeout: time.Second,
},
err: nil,
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
client, handler, err := grpcclient.SetupClientsClient(context.Background(), c.config)
assert.True(t, errors.Contains(err, c.err), fmt.Sprintf("expected %s to contain %s", err, c.err))
if err == nil {
assert.NotNil(t, client)
assert.NotNil(t, handler)
}
})
}
}
func TestSetupDomainsClient(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
registerDomainsServiceServer := func(srv *grpc.Server) {
grpcDomainsV1.RegisterDomainsServiceServer(srv, domainsgrpcapi.NewDomainsServer(new(domainsMocks.Service)))
}
gs := grpcserver.NewServer(ctx, cancel, "domains", server.Config{Port: "12345"}, registerDomainsServiceServer, mglog.NewMock())
go func() {
err := gs.Start()
assert.Nil(t, err, fmt.Sprintf("Unexpected error creating server %s", err))
}()
time.Sleep(time.Second)
defer func() {
err := gs.Stop()
assert.Nil(t, err, fmt.Sprintf("Unexpected error stopping server %s", err))
}()
cases := []struct {
desc string
config grpcclient.Config
err error
}{
{
desc: "successfully",
config: grpcclient.Config{
URL: "localhost:12345",
Timeout: time.Second,
},
err: nil,
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
client, handler, err := grpcclient.SetupDomainsClient(context.Background(), c.config)
assert.True(t, errors.Contains(err, c.err), fmt.Sprintf("expected %s to contain %s", err, c.err))
if err == nil {
assert.NotNil(t, client)
assert.NotNil(t, handler)
}
})
}
}
File diff suppressed because it is too large Load Diff
-9
View File
@@ -1,9 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Package oauth2 contains the domain concept definitions needed to support
// Magistrala ui service OAuth2 functionality.
package oauth2
-9
View File
@@ -1,9 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Package google contains the domain concept definitions needed to support
// Magistrala services for Google OAuth2 functionality.
package google
-116
View File
@@ -1,116 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package google
import (
"context"
"io"
"net/http"
"net/url"
"time"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
mgoauth2 "github.com/absmach/magistrala/pkg/oauth2"
uclient "github.com/absmach/magistrala/users"
"golang.org/x/oauth2"
googleoauth2 "golang.org/x/oauth2/google"
)
const (
providerName = "google"
defTimeout = 1 * time.Minute
userInfoURL = "https://www.googleapis.com/oauth2/v2/userinfo?access_token="
tokenInfoURL = "https://oauth2.googleapis.com/tokeninfo?access_token="
)
var scopes = []string{
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile",
}
var httpClient = &http.Client{
Timeout: defTimeout,
}
var _ mgoauth2.Provider = (*config)(nil)
type config struct {
config *oauth2.Config
state string
uiRedirectURL string
errorURL string
}
// NewProvider returns a new Google OAuth provider.
func NewProvider(cfg mgoauth2.Config, uiRedirectURL, errorURL string) mgoauth2.Provider {
return &config{
config: &oauth2.Config{
ClientID: cfg.ClientID,
ClientSecret: cfg.ClientSecret,
Endpoint: googleoauth2.Endpoint,
RedirectURL: cfg.RedirectURL,
Scopes: scopes,
},
state: cfg.State,
uiRedirectURL: uiRedirectURL,
errorURL: errorURL,
}
}
func (cfg *config) Name() string {
return providerName
}
func (cfg *config) State() string {
return cfg.state
}
func (cfg *config) RedirectURL() string {
return cfg.uiRedirectURL
}
func (cfg *config) ErrorURL() string {
return cfg.errorURL
}
func (cfg *config) IsEnabled() bool {
return cfg.config.ClientID != "" && cfg.config.ClientSecret != ""
}
func (cfg *config) Exchange(ctx context.Context, code string) (oauth2.Token, error) {
token, err := cfg.config.Exchange(ctx, code)
if err != nil {
return oauth2.Token{}, err
}
return *token, nil
}
func (cfg *config) UserInfo(accessToken string) (uclient.User, error) {
resp, err := httpClient.Get(userInfoURL + url.QueryEscape(accessToken))
if err != nil {
return uclient.User{}, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return uclient.User{}, svcerr.ErrAuthentication
}
data, err := io.ReadAll(resp.Body)
if err != nil {
return uclient.User{}, err
}
user, err := mgoauth2.NormalizeUser(data, providerName)
if err != nil {
return uclient.User{}, errors.Wrap(err, svcerr.ErrAuthentication)
}
return user, nil
}
-393
View File
@@ -1,393 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Code generated by mockery; DO NOT EDIT.
// github.com/vektra/mockery
// template: testify
package mocks
import (
"context"
"github.com/absmach/magistrala/users"
mock "github.com/stretchr/testify/mock"
"golang.org/x/oauth2"
)
// NewProvider creates a new instance of Provider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewProvider(t interface {
mock.TestingT
Cleanup(func())
}) *Provider {
mock := &Provider{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
// Provider is an autogenerated mock type for the Provider type
type Provider struct {
mock.Mock
}
type Provider_Expecter struct {
mock *mock.Mock
}
func (_m *Provider) EXPECT() *Provider_Expecter {
return &Provider_Expecter{mock: &_m.Mock}
}
// ErrorURL provides a mock function for the type Provider
func (_mock *Provider) ErrorURL() string {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for ErrorURL")
}
var r0 string
if returnFunc, ok := ret.Get(0).(func() string); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// Provider_ErrorURL_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ErrorURL'
type Provider_ErrorURL_Call struct {
*mock.Call
}
// ErrorURL is a helper method to define mock.On call
func (_e *Provider_Expecter) ErrorURL() *Provider_ErrorURL_Call {
return &Provider_ErrorURL_Call{Call: _e.mock.On("ErrorURL")}
}
func (_c *Provider_ErrorURL_Call) Run(run func()) *Provider_ErrorURL_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *Provider_ErrorURL_Call) Return(s string) *Provider_ErrorURL_Call {
_c.Call.Return(s)
return _c
}
func (_c *Provider_ErrorURL_Call) RunAndReturn(run func() string) *Provider_ErrorURL_Call {
_c.Call.Return(run)
return _c
}
// Exchange provides a mock function for the type Provider
func (_mock *Provider) Exchange(ctx context.Context, code string) (oauth2.Token, error) {
ret := _mock.Called(ctx, code)
if len(ret) == 0 {
panic("no return value specified for Exchange")
}
var r0 oauth2.Token
var r1 error
if returnFunc, ok := ret.Get(0).(func(context.Context, string) (oauth2.Token, error)); ok {
return returnFunc(ctx, code)
}
if returnFunc, ok := ret.Get(0).(func(context.Context, string) oauth2.Token); ok {
r0 = returnFunc(ctx, code)
} else {
r0 = ret.Get(0).(oauth2.Token)
}
if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = returnFunc(ctx, code)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Provider_Exchange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Exchange'
type Provider_Exchange_Call struct {
*mock.Call
}
// Exchange is a helper method to define mock.On call
// - ctx context.Context
// - code string
func (_e *Provider_Expecter) Exchange(ctx interface{}, code interface{}) *Provider_Exchange_Call {
return &Provider_Exchange_Call{Call: _e.mock.On("Exchange", ctx, code)}
}
func (_c *Provider_Exchange_Call) Run(run func(ctx context.Context, code string)) *Provider_Exchange_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 context.Context
if args[0] != nil {
arg0 = args[0].(context.Context)
}
var arg1 string
if args[1] != nil {
arg1 = args[1].(string)
}
run(
arg0,
arg1,
)
})
return _c
}
func (_c *Provider_Exchange_Call) Return(token oauth2.Token, err error) *Provider_Exchange_Call {
_c.Call.Return(token, err)
return _c
}
func (_c *Provider_Exchange_Call) RunAndReturn(run func(ctx context.Context, code string) (oauth2.Token, error)) *Provider_Exchange_Call {
_c.Call.Return(run)
return _c
}
// IsEnabled provides a mock function for the type Provider
func (_mock *Provider) IsEnabled() bool {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for IsEnabled")
}
var r0 bool
if returnFunc, ok := ret.Get(0).(func() bool); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// Provider_IsEnabled_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsEnabled'
type Provider_IsEnabled_Call struct {
*mock.Call
}
// IsEnabled is a helper method to define mock.On call
func (_e *Provider_Expecter) IsEnabled() *Provider_IsEnabled_Call {
return &Provider_IsEnabled_Call{Call: _e.mock.On("IsEnabled")}
}
func (_c *Provider_IsEnabled_Call) Run(run func()) *Provider_IsEnabled_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *Provider_IsEnabled_Call) Return(b bool) *Provider_IsEnabled_Call {
_c.Call.Return(b)
return _c
}
func (_c *Provider_IsEnabled_Call) RunAndReturn(run func() bool) *Provider_IsEnabled_Call {
_c.Call.Return(run)
return _c
}
// Name provides a mock function for the type Provider
func (_mock *Provider) Name() string {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for Name")
}
var r0 string
if returnFunc, ok := ret.Get(0).(func() string); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// Provider_Name_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Name'
type Provider_Name_Call struct {
*mock.Call
}
// Name is a helper method to define mock.On call
func (_e *Provider_Expecter) Name() *Provider_Name_Call {
return &Provider_Name_Call{Call: _e.mock.On("Name")}
}
func (_c *Provider_Name_Call) Run(run func()) *Provider_Name_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *Provider_Name_Call) Return(s string) *Provider_Name_Call {
_c.Call.Return(s)
return _c
}
func (_c *Provider_Name_Call) RunAndReturn(run func() string) *Provider_Name_Call {
_c.Call.Return(run)
return _c
}
// RedirectURL provides a mock function for the type Provider
func (_mock *Provider) RedirectURL() string {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for RedirectURL")
}
var r0 string
if returnFunc, ok := ret.Get(0).(func() string); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// Provider_RedirectURL_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RedirectURL'
type Provider_RedirectURL_Call struct {
*mock.Call
}
// RedirectURL is a helper method to define mock.On call
func (_e *Provider_Expecter) RedirectURL() *Provider_RedirectURL_Call {
return &Provider_RedirectURL_Call{Call: _e.mock.On("RedirectURL")}
}
func (_c *Provider_RedirectURL_Call) Run(run func()) *Provider_RedirectURL_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *Provider_RedirectURL_Call) Return(s string) *Provider_RedirectURL_Call {
_c.Call.Return(s)
return _c
}
func (_c *Provider_RedirectURL_Call) RunAndReturn(run func() string) *Provider_RedirectURL_Call {
_c.Call.Return(run)
return _c
}
// State provides a mock function for the type Provider
func (_mock *Provider) State() string {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for State")
}
var r0 string
if returnFunc, ok := ret.Get(0).(func() string); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// Provider_State_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'State'
type Provider_State_Call struct {
*mock.Call
}
// State is a helper method to define mock.On call
func (_e *Provider_Expecter) State() *Provider_State_Call {
return &Provider_State_Call{Call: _e.mock.On("State")}
}
func (_c *Provider_State_Call) Run(run func()) *Provider_State_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *Provider_State_Call) Return(s string) *Provider_State_Call {
_c.Call.Return(s)
return _c
}
func (_c *Provider_State_Call) RunAndReturn(run func() string) *Provider_State_Call {
_c.Call.Return(run)
return _c
}
// UserInfo provides a mock function for the type Provider
func (_mock *Provider) UserInfo(accessToken string) (users.User, error) {
ret := _mock.Called(accessToken)
if len(ret) == 0 {
panic("no return value specified for UserInfo")
}
var r0 users.User
var r1 error
if returnFunc, ok := ret.Get(0).(func(string) (users.User, error)); ok {
return returnFunc(accessToken)
}
if returnFunc, ok := ret.Get(0).(func(string) users.User); ok {
r0 = returnFunc(accessToken)
} else {
r0 = ret.Get(0).(users.User)
}
if returnFunc, ok := ret.Get(1).(func(string) error); ok {
r1 = returnFunc(accessToken)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Provider_UserInfo_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UserInfo'
type Provider_UserInfo_Call struct {
*mock.Call
}
// UserInfo is a helper method to define mock.On call
// - accessToken string
func (_e *Provider_Expecter) UserInfo(accessToken interface{}) *Provider_UserInfo_Call {
return &Provider_UserInfo_Call{Call: _e.mock.On("UserInfo", accessToken)}
}
func (_c *Provider_UserInfo_Call) Run(run func(accessToken string)) *Provider_UserInfo_Call {
_c.Call.Run(func(args mock.Arguments) {
var arg0 string
if args[0] != nil {
arg0 = args[0].(string)
}
run(
arg0,
)
})
return _c
}
func (_c *Provider_UserInfo_Call) Return(user users.User, err error) *Provider_UserInfo_Call {
_c.Call.Return(user, err)
return _c
}
func (_c *Provider_UserInfo_Call) RunAndReturn(run func(accessToken string) (users.User, error)) *Provider_UserInfo_Call {
_c.Call.Return(run)
return _c
}
-100
View File
@@ -1,100 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package oauth2
import (
"encoding/json"
"fmt"
"strings"
"github.com/absmach/magistrala/users"
)
type normalizedUser struct {
ID string `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Username string `json:"username"`
Email string `json:"email"`
Picture string `json:"picture"`
}
func NormalizeUser(data []byte, provider string) (users.User, error) {
var raw map[string]any
if err := json.Unmarshal(data, &raw); err != nil {
return users.User{}, err
}
normalized := normalizeProfile(raw)
userBytes, err := json.Marshal(normalized)
if err != nil {
return users.User{}, err
}
var user normalizedUser
if err := json.Unmarshal(userBytes, &user); err != nil {
return users.User{}, err
}
if err := validateUser(user); err != nil {
return users.User{}, err
}
return users.User{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email,
ProfilePicture: user.Picture,
Metadata: users.Metadata{"oauth_provider": provider},
}, nil
}
func normalizeProfile(raw map[string]any) map[string]any {
normalized := make(map[string]any)
keyMap := map[string][]string{
"id": {"id"},
"first_name": {"given_name", "first_name", "givenName", "firstname"},
"last_name": {"family_name", "last_name", "familyName", "lastname"},
"username": {"username", "user_name", "userName"},
"email": {"email", "email_address", "emailAddress"},
"picture": {"picture", "profile_picture", "profilePicture", "avatar"},
}
for stdKey, variants := range keyMap {
for _, variant := range variants {
if val, ok := raw[variant]; ok {
normalized[stdKey] = val
break
}
}
}
return normalized
}
func validateUser(user normalizedUser) error {
var missing []string
if user.ID == "" {
missing = append(missing, "id")
}
if user.FirstName == "" {
missing = append(missing, "first_name")
}
if user.LastName == "" {
missing = append(missing, "last_name")
}
if user.Email == "" {
missing = append(missing, "email")
}
if len(missing) > 0 {
return fmt.Errorf("missing required fields: %s", strings.Join(missing, ", "))
}
return nil
}
-160
View File
@@ -1,160 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package oauth2
import (
"testing"
"github.com/absmach/magistrala/users"
"github.com/stretchr/testify/assert"
)
func TestNormalizeUser(t *testing.T) {
cases := []struct {
desc string
inputJSON string
provider string
wantUser users.User
wantErrStr string
}{
{
desc: "valid user with standard keys",
inputJSON: `{
"id": "123",
"given_name": "Jane",
"family_name": "Doe",
"email": "jane@example.com",
"picture": "pic.jpg"
}`,
provider: "google",
wantUser: users.User{
ID: "123",
FirstName: "Jane",
LastName: "Doe",
Email: "jane@example.com",
ProfilePicture: "pic.jpg",
Metadata: users.Metadata{"oauth_provider": "google"},
},
wantErrStr: "",
},
{
desc: "missing required fields",
inputJSON: `{
"given_name": "Jane"
}`,
provider: "google",
wantUser: users.User{},
wantErrStr: "missing required fields: id, last_name, email",
},
{
desc: "invalid JSON",
inputJSON: `{invalid json`,
provider: "google",
wantUser: users.User{},
wantErrStr: "invalid character",
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
user, err := NormalizeUser([]byte(tc.inputJSON), tc.provider)
if tc.wantErrStr != "" {
assert.Error(t, err)
assert.Contains(t, err.Error(), tc.wantErrStr)
assert.Equal(t, tc.wantUser, user)
} else {
assert.NoError(t, err)
assert.Equal(t, tc.wantUser, user)
}
})
}
}
func TestNormalizeProfile(t *testing.T) {
cases := []struct {
desc string
raw map[string]any
expected map[string]any
}{
{
desc: "maps all variants to normalized keys",
raw: map[string]any{
"id": "id123",
"givenName": "John",
"familyName": "Smith",
"user_name": "jsmith",
"emailAddress": "john@smith.com",
"profilePicture": "pic.png",
},
expected: map[string]any{
"id": "id123",
"first_name": "John",
"last_name": "Smith",
"username": "jsmith",
"email": "john@smith.com",
"picture": "pic.png",
},
},
{
desc: "missing keys returns empty map",
raw: map[string]any{"foo": "bar"},
expected: map[string]any{},
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
got := normalizeProfile(tc.raw)
assert.Equal(t, tc.expected, got)
})
}
}
func TestValidateUser(t *testing.T) {
cases := []struct {
desc string
user normalizedUser
wantErr string
}{
{
desc: "valid user returns nil error",
user: normalizedUser{
ID: "1",
FirstName: "F",
LastName: "L",
Email: "e@example.com",
},
wantErr: "",
},
{
desc: "missing id returns error",
user: normalizedUser{
FirstName: "F",
LastName: "L",
Email: "e@example.com",
},
wantErr: "missing required fields: id",
},
{
desc: "multiple missing fields returns all in error",
user: normalizedUser{},
wantErr: "missing required fields: id, first_name, last_name, email",
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
err := validateUser(tc.user)
if tc.wantErr == "" {
assert.NoError(t, err)
} else {
assert.Error(t, err)
assert.Equal(t, tc.wantErr, err.Error())
}
})
}
}
-47
View File
@@ -1,47 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package oauth2
import (
"context"
"github.com/absmach/magistrala/users"
"golang.org/x/oauth2"
)
// Config is the configuration for the OAuth2 provider.
type Config struct {
ClientID string `env:"CLIENT_ID" envDefault:""`
ClientSecret string `env:"CLIENT_SECRET" envDefault:""`
State string `env:"STATE" envDefault:""`
RedirectURL string `env:"REDIRECT_URL" envDefault:""`
}
// Provider is an interface that provides the OAuth2 flow for a specific provider
// (e.g. Google, GitHub, etc.)
type Provider interface {
// Name returns the name of the OAuth2 provider.
Name() string
// State returns the current state for the OAuth2 flow.
State() string
// RedirectURL returns the URL to redirect the user to after completing the OAuth2 flow.
RedirectURL() string
// ErrorURL returns the URL to redirect the user to in case of an error during the OAuth2 flow.
ErrorURL() string
// IsEnabled checks if the OAuth2 provider is enabled.
IsEnabled() bool
// Exchange converts an authorization code into a token.
Exchange(ctx context.Context, code string) (oauth2.Token, error)
// UserInfo retrieves the user's information using the access token.
UserInfo(accessToken string) (users.User, error)
}
-393
View File
@@ -1,393 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk_test
import (
"context"
"net/http/httptest"
"testing"
"time"
"github.com/absmach/magistrala/alarms"
"github.com/absmach/magistrala/alarms/api"
amocks "github.com/absmach/magistrala/alarms/mocks"
mglog "github.com/absmach/magistrala/logger"
smqauthn "github.com/absmach/magistrala/pkg/authn"
authnmocks "github.com/absmach/magistrala/pkg/authn/mocks"
"github.com/absmach/magistrala/pkg/errors"
"github.com/absmach/magistrala/pkg/sdk"
"github.com/absmach/magistrala/pkg/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
const alarmID = "alarm-1"
var testAlarm = sdk.Alarm{
ID: alarmID,
RuleID: "rule-1",
DomainID: domainID,
ChannelID: "chan-1",
ClientID: "client-1",
Subtopic: "subtopic",
Status: "active",
Measurement: "temperature",
Value: "30.5",
Unit: "C",
Threshold: "25",
Cause: "threshold_exceeded",
Severity: 80,
AssigneeID: "user-1",
Metadata: sdk.Metadata{"key": "value"},
}
func setupAlarms() (*httptest.Server, *amocks.Service, *authnmocks.Authentication) {
asvc := new(amocks.Service)
logger := mglog.NewMock()
authn := new(authnmocks.Authentication)
am := smqauthn.NewAuthNMiddleware(authn, smqauthn.WithAllowUnverifiedUser(true))
idp := uuid.NewMock()
mux := api.MakeHandler(asvc, logger, idp, "", am)
return httptest.NewServer(mux), asvc, authn
}
func TestUpdateAlarm(t *testing.T) {
as, asvc, auth := setupAlarms()
defer as.Close()
conf := sdk.Config{
AlarmsURL: as.URL,
}
mgsdk := sdk.NewSDK(conf)
updated := testAlarm
updated.Status = "cleared"
svcAlarm := alarms.Alarm{
ID: alarmID,
RuleID: "rule-1",
DomainID: domainID,
ChannelID: "chan-1",
ClientID: "client-1",
Subtopic: "subtopic",
Status: alarms.ClearedStatus,
Measurement: "temperature",
Value: "30.5",
Unit: "C",
Threshold: "25",
Cause: "threshold_exceeded",
Severity: 80,
AssigneeID: "user-1",
Metadata: alarms.Metadata{"key": "value"},
}
cases := []struct {
desc string
alarm sdk.Alarm
token string
session smqauthn.Session
svcRes alarms.Alarm
svcErr error
authenticateErr error
wantErr bool
resp sdk.Alarm
}{
{
desc: "update alarm successfully",
alarm: updated,
token: validToken,
svcRes: svcAlarm,
resp: testAlarm,
},
{
desc: "update alarm with empty token",
alarm: updated,
token: "",
wantErr: true,
},
{
desc: "update non-existent alarm",
alarm: sdk.Alarm{ID: "non-existent"},
token: validToken,
svcErr: errors.New("not found"),
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := asvc.On("UpdateAlarm", mock.Anything, tc.session, mock.Anything).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.UpdateAlarm(context.Background(), tc.alarm, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestViewAlarm(t *testing.T) {
as, asvc, auth := setupAlarms()
defer as.Close()
conf := sdk.Config{
AlarmsURL: as.URL,
}
mgsdk := sdk.NewSDK(conf)
svcAlarm := alarms.Alarm{
ID: alarmID,
RuleID: "rule-1",
DomainID: domainID,
ChannelID: "chan-1",
ClientID: "client-1",
Subtopic: "subtopic",
Status: alarms.ActiveStatus,
Measurement: "temperature",
Value: "30.5",
Unit: "C",
Threshold: "25",
Cause: "threshold_exceeded",
Severity: 80,
AssigneeID: "user-1",
Metadata: alarms.Metadata{"key": "value"},
}
cases := []struct {
desc string
id string
token string
session smqauthn.Session
svcRes alarms.Alarm
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "view alarm successfully",
id: alarmID,
token: validToken,
svcRes: svcAlarm,
},
{
desc: "view alarm with empty token",
id: alarmID,
token: "",
wantErr: true,
},
{
desc: "view non-existent alarm",
id: "non-existent",
token: validToken,
svcErr: errors.New("not found"),
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := asvc.On("ViewAlarm", mock.Anything, tc.session, tc.id).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.ViewAlarm(context.Background(), tc.id, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestListAlarms(t *testing.T) {
as, asvc, auth := setupAlarms()
defer as.Close()
conf := sdk.Config{
AlarmsURL: as.URL,
}
mgsdk := sdk.NewSDK(conf)
svcAlarm := alarms.Alarm{
ID: alarmID,
RuleID: "rule-1",
DomainID: domainID,
ChannelID: "chan-1",
ClientID: "client-1",
Subtopic: "subtopic",
Status: alarms.ActiveStatus,
Measurement: "temperature",
Value: "30.5",
Unit: "C",
Threshold: "25",
Cause: "threshold_exceeded",
Severity: 80,
AssigneeID: "user-1",
Metadata: alarms.Metadata{"key": "value"},
}
svcAlarmsPage := alarms.AlarmsPage{
Total: 2,
Offset: 0,
Limit: 10,
Alarms: []alarms.Alarm{svcAlarm},
}
cases := []struct {
desc string
pm sdk.PageMetadata
token string
session smqauthn.Session
svcRes alarms.AlarmsPage
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "list alarms successfully",
pm: sdk.PageMetadata{Offset: 0, Limit: 10},
token: validToken,
svcRes: svcAlarmsPage,
},
{
desc: "list alarms with status and entity filters",
pm: sdk.PageMetadata{
Limit: 5,
Status: "active",
ChannelID: "chan-1",
ClientID: "client-1",
RuleID: "rule-1",
AssigneeID: "user-1",
Severity: 80,
},
token: validToken,
svcRes: svcAlarmsPage,
},
{
desc: "list alarms with time range and sorting",
pm: sdk.PageMetadata{
Limit: 10,
CreatedFrom: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
CreatedTo: time.Date(2024, 12, 31, 0, 0, 0, 0, time.UTC),
Order: "created_at",
Dir: "asc",
},
token: validToken,
svcRes: svcAlarmsPage,
},
{
desc: "list alarms with actor filters",
pm: sdk.PageMetadata{
Limit: 10,
UpdatedBy: "user-2",
AssignedBy: "user-3",
AcknowledgedBy: "user-4",
ResolvedBy: "user-5",
Subtopic: "subtopic-1",
},
token: validToken,
svcRes: svcAlarmsPage,
},
{
desc: "list alarms with empty metadata excludes severity",
pm: sdk.PageMetadata{},
token: validToken,
svcRes: alarms.AlarmsPage{},
},
{
desc: "list alarms with zero severity excluded",
pm: sdk.PageMetadata{Status: "active", Severity: 0},
token: validToken,
svcRes: alarms.AlarmsPage{},
},
{
desc: "list alarms with empty token",
pm: sdk.PageMetadata{Limit: 10},
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := asvc.On("ListAlarms", mock.Anything, tc.session, mock.Anything).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.ListAlarms(context.Background(), tc.pm, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.Equal(t, tc.svcRes.Total, result.Total)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestDeleteAlarm(t *testing.T) {
as, asvc, auth := setupAlarms()
defer as.Close()
conf := sdk.Config{
AlarmsURL: as.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
id string
token string
session smqauthn.Session
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "delete alarm successfully",
id: alarmID,
token: validToken,
},
{
desc: "delete alarm with empty token",
id: alarmID,
token: "",
wantErr: true,
},
{
desc: "delete non-existent alarm",
id: "non-existent",
token: validToken,
svcErr: errors.New("not found"),
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := asvc.On("DeleteAlarm", mock.Anything, tc.session, tc.id).Return(tc.svcErr)
err := mgsdk.DeleteAlarm(context.Background(), tc.id, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
svcCall.Unset()
authCall.Unset()
})
}
}
File diff suppressed because it is too large Load Diff
-77
View File
@@ -1,77 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk
import (
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPageMetadataQueryWithCertFilters(t *testing.T) {
pm := PageMetadata{
EntityID: "entity-id",
CommonName: "device-cn",
Organization: []string{"Acme", "QA"},
OrganizationalUnit: []string{"Platform"},
Country: []string{"RS"},
Province: []string{"Belgrade"},
Locality: []string{"Belgrade"},
StreetAddress: []string{"Nemanjina 4"},
PostalCode: []string{"11000"},
DNSNames: []string{"device.local"},
IPAddresses: []string{"127.0.0.1"},
EmailAddresses: []string{"device@example.com"},
TTL: "24h",
}
encoded, err := pm.query()
require.NoError(t, err)
values, err := url.ParseQuery(encoded)
require.NoError(t, err)
assert.Equal(t, "entity-id", values.Get("entity_id"))
assert.Equal(t, "device-cn", values.Get("common_name"))
assert.Equal(t, "24h", values.Get("ttl"))
assert.ElementsMatch(t, []string{"Acme", "QA"}, values["organization"])
assert.Equal(t, []string{"Platform"}, values["organizational_unit"])
assert.Equal(t, []string{"RS"}, values["country"])
assert.Equal(t, []string{"Belgrade"}, values["province"])
assert.Equal(t, []string{"Belgrade"}, values["locality"])
assert.Equal(t, []string{"Nemanjina 4"}, values["street_address"])
assert.Equal(t, []string{"11000"}, values["postal_code"])
assert.Equal(t, []string{"device.local"}, values["dns_names"])
assert.Equal(t, []string{"127.0.0.1"}, values["ip_addresses"])
assert.Equal(t, []string{"device@example.com"}, values["email_addresses"])
}
func TestCertStatusAliases(t *testing.T) {
assert.Equal(t, CertValid, Valid)
assert.Equal(t, CertRevoked, Revoked)
assert.Equal(t, CertUnknown, Unknown)
}
func TestCertTypeString(t *testing.T) {
tests := []struct {
desc string
typ CertType
expected string
}{
{desc: "root", typ: RootCA, expected: "root"},
{desc: "intermediate", typ: IntermediateCA, expected: "intermediate"},
{desc: "unknown", typ: CertType(99), expected: "unknown"},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
assert.Equal(t, tc.expected, tc.typ.String())
})
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-457
View File
@@ -1,457 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk_test
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"testing"
apiutil "github.com/absmach/magistrala/api/http/util"
"github.com/absmach/magistrala/consumers/notifiers"
httpapi "github.com/absmach/magistrala/consumers/notifiers/api"
notmocks "github.com/absmach/magistrala/consumers/notifiers/mocks"
"github.com/absmach/magistrala/internal/testsutil"
mglog "github.com/absmach/magistrala/logger"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/absmach/magistrala/pkg/sdk"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
var (
ownerID = testsutil.GenerateUUID(&testing.T{})
subID = testsutil.GenerateUUID(&testing.T{})
sdkSubReq = sdk.Subscription{
Topic: "topic",
Contact: "contact",
}
sdkSubRes = sdk.Subscription{
Topic: "topic",
Contact: "contact",
OwnerID: ownerID,
ID: subID,
}
notSubReq = notifiers.Subscription{
Contact: "contact",
Topic: "topic",
}
notSubRes = notifiers.Subscription{
Contact: "contact",
Topic: "topic",
OwnerID: ownerID,
ID: subID,
}
instanceID = "instanceID"
)
func setupSubscriptions() (*httptest.Server, *notmocks.Service) {
nsvc := new(notmocks.Service)
logger := mglog.NewMock()
mux := httpapi.MakeHandler(nsvc, logger, instanceID)
return httptest.NewServer(mux), nsvc
}
func TestCreateSubscription(t *testing.T) {
ts, nsvc := setupSubscriptions()
defer ts.Close()
sdkConf := sdk.Config{
UsersURL: ts.URL,
MsgContentType: contentType,
TLSVerification: false,
}
mgsdk := sdk.NewSDK(sdkConf)
cases := []struct {
desc string
subscription sdk.Subscription
token string
empty bool
id string
svcReq notifiers.Subscription
svcErr error
svcRes string
err errors.SDKError
}{
{
desc: "create new subscription",
subscription: sdkSubReq,
token: validToken,
empty: false,
svcReq: notSubReq,
svcRes: subID,
svcErr: nil,
err: nil,
},
{
desc: "create new subscription with empty token",
subscription: sdkSubReq,
token: "",
empty: true,
svcReq: notifiers.Subscription{},
svcRes: "",
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "create new subscription with invalid token",
subscription: sdkSubReq,
token: invalidToken,
empty: true,
svcReq: notSubReq,
svcRes: "",
svcErr: svcerr.ErrAuthentication,
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "create new subscription with empty topic",
subscription: sdk.Subscription{
Topic: "",
Contact: "contact",
},
token: validToken,
empty: true,
svcReq: notifiers.Subscription{},
svcErr: nil,
svcRes: "",
err: errors.NewSDKErrorWithStatus(apiutil.ErrInvalidTopic, http.StatusBadRequest),
},
{
desc: "create new subscription with empty contact",
subscription: sdk.Subscription{
Topic: "topic",
Contact: "",
},
token: validToken,
empty: true,
svcReq: notifiers.Subscription{},
svcErr: nil,
svcRes: "",
err: errors.NewSDKErrorWithStatus(apiutil.ErrInvalidContact, http.StatusBadRequest),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := nsvc.On("CreateSubscription", mock.Anything, tc.token, tc.svcReq).Return(tc.svcRes, tc.svcErr)
loc, err := mgsdk.CreateSubscription(context.Background(), tc.subscription.Topic, tc.subscription.Contact, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.empty, loc == "")
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "CreateSubscription", mock.Anything, tc.token, tc.svcReq)
assert.True(t, ok)
}
svcCall.Unset()
})
}
}
func TestViewSubscription(t *testing.T) {
ts, nsvc := setupSubscriptions()
defer ts.Close()
sdkConf := sdk.Config{
UsersURL: ts.URL,
MsgContentType: contentType,
TLSVerification: false,
}
mgsdk := sdk.NewSDK(sdkConf)
cases := []struct {
desc string
subID string
token string
svcRes notifiers.Subscription
svcErr error
response sdk.Subscription
err errors.SDKError
}{
{
desc: "view existing subscription",
subID: subID,
token: validToken,
svcRes: notSubRes,
svcErr: nil,
response: sdkSubRes,
err: nil,
},
{
desc: "view non-existent subscription",
subID: wrongID,
token: validToken,
svcRes: notifiers.Subscription{},
svcErr: svcerr.ErrNotFound,
response: sdk.Subscription{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound),
},
{
desc: "view subscription with invalid token",
subID: subID,
token: invalidToken,
svcRes: notifiers.Subscription{},
svcErr: svcerr.ErrAuthentication,
response: sdk.Subscription{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "view subscription with empty token",
subID: subID,
token: "",
svcRes: notifiers.Subscription{},
svcErr: nil,
response: sdk.Subscription{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := nsvc.On("ViewSubscription", mock.Anything, tc.token, tc.subID).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.ViewSubscription(context.Background(), tc.subID, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "ViewSubscription", mock.Anything, tc.token, tc.subID)
assert.True(t, ok)
}
svcCall.Unset()
})
}
}
func TestListSubscription(t *testing.T) {
ts, nsvc := setupSubscriptions()
defer ts.Close()
sdkConf := sdk.Config{
UsersURL: ts.URL,
MsgContentType: contentType,
TLSVerification: false,
}
mgsdk := sdk.NewSDK(sdkConf)
nSubs := 10
noSubs := []notifiers.Subscription{}
sdSubs := []sdk.Subscription{}
for i := 0; i < nSubs; i++ {
nosub := notifiers.Subscription{
OwnerID: ownerID,
Topic: fmt.Sprintf("topic_%d", i),
Contact: fmt.Sprintf("contact_%d", i),
}
noSubs = append(noSubs, nosub)
sdsub := sdk.Subscription{
OwnerID: ownerID,
Topic: fmt.Sprintf("topic_%d", i),
Contact: fmt.Sprintf("contact_%d", i),
}
sdSubs = append(sdSubs, sdsub)
}
cases := []struct {
desc string
token string
pageMeta sdk.PageMetadata
svcReq notifiers.PageMetadata
svcRes notifiers.Page
svcErr error
response sdk.SubscriptionPage
err errors.SDKError
}{
{
desc: "list all subscription",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: notifiers.PageMetadata{
Offset: 0,
Limit: 10,
},
svcRes: notifiers.Page{
Total: 10,
Subscriptions: noSubs,
},
svcErr: nil,
response: sdk.SubscriptionPage{
PageRes: sdk.PageRes{
Total: 10,
},
Subscriptions: sdSubs,
},
err: nil,
},
{
desc: "list subscription with specific topic",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
Topic: "topic_1",
},
svcReq: notifiers.PageMetadata{
Offset: 0,
Limit: 10,
Topic: "topic_1",
},
svcRes: notifiers.Page{
Total: uint(len(noSubs[1:2])),
Subscriptions: noSubs[1:2],
},
svcErr: nil,
response: sdk.SubscriptionPage{
PageRes: sdk.PageRes{
Total: uint64(len(sdSubs[1:2])),
},
Subscriptions: sdSubs[1:2],
},
err: nil,
},
{
desc: "list subscription with specific contact",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
Contact: "contact_1",
},
svcReq: notifiers.PageMetadata{
Offset: 0,
Limit: 10,
Contact: "contact_1",
},
svcRes: notifiers.Page{
Total: uint(len(noSubs[1:2])),
Subscriptions: noSubs[1:2],
},
svcErr: nil,
response: sdk.SubscriptionPage{
PageRes: sdk.PageRes{
Total: uint64(len(sdSubs[1:2])),
},
Subscriptions: sdSubs[1:2],
},
err: nil,
},
{
desc: "list subscription with invalid token",
token: invalidToken,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: notifiers.PageMetadata{
Offset: 0,
Limit: 10,
},
svcRes: notifiers.Page{},
svcErr: svcerr.ErrAuthentication,
response: sdk.SubscriptionPage{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "list subscription with empty token",
token: "",
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: notifiers.PageMetadata{},
svcRes: notifiers.Page{},
svcErr: nil,
response: sdk.SubscriptionPage{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := nsvc.On("ListSubscriptions", mock.Anything, tc.token, tc.svcReq).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.ListSubscriptions(context.Background(), tc.pageMeta, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "ListSubscriptions", mock.Anything, tc.token, tc.svcReq)
assert.True(t, ok)
}
svcCall.Unset()
})
}
}
func TestDeleteSubscription(t *testing.T) {
ts, nsvc := setupSubscriptions()
defer ts.Close()
sdkConf := sdk.Config{
UsersURL: ts.URL,
MsgContentType: contentType,
TLSVerification: false,
}
mgsdk := sdk.NewSDK(sdkConf)
cases := []struct {
desc string
subID string
token string
svcErr error
err errors.SDKError
}{
{
desc: "delete existing subscription",
subID: subID,
token: validToken,
svcErr: nil,
err: nil,
},
{
desc: "delete non-existent subscription",
subID: wrongID,
token: validToken,
svcErr: svcerr.ErrRemoveEntity,
err: errors.NewSDKErrorWithStatus(svcerr.ErrRemoveEntity, http.StatusUnprocessableEntity),
},
{
desc: "delete subscription with invalid token",
subID: subID,
token: invalidToken,
svcErr: svcerr.ErrAuthentication,
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "delete subscription with empty token",
subID: subID,
token: "",
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "delete subscription with empty subID",
subID: "",
token: validToken,
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingID, http.StatusBadRequest),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := nsvc.On("RemoveSubscription", mock.Anything, tc.token, tc.subID).Return(tc.svcErr)
err := mgsdk.DeleteSubscription(context.Background(), tc.subID, tc.token)
assert.Equal(t, tc.err, err)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "RemoveSubscription", mock.Anything, tc.token, tc.subID)
assert.True(t, ok)
}
svcCall.Unset()
})
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-129
View File
@@ -1,129 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk_test
import (
"fmt"
"testing"
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/pkg/errors"
sdk "github.com/absmach/magistrala/pkg/sdk"
"github.com/stretchr/testify/assert"
)
func TestHealth(t *testing.T) {
clientsTs, _, _ := setupClients()
defer clientsTs.Close()
usersTs, _, _ := setupUsers()
defer usersTs.Close()
groupsTs, _, _ := setupGroups()
defer groupsTs.Close()
channelsTs, _, _ := setupChannels()
defer channelsTs.Close()
domainsTs, _, _ := setupDomains()
defer domainsTs.Close()
journalTs, _, _ := setupJournal()
defer journalTs.Close()
fluxmqTs := setupFluxMQ("any")
defer fluxmqTs.Close()
sdkConf := sdk.Config{
ClientsURL: clientsTs.URL,
UsersURL: usersTs.URL,
HTTPAdapterURL: fluxmqTs.URL,
GroupsURL: groupsTs.URL,
ChannelsURL: channelsTs.URL,
DomainsURL: domainsTs.URL,
JournalURL: journalTs.URL,
MsgContentType: contentType,
TLSVerification: false,
}
mgsdk := sdk.NewSDK(sdkConf)
cases := []struct {
desc string
service string
empty bool
description string
status string
err errors.SDKError
}{
{
desc: "get clients service health check",
service: "clients",
empty: false,
err: nil,
description: "clients service",
status: "pass",
},
{
desc: "get users service health check",
service: "users",
empty: false,
err: nil,
description: "users service",
status: "pass",
},
{
desc: "get groups service health check",
service: "groups",
empty: false,
err: nil,
description: "groups service",
status: "pass",
},
{
desc: "get channels service health check",
service: "channels",
empty: false,
err: nil,
description: "channels service",
status: "pass",
},
{
desc: "get domains service health check",
service: "domains",
empty: false,
err: nil,
description: "domains service",
status: "pass",
},
{
desc: "get journal service health check",
service: "journal",
empty: false,
err: nil,
description: "journal-log service",
status: "pass",
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
h, err := mgsdk.Health(tc.service)
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected error %s, got %s", tc.desc, tc.err, err))
assert.Equal(t, tc.status, h.Status, fmt.Sprintf("%s: expected %s status, got %s", tc.desc, tc.status, h.Status))
assert.Equal(t, tc.empty, h.Version == "", fmt.Sprintf("%s: expected non-empty version", tc.desc))
assert.Equal(t, magistrala.Commit, h.Commit, fmt.Sprintf("%s: expected non-empty commit", tc.desc))
assert.Equal(t, tc.description, h.Description, fmt.Sprintf("%s: expected proper description, got %s", tc.desc, h.Description))
assert.Equal(t, magistrala.BuildTime, h.BuildTime, fmt.Sprintf("%s: expected default epoch date, got %s", tc.desc, h.BuildTime))
})
}
// FluxMQ returns a simpler health response without version/commit/description.
t.Run("get fluxmq service health check", func(t *testing.T) {
h, err := mgsdk.Health("fluxmq")
assert.Nil(t, err)
assert.Equal(t, "healthy", h.Status)
})
}
-471
View File
@@ -1,471 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk_test
import (
"context"
"fmt"
"net/http"
"testing"
"time"
apiutil "github.com/absmach/magistrala/api/http/util"
"github.com/absmach/magistrala/domains"
"github.com/absmach/magistrala/internal/testsutil"
smqauthn "github.com/absmach/magistrala/pkg/authn"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
sdk "github.com/absmach/magistrala/pkg/sdk"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
var (
sdkInvitation = generateTestInvitation(&testing.T{})
invitation = convertInvitation(sdkInvitation)
)
func TestSendInvitation(t *testing.T) {
is, svc, auth := setupDomains()
defer is.Close()
conf := sdk.Config{
DomainsURL: is.URL,
}
mgsdk := sdk.NewSDK(conf)
sendInvitationReq := sdk.Invitation{
InviteeUserID: invitation.InviteeUserID,
DomainID: invitation.DomainID,
RoleID: invitation.RoleID,
}
cases := []struct {
desc string
token string
session smqauthn.Session
sendInvitationReq sdk.Invitation
svcReq domains.Invitation
authenticateErr error
svcErr error
err error
}{
{
desc: "send invitation successfully",
token: validToken,
sendInvitationReq: sendInvitationReq,
svcReq: convertInvitation(sendInvitationReq),
svcErr: nil,
err: nil,
},
{
desc: "send invitation with invalid token",
token: invalidToken,
sendInvitationReq: sendInvitationReq,
svcReq: convertInvitation(sendInvitationReq),
authenticateErr: svcerr.ErrAuthentication,
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "send invitation with empty token",
token: "",
sendInvitationReq: sendInvitationReq,
svcReq: domains.Invitation{},
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "send invitation with empty userID",
token: validToken,
sendInvitationReq: sdk.Invitation{
InviteeUserID: "",
DomainID: invitation.DomainID,
RoleID: invitation.RoleID,
},
svcReq: domains.Invitation{},
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingID, http.StatusBadRequest),
},
{
desc: "send invitation with empty role ID",
token: validToken,
sendInvitationReq: sdk.Invitation{
InviteeUserID: invitation.InviteeUserID,
DomainID: invitation.DomainID,
RoleID: "",
},
svcReq: domains.Invitation{},
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingID, http.StatusBadRequest),
},
{
desc: "send inviation with invalid domainID",
token: validToken,
sendInvitationReq: sdk.Invitation{
InviteeUserID: invitation.InviteeUserID,
DomainID: wrongID,
RoleID: invitation.RoleID,
},
svcReq: domains.Invitation{
InviteeUserID: invitation.InviteeUserID,
DomainID: wrongID,
RoleID: invitation.RoleID,
},
svcErr: svcerr.ErrCreateEntity,
err: errors.NewSDKErrorWithStatus(svcerr.ErrCreateEntity, http.StatusUnprocessableEntity),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == valid {
tc.session = smqauthn.Session{
UserID: tc.sendInvitationReq.InviteeUserID,
DomainID: tc.sendInvitationReq.DomainID,
DomainUserID: fmt.Sprintf("%s_%s", tc.sendInvitationReq.DomainID, tc.sendInvitationReq.InviteeUserID),
}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("SendInvitation", mock.Anything, tc.session, tc.svcReq).Return(domains.Invitation{}, tc.svcErr)
err := mgsdk.SendInvitation(context.Background(), tc.sendInvitationReq, tc.token)
assert.Equal(t, tc.err, err)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "SendInvitation", mock.Anything, tc.session, tc.svcReq)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestListInvitation(t *testing.T) {
is, svc, auth := setupDomains()
defer is.Close()
conf := sdk.Config{
DomainsURL: is.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
token string
session smqauthn.Session
pageMeta sdk.PageMetadata
svcReq domains.InvitationPageMeta
svcRes domains.InvitationPage
svcErr error
authenticateErr error
response sdk.InvitationPage
err error
}{
{
desc: "list invitations successfully",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: domains.InvitationPageMeta{
Offset: 0,
Limit: 10,
},
svcRes: domains.InvitationPage{
Total: 1,
Invitations: []domains.Invitation{invitation},
},
svcErr: nil,
response: sdk.InvitationPage{
Total: 1,
Invitations: []sdk.Invitation{sdkInvitation},
},
err: nil,
},
{
desc: "list invitations with invalid token",
token: invalidToken,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: domains.InvitationPageMeta{
Offset: 0,
Limit: 10,
},
svcRes: domains.InvitationPage{},
authenticateErr: svcerr.ErrAuthentication,
response: sdk.InvitationPage{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "list invitations with empty token",
token: "",
pageMeta: sdk.PageMetadata{},
svcRes: domains.InvitationPage{},
svcErr: nil,
response: sdk.InvitationPage{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "list invitations with limit greater than max limit",
token: validToken,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 101,
},
svcReq: domains.InvitationPageMeta{},
svcRes: domains.InvitationPage{},
svcErr: nil,
response: sdk.InvitationPage{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrLimitSize, http.StatusBadRequest),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == valid {
tc.session = smqauthn.Session{DomainUserID: validID, UserID: validID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("ListInvitations", mock.Anything, tc.session, tc.svcReq).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.Invitations(context.Background(), tc.pageMeta, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "ListInvitations", mock.Anything, tc.session, tc.svcReq)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestAcceptInvitation(t *testing.T) {
is, svc, auth := setupDomains()
defer is.Close()
conf := sdk.Config{
DomainsURL: is.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
token string
session smqauthn.Session
domainID string
authenticateErr error
svcErr error
err error
}{
{
desc: "accept invitation successfully",
token: validToken,
domainID: invitation.DomainID,
svcErr: nil,
err: nil,
},
{
desc: "accept invitation with invalid token",
token: invalidToken,
domainID: invitation.DomainID,
authenticateErr: svcerr.ErrAuthentication,
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "accept invitation with empty token",
token: "",
domainID: invitation.DomainID,
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "accept invitation with invalid domainID",
token: validToken,
domainID: wrongID,
svcErr: svcerr.ErrNotFound,
err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == valid {
tc.session = smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: validID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("AcceptInvitation", mock.Anything, tc.session, tc.domainID).Return(domains.Invitation{}, tc.svcErr)
err := mgsdk.AcceptInvitation(context.Background(), tc.domainID, tc.token)
assert.Equal(t, tc.err, err)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "AcceptInvitation", mock.Anything, tc.session, tc.domainID)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestRejectInvitation(t *testing.T) {
is, svc, auth := setupDomains()
defer is.Close()
conf := sdk.Config{
DomainsURL: is.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
token string
session smqauthn.Session
domainID string
authenticateErr error
svcErr error
err error
}{
{
desc: "reject invitation successfully",
token: validToken,
domainID: invitation.DomainID,
svcErr: nil,
err: nil,
},
{
desc: "reject invitation with invalid token",
token: invalidToken,
domainID: invitation.DomainID,
authenticateErr: svcerr.ErrAuthentication,
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "reject invitation with empty token",
token: "",
domainID: invitation.DomainID,
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "reject invitation with invalid domainID",
token: validToken,
domainID: wrongID,
svcErr: svcerr.ErrNotFound,
err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == valid {
tc.session = smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: validID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("RejectInvitation", mock.Anything, tc.session, tc.domainID).Return(domains.Invitation{}, tc.svcErr)
err := mgsdk.RejectInvitation(context.Background(), tc.domainID, tc.token)
assert.Equal(t, tc.err, err)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "RejectInvitation", mock.Anything, tc.session, tc.domainID)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestDeleteInvitation(t *testing.T) {
is, svc, auth := setupDomains()
defer is.Close()
conf := sdk.Config{
DomainsURL: is.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
token string
session smqauthn.Session
inviteeUserID string
domainID string
authenticateErr error
svcErr error
err error
}{
{
desc: "delete invitation successfully",
token: validToken,
inviteeUserID: invitation.InviteeUserID,
domainID: invitation.DomainID,
svcErr: nil,
err: nil,
},
{
desc: "delete invitation with invalid token",
token: invalidToken,
inviteeUserID: invitation.InviteeUserID,
domainID: invitation.DomainID,
authenticateErr: svcerr.ErrAuthentication,
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "delete invitation with empty token",
token: "",
inviteeUserID: invitation.InviteeUserID,
domainID: invitation.DomainID,
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "delete invitation with empty domainID",
token: validToken,
inviteeUserID: invitation.InviteeUserID,
domainID: "",
svcErr: nil,
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingDomainID, http.StatusBadRequest),
},
{
desc: "delete invitation with invalid domainID",
token: validToken,
inviteeUserID: invitation.InviteeUserID,
domainID: wrongID,
svcErr: svcerr.ErrNotFound,
err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == valid {
tc.session = smqauthn.Session{UserID: tc.inviteeUserID, DomainID: tc.domainID, DomainUserID: fmt.Sprintf("%s_%s", tc.domainID, tc.inviteeUserID)}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := svc.On("DeleteInvitation", mock.Anything, tc.session, tc.inviteeUserID, tc.domainID).Return(tc.svcErr)
err := mgsdk.DeleteInvitation(context.Background(), tc.inviteeUserID, tc.domainID, tc.token)
assert.Equal(t, tc.err, err)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "DeleteInvitation", mock.Anything, tc.session, tc.inviteeUserID, tc.domainID)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func generateTestInvitation(t *testing.T) sdk.Invitation {
createdAt, err := time.Parse(time.RFC3339, "2024-01-01T00:00:00Z")
assert.Nil(t, err, fmt.Sprintf("Unexpected error parsing time: %v", err))
return sdk.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
InviteeUserID: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
RoleID: testsutil.GenerateUUID(t),
RoleName: "admin",
Actions: []string{"read", "update"},
CreatedAt: createdAt,
UpdatedAt: createdAt,
}
}
-364
View File
@@ -1,364 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk_test
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
apiutil "github.com/absmach/magistrala/api/http/util"
"github.com/absmach/magistrala/journal"
"github.com/absmach/magistrala/journal/api"
"github.com/absmach/magistrala/journal/mocks"
mglog "github.com/absmach/magistrala/logger"
smqauthn "github.com/absmach/magistrala/pkg/authn"
authnmocks "github.com/absmach/magistrala/pkg/authn/mocks"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
sdk "github.com/absmach/magistrala/pkg/sdk"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func setupJournal() (*httptest.Server, *mocks.Service, *authnmocks.Authentication) {
svc := new(mocks.Service)
authn := new(authnmocks.Authentication)
logger := mglog.NewMock()
am := smqauthn.NewAuthNMiddleware(authn, smqauthn.WithAllowUnverifiedUser(true))
mux := api.MakeHandler(svc, am, logger, "journal-log", "test")
return httptest.NewServer(mux), svc, authn
}
func TestRetrieveJournal(t *testing.T) {
js, svc, authn := setupJournal()
defer js.Close()
testJournal := generateTestJournal(t)
validEntityType := "group"
sdkConf := sdk.Config{
JournalURL: js.URL,
}
mgsdk := sdk.NewSDK(sdkConf)
cases := []struct {
desc string
token string
session smqauthn.Session
entityType string
entityID string
domainID string
pageMeta sdk.PageMetadata
svcReq journal.Page
svcRes journal.JournalsPage
svcErr error
authnErr error
response sdk.JournalsPage
err error
}{
{
desc: "retrieve user journal successfully",
token: validToken,
entityType: "user",
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: journal.Page{
Offset: 0,
Limit: 10,
EntityID: validID,
EntityType: journal.UserEntity,
Direction: "desc",
},
svcRes: journal.JournalsPage{
Total: 1,
Journals: []journal.Journal{convertJournal(testJournal)},
},
svcErr: nil,
response: sdk.JournalsPage{
Total: 1,
Journals: []sdk.Journal{testJournal},
},
err: nil,
},
{
desc: "retrieve channel journal successfully",
token: validToken,
entityType: "channel",
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: journal.Page{
Offset: 0,
Limit: 10,
EntityID: validID,
EntityType: journal.ChannelEntity,
Direction: "desc",
},
svcRes: journal.JournalsPage{
Total: 1,
Journals: []journal.Journal{convertJournal(testJournal)},
},
svcErr: nil,
response: sdk.JournalsPage{
Total: 1,
Journals: []sdk.Journal{testJournal},
},
err: nil,
},
{
desc: "retrieve group journal successfully",
token: validToken,
entityType: "group",
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: journal.Page{
Offset: 0,
Limit: 10,
EntityID: validID,
EntityType: journal.GroupEntity,
Direction: "desc",
},
svcRes: journal.JournalsPage{
Total: 1,
Journals: []journal.Journal{convertJournal(testJournal)},
},
svcErr: nil,
response: sdk.JournalsPage{
Total: 1,
Journals: []sdk.Journal{testJournal},
},
err: nil,
},
{
desc: "retrieve client journal successfully",
token: validToken,
entityType: "client",
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: journal.Page{
Offset: 0,
Limit: 10,
EntityID: validID,
EntityType: journal.ClientEntity,
Direction: "desc",
},
svcRes: journal.JournalsPage{
Total: 1,
Journals: []journal.Journal{convertJournal(testJournal)},
},
svcErr: nil,
response: sdk.JournalsPage{
Total: 1,
Journals: []sdk.Journal{testJournal},
},
err: nil,
},
{
desc: "retrieve journal with invalid token",
token: invalidToken,
entityType: validEntityType,
entityID: validID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: journal.Page{
Offset: 0,
Limit: 10,
EntityID: validID,
EntityType: journal.GroupEntity,
Direction: "desc",
},
svcRes: journal.JournalsPage{},
authnErr: svcerr.ErrAuthentication,
response: sdk.JournalsPage{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "retrieve journal with empty token",
token: "",
entityType: validEntityType,
entityID: validID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: journal.Page{},
svcRes: journal.JournalsPage{},
svcErr: nil,
response: sdk.JournalsPage{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "retrieve journal with invalid entity type",
token: validToken,
entityType: "invalid",
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: journal.Page{},
svcRes: journal.JournalsPage{},
svcErr: nil,
response: sdk.JournalsPage{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrInvalidEntityType, http.StatusBadRequest),
},
{
desc: "retrieve journal with empty entity ID",
token: validToken,
entityType: validEntityType,
entityID: "",
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: journal.Page{},
svcRes: journal.JournalsPage{},
svcErr: nil,
response: sdk.JournalsPage{},
err: errors.NewSDKError(apiutil.ErrMissingID),
},
{
desc: "retrieve journal with empty entity type",
token: validToken,
entityType: "",
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: journal.Page{},
svcRes: journal.JournalsPage{},
svcErr: nil,
response: sdk.JournalsPage{},
err: errors.NewSDKError(apiutil.ErrMissingEntityType),
},
{
desc: "retrieve journal with limit greater than default",
token: validToken,
entityType: validEntityType,
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 1000,
},
svcReq: journal.Page{},
svcRes: journal.JournalsPage{},
svcErr: nil,
response: sdk.JournalsPage{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrLimitSize, http.StatusBadRequest),
},
{
desc: "retrieve journal with invalid page metadata",
token: validToken,
entityType: validEntityType,
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
Metadata: map[string]any{
"key": make(chan int),
},
},
svcReq: journal.Page{},
svcRes: journal.JournalsPage{},
svcErr: nil,
response: sdk.JournalsPage{},
err: errors.NewSDKError(fmt.Errorf("json: unsupported type: chan int")),
},
{
desc: "retrieve journal with response that cannot be unmarshalled",
token: validToken,
entityType: validEntityType,
entityID: validID,
domainID: domainID,
pageMeta: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
svcReq: journal.Page{
Offset: 0,
Limit: 10,
EntityID: validID,
EntityType: journal.GroupEntity,
Direction: "desc",
},
svcRes: journal.JournalsPage{
Total: 1,
Journals: []journal.Journal{{
ID: validID,
Operation: "create",
OccurredAt: time.Now(),
Attributes: validMetadata,
Metadata: map[string]any{
"key": make(chan int),
},
}},
},
svcErr: nil,
response: sdk.JournalsPage{},
err: errors.NewSDKError(fmt.Errorf("unexpected end of JSON input")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: fmt.Sprintf("%s_%s", domainID, validID), UserID: validID, DomainID: domainID}
}
authCall := authn.On("Authenticate", mock.Anything, mock.Anything).Return(tc.session, tc.authnErr)
svcCall := svc.On("RetrieveAll", mock.Anything, tc.session, tc.svcReq).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.Journal(context.Background(), tc.entityType, tc.entityID, tc.domainID, tc.pageMeta, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "RetrieveAll", mock.Anything, tc.session, tc.svcReq)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func generateTestJournal(t *testing.T) sdk.Journal {
occuredAt, err := time.Parse(time.RFC3339, "2024-01-01T00:00:00Z")
assert.Nil(t, err, fmt.Sprintf("Unexpected error parsing time: %v", err))
return sdk.Journal{
ID: validID,
Operation: "create",
OccurredAt: occuredAt,
Attributes: validMetadata,
Metadata: validMetadata,
}
}
-183
View File
@@ -1,183 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk_test
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"
apiutil "github.com/absmach/magistrala/api/http/util"
"github.com/absmach/magistrala/pkg/errors"
sdk "github.com/absmach/magistrala/pkg/sdk"
"github.com/stretchr/testify/assert"
)
type publishReq struct {
Topic string `json:"topic"`
Payload []byte `json:"payload"`
QoS byte `json:"qos"`
Retain bool `json:"retain"`
}
func setupFluxMQ(secret string, expectedTopic ...string) *httptest.Server {
mux := http.NewServeMux()
mux.HandleFunc("POST /publish", func(w http.ResponseWriter, r *http.Request) {
username := r.Header.Get("X-FluxMQ-Username")
auth := r.Header.Get("Authorization")
if username == "" || auth == "" || auth != "Bearer "+secret {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
defer r.Body.Close()
var req publishReq
if err := json.Unmarshal(body, &req); err != nil {
http.Error(w, "invalid json", http.StatusBadRequest)
return
}
if req.Topic == "" {
http.Error(w, "empty topic", http.StatusBadRequest)
return
}
if len(expectedTopic) > 0 && req.Topic != expectedTopic[0] {
http.Error(w, fmt.Sprintf("unexpected topic: %s", req.Topic), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `{"status":"ok"}`)
})
mux.HandleFunc("GET /health", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `{"status":"healthy"}`)
})
return httptest.NewServer(mux)
}
func TestSendMessage(t *testing.T) {
clientSecret := "validSecret"
cases := []struct {
desc string
topic string
domainID string
wantTopic string
msg string
secret string
err errors.SDKError
}{
{
desc: "publish message successfully",
topic: "channelID",
domainID: "domainID",
wantTopic: "m/domainID/c/channelID",
msg: `[{"n":"current","t":-1,"v":1.6}]`,
secret: clientSecret,
err: nil,
},
{
desc: "publish message with subtopic",
topic: "channelID/sub/topic",
domainID: "domainID",
wantTopic: "m/domainID/c/channelID/sub/topic",
msg: `[{"n":"current","t":-1,"v":1.6}]`,
secret: clientSecret,
err: nil,
},
{
desc: "publish message with invalid secret",
topic: "channelID",
domainID: "domainID",
wantTopic: "m/domainID/c/channelID",
msg: `[{"n":"current","t":-1,"v":1.6}]`,
secret: "invalid",
err: errors.NewSDKErrorWithStatus(errors.Wrap(errors.New(""), errors.New("")), http.StatusUnauthorized),
},
{
desc: "publish message with empty secret",
topic: "channelID",
domainID: "domainID",
wantTopic: "m/domainID/c/channelID",
msg: `[{"n":"current","t":-1,"v":1.6}]`,
secret: "",
err: errors.NewSDKErrorWithStatus(errors.Wrap(errors.New(""), errors.New("")), http.StatusUnauthorized),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
ts := setupFluxMQ(clientSecret, tc.wantTopic)
defer ts.Close()
sdkConf := sdk.Config{
HTTPAdapterURL: ts.URL,
MsgContentType: "application/senml+json",
TLSVerification: false,
}
mgsdk := sdk.NewSDK(sdkConf)
err := mgsdk.SendMessage(context.Background(), tc.domainID, tc.topic, tc.msg, tc.secret)
if tc.err != nil {
assert.NotNil(t, err, fmt.Sprintf("%s: expected error, got nil", tc.desc))
} else {
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error: %v", tc.desc, err))
}
})
}
}
func TestSetContentType(t *testing.T) {
sdkConf := sdk.Config{
MsgContentType: "application/senml+json",
TLSVerification: false,
}
mgsdk := sdk.NewSDK(sdkConf)
cases := []struct {
desc string
cType sdk.ContentType
err errors.SDKError
}{
{
desc: "set senml+json content type",
cType: "application/senml+json",
err: nil,
},
{
desc: "set json content type",
cType: "application/json",
err: nil,
},
{
desc: "set invalid content type",
cType: "invalid",
err: errors.NewSDKError(apiutil.ErrUnsupportedContentType),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
err := mgsdk.SetContentType(tc.cType)
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected error %s, got %s", tc.desc, tc.err, err))
})
}
}
-243
View File
@@ -1,243 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk_test
import (
"context"
"net/http"
"net/http/httptest"
"testing"
grpcChannelsV1 "github.com/absmach/magistrala/api/grpc/channels/v1"
apiutil "github.com/absmach/magistrala/api/http/util"
chmocks "github.com/absmach/magistrala/channels/mocks"
climocks "github.com/absmach/magistrala/clients/mocks"
smqauthn "github.com/absmach/magistrala/pkg/authn"
authnmocks "github.com/absmach/magistrala/pkg/authn/mocks"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/absmach/magistrala/pkg/sdk"
"github.com/absmach/magistrala/pkg/transformers/senml"
"github.com/absmach/magistrala/readers"
readersapi "github.com/absmach/magistrala/readers/api/http"
readersmocks "github.com/absmach/magistrala/readers/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func setupReaders() (*httptest.Server, *authnmocks.Authentication, *readersmocks.MessageRepository) {
repo := new(readersmocks.MessageRepository)
authn := new(authnmocks.Authentication)
clientsGRPCClient = new(climocks.ClientsServiceClient)
channelsGRPCClient = new(chmocks.ChannelsServiceClient)
mux := readersapi.MakeHandler(repo, authn, clientsGRPCClient, channelsGRPCClient, "test", "")
return httptest.NewServer(mux), authn, repo
}
func TestReadMessages(t *testing.T) {
ts, authn, repo := setupReaders()
defer ts.Close()
channelID := "channelID"
msgValue := 1.6
boolVal := true
msg := senml.Message{
Name: "current",
Time: 1720000000,
Value: &msgValue,
Publisher: validID,
}
invalidMsg := "[{\"n\":\"current\",\"t\":-1,\"v\":1.6}]"
sdkConf := sdk.Config{
ReaderURL: ts.URL,
}
mgsdk := sdk.NewSDK(sdkConf)
cases := []struct {
desc string
token string
chanName string
domainID string
messagePageMeta sdk.MessagePageMetadata
authzErr error
authnErr error
repoRes readers.MessagesPage
repoErr error
response sdk.MessagesPage
err errors.SDKError
}{
{
desc: "read messages successfully",
token: validToken,
chanName: channelID,
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
Limit: 10,
Level: 0,
},
Publisher: validID,
BoolValue: &boolVal,
},
repoRes: readers.MessagesPage{
Total: 1,
Messages: []readers.Message{msg},
},
repoErr: nil,
response: sdk.MessagesPage{
PageRes: sdk.PageRes{
Total: 1,
},
Messages: []senml.Message{msg},
},
err: nil,
},
{
desc: "read messages successfully with subtopic",
token: validToken,
chanName: channelID + "/subtopic",
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
Publisher: validID,
},
repoRes: readers.MessagesPage{
Total: 1,
Messages: []readers.Message{msg},
},
repoErr: nil,
response: sdk.MessagesPage{
PageRes: sdk.PageRes{
Total: 1,
},
Messages: []senml.Message{msg},
},
err: nil,
},
{
desc: "read messages with invalid token",
token: invalidToken,
chanName: channelID,
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
Subtopic: "subtopic",
Publisher: validID,
},
authzErr: svcerr.ErrAuthorization,
repoRes: readers.MessagesPage{},
response: sdk.MessagesPage{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
},
{
desc: "read messages with empty token",
token: "",
chanName: channelID,
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
Subtopic: "subtopic",
Publisher: validID,
},
authnErr: svcerr.ErrAuthentication,
repoRes: readers.MessagesPage{},
response: sdk.MessagesPage{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
{
desc: "read messages with empty channel ID",
token: validToken,
chanName: "",
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
Subtopic: "subtopic",
Publisher: validID,
},
repoRes: readers.MessagesPage{},
repoErr: nil,
response: sdk.MessagesPage{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingID, http.StatusBadRequest),
},
{
desc: "read messages with invalid message page metadata",
token: validToken,
chanName: channelID,
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
Limit: 10,
Metadata: map[string]any{
"key": make(chan int),
},
},
Subtopic: "subtopic",
Publisher: validID,
},
repoRes: readers.MessagesPage{},
repoErr: nil,
response: sdk.MessagesPage{},
err: errors.NewSDKError(errors.New("json: unsupported type: chan int")),
},
{
desc: "read messages with response that cannot be unmarshalled",
token: validToken,
chanName: channelID,
domainID: validID,
messagePageMeta: sdk.MessagePageMetadata{
PageMetadata: sdk.PageMetadata{
Offset: 0,
Limit: 10,
},
Subtopic: "subtopic",
Publisher: validID,
},
repoRes: readers.MessagesPage{
Total: 1,
Messages: []readers.Message{invalidMsg},
},
repoErr: nil,
response: sdk.MessagesPage{},
err: errors.NewSDKError(errors.New("json: cannot unmarshal string into Go struct field MessagesPage.messages of type senml.Message")),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
authCall1 := authn.On("Authenticate", mock.Anything, tc.token).Return(smqauthn.Session{UserID: validID}, tc.authnErr)
authzCall := channelsGRPCClient.On("Authorize", mock.Anything, mock.Anything).Return(&grpcChannelsV1.AuthzRes{Authorized: true}, tc.authzErr)
repoCall := repo.On("ReadAll", channelID, mock.Anything).Return(tc.repoRes, tc.repoErr)
response, err := mgsdk.ReadMessages(context.Background(), tc.messagePageMeta, tc.chanName, tc.domainID, tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, response)
if tc.err == nil {
ok := repoCall.Parent.AssertCalled(t, "ReadAll", channelID, mock.Anything)
assert.True(t, ok)
}
authCall1.Unset()
authzCall.Unset()
repoCall.Unset()
})
}
}
-870
View File
@@ -1,870 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk_test
import (
"context"
"errors"
"net/http/httptest"
"testing"
"time"
mglog "github.com/absmach/magistrala/logger"
smqauthn "github.com/absmach/magistrala/pkg/authn"
authnmocks "github.com/absmach/magistrala/pkg/authn/mocks"
pkgSch "github.com/absmach/magistrala/pkg/schedule"
"github.com/absmach/magistrala/pkg/sdk"
"github.com/absmach/magistrala/reports"
"github.com/absmach/magistrala/reports/api"
rmocks "github.com/absmach/magistrala/reports/mocks"
"github.com/go-chi/chi/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
const (
reportConfigID = "report-config-1"
reportName = "daily-report"
reportUpdatedName = "updated daily-report"
reportDescription = "Daily temperature report"
reportUpdatedDesc = "updated Daily temperature report"
validTemplate = `<!DOCTYPE html>
<html>
<head>
<title>{{$.Title}}</title>
<style>
body { font-family: Arial, sans-serif; }
.header { background-color: #f0f0f0; padding: 10px; }
.content { padding: 20px; }
</style>
</head>
<body>
<div class="header">
<h1>{{$.Title}}</h1>
<p>Generated on: {{$.GeneratedDate}}</p>
</div>
<div class="content">
<h2>Messages</h2>
{{range .Messages}}
<div class="message">
<p>Time: {{formatTime .Time}}</p>
<p>Value: {{formatValue .}}</p>
</div>
{{end}}
</div>
</body>
</html>`
)
var (
now = time.Now().UTC().Truncate(time.Minute)
future = now.Add(1 * time.Hour)
schedule = pkgSch.Schedule{
StartDateTime: future,
Recurring: pkgSch.Daily,
RecurringPeriod: 1,
Time: future,
}
metrics = []reports.ReqMetric{
{
ChannelID: "channel1",
ClientIDs: []string{"client1"},
Name: "metric_name",
},
}
config = reports.MetricConfig{
From: "now()-1h",
To: "now()",
Title: "test_title",
Aggregation: reports.AggConfig{AggType: reports.AggregationAVG, Interval: "1h"},
}
email = reports.EmailSetting{
To: []string{"test@example.com"},
Subject: "Test Report",
}
testReportConfig = sdk.ReportConfig{
ID: reportConfigID,
Name: reportName,
Description: reportDescription,
DomainID: domainID,
Status: "enabled",
Schedule: schedule,
Metrics: metrics,
Config: &config,
Email: &email,
}
)
func setupReports() (*httptest.Server, *rmocks.Service, *authnmocks.Authentication) {
rsvc := new(rmocks.Service)
log := mglog.NewMock()
authn := new(authnmocks.Authentication)
am := smqauthn.NewAuthNMiddleware(authn, smqauthn.WithAllowUnverifiedUser(true))
mux := chi.NewRouter()
_ = api.MakeHandler(rsvc, am, mux, log, "")
return httptest.NewServer(mux), rsvc, authn
}
func TestAddReportConfig(t *testing.T) {
rs, rsvc, auth := setupReports()
defer rs.Close()
conf := sdk.Config{
ReportsURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcCfg := reports.ReportConfig{
ID: reportConfigID,
Name: reportName,
Description: reportDescription,
DomainID: domainID,
Status: reports.EnabledStatus,
Schedule: schedule,
Metrics: []reports.ReqMetric{
{
ChannelID: "channel1",
ClientIDs: []string{"client1"},
Name: "metric_name",
},
},
Config: &reports.MetricConfig{
From: "now()-1h",
To: "now()",
Title: "test_title",
Aggregation: reports.AggConfig{AggType: reports.AggregationAVG, Interval: "1h"},
},
Email: &reports.EmailSetting{
To: []string{"test@example.com"},
Subject: "Test Report",
},
}
cases := []struct {
desc string
cfg sdk.ReportConfig
token string
session smqauthn.Session
svcRes reports.ReportConfig
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "add report config successfully",
cfg: testReportConfig,
token: validToken,
svcRes: svcCfg,
},
{
desc: "add report config with empty token",
cfg: sdk.ReportConfig{Name: "daily-report"},
token: "",
wantErr: true,
svcErr: errors.New("missing or invalid bearer user token"),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("AddReportConfig", mock.Anything, tc.session, mock.Anything).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.AddReportConfig(context.Background(), tc.cfg, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestViewReportConfig(t *testing.T) {
rs, rsvc, auth := setupReports()
defer rs.Close()
conf := sdk.Config{
ReportsURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcCfg := reports.ReportConfig{
ID: reportConfigID,
Name: reportName,
Description: reportDescription,
DomainID: domainID,
Status: reports.EnabledStatus,
Metrics: metrics,
Config: &config,
Email: &email,
}
cases := []struct {
desc string
id string
token string
session smqauthn.Session
svcRes reports.ReportConfig
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "view report config successfully",
id: reportConfigID,
token: validToken,
svcRes: svcCfg,
},
{
desc: "view report config with empty token",
id: reportConfigID,
token: "",
wantErr: true,
},
{
desc: "view non-existent report config",
id: "non-existent",
token: validToken,
svcErr: errors.New("not found"),
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("ViewReportConfig", mock.Anything, tc.session, tc.id, mock.Anything).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.ViewReportConfig(context.Background(), tc.id, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUpdateReportConfig(t *testing.T) {
rs, rsvc, auth := setupReports()
defer rs.Close()
conf := sdk.Config{
ReportsURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
updatedConfig := testReportConfig
updatedConfig.Name = reportUpdatedName
updatedConfig.Description = reportUpdatedDesc
svcCfg := reports.ReportConfig{
ID: reportConfigID,
Name: reportUpdatedName,
Description: reportUpdatedDesc,
DomainID: domainID,
Status: reports.EnabledStatus,
Metrics: metrics,
Config: &config,
Email: &email,
}
cases := []struct {
desc string
cfg sdk.ReportConfig
token string
session smqauthn.Session
svcRes reports.ReportConfig
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "update report config successfully",
cfg: updatedConfig,
token: validToken,
svcRes: svcCfg,
},
{
desc: "update report config with empty token",
cfg: sdk.ReportConfig{ID: reportConfigID, Name: "updated-report"},
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("UpdateReportConfig", mock.Anything, tc.session, mock.Anything).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.UpdateReportConfig(context.Background(), tc.cfg, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUpdateReportSchedule(t *testing.T) {
rs, rsvc, auth := setupReports()
defer rs.Close()
conf := sdk.Config{
ReportsURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcCfg := reports.ReportConfig{
ID: reportConfigID,
Name: reportName,
Status: reports.EnabledStatus,
}
cases := []struct {
desc string
cfg sdk.ReportConfig
token string
session smqauthn.Session
svcRes reports.ReportConfig
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "update report schedule successfully",
cfg: sdk.ReportConfig{ID: reportConfigID, Schedule: map[string]any{"cron": "0 9 * * *"}},
token: validToken,
svcRes: svcCfg,
},
{
desc: "update report schedule with empty token",
cfg: sdk.ReportConfig{ID: reportConfigID},
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("UpdateReportSchedule", mock.Anything, tc.session, mock.Anything).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.UpdateReportSchedule(context.Background(), tc.cfg, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestRemoveReportConfig(t *testing.T) {
rs, rsvc, auth := setupReports()
defer rs.Close()
conf := sdk.Config{
ReportsURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
id string
token string
session smqauthn.Session
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "remove report config successfully",
id: reportConfigID,
token: validToken,
},
{
desc: "remove report config with empty token",
id: reportConfigID,
token: "",
wantErr: true,
},
{
desc: "remove non-existent report config",
id: "non-existent",
token: validToken,
svcErr: errors.New("not found"),
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("RemoveReportConfig", mock.Anything, tc.session, tc.id).Return(tc.svcErr)
err := mgsdk.RemoveReportConfig(context.Background(), tc.id, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
svcCall.Unset()
authCall.Unset()
})
}
}
func TestListReportsConfig(t *testing.T) {
rs, rsvc, auth := setupReports()
defer rs.Close()
conf := sdk.Config{
ReportsURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcPage := reports.ReportConfigPage{}
cases := []struct {
desc string
pm sdk.PageMetadata
token string
session smqauthn.Session
svcRes reports.ReportConfigPage
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "list reports config successfully",
pm: sdk.PageMetadata{Offset: 0, Limit: 10},
token: validToken,
svcRes: svcPage,
},
{
desc: "list reports config with filters",
pm: sdk.PageMetadata{
Limit: 10,
Name: "daily",
Status: "enabled",
Dir: "desc",
Order: "created_at",
},
token: validToken,
svcRes: svcPage,
},
{
desc: "list reports config with empty metadata excludes filter params",
pm: sdk.PageMetadata{},
token: validToken,
svcRes: reports.ReportConfigPage{},
},
{
desc: "list reports config with empty token",
pm: sdk.PageMetadata{Limit: 10},
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("ListReportsConfig", mock.Anything, tc.session, mock.Anything).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.ListReportsConfig(context.Background(), tc.pm, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotNil(t, result)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestEnableReportConfig(t *testing.T) {
rs, rsvc, auth := setupReports()
defer rs.Close()
conf := sdk.Config{
ReportsURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcCfg := reports.ReportConfig{
ID: reportConfigID,
Status: reports.EnabledStatus,
}
cases := []struct {
desc string
id string
token string
session smqauthn.Session
svcRes reports.ReportConfig
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "enable report config successfully",
id: reportConfigID,
token: validToken,
svcRes: svcCfg,
},
{
desc: "enable report config with empty token",
id: reportConfigID,
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("EnableReportConfig", mock.Anything, tc.session, tc.id).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.EnableReportConfig(context.Background(), tc.id, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestDisableReportConfig(t *testing.T) {
rs, rsvc, auth := setupReports()
defer rs.Close()
conf := sdk.Config{
ReportsURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcCfg := reports.ReportConfig{
ID: reportConfigID,
Status: reports.DisabledStatus,
}
cases := []struct {
desc string
id string
token string
session smqauthn.Session
svcRes reports.ReportConfig
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "disable report config successfully",
id: reportConfigID,
token: validToken,
svcRes: svcCfg,
},
{
desc: "disable report config with empty token",
id: reportConfigID,
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("DisableReportConfig", mock.Anything, tc.session, tc.id).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.DisableReportConfig(context.Background(), tc.id, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUpdateReportTemplate(t *testing.T) {
rs, rsvc, auth := setupReports()
defer rs.Close()
conf := sdk.Config{
ReportsURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
cfg sdk.ReportConfig
token string
session smqauthn.Session
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "update report template successfully",
cfg: sdk.ReportConfig{
ID: reportConfigID,
ReportTemplate: validTemplate,
},
token: validToken,
},
{
desc: "update report template with empty token",
cfg: sdk.ReportConfig{ID: reportConfigID},
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("UpdateReportTemplate", mock.Anything, tc.session, mock.Anything).Return(tc.svcErr)
err := mgsdk.UpdateReportTemplate(context.Background(), tc.cfg, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
svcCall.Unset()
authCall.Unset()
})
}
}
func TestViewReportTemplate(t *testing.T) {
rs, rsvc, auth := setupReports()
defer rs.Close()
conf := sdk.Config{
ReportsURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcTmpl := reports.ReportTemplate(validTemplate)
cases := []struct {
desc string
id string
token string
session smqauthn.Session
svcRes reports.ReportTemplate
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "view report template successfully",
id: reportConfigID,
token: validToken,
svcRes: svcTmpl,
},
{
desc: "view report template with empty token",
id: reportConfigID,
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("ViewReportTemplate", mock.Anything, tc.session, tc.id).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.ViewReportTemplate(context.Background(), tc.id, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestDeleteReportTemplate(t *testing.T) {
rs, rsvc, auth := setupReports()
defer rs.Close()
conf := sdk.Config{
ReportsURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
id string
token string
session smqauthn.Session
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "delete report template successfully",
id: reportConfigID,
token: validToken,
},
{
desc: "delete report template with empty token",
id: reportConfigID,
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("DeleteReportTemplate", mock.Anything, tc.session, tc.id).Return(tc.svcErr)
err := mgsdk.DeleteReportTemplate(context.Background(), tc.id, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
svcCall.Unset()
authCall.Unset()
})
}
}
func TestGenerateReport(t *testing.T) {
rs, rsvc, auth := setupReports()
defer rs.Close()
conf := sdk.Config{
ReportsURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcPage := reports.ReportPage{}
config := sdk.ReportConfig{
ID: reportConfigID,
Name: reportName,
Description: reportDescription,
DomainID: domainID,
Metrics: metrics,
Config: &config,
ReportTemplate: reports.ReportTemplate(validTemplate),
}
cases := []struct {
desc string
cfg sdk.ReportConfig
action sdk.ReportAction
token string
session smqauthn.Session
svcRes reports.ReportPage
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "generate report successfully",
cfg: config,
action: sdk.ViewReportAction,
token: validToken,
svcRes: svcPage,
},
{
desc: "generate report with download action",
cfg: config,
action: sdk.DownloadReportAction,
token: validToken,
svcRes: svcPage,
},
{
desc: "generate report with empty token",
cfg: config,
action: sdk.ViewReportAction,
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{
DomainUserID: domainID + "_" + validID,
UserID: validID,
DomainID: domainID,
}
}
authCall := auth.On(
"Authenticate",
mock.Anything,
tc.token,
).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On(
"GenerateReport",
mock.Anything,
tc.session,
mock.Anything,
mock.Anything,
).Return(tc.svcRes, tc.svcErr)
page, file, err := mgsdk.GenerateReport(
context.Background(),
tc.cfg,
tc.action,
domainID,
tc.token,
)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
if tc.action == sdk.DownloadReportAction {
// download should return file
assert.NotNil(t, file)
} else {
// view/email should return page
assert.Equal(t, tc.svcRes.Total, page.Total)
}
}
svcCall.Unset()
authCall.Unset()
})
}
}
-588
View File
@@ -1,588 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk_test
import (
"context"
"errors"
"net/http/httptest"
"testing"
mglog "github.com/absmach/magistrala/logger"
smqauthn "github.com/absmach/magistrala/pkg/authn"
authnmocks "github.com/absmach/magistrala/pkg/authn/mocks"
"github.com/absmach/magistrala/pkg/sdk"
"github.com/absmach/magistrala/re"
"github.com/absmach/magistrala/re/api"
remocks "github.com/absmach/magistrala/re/mocks"
"github.com/go-chi/chi/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
const ruleID = "rule-1"
var testRule = sdk.Rule{
ID: ruleID,
Name: "temperature-rule",
InputChannel: "chan-1",
InputTopic: "sensors/temperature",
Status: "enabled",
Tags: []string{"temperature", "alerts"},
}
func setupRules() (*httptest.Server, *remocks.Service, *authnmocks.Authentication) {
rsvc := new(remocks.Service)
log := mglog.NewMock()
authn := new(authnmocks.Authentication)
am := smqauthn.NewAuthNMiddleware(authn, smqauthn.WithAllowUnverifiedUser(true))
mux := chi.NewRouter()
_ = api.MakeHandler(rsvc, am, mux, log, "")
return httptest.NewServer(mux), rsvc, authn
}
func TestAddRule(t *testing.T) {
rs, rsvc, auth := setupRules()
defer rs.Close()
conf := sdk.Config{
RulesEngineURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcRule := re.Rule{
ID: ruleID,
Name: "temperature-rule",
InputChannel: "chan-1",
Status: re.EnabledStatus,
}
cases := []struct {
desc string
rule sdk.Rule
token string
session smqauthn.Session
svcRes re.Rule
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "add rule successfully",
rule: sdk.Rule{Name: "temp-rule", InputChannel: "chan-1"},
token: validToken,
svcRes: svcRule,
},
{
desc: "add rule with empty token",
rule: sdk.Rule{Name: "temp-rule"},
token: "",
wantErr: true,
},
{
desc: "add rule with bad request",
rule: sdk.Rule{},
token: validToken,
svcErr: errors.New("bad request"),
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("AddRule", mock.Anything, tc.session, mock.Anything).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.AddRule(context.Background(), tc.rule, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestViewRule(t *testing.T) {
rs, rsvc, auth := setupRules()
defer rs.Close()
conf := sdk.Config{
RulesEngineURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcRule := re.Rule{
ID: ruleID,
Name: "temperature-rule",
InputChannel: "chan-1",
Status: re.EnabledStatus,
}
cases := []struct {
desc string
id string
token string
session smqauthn.Session
svcRes re.Rule
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "view rule successfully",
id: ruleID,
token: validToken,
svcRes: svcRule,
},
{
desc: "view rule with empty token",
id: ruleID,
token: "",
wantErr: true,
},
{
desc: "view non-existent rule",
id: "non-existent",
token: validToken,
svcErr: errors.New("not found"),
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("ViewRule", mock.Anything, tc.session, tc.id, mock.Anything).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.ViewRule(context.Background(), tc.id, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUpdateRule(t *testing.T) {
rs, rsvc, auth := setupRules()
defer rs.Close()
conf := sdk.Config{
RulesEngineURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
updatedRule := testRule
updatedRule.Name = "updated-rule"
svcRule := re.Rule{
ID: ruleID,
Name: "updated-rule",
InputChannel: "chan-1",
InputTopic: "sensors/temperature",
Status: re.EnabledStatus,
Tags: []string{"temperature", "alerts"},
}
cases := []struct {
desc string
rule sdk.Rule
token string
session smqauthn.Session
svcRes re.Rule
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "update rule successfully",
rule: updatedRule,
token: validToken,
svcRes: svcRule,
},
{
desc: "update rule with empty token",
rule: updatedRule,
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("UpdateRule", mock.Anything, tc.session, mock.Anything).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.UpdateRule(context.Background(), tc.rule, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUpdateRuleTags(t *testing.T) {
rs, rsvc, auth := setupRules()
defer rs.Close()
conf := sdk.Config{
RulesEngineURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcRule := re.Rule{
ID: ruleID,
Tags: []string{"new-tag"},
}
cases := []struct {
desc string
rule sdk.Rule
token string
session smqauthn.Session
svcRes re.Rule
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "update rule tags successfully",
rule: sdk.Rule{ID: ruleID, Tags: []string{"new-tag"}},
token: validToken,
svcRes: svcRule,
},
{
desc: "update rule tags with empty token",
rule: sdk.Rule{ID: ruleID},
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("UpdateRuleTags", mock.Anything, tc.session, mock.Anything).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.UpdateRuleTags(context.Background(), tc.rule, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestUpdateRuleSchedule(t *testing.T) {
rs, rsvc, auth := setupRules()
defer rs.Close()
conf := sdk.Config{
RulesEngineURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcRule := re.Rule{
ID: ruleID,
}
cases := []struct {
desc string
rule sdk.Rule
token string
session smqauthn.Session
svcRes re.Rule
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "update rule schedule successfully",
rule: sdk.Rule{ID: ruleID, Schedule: map[string]any{"cron": "0 * * * *"}},
token: validToken,
svcRes: svcRule,
},
{
desc: "update rule schedule with empty token",
rule: sdk.Rule{ID: ruleID},
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("UpdateRuleSchedule", mock.Anything, tc.session, mock.Anything).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.UpdateRuleSchedule(context.Background(), tc.rule, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestListRules(t *testing.T) {
rs, rsvc, auth := setupRules()
defer rs.Close()
conf := sdk.Config{
RulesEngineURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcPage := re.Page{}
cases := []struct {
desc string
pm sdk.PageMetadata
token string
session smqauthn.Session
svcRes re.Page
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "list rules successfully",
pm: sdk.PageMetadata{Offset: 0, Limit: 10},
token: validToken,
svcRes: svcPage,
},
{
desc: "list rules with filters",
pm: sdk.PageMetadata{
Limit: 5,
Name: "temp",
Status: "enabled",
InputChannel: "chan-1",
Tag: "temperature",
Dir: "desc",
Order: "created_at",
},
token: validToken,
svcRes: svcPage,
},
{
desc: "list rules with empty metadata excludes filter params",
pm: sdk.PageMetadata{},
token: validToken,
svcRes: re.Page{},
},
{
desc: "list rules with empty token",
pm: sdk.PageMetadata{Limit: 10},
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("ListRules", mock.Anything, tc.session, mock.Anything).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.ListRules(context.Background(), tc.pm, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotNil(t, result)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestEnableRule(t *testing.T) {
rs, rsvc, auth := setupRules()
defer rs.Close()
conf := sdk.Config{
RulesEngineURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcRule := re.Rule{
ID: ruleID,
Status: re.EnabledStatus,
}
cases := []struct {
desc string
id string
token string
session smqauthn.Session
svcRes re.Rule
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "enable rule successfully",
id: ruleID,
token: validToken,
svcRes: svcRule,
},
{
desc: "enable rule with empty token",
id: ruleID,
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("EnableRule", mock.Anything, tc.session, tc.id).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.EnableRule(context.Background(), tc.id, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestDisableRule(t *testing.T) {
rs, rsvc, auth := setupRules()
defer rs.Close()
conf := sdk.Config{
RulesEngineURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
svcRule := re.Rule{
ID: ruleID,
Status: re.DisabledStatus,
}
cases := []struct {
desc string
id string
token string
session smqauthn.Session
svcRes re.Rule
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "disable rule successfully",
id: ruleID,
token: validToken,
svcRes: svcRule,
},
{
desc: "disable rule with empty token",
id: ruleID,
token: "",
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("DisableRule", mock.Anything, tc.session, tc.id).Return(tc.svcRes, tc.svcErr)
result, err := mgsdk.DisableRule(context.Background(), tc.id, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
if !tc.wantErr {
assert.NotEmpty(t, result.ID)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func TestRemoveRule(t *testing.T) {
rs, rsvc, auth := setupRules()
defer rs.Close()
conf := sdk.Config{
RulesEngineURL: rs.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
id string
token string
session smqauthn.Session
svcErr error
authenticateErr error
wantErr bool
}{
{
desc: "remove rule successfully",
id: ruleID,
token: validToken,
},
{
desc: "remove rule with empty token",
id: ruleID,
token: "",
wantErr: true,
},
{
desc: "remove non-existent rule",
id: "non-existent",
token: validToken,
svcErr: errors.New("not found"),
wantErr: true,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
if tc.token == validToken {
tc.session = smqauthn.Session{DomainUserID: domainID + "_" + validID, UserID: validID, DomainID: domainID}
}
authCall := auth.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authenticateErr)
svcCall := rsvc.On("RemoveRule", mock.Anything, tc.session, tc.id).Return(tc.svcErr)
err := mgsdk.RemoveRule(context.Background(), tc.id, domainID, tc.token)
assert.Equal(t, tc.wantErr, err != nil)
svcCall.Unset()
authCall.Unset()
})
}
}
-314
View File
@@ -1,314 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk_test
import (
"fmt"
"os"
"regexp"
"testing"
"time"
"github.com/absmach/magistrala/channels"
chmocks "github.com/absmach/magistrala/channels/mocks"
"github.com/absmach/magistrala/clients"
climocks "github.com/absmach/magistrala/clients/mocks"
"github.com/absmach/magistrala/domains"
groups "github.com/absmach/magistrala/groups"
"github.com/absmach/magistrala/internal/nullable"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/journal"
"github.com/absmach/magistrala/pkg/roles"
sdk "github.com/absmach/magistrala/pkg/sdk"
"github.com/absmach/magistrala/pkg/uuid"
"github.com/absmach/magistrala/users"
"github.com/stretchr/testify/assert"
)
const (
invalidIdentity = "invalididentity"
Identity = "identity"
Email = "email"
InvalidEmail = "invalidemail"
secret = "strongsecret"
invalidToken = "invalid"
contentType = sdk.CTJSON
invalid = "invalid"
wrongID = "wrongID"
roleName = "roleName"
)
var (
idProvider = uuid.New()
validMetadata = sdk.Metadata{"role": "client"}
user = generateTestUser(&testing.T{})
description = "shortdescription"
gName = "groupname"
validToken = "valid"
limit uint64 = 5
offset uint64 = 0
total uint64 = 200
passRegex = regexp.MustCompile("^.{8,}$")
validID = testsutil.GenerateUUID(&testing.T{})
clientsGRPCClient *climocks.ClientsServiceClient
channelsGRPCClient *chmocks.ChannelsServiceClient
)
func generateUUID(t *testing.T) string {
ulid, err := idProvider.ID()
assert.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
return ulid
}
func convertUsers(cs []sdk.User) []users.User {
ccs := []users.User{}
for _, c := range cs {
ccs = append(ccs, convertUser(c))
}
return ccs
}
func convertClients(cs ...sdk.Client) []clients.Client {
ccs := []clients.Client{}
for _, c := range cs {
ccs = append(ccs, convertClient(c))
}
return ccs
}
func convertGroups(cs []sdk.Group) []groups.Group {
cgs := []groups.Group{}
for _, c := range cs {
cgs = append(cgs, convertGroup(c))
}
return cgs
}
func convertChannels(cs []sdk.Channel) []channels.Channel {
chs := []channels.Channel{}
for _, c := range cs {
chs = append(chs, convertChannel(c))
}
return chs
}
func convertGroup(g sdk.Group) groups.Group {
if g.Status == "" {
g.Status = groups.EnabledStatus.String()
}
status, err := groups.ToStatus(g.Status)
if err != nil {
return groups.Group{}
}
var desc nullable.Value[string]
if g.Description != "" {
desc = nullable.New(g.Description)
}
return groups.Group{
ID: g.ID,
Domain: g.DomainID,
Parent: g.ParentID,
Name: g.Name,
Description: desc,
Tags: g.Tags,
Metadata: groups.Metadata(g.Metadata),
Level: g.Level,
Path: g.Path,
Children: convertChildren(g.Children),
CreatedAt: g.CreatedAt,
UpdatedAt: g.UpdatedAt,
Status: status,
RoleID: g.RoleID,
RoleName: g.RoleName,
Actions: g.Actions,
AccessType: g.AccessType,
AccessProviderId: g.AccessProviderId,
AccessProviderRoleId: g.AccessProviderRoleId,
AccessProviderRoleName: g.AccessProviderRoleName,
AccessProviderRoleActions: g.AccessProviderRoleActions,
}
}
func convertChildren(gs []*sdk.Group) []*groups.Group {
var cg []*groups.Group
if len(gs) == 0 {
return cg
}
for _, g := range gs {
insert := convertGroup(*g)
cg = append(cg, &insert)
}
return cg
}
func convertUser(c sdk.User) users.User {
if c.Status == "" {
c.Status = users.EnabledStatus.String()
}
status, err := users.ToStatus(c.Status)
if err != nil {
return users.User{}
}
role, err := users.ToRole(c.Role)
if err != nil {
return users.User{}
}
return users.User{
ID: c.ID,
FirstName: c.FirstName,
LastName: c.LastName,
Tags: c.Tags,
Email: c.Email,
Credentials: users.Credentials(c.Credentials),
Metadata: users.Metadata(c.Metadata),
PrivateMetadata: users.Metadata(c.PrivateMetadata),
CreatedAt: c.CreatedAt,
UpdatedAt: c.UpdatedAt,
Status: status,
Role: role,
ProfilePicture: c.ProfilePicture,
}
}
func convertClient(c sdk.Client) clients.Client {
if c.Status == "" {
c.Status = clients.EnabledStatus.String()
}
status, err := clients.ToStatus(c.Status)
if err != nil {
return clients.Client{}
}
return clients.Client{
ID: c.ID,
Name: c.Name,
Tags: c.Tags,
Domain: c.DomainID,
ParentGroup: c.ParentGroup,
Credentials: clients.Credentials(c.Credentials),
Metadata: clients.Metadata(c.Metadata),
PrivateMetadata: clients.Metadata(c.PrivateMetadata),
CreatedAt: c.CreatedAt,
UpdatedAt: c.UpdatedAt,
UpdatedBy: c.UpdatedBy,
Status: status,
}
}
func convertChannel(g sdk.Channel) channels.Channel {
if g.Status == "" {
g.Status = channels.EnabledStatus.String()
}
status, err := channels.ToStatus(g.Status)
if err != nil {
return channels.Channel{}
}
return channels.Channel{
ID: g.ID,
Name: g.Name,
Tags: g.Tags,
ParentGroup: g.ParentGroup,
Route: g.Route,
Domain: g.DomainID,
Metadata: channels.Metadata(g.Metadata),
CreatedAt: g.CreatedAt,
UpdatedAt: g.UpdatedAt,
UpdatedBy: g.UpdatedBy,
Status: status,
}
}
func convertInvitation(i sdk.Invitation) domains.Invitation {
return domains.Invitation{
InvitedBy: i.InvitedBy,
InviteeUserID: i.InviteeUserID,
DomainID: i.DomainID,
RoleID: i.RoleID,
RoleName: i.RoleName,
Actions: i.Actions,
CreatedAt: i.CreatedAt,
UpdatedAt: i.UpdatedAt,
ConfirmedAt: i.ConfirmedAt,
RejectedAt: i.RejectedAt,
}
}
func convertJournal(j sdk.Journal) journal.Journal {
return journal.Journal{
ID: j.ID,
Operation: j.Operation,
OccurredAt: j.OccurredAt,
Attributes: j.Attributes,
Metadata: j.Metadata,
}
}
func generateTestUser(t *testing.T) sdk.User {
createdAt, err := time.Parse(time.RFC3339, "2024-01-01T00:00:00Z")
assert.Nil(t, err, fmt.Sprintf("Unexpected error parsing time: %v", err))
return sdk.User{
ID: generateUUID(t),
FirstName: "userfirstname",
LastName: "userlastname",
Email: "useremail@example.com",
Credentials: sdk.Credentials{
Username: "username",
Secret: secret,
},
Tags: []string{"tag1", "tag2"},
Metadata: validMetadata,
PrivateMetadata: validMetadata,
CreatedAt: createdAt,
UpdatedAt: createdAt,
Status: users.EnabledStatus.String(),
Role: users.UserRole.String(),
}
}
func convertRole(r roles.Role) sdk.Role {
return sdk.Role{
ID: r.ID,
Name: r.Name,
EntityID: r.EntityID,
CreatedBy: r.CreatedBy,
CreatedAt: r.CreatedAt,
UpdatedBy: r.UpdatedBy,
UpdatedAt: r.UpdatedAt,
}
}
func convertRoleProvision(r roles.RoleProvision) sdk.Role {
return sdk.Role{
ID: r.ID,
Name: r.Name,
EntityID: r.EntityID,
CreatedBy: r.CreatedBy,
CreatedAt: r.CreatedAt,
UpdatedBy: r.UpdatedBy,
UpdatedAt: r.UpdatedAt,
OptionalActions: r.OptionalActions,
OptionalMembers: r.OptionalMembers,
}
}
func TestMain(m *testing.M) {
exitCode := m.Run()
os.Exit(exitCode)
}
-189
View File
@@ -1,189 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk_test
import (
"context"
"net/http"
"testing"
grpcTokenV1 "github.com/absmach/magistrala/api/grpc/token/v1"
apiutil "github.com/absmach/magistrala/api/http/util"
smqauth "github.com/absmach/magistrala/auth"
smqauthn "github.com/absmach/magistrala/pkg/authn"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
sdk "github.com/absmach/magistrala/pkg/sdk"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func TestIssueToken(t *testing.T) {
ts, svc, _ := setupUsers()
defer ts.Close()
client := generateTestUser(t)
token := generateTestToken()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
login sdk.Login
svcRes *grpcTokenV1.Token
svcErr error
response sdk.Token
err errors.SDKError
}{
{
desc: "issue token successfully",
login: sdk.Login{
Username: client.Credentials.Username,
Password: client.Credentials.Secret,
},
svcRes: &grpcTokenV1.Token{
AccessToken: token.AccessToken,
RefreshToken: &token.RefreshToken,
AccessType: smqauth.AccessKey.String(),
},
svcErr: nil,
response: token,
err: nil,
},
{
desc: "issue token with invalid identity",
login: sdk.Login{
Username: invalidIdentity,
Password: client.Credentials.Secret,
},
svcRes: &grpcTokenV1.Token{},
svcErr: svcerr.ErrAuthentication,
response: sdk.Token{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "issue token with invalid secret",
login: sdk.Login{
Username: client.Credentials.Username,
Password: "invalid",
},
svcRes: &grpcTokenV1.Token{},
svcErr: svcerr.ErrLogin,
response: sdk.Token{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrLogin, http.StatusUnauthorized),
},
{
desc: "issue token with empty identity",
login: sdk.Login{
Username: "",
Password: client.Credentials.Secret,
},
svcRes: &grpcTokenV1.Token{},
svcErr: nil,
response: sdk.Token{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingUsernameEmail, http.StatusBadRequest),
},
{
desc: "issue token with empty secret",
login: sdk.Login{
Username: client.Credentials.Username,
Password: "",
},
svcRes: &grpcTokenV1.Token{},
svcErr: nil,
response: sdk.Token{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingPass, http.StatusBadRequest),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
svcCall := svc.On("IssueToken", mock.Anything, tc.login.Username, tc.login.Password, tc.login.Description).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.CreateToken(context.Background(), tc.login)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "IssueToken", mock.Anything, tc.login.Username, tc.login.Password, tc.login.Description)
assert.True(t, ok)
}
svcCall.Unset()
})
}
}
func TestRefreshToken(t *testing.T) {
ts, svc, auth := setupUsers()
defer ts.Close()
token := generateTestToken()
conf := sdk.Config{
UsersURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
cases := []struct {
desc string
token string
svcRes *grpcTokenV1.Token
svcErr error
identifyErr error
response sdk.Token
err errors.SDKError
}{
{
desc: "refresh token successfully",
token: token.RefreshToken,
svcRes: &grpcTokenV1.Token{
AccessToken: token.AccessToken,
RefreshToken: &token.RefreshToken,
AccessType: token.AccessType,
},
response: token,
err: nil,
},
{
desc: "refresh token with invalid token",
token: invalidToken,
svcRes: nil,
identifyErr: svcerr.ErrAuthentication,
response: sdk.Token{},
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthentication, http.StatusUnauthorized),
},
{
desc: "refresh token with empty token",
token: "",
response: sdk.Token{},
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
authCall := auth.On("Authenticate", mock.Anything, mock.Anything).Return(smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: validID}, tc.identifyErr)
svcCall := svc.On("RefreshToken", mock.Anything, smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: validID}, tc.token).Return(tc.svcRes, tc.svcErr)
resp, err := mgsdk.RefreshToken(context.Background(), tc.token)
assert.Equal(t, tc.err, err)
assert.Equal(t, tc.response, resp)
if tc.err == nil {
ok := svcCall.Parent.AssertCalled(t, "RefreshToken", mock.Anything, smqauthn.Session{DomainUserID: validID, UserID: validID, DomainID: validID}, tc.token)
assert.True(t, ok)
}
svcCall.Unset()
authCall.Unset()
})
}
}
func generateTestToken() sdk.Token {
return sdk.Token{
AccessToken: "access_token",
RefreshToken: "refresh_token",
AccessType: smqauth.AccessKey.String(),
}
}
-160
View File
@@ -1,160 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package sdk_test
import (
"context"
"net"
"net/http"
"net/http/httptest"
"strings"
"sync/atomic"
"testing"
"github.com/absmach/magistrala/pkg/sdk"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestTransport verifies that IdleConnTimeout=90s keeps connections pooled for
// healthy servers, and that network errors (EOF, reset) surface as descriptive errors.
func TestTransport(t *testing.T) {
cases := []struct {
desc string
serverFunc func(t *testing.T) (url string, cleanup func())
ctxFunc func() context.Context
wantErr bool
errContains string
}{
{
desc: "make request successfully with connection reuse",
serverFunc: func(t *testing.T) (string, func()) {
t.Helper()
var connCount atomic.Int32
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"clients":[{"id":"1","name":"test-client"}]}`))
}))
srv.Config.ConnState = func(_ net.Conn, state http.ConnState) {
if state == http.StateNew {
connCount.Add(1)
}
}
srv.Start()
return srv.URL, func() {
srv.Close()
assert.Equal(t, int32(1), connCount.Load(), "expected connections to be reused (keep-alives enabled)")
}
},
wantErr: false,
},
{
desc: "make request with server closing connection",
serverFunc: func(t *testing.T) (string, func()) {
t.Helper()
ln, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
go func() {
for {
conn, err := ln.Accept()
if err != nil {
return
}
conn.Close()
}
}()
return "http://" + ln.Addr().String(), func() { ln.Close() }
},
wantErr: true,
errContains: "request failed",
},
{
desc: "make request with connection reset by peer",
serverFunc: func(t *testing.T) (string, func()) {
t.Helper()
ln, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
go func() {
for {
conn, err := ln.Accept()
if err != nil {
return
}
tcpConn, ok := conn.(*net.TCPConn)
if ok {
_ = tcpConn.SetLinger(0)
}
conn.Close()
}
}()
return "http://" + ln.Addr().String(), func() { ln.Close() }
},
wantErr: true,
errContains: "request failed",
},
{
desc: "make request with unreachable server",
serverFunc: func(t *testing.T) (string, func()) {
t.Helper()
ln, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
addr := ln.Addr().String()
ln.Close()
return "http://" + addr, func() {}
},
wantErr: true,
errContains: "request failed",
},
{
desc: "make request with cancelled context",
serverFunc: func(t *testing.T) (string, func()) {
t.Helper()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}))
return srv.URL, srv.Close
},
ctxFunc: func() context.Context {
ctx, cancel := context.WithCancel(context.Background())
cancel()
return ctx
},
wantErr: true,
errContains: "request failed",
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
url, cleanup := tc.serverFunc(t)
defer cleanup()
smqsdk := sdk.NewSDK(sdk.Config{ClientsURL: url})
ctx := context.Background()
if tc.ctxFunc != nil {
ctx = tc.ctxFunc()
}
client := sdk.Client{Name: "test-client"}
for i := 0; i < 2; i++ {
_, err := smqsdk.CreateClients(ctx, []sdk.Client{client}, domainID, validToken)
if tc.wantErr {
require.Error(t, err)
if tc.errContains != "" {
assert.True(t, strings.Contains(err.Error(), tc.errContains),
"expected error %q to contain %q", err.Error(), tc.errContains)
}
break
}
require.NoError(t, err)
}
})
}
}
File diff suppressed because it is too large Load Diff
-970
View File
@@ -1,970 +0,0 @@
//go:build oldservices
// +build oldservices
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package http_test
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
grpcChannelsV1 "github.com/absmach/magistrala/api/grpc/channels/v1"
grpcClientsV1 "github.com/absmach/magistrala/api/grpc/clients/v1"
apiutil "github.com/absmach/magistrala/api/http/util"
chmocks "github.com/absmach/magistrala/channels/mocks"
climocks "github.com/absmach/magistrala/clients/mocks"
"github.com/absmach/magistrala/internal/testsutil"
smqauthn "github.com/absmach/magistrala/pkg/authn"
authnmocks "github.com/absmach/magistrala/pkg/authn/mocks"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/absmach/magistrala/pkg/transformers/senml"
"github.com/absmach/magistrala/readers"
customhttp "github.com/absmach/magistrala/readers/api/http"
"github.com/absmach/magistrala/readers/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
const (
svcName = "test-service"
clientToken = "1"
userToken = "token"
invalidToken = "invalid"
numOfMessages = 100
valueFields = 5
subtopic = "topic"
mqttProt = "mqtt"
httpProt = "http"
msgName = "temperature"
instanceID = "5de9b29a-feb9-11ed-be56-0242ac120002"
domainID = "b4d7d79e-fd99-4c2b-ac09-524e43df6888"
)
var (
v float64 = 5
vs = "value"
vb = true
vd = "dataValue"
sum float64 = 42
validSession = smqauthn.Session{UserID: testsutil.GenerateUUID(&testing.T{})}
)
func newServer(repo *mocks.MessageRepository, authn *authnmocks.Authentication, clients *climocks.ClientsServiceClient, channels *chmocks.ChannelsServiceClient) *httptest.Server {
mux := customhttp.MakeHandler(repo, authn, clients, channels, svcName, instanceID)
return httptest.NewServer(mux)
}
type testRequest struct {
client *http.Client
method string
url string
token string
key string
}
func (tr testRequest) make() (*http.Response, error) {
req, err := http.NewRequest(tr.method, tr.url, http.NoBody)
if err != nil {
return nil, err
}
if tr.token != "" {
req.Header.Set("Authorization", apiutil.BearerPrefix+tr.token)
}
if tr.key != "" {
req.Header.Set("Authorization", apiutil.ClientPrefix+tr.key)
}
return tr.client.Do(req)
}
func TestReadAll(t *testing.T) {
chanID := testsutil.GenerateUUID(t)
pubID := testsutil.GenerateUUID(t)
pubID2 := testsutil.GenerateUUID(t)
now := time.Now().Unix()
var messages []senml.Message
var queryMsgs []senml.Message
var valueMsgs []senml.Message
var boolMsgs []senml.Message
var stringMsgs []senml.Message
var dataMsgs []senml.Message
for i := 0; i < numOfMessages; i++ {
// Mix possible values as well as value sum.
msg := senml.Message{
Channel: chanID,
Publisher: pubID,
Protocol: mqttProt,
Time: float64(now - int64(i)),
Name: "name",
}
count := i % valueFields
switch count {
case 0:
msg.Value = &v
valueMsgs = append(valueMsgs, msg)
case 1:
msg.BoolValue = &vb
boolMsgs = append(boolMsgs, msg)
case 2:
msg.StringValue = &vs
stringMsgs = append(stringMsgs, msg)
case 3:
msg.DataValue = &vd
dataMsgs = append(dataMsgs, msg)
case 4:
msg.Sum = &sum
msg.Subtopic = subtopic
msg.Protocol = httpProt
msg.Publisher = pubID2
msg.Name = msgName
queryMsgs = append(queryMsgs, msg)
}
messages = append(messages, msg)
}
repo := new(mocks.MessageRepository)
authn := new(authnmocks.Authentication)
clients := new(climocks.ClientsServiceClient)
channels := new(chmocks.ChannelsServiceClient)
ts := newServer(repo, authn, clients, channels)
defer ts.Close()
cases := []struct {
desc string
req string
url string
token string
key string
status int
res pageRes
authnErr error
authzRes *grpcChannelsV1.AuthzRes
authzErr error
err error
}{
{
desc: "read page with valid offset and limit",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Order: "time", Dir: "desc"},
Total: uint64(len(messages)),
Messages: messages[0:10],
},
},
{
desc: "read page with valid offset and limit as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Order: "time", Dir: "desc"},
Total: uint64(len(messages)),
Messages: messages[0:10],
},
},
{
desc: "read page with negative offset as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=-1&limit=10", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with negative limit as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=-10", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with zero limit as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=0", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with non-integer offset as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=abc&limit=10", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with non-integer limit as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=abc", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with invalid channel id as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, ""),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with multiple offset as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&offset=1&limit=10", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with multiple limit as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=20&limit=10", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with empty token as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: "",
authnErr: svcerr.ErrAuthentication,
status: http.StatusUnauthorized,
err: svcerr.ErrAuthentication,
},
{
desc: "read page with default offset as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?limit=10", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Order: "time", Dir: "desc"},
Total: uint64(len(messages)),
Messages: messages[0:10],
},
},
{
desc: "read page with default limit as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Order: "time", Dir: "desc"},
Total: uint64(len(messages)),
Messages: messages[0:10],
},
},
{
desc: "read page with senml format as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?format=messages", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Order: "time", Dir: "desc"},
Total: uint64(len(messages)),
Messages: messages[0:10],
},
},
{
desc: "read page with subtopic as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, domainID, chanID, subtopic, httpProt),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Subtopic: subtopic, Format: "messages", Protocol: httpProt, Order: "time", Dir: "desc"},
Total: uint64(len(queryMsgs)),
Messages: queryMsgs[0:10],
},
},
{
desc: "read page with subtopic and protocol as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, domainID, chanID, subtopic, httpProt),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Subtopic: subtopic, Format: "messages", Protocol: httpProt, Order: "time", Dir: "desc"},
Total: uint64(len(queryMsgs)),
Messages: queryMsgs[0:10],
},
},
{
desc: "read page with publisher as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?publisher=%s", ts.URL, domainID, chanID, pubID2),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Publisher: pubID2, Order: "time", Dir: "desc"},
Total: uint64(len(queryMsgs)),
Messages: queryMsgs[0:10],
},
},
{
desc: "read page with protocol as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?protocol=http", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Protocol: httpProt, Order: "time", Dir: "desc"},
Total: uint64(len(queryMsgs)),
Messages: queryMsgs[0:10],
},
},
{
desc: "read page with name as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?name=%s", ts.URL, domainID, chanID, msgName),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Name: msgName, Order: "time", Dir: "desc"},
Total: uint64(len(queryMsgs)),
Messages: queryMsgs[0:10],
},
},
{
desc: "read page with value as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f", ts.URL, domainID, chanID, v),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Value: v, Order: "time", Dir: "desc"},
Total: uint64(len(valueMsgs)),
Messages: valueMsgs[0:10],
},
},
{
desc: "read page with value and equal comparator as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v, readers.EqualKey),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Value: v, Comparator: readers.EqualKey, Order: "time", Dir: "desc"},
Total: uint64(len(valueMsgs)),
Messages: valueMsgs[0:10],
},
},
{
desc: "read page with value and lower-than comparator as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v+1, readers.LowerThanKey),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Value: v + 1, Comparator: readers.LowerThanKey, Order: "time", Dir: "desc"},
Total: uint64(len(valueMsgs)),
Messages: valueMsgs[0:10],
},
},
{
desc: "read page with value and lower-than-or-equal comparator as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v+1, readers.LowerThanEqualKey),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Value: v + 1, Comparator: readers.LowerThanEqualKey, Order: "time", Dir: "desc"},
Total: uint64(len(valueMsgs)),
Messages: valueMsgs[0:10],
},
},
{
desc: "read page with value and greater-than comparator as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v-1, readers.GreaterThanKey),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Value: v - 1, Comparator: readers.GreaterThanKey, Order: "time", Dir: "desc"},
Total: uint64(len(valueMsgs)),
Messages: valueMsgs[0:10],
},
},
{
desc: "read page with value and greater-than-or-equal comparator as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v-1, readers.GreaterThanEqualKey),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Value: v - 1, Comparator: readers.GreaterThanEqualKey, Order: "time", Dir: "desc"},
Total: uint64(len(valueMsgs)),
Messages: valueMsgs[0:10],
},
},
{
desc: "read page with non-float value as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=ab01", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with value and wrong comparator as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=wrong", ts.URL, domainID, chanID, v-1),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with boolean value as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?vb=true", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", BoolValue: true, Order: "time", Dir: "desc"},
Total: uint64(len(boolMsgs)),
Messages: boolMsgs[0:10],
},
},
{
desc: "read page with non-boolean value as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?vb=yes", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with string value as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?vs=%s", ts.URL, domainID, chanID, vs),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", StringValue: vs, Order: "time", Dir: "desc"},
Total: uint64(len(stringMsgs)),
Messages: stringMsgs[0:10],
},
},
{
desc: "read page with data value as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?vd=%s", ts.URL, domainID, chanID, vd),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", DataValue: vd, Order: "time", Dir: "desc"},
Total: uint64(len(dataMsgs)),
Messages: dataMsgs[0:10],
},
},
{
desc: "read page with non-float from as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?from=ABCD", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with non-float to as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?to=ABCD", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with from/to as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?from=%f&to=%f", ts.URL, domainID, chanID, messages[19].Time, messages[4].Time),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", From: messages[19].Time, To: messages[4].Time, Order: "time", Dir: "desc"},
Total: uint64(len(messages[5:20])),
Messages: messages[5:15],
},
},
{
desc: "read page with aggregation as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with interval as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?interval=10h", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Order: "time", Dir: "desc"},
Total: uint64(len(messages)),
Messages: messages[0:10],
},
},
{
desc: "read page with aggregation and interval as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h", ts.URL, domainID, chanID),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval, to and from as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&from=%f&to=%f", ts.URL, domainID, chanID, messages[19].Time, messages[4].Time),
key: clientToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Aggregation: "MAX", Interval: "10h", From: messages[19].Time, To: messages[4].Time, Order: "time", Dir: "desc"},
Total: uint64(len(messages[5:20])),
Messages: messages[5:15],
},
},
{
desc: "read page with invalid aggregation and valid interval, to and from as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=invalid&interval=10h&from=%f&to=%f", ts.URL, domainID, chanID, messages[19].Time, messages[4].Time),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with invalid interval and valid aggregation, to and from as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10hrs&from=%f&to=%f", ts.URL, domainID, chanID, messages[19].Time, messages[4].Time),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval and to with missing from as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&to=%f", ts.URL, domainID, chanID, messages[4].Time),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval and to with invalid from as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&to=ABCD&from=%f", ts.URL, domainID, chanID, messages[4].Time),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval and to with invalid to as client",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&from=%f&to=ABCD", ts.URL, domainID, chanID, messages[4].Time),
key: clientToken,
status: http.StatusBadRequest,
},
{
desc: "read page with valid offset and limit as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Order: "time", Dir: "desc"},
Total: uint64(len(messages)),
Messages: messages[0:10],
},
},
{
desc: "read page with invalid client key",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
key: "invalid",
authnErr: svcerr.ErrAuthentication,
status: http.StatusUnauthorized,
err: svcerr.ErrAuthentication,
},
{
desc: "read page with unauthorized client key",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
key: clientToken,
authnErr: nil,
authzRes: &grpcChannelsV1.AuthzRes{Authorized: false},
status: http.StatusForbidden,
err: svcerr.ErrAuthorization,
},
{
desc: "read page with negative offset as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=-1&limit=10", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with negative limit as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=-10", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with zero limit as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=0", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with non-integer offset as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=abc&limit=10", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with non-integer limit as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=abc", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with invalid channel id as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, ""),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with invalid token as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: invalidToken,
authnErr: svcerr.ErrAuthentication,
status: http.StatusUnauthorized,
err: svcerr.ErrAuthorization,
},
{
desc: "read page with unauthorized as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: userToken,
authzRes: &grpcChannelsV1.AuthzRes{Authorized: false},
status: http.StatusForbidden,
err: svcerr.ErrAuthorization,
},
{
desc: "read page with multiple offset as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&offset=1&limit=10", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with multiple limit as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=20&limit=10", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with empty token as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: "",
authnErr: svcerr.ErrAuthentication,
status: http.StatusUnauthorized,
err: svcerr.ErrAuthorization,
},
{
desc: "read page with default offset as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?limit=10", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Order: "time", Dir: "desc"},
Total: uint64(len(messages)),
Messages: messages[0:10],
},
},
{
desc: "read page with default limit as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Order: "time", Dir: "desc"},
Total: uint64(len(messages)),
Messages: messages[0:10],
},
},
{
desc: "read page with senml format as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?format=messages", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Order: "time", Dir: "desc"},
Total: uint64(len(messages)),
Messages: messages[0:10],
},
},
{
desc: "read page with subtopic as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, domainID, chanID, subtopic, httpProt),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Subtopic: subtopic, Protocol: httpProt, Order: "time", Dir: "desc"},
Total: uint64(len(queryMsgs)),
Messages: queryMsgs[0:10],
},
},
{
desc: "read page with subtopic and protocol as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?subtopic=%s&protocol=%s", ts.URL, domainID, chanID, subtopic, httpProt),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Subtopic: subtopic, Protocol: httpProt, Order: "time", Dir: "desc"},
Total: uint64(len(queryMsgs)),
Messages: queryMsgs[0:10],
},
},
{
desc: "read page with publisher as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?publisher=%s", ts.URL, domainID, chanID, pubID2),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Publisher: pubID2, Order: "time", Dir: "desc"},
Total: uint64(len(queryMsgs)),
Messages: queryMsgs[0:10],
},
},
{
desc: "read page with protocol as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?protocol=http", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Protocol: httpProt, Order: "time", Dir: "desc"},
Total: uint64(len(queryMsgs)),
Messages: queryMsgs[0:10],
},
},
{
desc: "read page with name as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?name=%s", ts.URL, domainID, chanID, msgName),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Name: msgName, Order: "time", Dir: "desc"},
Total: uint64(len(queryMsgs)),
Messages: queryMsgs[0:10],
},
},
{
desc: "read page with value as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f", ts.URL, domainID, chanID, v),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Value: v, Order: "time", Dir: "desc"},
Total: uint64(len(valueMsgs)),
Messages: valueMsgs[0:10],
},
},
{
desc: "read page with value and equal comparator as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v, readers.EqualKey),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Value: v, Comparator: readers.EqualKey, Order: "time", Dir: "desc"},
Total: uint64(len(valueMsgs)),
Messages: valueMsgs[0:10],
},
},
{
desc: "read page with value and lower-than comparator as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v+1, readers.LowerThanKey),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Value: v + 1, Comparator: readers.LowerThanKey, Order: "time", Dir: "desc"},
Total: uint64(len(valueMsgs)),
Messages: valueMsgs[0:10],
},
},
{
desc: "read page with value and lower-than-or-equal comparator as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v+1, readers.LowerThanEqualKey),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Value: v + 1, Comparator: readers.LowerThanEqualKey, Order: "time", Dir: "desc"},
Total: uint64(len(valueMsgs)),
Messages: valueMsgs[0:10],
},
},
{
desc: "read page with value and greater-than comparator as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v-1, readers.GreaterThanKey),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Order: "time", Dir: "desc", Format: "messages", Value: v - 1, Comparator: readers.GreaterThanKey},
Total: uint64(len(valueMsgs)),
Messages: valueMsgs[0:10],
},
},
{
desc: "read page with value and greater-than-or-equal comparator as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=%s", ts.URL, domainID, chanID, v-1, readers.GreaterThanEqualKey),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Order: "time", Dir: "desc", Limit: 10, Format: "messages", Value: v - 1, Comparator: readers.GreaterThanEqualKey},
Total: uint64(len(valueMsgs)),
Messages: valueMsgs[0:10],
},
},
{
desc: "read page with non-float value as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=ab01", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with value and wrong comparator as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?v=%f&comparator=wrong", ts.URL, domainID, chanID, v-1),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with boolean value as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?vb=true", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", BoolValue: true, Order: "time", Dir: "desc"},
Total: uint64(len(boolMsgs)),
Messages: boolMsgs[0:10],
},
},
{
desc: "read page with non-boolean value as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?vb=yes", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with string value as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?vs=%s", ts.URL, domainID, chanID, vs),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", StringValue: vs, Order: "time", Dir: "desc"},
Total: uint64(len(stringMsgs)),
Messages: stringMsgs[0:10],
},
},
{
desc: "read page with data value as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?vd=%s", ts.URL, domainID, chanID, vd),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", DataValue: vd, Order: "time", Dir: "desc"},
Total: uint64(len(dataMsgs)),
Messages: dataMsgs[0:10],
},
},
{
desc: "read page with non-float from as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?from=ABCD", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with non-float to as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?to=ABCD", ts.URL, domainID, chanID),
token: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with from/to as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?from=%f&to=%f", ts.URL, domainID, chanID, messages[19].Time, messages[4].Time),
token: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", From: messages[19].Time, To: messages[4].Time, Order: "time", Dir: "desc"},
Total: uint64(len(messages[5:20])),
Messages: messages[5:15],
},
},
{
desc: "read page with aggregation as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX", ts.URL, domainID, chanID),
key: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with interval as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?interval=10h", ts.URL, domainID, chanID),
key: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Order: "time", Dir: "desc"},
Total: uint64(len(messages)),
Messages: messages[0:10],
},
},
{
desc: "read page with aggregation and interval as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h", ts.URL, domainID, chanID),
key: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval, to and from as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&from=%f&to=%f", ts.URL, domainID, chanID, messages[19].Time, messages[4].Time),
key: userToken,
status: http.StatusOK,
res: pageRes{
PageMetadata: readers.PageMetadata{Limit: 10, Format: "messages", Aggregation: "MAX", Interval: "10h", From: messages[19].Time, To: messages[4].Time, Order: "time", Dir: "desc"},
Total: uint64(len(messages[5:20])),
Messages: messages[5:15],
},
},
{
desc: "read page with invalid aggregation and valid interval, to and from as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=invalid&interval=10h&from=%f&to=%f", ts.URL, domainID, chanID, messages[19].Time, messages[4].Time),
key: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with invalid interval and valid aggregation, to and from as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10hrs&from=%f&to=%f", ts.URL, domainID, chanID, messages[19].Time, messages[4].Time),
key: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval and to with missing from as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&to=%f", ts.URL, domainID, chanID, messages[4].Time),
key: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval and to with invalid from as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&to=ABCD&from=%f", ts.URL, domainID, chanID, messages[4].Time),
key: userToken,
status: http.StatusBadRequest,
},
{
desc: "read page with aggregation, interval and to with invalid to as user",
url: fmt.Sprintf("%s/%s/channels/%s/messages?aggregation=MAX&interval=10h&from=%f&to=ABCD", ts.URL, domainID, chanID, messages[4].Time),
key: userToken,
status: http.StatusBadRequest,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
authnCall := authn.On("Authenticate", mock.Anything, tc.token).Return(validSession, tc.authnErr)
if tc.key != "" {
authnCall = clients.On("Authenticate", mock.Anything, &grpcClientsV1.AuthnReq{
Token: smqauthn.AuthPack(smqauthn.DomainAuth, domainID, tc.key),
}).Return(&grpcClientsV1.AuthnRes{Id: testsutil.GenerateUUID(t), Authenticated: true}, tc.authnErr)
}
if tc.authzRes == nil {
tc.authzRes = &grpcChannelsV1.AuthzRes{Authorized: true}
}
authzCall := channels.On("Authorize", mock.Anything, mock.Anything).Return(tc.authzRes, tc.authzErr)
repoCall := repo.On("ReadAll", chanID, tc.res.PageMetadata).Return(readers.MessagesPage{Total: tc.res.Total, Messages: fromSenml(tc.res.Messages)}, nil)
req := testRequest{
client: ts.Client(),
method: http.MethodGet,
url: tc.url,
token: tc.token,
key: tc.key,
}
res, err := req.make()
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
var page pageRes
err = json.NewDecoder(res.Body).Decode(&page)
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected %d got %d", tc.desc, tc.status, res.StatusCode))
assert.Equal(t, tc.res.Total, page.Total, fmt.Sprintf("%s: expected %d got %d", tc.desc, tc.res.Total, page.Total))
assert.ElementsMatch(t, tc.res.Messages, page.Messages, fmt.Sprintf("%s: got incorrect body from response", tc.desc))
authzCall.Unset()
authnCall.Unset()
repoCall.Unset()
})
}
}
type pageRes struct {
readers.PageMetadata
Total uint64 `json:"total"`
Messages []senml.Message `json:"messages"`
}
func fromSenml(in []senml.Message) []readers.Message {
var ret []readers.Message
for _, m := range in {
ret = append(ret, m)
}
return ret
}