NOISSUE - Implement Thing Delete (#179)

* add: delete function in things interface

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: remove things

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: things event streams

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: things delete test

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: delete thing http transport

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: delete thing sdk, sdk_test, cli

Signed-off-by: Arvindh <arvindh91@gmail.com>

* gofumpt -ed

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: openapi

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: things change status response

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: openapi

Signed-off-by: Arvindh <arvindh91@gmail.com>

* rename events: from thing delete to thing remove

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix wordings

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix wordings in openapi

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: type check

Signed-off-by: Arvindh <arvindh91@gmail.com>

* update open api yaml

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix things mocks

Signed-off-by: Arvindh <arvindh91@gmail.com>

---------

Signed-off-by: Arvindh <arvindh91@gmail.com>
This commit is contained in:
Arvindh
2023-12-21 00:25:38 +05:30
committed by GitHub
parent 822515e9cf
commit 1fe2e74a74
23 changed files with 471 additions and 46 deletions
+21 -5
View File
@@ -169,7 +169,26 @@ paths:
description: Missing or invalid content type.
"500":
$ref: "#/components/responses/ServiceError"
delete:
summary: Delete thing for a thing with the given id.
description: |
Delete thing removes a thing with the given id from repo
and removes all the policies related to this thing.
tags:
- Things
parameters:
- $ref: "#/components/parameters/ThingID"
security:
- bearerAuth: []
responses:
"204":
description: Thing deleted.
"401":
description: Missing or invalid access token provided.
"403":
description: Unauthorized access to thing id.
"500":
$ref: "#/components/responses/ServiceError"
/things/{thingID}/tags:
patch:
summary: Updates tags the thing.
@@ -479,7 +498,7 @@ paths:
- bearerAuth: []
responses:
"204":
$ref: "#/components/responses/ChannelDeleteRes"
description: Channel deleted.
"401":
description: Missing or invalid access token provided.
"403":
@@ -1771,9 +1790,6 @@ components:
schema:
$ref: "#/components/schemas/Channel"
ChannelDeleteRes:
description: Channel Deleted.
ChannelPageRes:
description: Data retrieved.
content:
+20
View File
@@ -571,6 +571,26 @@ paths:
description: Group does not exist.
"500":
$ref: "#/components/responses/ServiceError"
delete:
summary: Delete group for a group with the given id.
description: |
Delete group removes a group with the given id from repo
and removes all the policies related to this group.
tags:
- Groups
parameters:
- $ref: "#/components/parameters/GroupID"
security:
- bearerAuth: []
responses:
"204":
description: Group deleted.
"401":
description: Missing or invalid access token provided.
"403":
description: Unauthorized access to group id.
"500":
$ref: "#/components/responses/ServiceError"
/groups/{groupID}/children:
get:
+2 -15
View File
@@ -9,7 +9,6 @@ import (
"time"
"github.com/absmach/magistrala/bootstrap"
"github.com/absmach/magistrala/pkg/clients"
"github.com/absmach/magistrala/pkg/events"
)
@@ -61,20 +60,8 @@ func (es *eventHandler) Handle(ctx context.Context, event events.Event) error {
}
func decodeRemoveThing(event map[string]interface{}) removeEvent {
status := read(event, "status", "")
st, err := clients.ToStatus(status)
if err != nil {
return removeEvent{}
}
switch st {
case clients.EnabledStatus:
return removeEvent{}
case clients.DisabledStatus:
return removeEvent{
id: read(event, "id", ""),
}
default:
return removeEvent{}
return removeEvent{
id: read(event, "id", ""),
}
}
+18
View File
@@ -81,6 +81,24 @@ var cmdThings = []cobra.Command{
logJSON(t)
},
},
{
Use: "delete <thing_id> <user_auth_token>",
Short: "Delete thing",
Long: "Delete thing by id\n" +
"Usage:\n" +
"\tmagistrala-cli things delete <thing_id> $USERTOKEN - delete thing with <thing_id>\n",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 2 {
logUsage(cmd.Use)
return
}
if err := sdk.DeleteThing(args[0], args[1]); err != nil {
logError(err)
return
}
logOK()
},
},
{
Use: "identify <thing_key>",
Short: "Identify thing",
+7
View File
@@ -453,6 +453,13 @@ type SDK interface {
// fmt.Println(users)
ListThingUsers(thingID string, pm PageMetadata, token string) (UsersPage, errors.SDKError)
// DeleteThing deletes a thing with the given id.
//
// example:
// err := sdk.DeleteThing("thingID", "token")
// fmt.Println(err)
DeleteThing(id, token string) errors.SDKError
// CreateGroup creates new group and returns its id.
//
// example:
+6
View File
@@ -294,3 +294,9 @@ func (sdk mgSDK) ListThingUsers(thingID string, pm PageMetadata, token string) (
return up, nil
}
func (sdk mgSDK) DeleteThing(id, token string) errors.SDKError {
url := fmt.Sprintf("%s/%s/%s", sdk.thingsURL, thingsEndpoint, id)
_, _, sdkerr := sdk.processRequest(http.MethodDelete, url, token, nil, nil, http.StatusNoContent)
return sdkerr
}
+43
View File
@@ -1276,3 +1276,46 @@ func TestShareThing(t *testing.T) {
repoCall3.Unset()
}
}
func TestDeleteThing(t *testing.T) {
ts, cRepo, _, auth, cache := setupThings()
defer ts.Close()
conf := sdk.Config{
ThingsURL: ts.URL,
}
mgsdk := sdk.NewSDK(conf)
thing := sdk.Thing{
ID: generateUUID(t),
Name: "clientname",
Tags: []string{"tag1", "tag2"},
Credentials: sdk.Credentials{Secret: generateUUID(t)},
Status: mgclients.EnabledStatus.String(),
}
repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil)
repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: false}, nil)
repoCall2 := cRepo.On("Delete", mock.Anything, mock.Anything).Return(nil)
repoCall4 := cache.On("Remove", mock.Anything, thing.ID).Return(nil)
err := mgsdk.DeleteThing("wrongID", validToken)
assert.Equal(t, err, errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), fmt.Sprintf("Delete thing with wrong id: expected %v got %v", svcerr.ErrNotFound, err))
repoCall.Unset()
repoCall1.Unset()
repoCall2.Unset()
repoCall = auth.On("DeletePolicy", mock.Anything, mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: true}, nil)
repoCall1 = auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: validToken}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil)
repoCall2 = auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil)
repoCall3 := cRepo.On("Delete", mock.Anything, mock.Anything).Return(nil)
err = mgsdk.DeleteThing(thing.ID, validToken)
assert.Nil(t, err, fmt.Sprintf("Delete thing with correct id: expected %v got %v", nil, err))
ok := repoCall3.Parent.AssertCalled(t, "Delete", mock.Anything, thing.ID)
assert.True(t, ok, "Delete was not called on deleting thing with correct id")
repoCall.Unset()
repoCall1.Unset()
repoCall2.Unset()
repoCall3.Unset()
repoCall4.Unset()
}
+20
View File
@@ -778,6 +778,26 @@ func (_m *SDK) DeleteSubscription(id string, token string) errors.SDKError {
return r0
}
// DeleteThing provides a mock function with given fields: id, token
func (_m *SDK) DeleteThing(id string, token string) errors.SDKError {
ret := _m.Called(id, token)
if len(ret) == 0 {
panic("no return value specified for DeleteThing")
}
var r0 errors.SDKError
if rf, ok := ret.Get(0).(func(string, string) errors.SDKError); ok {
r0 = rf(id, token)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(errors.SDKError)
}
}
return r0
}
// DisableChannel provides a mock function with given fields: id, token
func (_m *SDK) DisableChannel(id string, token string) (sdk.Channel, errors.SDKError) {
ret := _m.Called(id, token)
+16
View File
@@ -108,6 +108,13 @@ func clientsHandler(svc things.Service, r *chi.Mux, logger mglog.Logger) http.Ha
api.EncodeResponse,
opts...,
), "unshare_thing").ServeHTTP)
r.Delete("/{thingID}", otelhttp.NewHandler(kithttp.NewServer(
deleteClientEndpoint(svc),
decodeDeleteClientReq,
api.EncodeResponse,
opts...,
), "delete_thing").ServeHTTP)
})
// Ideal location: things service, channels endpoint
@@ -376,3 +383,12 @@ func decodeThingUnshareRequest(_ context.Context, r *http.Request) (interface{},
return req, nil
}
func decodeDeleteClientReq(_ context.Context, r *http.Request) (interface{}, error) {
req := deleteClientReq{
token: apiutil.ExtractBearerToken(r),
id: chi.URLParam(r, "thingID"),
}
return req, nil
}
+17 -2
View File
@@ -216,7 +216,7 @@ func enableClientEndpoint(svc things.Service) endpoint.Endpoint {
return nil, err
}
return deleteClientRes{Client: client}, nil
return changeClientStatusRes{Client: client}, nil
}
}
@@ -232,7 +232,7 @@ func disableClientEndpoint(svc things.Service) endpoint.Endpoint {
return nil, err
}
return deleteClientRes{Client: client}, nil
return changeClientStatusRes{Client: client}, nil
}
}
@@ -401,3 +401,18 @@ func thingUnshareEndpoint(svc things.Service) endpoint.Endpoint {
return thingUnshareRes{}, nil
}
}
func deleteClientEndpoint(svc things.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(deleteClientReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
if err := svc.DeleteClient(ctx, req.token, req.id); err != nil {
return nil, err
}
return deleteClientRes{}, nil
}
}
+15
View File
@@ -365,3 +365,18 @@ func (req *thingUnshareRequest) validate() error {
}
return nil
}
type deleteClientReq struct {
token string
id string
}
func (req deleteClientReq) validate() error {
if req.token == "" {
return apiutil.ErrBearerToken
}
if req.id == "" {
return apiutil.ErrMissingID
}
return nil
}
+18 -3
View File
@@ -22,6 +22,7 @@ var (
_ magistrala.Response = (*unassignUsersGroupsRes)(nil)
_ magistrala.Response = (*connectChannelThingRes)(nil)
_ magistrala.Response = (*disconnectChannelThingRes)(nil)
_ magistrala.Response = (*changeClientStatusRes)(nil)
)
type pageRes struct {
@@ -138,20 +139,34 @@ func (res viewMembersRes) Empty() bool {
return false
}
type deleteClientRes struct {
type changeClientStatusRes struct {
mgclients.Client
}
func (res deleteClientRes) Code() int {
func (res changeClientStatusRes) Code() int {
return http.StatusOK
}
func (res changeClientStatusRes) Headers() map[string]string {
return map[string]string{}
}
func (res changeClientStatusRes) Empty() bool {
return false
}
type deleteClientRes struct{}
func (res deleteClientRes) Code() int {
return http.StatusNoContent
}
func (res deleteClientRes) Headers() map[string]string {
return map[string]string{}
}
func (res deleteClientRes) Empty() bool {
return false
return true
}
type assignUsersGroupsRes struct{}
+12
View File
@@ -192,3 +192,15 @@ func (lm *loggingMiddleware) Unshare(ctx context.Context, token, id, relation st
}(time.Now())
return lm.svc.Unshare(ctx, token, id, relation, userids...)
}
func (lm *loggingMiddleware) DeleteClient(ctx context.Context, token, id string) (err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method delete_client for thing id %s took %s to complete", id, time.Since(begin))
if err != nil {
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
return
}
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
}(time.Now())
return lm.svc.DeleteClient(ctx, token, id)
}
+8
View File
@@ -141,3 +141,11 @@ func (ms *metricsMiddleware) Unshare(ctx context.Context, token, id, relation st
}(time.Now())
return ms.svc.Unshare(ctx, token, id, relation, userids...)
}
func (ms *metricsMiddleware) DeleteClient(ctx context.Context, token, id string) error {
defer func(begin time.Time) {
ms.counter.With("method", "delete_client").Add(1)
ms.latency.With("method", "delete_client").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.DeleteClient(ctx, token, id)
}
+27 -14
View File
@@ -14,22 +14,23 @@ import (
)
const (
clientPrefix = "thing."
clientCreate = clientPrefix + "create"
clientUpdate = clientPrefix + "update"
clientRemove = clientPrefix + "remove"
clientView = clientPrefix + "view"
clientViewPerms = clientPrefix + "view_perms"
clientList = clientPrefix + "list"
clientListByGroup = clientPrefix + "list_by_channel"
clientIdentify = clientPrefix + "identify"
clientAuthorize = clientPrefix + "authorize"
clientPrefix = "thing."
clientCreate = clientPrefix + "create"
clientUpdate = clientPrefix + "update"
clientChangeStatus = clientPrefix + "change_status"
clientRemove = clientPrefix + "remove"
clientView = clientPrefix + "view"
clientViewPerms = clientPrefix + "view_perms"
clientList = clientPrefix + "list"
clientListByGroup = clientPrefix + "list_by_channel"
clientIdentify = clientPrefix + "identify"
clientAuthorize = clientPrefix + "authorize"
)
var (
_ events.Event = (*createClientEvent)(nil)
_ events.Event = (*updateClientEvent)(nil)
_ events.Event = (*removeClientEvent)(nil)
_ events.Event = (*changeStatusClientEvent)(nil)
_ events.Event = (*viewClientEvent)(nil)
_ events.Event = (*viewClientPermsEvent)(nil)
_ events.Event = (*listClientEvent)(nil)
@@ -37,6 +38,7 @@ var (
_ events.Event = (*identifyClientEvent)(nil)
_ events.Event = (*authorizeClientEvent)(nil)
_ events.Event = (*shareClientEvent)(nil)
_ events.Event = (*removeClientEvent)(nil)
)
type createClientEvent struct {
@@ -125,16 +127,16 @@ func (uce updateClientEvent) Encode() (map[string]interface{}, error) {
return val, nil
}
type removeClientEvent struct {
type changeStatusClientEvent struct {
id string
status string
updatedAt time.Time
updatedBy string
}
func (rce removeClientEvent) Encode() (map[string]interface{}, error) {
func (rce changeStatusClientEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"operation": clientRemove,
"operation": clientChangeStatus,
"id": rce.id,
"status": rce.status,
"updated_at": rce.updatedAt,
@@ -380,3 +382,14 @@ func (sce shareClientEvent) Encode() (map[string]interface{}, error) {
"user_ids": strings.Join(sce.userIDs, ","),
}, nil
}
type removeClientEvent struct {
id string
}
func (dce removeClientEvent) Encode() (map[string]interface{}, error) {
return map[string]interface{}{
"operation": clientRemove,
"id": dce.id,
}, nil
}
+18 -4
View File
@@ -162,7 +162,7 @@ func (es *eventStore) EnableClient(ctx context.Context, token, id string) (mgcli
return cli, err
}
return es.delete(ctx, cli)
return es.changeStatus(ctx, cli)
}
func (es *eventStore) DisableClient(ctx context.Context, token, id string) (mgclients.Client, error) {
@@ -171,11 +171,11 @@ func (es *eventStore) DisableClient(ctx context.Context, token, id string) (mgcl
return cli, err
}
return es.delete(ctx, cli)
return es.changeStatus(ctx, cli)
}
func (es *eventStore) delete(ctx context.Context, cli mgclients.Client) (mgclients.Client, error) {
event := removeClientEvent{
func (es *eventStore) changeStatus(ctx context.Context, cli mgclients.Client) (mgclients.Client, error) {
event := changeStatusClientEvent{
id: cli.ID,
updatedAt: cli.UpdatedAt,
updatedBy: cli.UpdatedBy,
@@ -251,3 +251,17 @@ func (es *eventStore) Unshare(ctx context.Context, token, id, relation string, u
return es.Publish(ctx, event)
}
func (es *eventStore) DeleteClient(ctx context.Context, token, id string) error {
if err := es.svc.DeleteClient(ctx, token, id); err != nil {
return err
}
event := removeClientEvent{id}
if err := es.Publish(ctx, event); err != nil {
return err
}
return nil
}
+18
View File
@@ -45,6 +45,24 @@ func (_m *Repository) ChangeStatus(ctx context.Context, client clients.Client) (
return r0, r1
}
// Delete provides a mock function with given fields: ctx, id
func (_m *Repository) Delete(ctx context.Context, id string) error {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for Delete")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// RetrieveAll provides a mock function with given fields: ctx, pm
func (_m *Repository) RetrieveAll(ctx context.Context, pm clients.Page) (clients.ClientsPage, error) {
ret := _m.Called(ctx, pm)
+11
View File
@@ -34,6 +34,9 @@ type Repository interface {
// RetrieveBySecret retrieves a client based on the secret (key).
RetrieveBySecret(ctx context.Context, key string) (mgclients.Client, error)
// Delete deletes client with given id
Delete(ctx context.Context, id string) error
}
// NewRepository instantiates a PostgreSQL
@@ -107,3 +110,11 @@ func (repo clientRepo) RetrieveBySecret(ctx context.Context, key string) (mgclie
return pgclients.ToClient(dbc)
}
func (repo clientRepo) Delete(ctx context.Context, id string) error {
q := "DELETE FROM clients AS c WHERE c.id = $1 ;"
if _, err := repo.DB.ExecContext(ctx, q, id); err != nil {
return postgres.HandleError(repoerr.ErrRemoveEntity, err)
}
return nil
}
+38 -3
View File
@@ -20,9 +20,10 @@ import (
const maxNameSize = 1024
var (
invalidName = strings.Repeat("m", maxNameSize+10)
clientIdentity = "client-identity@example.com"
clientName = "client name"
invalidName = strings.Repeat("m", maxNameSize+10)
clientIdentity = "client-identity@example.com"
clientName = "client name"
invalidClientID = "invalidClientID"
)
func TestClientsSave(t *testing.T) {
@@ -192,3 +193,37 @@ func TestClientsRetrieveBySecret(t *testing.T) {
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
}
}
func TestDelete(t *testing.T) {
t.Cleanup(func() {
_, err := db.Exec("DELETE FROM clients")
require.Nil(t, err, fmt.Sprintf("clean clients unexpected error: %s", err))
})
repo := postgres.NewRepository(database)
client := clients.Client{
ID: testsutil.GenerateUUID(t),
Name: clientName,
Credentials: clients.Credentials{
Identity: clientIdentity,
Secret: testsutil.GenerateUUID(t),
},
Status: clients.EnabledStatus,
}
_, err := repo.Save(context.Background(), client)
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
cases := map[string]struct {
id string
err error
}{
"delete client id": {client.ID, nil},
"delete invalid client id ": {invalidClientID, nil},
}
for desc, tc := range cases {
err := repo.Delete(context.Background(), tc.id)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
}
}
+49
View File
@@ -442,6 +442,55 @@ func (svc service) Unshare(ctx context.Context, token, id, relation string, user
return nil
}
func (svc service) DeleteClient(ctx context.Context, token, id string) error {
res, err := svc.identify(ctx, token)
if err != nil {
return err
}
if _, err := svc.authorize(ctx, auth.UserType, auth.UsersKind, res.GetId(), auth.DeletePermission, auth.ThingType, id); err != nil {
return err
}
// Remove from cache
if err := svc.clientCache.Remove(ctx, id); err != nil {
return errors.Wrap(repoerr.ErrRemoveEntity, err)
}
// Remove policy of groups
if _, err := svc.auth.DeletePolicy(ctx, &magistrala.DeletePolicyReq{
SubjectType: auth.GroupType,
Object: id,
ObjectType: auth.ThingType,
}); err != nil {
return err
}
// Remove policy from domain
if _, err := svc.auth.DeletePolicy(ctx, &magistrala.DeletePolicyReq{
SubjectType: auth.DomainType,
Object: id,
ObjectType: auth.ThingType,
}); err != nil {
return err
}
// Remove thing from database
if err := svc.clients.Delete(ctx, id); err != nil {
return err
}
// Remove policy of users
if _, err := svc.auth.DeletePolicy(ctx, &magistrala.DeletePolicyReq{
SubjectType: auth.UserType,
Object: id,
ObjectType: auth.ThingType,
}); err != nil {
return err
}
return nil
}
func (svc service) changeClientStatus(ctx context.Context, token string, client mgclients.Client) (mgclients.Client, error) {
userID, err := svc.authorize(ctx, auth.UserType, auth.TokenKind, token, auth.DeletePermission, auth.ThingType, client.ID)
if err != nil {
+77
View File
@@ -1132,6 +1132,83 @@ func TestListMembers(t *testing.T) {
}
}
func TestDeleteClient(t *testing.T) {
svc, cRepo, auth, cache := newService()
client := mgclients.Client{
ID: testsutil.GenerateUUID(t),
Name: "TestClient",
Credentials: mgclients.Credentials{
Identity: "TestClient@example.com",
Secret: "password",
},
Tags: []string{"tag1", "tag2"},
Metadata: mgclients.Metadata{"role": "client"},
}
invalidClientID := "invalidClientID"
_ = invalidClientID
cases := []struct {
desc string
token string
clientID string
err error
}{
{
desc: "Delete client with authorized token",
token: validToken,
clientID: client.ID,
err: nil,
},
{
desc: "Delete client with unauthorized token",
token: authmocks.InvalidValue,
clientID: client.ID,
err: errors.ErrAuthentication,
},
{
desc: "Delete invalid client",
token: validToken,
clientID: authmocks.InvalidValue,
err: errors.ErrAuthorization,
},
{
desc: "Delete repo error ",
token: validToken,
clientID: client.ID,
err: errors.ErrRemoveEntity,
},
{
desc: "Delete policy error ",
token: validToken,
clientID: client.ID,
err: errors.ErrUnidentified,
},
}
for _, tc := range cases {
repoCall := auth.On("Identify", mock.Anything, &magistrala.IdentityReq{Token: tc.token}).Return(&magistrala.IdentityRes{Id: validID, DomainId: testsutil.GenerateUUID(t)}, nil)
repoCall1 := auth.On("Authorize", mock.Anything, mock.Anything).Return(&magistrala.AuthorizeRes{Authorized: true}, nil)
repoCall2 := auth.On("DeletePolicy", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: true}, nil)
repoCall3 := cache.On("Remove", mock.Anything, tc.clientID).Return(nil)
repoCall4 := cRepo.On("Delete", context.Background(), tc.clientID).Return(nil)
if tc.err == errors.ErrRemoveEntity {
repoCall4.Unset()
repoCall4 = cRepo.On("Delete", context.Background(), tc.clientID).Return(errors.ErrRemoveEntity)
}
if tc.err == errors.ErrUnidentified {
repoCall2.Unset()
repoCall2 = auth.On("DeletePolicy", mock.Anything, mock.Anything).Return(&magistrala.DeletePolicyRes{Deleted: false}, errors.ErrUnidentified)
}
err := svc.DeleteClient(context.Background(), tc.token, tc.clientID)
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
repoCall.Unset()
repoCall1.Unset()
repoCall2.Unset()
repoCall3.Unset()
repoCall4.Unset()
}
}
func getIDs(clients []mgclients.Client) []string {
ids := []string{}
for _, client := range clients {
+3
View File
@@ -57,6 +57,9 @@ type Service interface {
// Authorize used for AuthZ gRPC server implementation and Things authorization.
Authorize(ctx context.Context, req *magistrala.AuthorizeReq) (string, error)
// DeleteClient deletes client with given ID.
DeleteClient(ctx context.Context, token, id string) error
}
// Cache contains thing caching interface.
+7
View File
@@ -133,3 +133,10 @@ func (tm *tracingMiddleware) Unshare(ctx context.Context, token, id, relation st
defer span.End()
return tm.svc.Unshare(ctx, token, id, relation, userids...)
}
// DeleteClient traces the "DeleteClient" operation of the wrapped things.Service.
func (tm *tracingMiddleware) DeleteClient(ctx context.Context, token, id string) error {
ctx, span := tm.tracer.Start(ctx, "delete_client", trace.WithAttributes(attribute.String("id", id)))
defer span.End()
return tm.svc.DeleteClient(ctx, token, id)
}