Repository: mainflux/mainflux
Pull request #1518 (2021-12-03 03:37:00)

 MF-1425 - Support external UUIDs for Things and Channels
MF-1425 Enhancement to supply an external UUID for Things and Channels.

### What does this do?
This is the same PR for [ MF-1425 - Enhancement for external UUID #1426 ](https://github.com/mainflux/mainflux/pull/1426), but tried to resolve the conflicts

### Which issue(s) does this PR fix/relate to?
Resolves #MF-1425 

### Notes
I cannot @aspnair. If anyone in Mainflux can review this, and may ask aspnair take a look as well.

codecov-commenter: # [Codecov](https://codecov.io/gh/mainflux/mainflux/pull/1518?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux) Report
> Merging [#1518](https://codecov.io/gh/mainflux/mainflux/pull/1518?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux) (9abfdb6) into [master](https://codecov.io/gh/mainflux/mainflux/commit/31d30b204da41bf5ed3dc88249116d5991d23a48?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux) (31d30b2) will **decrease** coverage by `0.06%`.
> The diff coverage is `41.37%`.

> :exclamation: Current head 9abfdb6 differs from pull request most recent head 9718f27. Consider uploading reports for the commit 9718f27 to get more accurate results
[![Impacted file tree graph](https://codecov.io/gh/mainflux/mainflux/pull/1518/graphs/tree.svg?width=650&height=150&src=pr&token=L932pGLS8b&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux)](https://codecov.io/gh/mainflux/mainflux/pull/1518?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux)

```diff
@@            Coverage Diff             @@
##           master    #1518      +/-   ##
==========================================
- Coverage   68.14%   68.08%   -0.07%     
==========================================
  Files         132      132              
  Lines       10781    10800      +19     
==========================================
+ Hits         7347     7353       +6     
- Misses       2839     2848       +9     
- Partials      595      599       +4     
```


| [Impacted Files](https://codecov.io/gh/mainflux/mainflux/pull/1518?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux) | Coverage Δ | |
|---|---|---|
| [things/api/things/http/requests.go](https://codecov.io/gh/mainflux/mainflux/pull/1518/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux#diff-dGhpbmdzL2FwaS90aGluZ3MvaHR0cC9yZXF1ZXN0cy5nbw==) | `72.91% <0.00%> (-7.24%)` | :arrow_down: |
| [things/service.go](https://codecov.io/gh/mainflux/mainflux/pull/1518/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux#diff-dGhpbmdzL3NlcnZpY2UuZ28=) | `66.07% <66.66%> (+0.36%)` | :arrow_up: |
| [things/api/things/http/endpoint.go](https://codecov.io/gh/mainflux/mainflux/pull/1518/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux#diff-dGhpbmdzL2FwaS90aGluZ3MvaHR0cC9lbmRwb2ludC5nbw==) | `91.56% <100.00%> (+0.07%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/mainflux/mainflux/pull/1518?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/mainflux/mainflux/pull/1518?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux). Last update [31d30b2...9718f27](https://codecov.io/gh/mainflux/mainflux/pull/1518?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux).

qs-wang: @manuio  this is resolving the conflicts for https://github.com/mainflux/mainflux/pull/1426. Could you take a look?
manuio: @qs-wang Would be possible to commit this work in the @aspnair PR?
qs-wang: @manuio  that would mean I have to create a PR on @aspnair's repo, and he have to accept it. I can try, but don't know if @aspnair would response. 

@aspnair  would you be happy to accept my PR in your repo?
aspnair: @qs-wang I can accept the to  PR #1426 . But, I am not sure how to do that.
qs-wang: @aspnair grea!  I'll create a PR in your forked repo later on. You would need accept the PR, and merge it to your main. This way the changes will be showing up in your PR here automatically.  

drasko: That would be great.

If that fails, we can also merge temporarily @aspnair's PR under condition that @qs-wang will send PR just after.
qs-wang: @aspnair  The PR in your repo is there at https://github.com/aspnair/mainflux/pull/1/.  Please accept it, and we can see how is it going there. 
drasko: Guys, let's make it simple - this PR seems to contain all the changes that @aspnair made. It was rebased to his branch, and contains correct commit history.

I would like that we take it from here, it would be easier to handle.

@qs-wang can you please fix CI, seems like `email` is declared somewhere, but not used.

@dusanb94 can you please check this PR and take a look if all necessery commits are included, and also give appropriate directions what has to be done.
qs-wang: @drasko CI fixed.

@dusanb94  Please review and let me know if anything is missing
qs-wang: @drasko @dusanb94 Any updates? Please let me know if there's anything need to be changed. I think there was a long review process going over already.  I would expect we can make the review process shorter this time, otherwise I may be facing conflicts again. 
manuio: @qs-wang The PR looks good. Please update your branch and we can probably merge. 
qs-wang: @manuio rebased to latest main flux master. 
drasko: This looks good now, we are getting ready to merge it. However, I think this feature must be documented in the [official docs](https://github.com/mainflux/docs) as well.

@qs-wang can you take this one as well?
qs-wang: > 

Sure, I can try. Should be added to the provision section, right?
qs-wang: > Sure, I can try. Should be added to the provision section, right?

@drasko Should be a separate PR?
drasko: > > Sure, I can try. Should be added to the provision section, right?
> 
> @drasko Should be a separate PR?

Yes, separate one. It is against other repo.


Patch:
diff --git a/api/things.yml b/api/things.yml
index 59109116..e36088a6 100644
--- a/api/things.yml
+++ b/api/things.yml
@@ -636,7 +636,10 @@ components:
         id:
           type: string
           format: uuid
-          description: Thing unique identifier
+          description: Thing unique identifier. This can be either
+            provided by the user or left blank. If the user provides a UUID,
+            it would be validated. If there is not one provided then
+            the service will generate one in UUID format.
     ThingReqSchema:
       type: object
       properties:
diff --git a/things/api/things/http/endpoint.go b/things/api/things/http/endpoint.go
index 52641a8a..fc343f8e 100644
--- a/things/api/things/http/endpoint.go
+++ b/things/api/things/http/endpoint.go
@@ -22,6 +22,7 @@ func createThingEndpoint(svc things.Service) endpoint.Endpoint {
 
 		th := things.Thing{
 			Key:      req.Key,
+			ID:       req.ID,
 			Name:     req.Name,
 			Metadata: req.Metadata,
 		}
@@ -52,6 +53,7 @@ func createThingsEndpoint(svc things.Service) endpoint.Endpoint {
 			th := things.Thing{
 				Name:     tReq.Name,
 				Key:      tReq.Key,
+				ID:       tReq.ID,
 				Metadata: tReq.Metadata,
 			}
 			ths = append(ths, th)
@@ -263,7 +265,11 @@ func createChannelEndpoint(svc things.Service) endpoint.Endpoint {
 			return nil, err
 		}
 
-		ch := things.Channel{Name: req.Name, Metadata: req.Metadata}
+		ch := things.Channel{
+			Name:     req.Name,
+			ID:       req.ID,
+			Metadata: req.Metadata}
+
 		saved, err := svc.CreateChannels(ctx, req.token, ch)
 		if err != nil {
 			return nil, err
@@ -290,6 +296,7 @@ func createChannelsEndpoint(svc things.Service) endpoint.Endpoint {
 			ch := things.Channel{
 				Metadata: cReq.Metadata,
 				Name:     cReq.Name,
+				ID:       cReq.ID,
 			}
 			chs = append(chs, ch)
 		}
diff --git a/things/api/things/http/requests.go b/things/api/things/http/requests.go
index f481aa1d..7b55d876 100644
--- a/things/api/things/http/requests.go
+++ b/things/api/things/http/requests.go
@@ -4,6 +4,7 @@
 package http
 
 import (
+	"github.com/gofrs/uuid"
 	"github.com/mainflux/mainflux/auth"
 	"github.com/mainflux/mainflux/things"
 )
@@ -24,14 +25,28 @@ type createThingReq struct {
 	token    string
 	Name     string                 `json:"name,omitempty"`
 	Key      string                 `json:"key,omitempty"`
+	ID       string                 `json:"id,omitempty"`
 	Metadata map[string]interface{} `json:"metadata,omitempty"`
 }
 
+func validateUUID(extID string) (err error) {
+	id, err := uuid.FromString(extID)
+	if id.String() != extID || err != nil {
+		return things.ErrMalformedEntity
+	}
+
+	return nil
+}
+
 func (req createThingReq) validate() error {
 	if req.token == "" {
 		return things.ErrUnauthorizedAccess
 	}
 
+	if req.ID != "" && validateUUID(req.ID) != nil {
+		return things.ErrMalformedEntity
+	}
+
 	if len(req.Name) > maxNameSize {
 		return things.ErrMalformedEntity
 	}
@@ -54,6 +69,10 @@ func (req createThingsReq) validate() error {
 	}
 
 	for _, thing := range req.Things {
+		if thing.ID != "" && validateUUID(thing.ID) != nil {
+			return things.ErrMalformedEntity
+		}
+
 		if len(thing.Name) > maxNameSize {
 			return things.ErrMalformedEntity
 		}
@@ -129,6 +148,7 @@ func (req updateKeyReq) validate() error {
 type createChannelReq struct {
 	token    string
 	Name     string                 `json:"name,omitempty"`
+	ID       string                 `json:"id,omitempty"`
 	Metadata map[string]interface{} `json:"metadata,omitempty"`
 }
 
@@ -137,6 +157,10 @@ func (req createChannelReq) validate() error {
 		return things.ErrUnauthorizedAccess
 	}
 
+	if req.ID != "" && validateUUID(req.ID) != nil {
+		return things.ErrMalformedEntity
+	}
+
 	if len(req.Name) > maxNameSize {
 		return things.ErrMalformedEntity
 	}
@@ -159,6 +183,10 @@ func (req createChannelsReq) validate() error {
 	}
 
 	for _, channel := range req.Channels {
+		if channel.ID != "" && validateUUID(channel.ID) != nil {
+			return things.ErrMalformedEntity
+		}
+
 		if len(channel.Name) > maxNameSize {
 			return things.ErrMalformedEntity
 		}
diff --git a/things/mocks/auth.go b/things/mocks/auth.go
index d6803094..9e0051f9 100644
--- a/things/mocks/auth.go
+++ b/things/mocks/auth.go
@@ -69,12 +69,7 @@ func (svc authServiceMock) AddPolicy(ctx context.Context, in *mainflux.AddPolicy
 		return &mainflux.AddPolicyRes{}, things.ErrMalformedEntity
 	}
 
-	// Mock thingsRepository saves the thing ID after padding the ID by 3. (see things/mocks/things.go)
-	// Since we are adding policies within the Service layer, we are storing them as a full ID which is
-	// eventually not compatible with the one inside  of the mock things repository. Therefore, we are
-	// getting last three part of the ID as below.
 	obj := in.GetObj()
-	obj = obj[len(obj)-3:]
 	svc.policies[in.GetSub()] = append(svc.policies[in.GetSub()], MockSubjectSet{Object: obj, Relation: in.GetAct()})
 	return &mainflux.AddPolicyRes{Authorized: true}, nil
 }
diff --git a/things/mocks/channels.go b/things/mocks/channels.go
index f459eb5f..aedda933 100644
--- a/things/mocks/channels.go
+++ b/things/mocks/channels.go
@@ -6,7 +6,6 @@ package mocks
 import (
 	"context"
 	"fmt"
-	"strconv"
 	"strings"
 	"sync"
 
@@ -48,7 +47,9 @@ func (crm *channelRepositoryMock) Save(_ context.Context, channels ...things.Cha
 
 	for i := range channels {
 		crm.counter++
-		channels[i].ID = fmt.Sprintf("%03d", crm.counter)
+		if channels[i].ID == "" {
+			channels[i].ID = fmt.Sprintf("%03d", crm.counter)
+		}
 		crm.channels[key(channels[i].Owner, channels[i].ID)] = channels[i]
 	}
 
@@ -136,7 +137,7 @@ func (crm *channelRepositoryMock) RetrieveByThing(_ context.Context, owner, thID
 	switch pm.Disconnected {
 	case false:
 		for _, co := range crm.cconns[thID] {
-			id, _ := strconv.ParseUint(co.ID, 10, 64)
+			id := parseID(co.ID)
 			if id >= first && id < last {
 				chs = append(chs, co)
 			}
@@ -144,7 +145,7 @@ func (crm *channelRepositoryMock) RetrieveByThing(_ context.Context, owner, thID
 	default:
 		for _, ch := range crm.channels {
 			conn := false
-			id, _ := strconv.ParseUint(ch.ID, 10, 64)
+			id := parseID(ch.ID)
 			if id >= first && id < last {
 				for _, co := range crm.cconns[thID] {
 					if ch.ID == co.ID {
diff --git a/things/mocks/commons.go b/things/mocks/commons.go
index 909c6ce6..5177e6bc 100644
--- a/things/mocks/commons.go
+++ b/things/mocks/commons.go
@@ -6,10 +6,13 @@ package mocks
 import (
 	"fmt"
 	"sort"
+	"strconv"
 
 	"github.com/mainflux/mainflux/things"
 )
 
+const uuidLen = 36
+
 // Since mocks will store data in map, and they need to resemble the real
 // identifiers as much as possible, a key will be created as combination of
 // owner and their own identifiers. This will allow searching either by
@@ -83,3 +86,14 @@ func sortChannels(pm things.PageMetadata, chs []things.Channel) []things.Channel
 
 	return chs
 }
+
+func parseID(ID string) (id uint64) {
+	var serialNum string
+
+	if len(ID) == uuidLen {
+		serialNum = ID[len(ID)-6:]
+	}
+	id, _ = strconv.ParseUint(serialNum, 10, 64)
+
+	return
+}
diff --git a/things/mocks/things.go b/things/mocks/things.go
index 3df7932b..7215ce0d 100644
--- a/things/mocks/things.go
+++ b/things/mocks/things.go
@@ -6,7 +6,6 @@ package mocks
 import (
 	"context"
 	"fmt"
-	"strconv"
 	"strings"
 	"sync"
 
@@ -55,7 +54,9 @@ func (trm *thingRepositoryMock) Save(_ context.Context, ths ...things.Thing) ([]
 		}
 
 		trm.counter++
-		ths[i].ID = fmt.Sprintf("%03d", trm.counter)
+		if ths[i].ID == "" {
+			ths[i].ID = fmt.Sprintf("%03d", trm.counter)
+		}
 		trm.things[key(ths[i].Owner, ths[i].ID)] = ths[i]
 	}
 
@@ -128,7 +129,7 @@ func (trm *thingRepositoryMock) RetrieveAll(_ context.Context, owner string, pm
 	// itself (see mocks/commons.go).
 	prefix := fmt.Sprintf("%s-", owner)
 	for k, v := range trm.things {
-		id, _ := strconv.ParseUint(v.ID, 10, 64)
+		id := parseID(v.ID)
 		if strings.HasPrefix(k, prefix) && id >= first && id < last {
 			ths = append(ths, v)
 		}
@@ -167,7 +168,7 @@ func (trm *thingRepositoryMock) RetrieveByIDs(_ context.Context, thingIDs []stri
 	for _, id := range thingIDs {
 		suffix := fmt.Sprintf("-%s", id)
 		for k, v := range trm.things {
-			id, _ := strconv.ParseUint(v.ID, 10, 64)
+			id := parseID(v.ID)
 			if strings.HasSuffix(k, suffix) && id >= first && id < last {
 				items = append(items, v)
 			}
@@ -205,7 +206,7 @@ func (trm *thingRepositoryMock) RetrieveByChannel(_ context.Context, owner, chID
 	switch pm.Disconnected {
 	case false:
 		for _, co := range trm.tconns[chID] {
-			id, _ := strconv.ParseUint(co.ID, 10, 64)
+			id := parseID(co.ID)
 			if id >= first && id < last {
 				ths = append(ths, co)
 			}
@@ -213,7 +214,7 @@ func (trm *thingRepositoryMock) RetrieveByChannel(_ context.Context, owner, chID
 	default:
 		for _, th := range trm.things {
 			conn := false
-			id, _ := strconv.ParseUint(th.ID, 10, 64)
+			id := parseID(th.ID)
 			if id >= first && id < last {
 				for _, co := range trm.tconns[chID] {
 					if th.ID == co.ID {
diff --git a/things/service.go b/things/service.go
index 26a634dc..03a1dadf 100644
--- a/things/service.go
+++ b/things/service.go
@@ -192,6 +192,7 @@ func (ts *thingsService) CreateThings(ctx context.Context, token string, things
 	ths := []Thing{}
 	for _, thing := range things {
 		th, err := ts.createThing(ctx, &thing, res)
+
 		if err != nil {
 			return []Thing{}, err
 		}
@@ -203,18 +204,24 @@ func (ts *thingsService) CreateThings(ctx context.Context, token string, things
 
 // createThing saves the Thing and adds identity as an owner(Read, Write, Delete policies) of the Thing.
 func (ts *thingsService) createThing(ctx context.Context, thing *Thing, identity *mainflux.UserIdentity) (Thing, error) {
-	thID, err := ts.idProvider.ID()
-	if err != nil {
-		return Thing{}, errors.Wrap(ErrCreateUUID, err)
-	}
-	thing.ID = thID
+
 	thing.Owner = identity.GetEmail()
 
+	if thing.ID == "" {
+		id, err := ts.idProvider.ID()
+		if err != nil {
+			return Thing{}, errors.Wrap(ErrCreateUUID, err)
+		}
+		thing.ID = id
+	}
+
 	if thing.Key == "" {
-		thing.Key, err = ts.idProvider.ID()
+		key, err := ts.idProvider.ID()
+
 		if err != nil {
 			return Thing{}, errors.Wrap(ErrCreateUUID, err)
 		}
+		thing.Key = key
 	}
 
 	ths, err := ts.things.Save(ctx, *thing)
@@ -401,11 +408,13 @@ func (ts *thingsService) CreateChannels(ctx context.Context, token string, chann
 }
 
 func (ts *thingsService) createChannel(ctx context.Context, channel *Channel, identity *mainflux.UserIdentity) (Channel, error) {
-	chID, err := ts.idProvider.ID()
-	if err != nil {
-		return Channel{}, errors.Wrap(ErrCreateUUID, err)
+	if channel.ID == "" {
+		chID, err := ts.idProvider.ID()
+		if err != nil {
+			return Channel{}, errors.Wrap(ErrCreateUUID, err)
+		}
+		channel.ID = chID
 	}
-	channel.ID = chID
 	channel.Owner = identity.GetEmail()
 
 	chs, err := ts.channels.Save(ctx, *channel)


Test patch:
diff --git a/pkg/sdk/go/channels_test.go b/pkg/sdk/go/channels_test.go
index e628ca18..0ad32f60 100644
--- a/pkg/sdk/go/channels_test.go
+++ b/pkg/sdk/go/channels_test.go
@@ -15,7 +15,10 @@ import (
 )
 
 var (
-	channel      = sdk.Channel{ID: "001", Name: "test"}
+	ch1          = sdk.Channel{Name: "test1"}
+	ch2          = sdk.Channel{ID: "fe6b4e92-cc98-425e-b0aa-000000000001", Name: "test1"}
+	ch3          = sdk.Channel{ID: "fe6b4e92-cc98-425e-b0aa-000000000002", Name: "test2"}
+	chPrefix     = "fe6b4e92-cc98-425e-b0aa-"
 	emptyChannel = sdk.Channel{}
 )
 
@@ -24,6 +27,8 @@ func TestCreateChannel(t *testing.T) {
 	ts := newThingsServer(svc)
 	defer ts.Close()
 
+	chWrongExtID := sdk.Channel{ID: "b0aa-000000000001", Name: "1", Metadata:metadata}
+
 	sdkConf := sdk.Config{
 		ThingsURL:       ts.URL,
 		MsgContentType:  contentType,
@@ -41,21 +46,21 @@ func TestCreateChannel(t *testing.T) {
 	}{
 		{
 			desc:    "create new channel",
-			channel: channel,
+			channel: ch1,
 			token:   token,
 			err:     nil,
 			empty:   false,
 		},
 		{
 			desc:    "create new channel with empty token",
-			channel: channel,
+			channel: ch1,
 			token:   "",
 			err:     createError(sdk.ErrFailedCreation, http.StatusUnauthorized),
 			empty:   true,
 		},
 		{
 			desc:    "create new channel with invalid token",
-			channel: channel,
+			channel: ch1,
 			token:   wrongValue,
 			err:     createError(sdk.ErrFailedCreation, http.StatusUnauthorized),
 			empty:   true,
@@ -67,6 +72,20 @@ func TestCreateChannel(t *testing.T) {
 			err:     nil,
 			empty:   false,
 		},
+		{
+			desc:   "create a new channel with external UUID",
+			channel: ch2,
+			token:   token,
+			err:     nil,
+			empty:   false,
+		},
+		{
+			desc:   "create a new channel with wrong external UUID",
+			channel: chWrongExtID,
+			token:   token,
+			err:     createError(sdk.ErrFailedCreation, http.StatusBadRequest),
+			empty:   true,
+		},
 	}
 
 	for _, tc := range cases {
@@ -90,8 +109,8 @@ func TestCreateChannels(t *testing.T) {
 	mainfluxSDK := sdk.NewSDK(sdkConf)
 
 	channels := []sdk.Channel{
-		sdk.Channel{ID: "001", Name: "1"},
-		sdk.Channel{ID: "002", Name: "2"},
+		ch2,
+		ch3,
 	}
 
 	cases := []struct {
@@ -151,7 +170,7 @@ func TestChannel(t *testing.T) {
 	}
 
 	mainfluxSDK := sdk.NewSDK(sdkConf)
-	id, err := mainfluxSDK.CreateChannel(channel, token)
+	id, err := mainfluxSDK.CreateChannel(ch2, token)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
 	cases := []struct {
@@ -166,7 +185,7 @@ func TestChannel(t *testing.T) {
 			chanID:   id,
 			token:    token,
 			err:      nil,
-			response: channel,
+			response: ch2,
 		},
 		{
 			desc:     "get non-existent channel",
@@ -204,7 +223,9 @@ func TestChannels(t *testing.T) {
 	var channels []sdk.Channel
 	mainfluxSDK := sdk.NewSDK(sdkConf)
 	for i := 1; i < 101; i++ {
-		ch := sdk.Channel{ID: fmt.Sprintf("%03d", i), Name: "test"}
+		id := fmt.Sprintf("%s%012d", chPrefix, i)
+		name := fmt.Sprintf("test-%d", i)
+		ch := sdk.Channel{ID: id, Name: name}
 		_, err := mainfluxSDK.CreateChannel(ch, token)
 		require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 		channels = append(channels, ch)
@@ -294,10 +315,9 @@ func TestChannelsByThing(t *testing.T) {
 	var chsDiscoNum = 1
 	var channels []sdk.Channel
 	for i := 1; i < n+1; i++ {
-		ch := sdk.Channel{
-			ID:   fmt.Sprintf("%03d", i),
-			Name: "test",
-		}
+		id := fmt.Sprintf("%s%012d", chPrefix, i)
+		name := fmt.Sprintf("test-%d", i)
+		ch := sdk.Channel{ID: id, Name: name}
 		cid, err := mainfluxSDK.CreateChannel(ch, token)
 		require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
@@ -419,7 +439,7 @@ func TestUpdateChannel(t *testing.T) {
 	}
 
 	mainfluxSDK := sdk.NewSDK(sdkConf)
-	id, err := mainfluxSDK.CreateChannel(channel, token)
+	id, err := mainfluxSDK.CreateChannel(ch2, token)
 	require.Nil(t, err, fmt.Sprintf("unexpected error %s", err))
 
 	cases := []struct {
@@ -477,7 +497,7 @@ func TestDeleteChannel(t *testing.T) {
 	}
 
 	mainfluxSDK := sdk.NewSDK(sdkConf)
-	id, err := mainfluxSDK.CreateChannel(channel, token)
+	id, err := mainfluxSDK.CreateChannel(ch2, token)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
 	cases := []struct {
diff --git a/pkg/sdk/go/things_test.go b/pkg/sdk/go/things_test.go
index bf6c0129..2d58ad2e 100644
--- a/pkg/sdk/go/things_test.go
+++ b/pkg/sdk/go/things_test.go
@@ -29,14 +29,13 @@ const (
 	wrongValue  = "wrong_value"
 	badID       = "999"
 	emptyValue  = ""
-
-	keyPrefix = "123e4567-e89b-12d3-a456-"
 )
 
 var (
 	metadata   = map[string]interface{}{"meta": "data"}
 	metadata2  = map[string]interface{}{"meta": "data2"}
-	thing      = sdk.Thing{ID: "001", Name: "test_device", Metadata: metadata}
+	th1        = sdk.Thing{ID: "fe6b4e92-cc98-425e-b0aa-000000000001", Name: "test1", Metadata: metadata}
+	th2        = sdk.Thing{ID: "fe6b4e92-cc98-425e-b0aa-000000000002", Name: "test2", Metadata: metadata}
 	emptyThing = sdk.Thing{}
 )
 
@@ -82,28 +81,28 @@ func TestCreateThing(t *testing.T) {
 	}{
 		{
 			desc:     "create new thing",
-			thing:    thing,
+			thing:    th1,
 			token:    token,
 			err:      nil,
-			location: "001",
+			location: th1.ID,
 		},
 		{
 			desc:     "create new empty thing",
 			thing:    emptyThing,
 			token:    token,
 			err:      nil,
-			location: "002",
+			location: fmt.Sprintf("%s%012d", uuid.Prefix, 2),
 		},
 		{
 			desc:     "create new thing with empty token",
-			thing:    thing,
+			thing:    th1,
 			token:    "",
 			err:      createError(sdk.ErrFailedCreation, http.StatusUnauthorized),
 			location: "",
 		},
 		{
 			desc:     "create new thing with invalid token",
-			thing:    thing,
+			thing:    th1,
 			token:    wrongValue,
 			err:      createError(sdk.ErrFailedCreation, http.StatusUnauthorized),
 			location: "",
@@ -131,8 +130,16 @@ func TestCreateThings(t *testing.T) {
 	mainfluxSDK := sdk.NewSDK(sdkConf)
 
 	things := []sdk.Thing{
-		sdk.Thing{ID: "001", Name: "1", Key: "1"},
-		sdk.Thing{ID: "002", Name: "2", Key: "2"},
+		th1,
+		th2,
+	}
+	thsExtID := []sdk.Thing{
+		{ID: th1.ID, Name: "1", Key: "1", Metadata: metadata},
+		{ID: th2.ID, Name: "2", Key: "2", Metadata: metadata},
+	}
+	thsWrongExtID := []sdk.Thing{
+		{ID: "b0aa-000000000001", Name: "1", Key: "1", Metadata: metadata},
+		{ID: "b0aa-000000000002", Name: "2", Key: "2", Metadata: metadata2},
 	}
 
 	cases := []struct {
@@ -170,6 +177,20 @@ func TestCreateThings(t *testing.T) {
 			err:    createError(sdk.ErrFailedCreation, http.StatusUnauthorized),
 			res:    []sdk.Thing{},
 		},
+		{
+			desc:   "create new things with external UUID",
+			things: thsExtID,
+			token:  token,
+			err:    nil,
+			res:    things,
+		},
+		{
+			desc:   "create new things with wrong external UUID",
+			things: thsWrongExtID,
+			token:  token,
+			err:    createError(sdk.ErrFailedCreation, http.StatusBadRequest),
+			res:    []sdk.Thing{},
+		},
 	}
 	for _, tc := range cases {
 		res, err := mainfluxSDK.CreateThings(tc.things, tc.token)
@@ -193,9 +214,9 @@ func TestThing(t *testing.T) {
 	}
 
 	mainfluxSDK := sdk.NewSDK(sdkConf)
-	id, err := mainfluxSDK.CreateThing(thing, token)
+	id, err := mainfluxSDK.CreateThing(th1, token)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
-	thing.Key = fmt.Sprintf("%s%012d", keyPrefix, 2)
+	th1.Key = fmt.Sprintf("%s%012d", uuid.Prefix, 1)
 
 	cases := []struct {
 		desc     string
@@ -209,7 +230,7 @@ func TestThing(t *testing.T) {
 			thID:     id,
 			token:    token,
 			err:      nil,
-			response: thing,
+			response: th1,
 		},
 		{
 			desc:     "get non-existent thing",
@@ -248,10 +269,12 @@ func TestThings(t *testing.T) {
 
 	mainfluxSDK := sdk.NewSDK(sdkConf)
 	for i := 1; i < 101; i++ {
-		th := sdk.Thing{ID: fmt.Sprintf("%03d", i), Name: "test_device", Metadata: metadata}
+		id := fmt.Sprintf("%s%012d", chPrefix, i)
+		name := fmt.Sprintf("test-%d", i)
+		th := sdk.Thing{ID: id, Name: name, Metadata: metadata}
 		_, err := mainfluxSDK.CreateThing(th, token)
 		require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
-		th.Key = fmt.Sprintf("%s%012d", keyPrefix, 2*i)
+		th.Key = fmt.Sprintf("%s%012d", uuid.Prefix, i)
 		things = append(things, th)
 	}
 
@@ -340,11 +363,13 @@ func TestThingsByChannel(t *testing.T) {
 	var thsDiscoNum = 1
 	var things []sdk.Thing
 	for i := 1; i < n+1; i++ {
+		id := fmt.Sprintf("%s%012d", chPrefix, i)
+		name := fmt.Sprintf("test-%d", i)
 		th := sdk.Thing{
-			ID:       fmt.Sprintf("%03d", i),
-			Name:     "test_device",
+			ID:       id,
+			Name:     name,
 			Metadata: metadata,
-			Key:      fmt.Sprintf("%s%012d", keyPrefix, 2*i+1),
+			Key:      fmt.Sprintf("%s%012d", uuid.Prefix, 2*i+1),
 		}
 		tid, err := mainfluxSDK.CreateThing(th, token)
 		require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
@@ -467,9 +492,9 @@ func TestUpdateThing(t *testing.T) {
 	}
 
 	mainfluxSDK := sdk.NewSDK(sdkConf)
-	id, err := mainfluxSDK.CreateThing(thing, token)
+	id, err := mainfluxSDK.CreateThing(th1, token)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
-	thing.Name = "test2"
+	th1.Name = "test2"
 
 	cases := []struct {
 		desc  string
@@ -546,7 +571,7 @@ func TestDeleteThing(t *testing.T) {
 	}
 
 	mainfluxSDK := sdk.NewSDK(sdkConf)
-	id, err := mainfluxSDK.CreateThing(thing, token)
+	id, err := mainfluxSDK.CreateThing(th1, token)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
 	cases := []struct {
@@ -614,13 +639,13 @@ func TestConnectThing(t *testing.T) {
 	}
 
 	mainfluxSDK := sdk.NewSDK(sdkConf)
-	thingID, err := mainfluxSDK.CreateThing(thing, token)
+	thingID, err := mainfluxSDK.CreateThing(th1, token)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
-	chanID1, err := mainfluxSDK.CreateChannel(channel, token)
+	chanID1, err := mainfluxSDK.CreateChannel(ch2, token)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
-	chanID2, err := mainfluxSDK.CreateChannel(channel, otherToken)
+	chanID2, err := mainfluxSDK.CreateChannel(ch3, otherToken)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
 	cases := []struct {
@@ -715,13 +740,13 @@ func TestConnect(t *testing.T) {
 	}
 
 	mainfluxSDK := sdk.NewSDK(sdkConf)
-	thingID, err := mainfluxSDK.CreateThing(thing, token)
+	thingID, err := mainfluxSDK.CreateThing(th1, token)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
-	chanID1, err := mainfluxSDK.CreateChannel(channel, token)
+	chanID1, err := mainfluxSDK.CreateChannel(ch2, token)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
-	chanID2, err := mainfluxSDK.CreateChannel(channel, otherToken)
+	chanID2, err := mainfluxSDK.CreateChannel(ch3, otherToken)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
 	cases := []struct {
@@ -818,10 +843,10 @@ func TestDisconnectThing(t *testing.T) {
 
 	mainfluxSDK := sdk.NewSDK(sdkConf)
 
-	thingID, err := mainfluxSDK.CreateThing(thing, token)
+	thingID, err := mainfluxSDK.CreateThing(th1, token)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
-	chanID1, err := mainfluxSDK.CreateChannel(channel, token)
+	chanID1, err := mainfluxSDK.CreateChannel(ch2, token)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
 	conIDs := sdk.ConnectionIDs{
@@ -831,7 +856,7 @@ func TestDisconnectThing(t *testing.T) {
 	err = mainfluxSDK.Connect(conIDs, token)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
-	chanID2, err := mainfluxSDK.CreateChannel(channel, otherToken)
+	chanID2, err := mainfluxSDK.CreateChannel(ch2, otherToken)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 
 	cases := []struct {
diff --git a/things/api/things/http/endpoint_test.go b/things/api/things/http/endpoint_test.go
index 378dd056..91b15e4a 100644
--- a/things/api/things/http/endpoint_test.go
+++ b/things/api/things/http/endpoint_test.go
@@ -36,6 +36,7 @@ const (
 	nameKey     = "name"
 	ascKey      = "asc"
 	descKey     = "desc"
+	prefix      = "fe6b4e92-cc98-425e-b0aa-"
 )
 
 var (
@@ -131,7 +132,7 @@ func TestCreateThing(t *testing.T) {
 			contentType: contentType,
 			auth:        token,
 			status:      http.StatusCreated,
-			location:    "/things/001",
+			location:    fmt.Sprintf("/things/%s%012d", uuid.Prefix, 1),
 		},
 		{
 			desc:        "add thing with existing key",
@@ -147,7 +148,7 @@ func TestCreateThing(t *testing.T) {
 			contentType: contentType,
 			auth:        token,
 			status:      http.StatusCreated,
-			location:    "/things/002",
+			location:    fmt.Sprintf("/things/%s%012d", uuid.Prefix, 3),
 		},
 		{
 			desc:        "add thing with invalid auth token",
@@ -766,7 +767,10 @@ func TestListThings(t *testing.T) {
 
 	data := []thingRes{}
 	for i := 0; i < 100; i++ {
-		ths, err := svc.CreateThings(context.Background(), token, thing)
+		id := fmt.Sprintf("%s%012d", prefix, i + 1)
+		thing1 := thing
+		thing1.ID = id
+		ths, err := svc.CreateThings(context.Background(), token, thing1)
 		require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 		th := ths[0]
 		data = append(data, thingRes{
@@ -1003,7 +1007,8 @@ func TestSearchThings(t *testing.T) {
 	data := []thingRes{}
 	for i := 0; i < 100; i++ {
 		name := "name_" + fmt.Sprintf("%03d", i+1)
-		ths, err := svc.CreateThings(context.Background(), token, things.Thing{Name: name, Metadata: map[string]interface{}{"test": name}})
+		id := fmt.Sprintf("%s%012d", prefix, i + 1)
+		ths, err := svc.CreateThings(context.Background(), token, things.Thing{ID: id, Name: name, Metadata: map[string]interface{}{"test": name}})
 		require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 		th := ths[0]
 		data = append(data, thingRes{
@@ -1170,7 +1175,10 @@ func TestListThingsByChannel(t *testing.T) {
 
 	data := []thingRes{}
 	for i := 0; i < 101; i++ {
-		ths, err := svc.CreateThings(context.Background(), token, thing)
+		id := fmt.Sprintf("%s%012d", prefix, i + 1)
+		thing1 := thing
+		thing1.ID = id
+		ths, err := svc.CreateThings(context.Background(), token, thing1)
 		require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 		th := ths[0]
 		err = svc.Connect(context.Background(), token, []string{ch.ID}, []string{th.ID})
@@ -1418,7 +1426,7 @@ func TestCreateChannel(t *testing.T) {
 			contentType: contentType,
 			auth:        token,
 			status:      http.StatusCreated,
-			location:    "/channels/001",
+			location:    fmt.Sprintf("/channels/%s%012d", uuid.Prefix, 1),
 		},
 		{
 			desc:        "create new channel with invalid token",
@@ -1450,7 +1458,7 @@ func TestCreateChannel(t *testing.T) {
 			contentType: contentType,
 			auth:        token,
 			status:      http.StatusCreated,
-			location:    "/channels/002",
+			location:    fmt.Sprintf("/channels/%s%012d", uuid.Prefix, 2),
 		},
 		{
 			desc:        "create new channel with empty request",
@@ -2021,7 +2029,10 @@ func TestListChannelsByThing(t *testing.T) {
 
 	channels := []channelRes{}
 	for i := 0; i < 101; i++ {
-		chs, err := svc.CreateChannels(context.Background(), token, channel)
+		id := fmt.Sprintf("%s%012d", prefix, i + 1)
+		channel1 := channel
+		channel1.ID = id
+		chs, err := svc.CreateChannels(context.Background(), token, channel1)
 		require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 		ch := chs[0]
 		err = svc.Connect(context.Background(), token, []string{ch.ID}, []string{th.ID})
diff --git a/things/redis/streams_test.go b/things/redis/streams_test.go
index 4438b300..90c609a9 100644
--- a/things/redis/streams_test.go
+++ b/things/redis/streams_test.go
@@ -76,7 +76,7 @@ func TestCreateThings(t *testing.T) {
 			key: token,
 			err: nil,
 			event: map[string]interface{}{
-				"id":        "001",
+				"id":        "123e4567-e89b-12d3-a456-000000000001",
 				"name":      "a",
 				"owner":     email,
 				"metadata":  "{\"test\":\"test\"}",
@@ -302,7 +302,7 @@ func TestCreateChannels(t *testing.T) {
 			key:  token,
 			err:  nil,
 			event: map[string]interface{}{
-				"id":        "001",
+				"id":        "123e4567-e89b-12d3-a456-000000000001",
 				"name":      "a",
 				"metadata":  "{\"test\":\"test\"}",
 				"owner":     email,
diff --git a/things/service_test.go b/things/service_test.go
index a4bcf813..b87c92b5 100644
--- a/things/service_test.go
+++ b/things/service_test.go
@@ -26,11 +26,15 @@ const (
 	token      = "token"
 	token2     = "token2"
 	n          = uint64(10)
+	prefix     = "fe6b4e92-cc98-425e-b0aa-"
 )
 
 var (
-	thing   = things.Thing{Name: "test"}
-	channel = things.Channel{Name: "test"}
+	thing     = things.Thing{Name: "test"}
+	thingList = [n]things.Thing{}
+	channel   = things.Channel{Name: "test"}
+	thsExtID  = []things.Thing{{ID: prefix + "000000000001", Name: "a"}, {ID: prefix + "000000000002", Name: "b"}}
+	chsExtID  = []things.Channel{{ID: prefix + "000000000001", Name: "a"}, {ID: prefix + "000000000002", Name: "b"}}
 )
 
 func newService(tokens map[string]string) things.Service {
@@ -48,6 +52,14 @@ func newService(tokens map[string]string) things.Service {
 	return things.New(auth, thingsRepo, channelsRepo, chanCache, thingCache, idProvider)
 }
 
+func TestInit(t *testing.T) {
+	for i := uint64(0); i < n; i++ {
+		thingList[i].Name = fmt.Sprintf("name-%d", i+1)
+		thingList[i].ID = fmt.Sprintf("%s%012d", prefix, i+1)
+		thingList[i].Key = fmt.Sprintf("%s1%011d", prefix, i+1)
+	}
+}
+
 func TestCreateThings(t *testing.T) {
 	svc := newService(map[string]string{token: email})
 
@@ -69,6 +81,18 @@ func TestCreateThings(t *testing.T) {
 			token:  wrongValue,
 			err:    things.ErrUnauthorizedAccess,
 		},
+		{
+			desc:   "create new things with external UUID",
+			things: thsExtID,
+			token:  token,
+			err:    nil,
+		},
+		{
+			desc:   "create new things with external wrong UUID",
+			things: []things.Thing{{ID: "b0aa-000000000001", Name: "a"}, {ID: "b0aa-000000000002", Name: "b"}},
+			token:  token,
+			err:    nil,
+		},
 	}
 
 	for _, tc := range cases {
@@ -79,7 +103,7 @@ func TestCreateThings(t *testing.T) {
 
 func TestUpdateThing(t *testing.T) {
 	svc := newService(map[string]string{token: email})
-	ths, err := svc.CreateThings(context.Background(), token, thing)
+	ths, err := svc.CreateThings(context.Background(), token, thingList[0])
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
 	th := ths[0]
 	other := things.Thing{ID: wrongID, Key: "x"}
@@ -161,7 +185,7 @@ func TestUpdateKey(t *testing.T) {
 
 func TestShareThing(t *testing.T) {
 	svc := newService(map[string]string{token: email, token2: email2})
-	ths, err := svc.CreateThings(context.Background(), token, thing)
+	ths, err := svc.CreateThings(context.Background(), token, thingList[0])
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
 	th := ths[0]
 	policies := []string{"read"}
@@ -217,7 +241,7 @@ func TestShareThing(t *testing.T) {
 
 func TestViewThing(t *testing.T) {
 	svc := newService(map[string]string{token: email})
-	ths, err := svc.CreateThings(context.Background(), token, thing)
+	ths, err := svc.CreateThings(context.Background(), token, thingList[0])
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
 	th := ths[0]
 
@@ -254,12 +278,11 @@ func TestListThings(t *testing.T) {
 
 	m := make(map[string]interface{})
 	m["serial"] = "123456"
-	thing.Metadata = m
+	thingList[0].Metadata = m
 
 	var ths []things.Thing
 	for i := uint64(0); i < n; i++ {
-		th := thing
-		th.Name = fmt.Sprintf("name-%d", i)
+		th := thingList[i]
 		ths = append(ths, th)
 	}
 
@@ -382,8 +405,7 @@ func TestListThingsByChannel(t *testing.T) {
 
 	var ths []things.Thing
 	for i := uint64(0); i < n; i++ {
-		th := thing
-		th.Name = fmt.Sprintf("name-%d", i)
+		th := thingList[i]
 		ths = append(ths, th)
 	}
 
@@ -555,7 +577,7 @@ func TestListThingsByChannel(t *testing.T) {
 
 func TestRemoveThing(t *testing.T) {
 	svc := newService(map[string]string{token: email})
-	ths, err := svc.CreateThings(context.Background(), token, thing)
+	ths, err := svc.CreateThings(context.Background(), token, thingList[0])
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
 	sth := ths[0]
 
@@ -618,6 +640,18 @@ func TestCreateChannels(t *testing.T) {
 			token:    wrongValue,
 			err:      things.ErrUnauthorizedAccess,
 		},
+		{
+			desc:     "create new channels with external UUID",
+			channels: chsExtID,
+			token:    token,
+			err:      nil,
+		},
+		{
+			desc:     "create new channels with invalid external UUID",
+			channels: []things.Channel{{ID: "b0aa-000000000001", Name: "a"}, {ID: "b0aa-000000000002", Name: "b"}},
+			token:    token,
+			err:      nil,
+		},
 	}
 
 	for _, cc := range cases {
@@ -849,7 +883,7 @@ func TestListChannels(t *testing.T) {
 func TestListChannelsByThing(t *testing.T) {
 	svc := newService(map[string]string{token: email})
 
-	ths, err := svc.CreateThings(context.Background(), token, thing)
+	ths, err := svc.CreateThings(context.Background(), token, thingList[0])
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
 	th := ths[0]
 
@@ -1075,7 +1109,7 @@ func TestRemoveChannel(t *testing.T) {
 func TestConnect(t *testing.T) {
 	svc := newService(map[string]string{token: email})
 
-	ths, err := svc.CreateThings(context.Background(), token, thing)
+	ths, err := svc.CreateThings(context.Background(), token, thingList[0])
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
 	th := ths[0]
 	chs, err := svc.CreateChannels(context.Background(), token, channel)
@@ -1128,7 +1162,7 @@ func TestConnect(t *testing.T) {
 func TestDisconnect(t *testing.T) {
 	svc := newService(map[string]string{token: email})
 
-	ths, err := svc.CreateThings(context.Background(), token, thing)
+	ths, err := svc.CreateThings(context.Background(), token, thingList[0])
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
 	th := ths[0]
 	chs, err := svc.CreateChannels(context.Background(), token, channel)
@@ -1191,7 +1225,7 @@ func TestDisconnect(t *testing.T) {
 func TestCanAccessByKey(t *testing.T) {
 	svc := newService(map[string]string{token: email})
 
-	ths, err := svc.CreateThings(context.Background(), token, thing)
+	ths, err := svc.CreateThings(context.Background(), token, thingList[0])
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
 	chs, err := svc.CreateChannels(context.Background(), token, channel, channel)
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
@@ -1234,7 +1268,7 @@ func TestCanAccessByKey(t *testing.T) {
 func TestCanAccessByID(t *testing.T) {
 	svc := newService(map[string]string{token: email})
 
-	ths, err := svc.CreateThings(context.Background(), token, thing, thing)
+	ths, err := svc.CreateThings(context.Background(), token, thingList[0], thingList[1])
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
 	th := ths[0]
 	chs, err := svc.CreateChannels(context.Background(), token, channel)
@@ -1313,7 +1347,7 @@ func TestIsChannelOwner(t *testing.T) {
 func TestIdentify(t *testing.T) {
 	svc := newService(map[string]string{token: email})
 
-	ths, err := svc.CreateThings(context.Background(), token, thing)
+	ths, err := svc.CreateThings(context.Background(), token, thingList[0])
 	require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
 	th := ths[0]