Repository: mainflux/mainflux
Pull request #1617 (2022-06-14 16:00:58)

NOISSUE - Add identify thing method to SDK
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

### What does this do?
Adds identify thing to SDK

### Which issue(s) does this PR fix/relate to?
N/A

### List any changes that modify/break current functionality
N/A

### Have you included tests for your changes?
No

### Did you document any new/modified functionality?
No

### Notes
N/A

codecov-commenter: # [Codecov](https://codecov.io/gh/mainflux/mainflux/pull/1617?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux) Report
> Merging [#1617](https://codecov.io/gh/mainflux/mainflux/pull/1617?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux) (90910df) into [master](https://codecov.io/gh/mainflux/mainflux/commit/5b8780de4a6a8f04c7523886fe6bf7ee3728187c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux) (5b8780d) will **decrease** coverage by `0.15%`.
> The diff coverage is `0.00%`.

```diff
@@            Coverage Diff             @@
##           master    #1617      +/-   ##
==========================================
- Coverage   69.04%   68.89%   -0.16%     
==========================================
  Files         138      138              
  Lines       11163    11185      +22     
==========================================
- Hits         7708     7706       -2     
- Misses       2790     2814      +24     
  Partials      665      665              
```


| [Impacted Files](https://codecov.io/gh/mainflux/mainflux/pull/1617?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux) | Coverage Δ | |
|---|---|---|
| [pkg/sdk/go/sdk.go](https://codecov.io/gh/mainflux/mainflux/pull/1617/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux#diff-cGtnL3Nkay9nby9zZGsuZ28=) | `100.00% <ø> (ø)` | |
| [pkg/sdk/go/things.go](https://codecov.io/gh/mainflux/mainflux/pull/1617/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux#diff-cGtnL3Nkay9nby90aGluZ3MuZ28=) | `50.00% <0.00%> (-7.75%)` | :arrow_down: |
| [things/service.go](https://codecov.io/gh/mainflux/mainflux/pull/1617/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux#diff-dGhpbmdzL3NlcnZpY2UuZ28=) | `65.35% <0.00%> (-0.72%)` | :arrow_down: |

:mega: Codecov can now indicate which changes are the most critical in Pull Requests. [Learn more](https://about.codecov.io/product/feature/runtime-insights/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=mainflux)

0x6f736f646f: > What's the purpose of this PR? There is no `identify` HTTP endpoint and for a reason - there should not be such an endpoint. How did you test the code before sending a PR?

I saw it [here](https://api.mainflux.io/?urls.primaryName=things.yml#/identity/post_identify)

Patch:
diff --git a/cli/things.go b/cli/things.go
index 86dde21a..27a5cad6 100644
--- a/cli/things.go
+++ b/cli/things.go
@@ -94,6 +94,25 @@ var cmdThings = []cobra.Command{
 			logOK()
 		},
 	},
+	{
+		Use:   "identify <thing_key>",
+		Short: "Identify thing",
+		Long:  "Validates thing's key and returns its ID",
+		Run: func(cmd *cobra.Command, args []string) {
+			if len(args) != 1 {
+				logUsage(cmd.Use)
+				return
+			}
+
+			i, err := sdk.IdentifyThing(args[0])
+			if err != nil {
+				logError(err)
+				return
+			}
+
+			logJSON(i)
+		},
+	},
 	{
 		Use:   "update <JSON_string> <user_auth_token>",
 		Short: "Update thing",
@@ -201,9 +220,9 @@ var cmdThings = []cobra.Command{
 // NewThingsCmd returns things command.
 func NewThingsCmd() *cobra.Command {
 	cmd := cobra.Command{
-		Use:   "things [create | get | update | delete | connect | disconnect | connections | not-connected]",
+		Use:   "things [create | get | update | delete | identify | connect | disconnect | connections | not-connected]",
 		Short: "Things management",
-		Long:  `Things management: create, get, update or delete Thing, connect or disconnect Thing from Channel and get the list of Channels connected or disconnected from a Thing`,
+		Long:  `Things management: create, get, update, identify or delete Thing, connect or disconnect Thing from Channel and get the list of Channels connected or disconnected from a Thing`,
 	}
 
 	for i := range cmdThings {
diff --git a/pkg/sdk/go/sdk.go b/pkg/sdk/go/sdk.go
index 20af46eb..41221ac2 100644
--- a/pkg/sdk/go/sdk.go
+++ b/pkg/sdk/go/sdk.go
@@ -175,6 +175,9 @@ type SDK interface {
 	// DeleteThing removes existing thing.
 	DeleteThing(id, token string) error
 
+	// IdentifyThing validates thing's key and returns its ID
+	IdentifyThing(key string) (string, error)
+
 	// CreateGroup creates new group and returns its id.
 	CreateGroup(group Group, token string) (string, error)
 
diff --git a/pkg/sdk/go/things.go b/pkg/sdk/go/things.go
index 5421bb40..6bdbcdbb 100644
--- a/pkg/sdk/go/things.go
+++ b/pkg/sdk/go/things.go
@@ -14,8 +14,19 @@ import (
 	"github.com/mainflux/mainflux/pkg/errors"
 )
 
-const thingsEndpoint = "things"
-const connectEndpoint = "connect"
+const (
+	thingsEndpoint   = "things"
+	connectEndpoint  = "connect"
+	identifyEndpoint = "identify"
+)
+
+type identifyThingReq struct {
+	Token string `json:"token,omitempty"`
+}
+
+type identifyThingResp struct {
+	ID string `json:"id,omitempty"`
+}
 
 func (sdk mfSDK) CreateThing(t Thing, token string) (string, error) {
 	data, err := json.Marshal(t)
@@ -220,6 +231,42 @@ func (sdk mfSDK) DeleteThing(id, token string) error {
 	return nil
 }
 
+func (sdk mfSDK) IdentifyThing(key string) (string, error) {
+	idReq := identifyThingReq{Token: key}
+	data, err := json.Marshal(idReq)
+	if err != nil {
+		return "", err
+	}
+	url := fmt.Sprintf("%s/%s", sdk.thingsURL, identifyEndpoint)
+
+	req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(data))
+	if err != nil {
+		return "", err
+	}
+
+	resp, err := sdk.sendRequest(req, "", string(CTJSON))
+	if err != nil {
+		return "", err
+	}
+	defer resp.Body.Close()
+
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return "", err
+	}
+
+	if resp.StatusCode != http.StatusOK {
+		return "", errors.Wrap(ErrFailedFetch, errors.New(resp.Status))
+	}
+
+	var i identifyThingResp
+	if err := json.Unmarshal(body, &i); err != nil {
+		return "", err
+	}
+
+	return i.ID, err
+}
+
 func (sdk mfSDK) Connect(connIDs ConnectionIDs, token string) error {
 	data, err := json.Marshal(connIDs)
 	if err != nil {


Test patch:
diff --git a/pkg/sdk/go/things_test.go b/pkg/sdk/go/things_test.go
index 5f5496d3..18908ce8 100644
--- a/pkg/sdk/go/things_test.go
+++ b/pkg/sdk/go/things_test.go
@@ -13,6 +13,7 @@ import (
 	sdk "github.com/mainflux/mainflux/pkg/sdk/go"
 	"github.com/mainflux/mainflux/pkg/uuid"
 	"github.com/mainflux/mainflux/things"
+	authapi "github.com/mainflux/mainflux/things/api/auth/http"
 	httpapi "github.com/mainflux/mainflux/things/api/things/http"
 	"github.com/mainflux/mainflux/things/mocks"
 	"github.com/opentracing/opentracing-go/mocktracer"
@@ -29,6 +30,7 @@ const (
 	otherToken  = "other_token"
 	wrongValue  = "wrong_value"
 	badID       = "999"
+	badKey      = "999"
 	emptyValue  = ""
 )
 
@@ -61,6 +63,12 @@ func newThingsServer(svc things.Service) *httptest.Server {
 	return httptest.NewServer(mux)
 }
 
+func newAuthServer(svc things.Service) *httptest.Server {
+	logger := logger.NewMock()
+	mux := authapi.MakeHandler(mocktracer.New(), svc, logger)
+	return httptest.NewServer(mux)
+}
+
 func TestCreateThing(t *testing.T) {
 	svc := newThingsService(map[string]string{token: email})
 	ts := newThingsServer(svc)
@@ -639,6 +647,64 @@ func TestDeleteThing(t *testing.T) {
 	}
 }
 
+func TestIdentifyThing(t *testing.T) {
+	svc := newThingsService(map[string]string{token: email})
+	ts := newThingsServer(svc)
+	as := newAuthServer(svc)
+	defer ts.Close()
+	defer as.Close()
+	sdkConf := sdk.Config{
+		ThingsURL:       ts.URL,
+		MsgContentType:  contentType,
+		TLSVerification: false,
+	}
+	authSdkConf := sdk.Config{
+		ThingsURL:       as.URL,
+		MsgContentType:  contentType,
+		TLSVerification: false,
+	}
+
+	mainfluxSDK := sdk.NewSDK(sdkConf)
+	mainfluxAuthSDK := sdk.NewSDK(authSdkConf)
+	th := sdk.Thing{ID: "fe6b4e92-cc98-425e-b0aa-000000007891", Name: "identify"}
+	id, err := mainfluxSDK.CreateThing(th, token)
+	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
+	thing, err := mainfluxSDK.Thing(th.ID, token)
+	require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
+
+	cases := []struct {
+		desc     string
+		thingKey string
+		err      error
+		response string
+	}{
+		{
+			desc:     "identify thing with valid key",
+			thingKey: thing.Key,
+			err:      nil,
+			response: id,
+		},
+		{
+			desc:     "identify thing with invalid key",
+			thingKey: badKey,
+			err:      createError(sdk.ErrFailedFetch, http.StatusNotFound),
+			response: "",
+		},
+		{
+			desc:     "identify thing with empty key",
+			thingKey: "",
+			err:      createError(sdk.ErrFailedFetch, http.StatusUnauthorized),
+			response: "",
+		},
+	}
+
+	for _, tc := range cases {
+		thingID, err := mainfluxAuthSDK.IdentifyThing(tc.thingKey)
+		assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected error %s, got %s", tc.desc, tc.err, err))
+		assert.Equal(t, tc.response, thingID, fmt.Sprintf("%s: expected response id %s, got %s", tc.desc, tc.response, thingID))
+	}
+}
+
 func TestConnectThing(t *testing.T) {
 	svc := newThingsService(map[string]string{
 		token:      email,