mirror of
https://github.com/mainflux/mainflux.git
synced 2025-04-24 13:48:49 +08:00
MF-707 - Allow custom Thing key (#726)
* Add support for setting up thing key manually Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com> * Fix existing tests and add new ones Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com> * Update SQL schema for things entity Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com> * Add update thing key endpoint to swagger docs Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com> * Fix response code when handling conflicting key Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>
This commit is contained in:
parent
aba7d909ed
commit
dc9333237f
@ -162,6 +162,10 @@ func (svc *mainfluxThings) UpdateThing(string, things.Thing) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) UpdateKey(string, string, string) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (svc *mainfluxThings) ListThings(string, uint64, uint64) (things.ThingsPage, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
@ -23,10 +23,11 @@ func addThingEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
}
|
||||
|
||||
thing := things.Thing{
|
||||
Key: req.Key,
|
||||
Name: req.Name,
|
||||
Metadata: req.Metadata,
|
||||
}
|
||||
saved, err := svc.AddThing(req.key, thing)
|
||||
saved, err := svc.AddThing(req.token, thing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -53,7 +54,24 @@ func updateThingEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
Metadata: req.Metadata,
|
||||
}
|
||||
|
||||
if err := svc.UpdateThing(req.key, thing); err != nil {
|
||||
if err := svc.UpdateThing(req.token, thing); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := thingRes{id: req.id, created: false}
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateKeyEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
return func(_ context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(updateKeyReq)
|
||||
|
||||
if err := req.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := svc.UpdateKey(req.token, req.id, req.Key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -70,7 +88,7 @@ func viewThingEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
thing, err := svc.ViewThing(req.key, req.id)
|
||||
thing, err := svc.ViewThing(req.token, req.id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -94,7 +112,7 @@ func listThingsEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
page, err := svc.ListThings(req.key, req.offset, req.limit)
|
||||
page, err := svc.ListThings(req.token, req.offset, req.limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -130,7 +148,7 @@ func listThingsByChannelEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
page, err := svc.ListThingsByChannel(req.key, req.id, req.offset, req.limit)
|
||||
page, err := svc.ListThingsByChannel(req.token, req.id, req.offset, req.limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -171,7 +189,7 @@ func removeThingEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := svc.RemoveThing(req.key, req.id); err != nil {
|
||||
if err := svc.RemoveThing(req.token, req.id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -188,7 +206,7 @@ func createChannelEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
}
|
||||
|
||||
channel := things.Channel{Name: req.Name, Metadata: req.Metadata}
|
||||
saved, err := svc.CreateChannel(req.key, channel)
|
||||
saved, err := svc.CreateChannel(req.token, channel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -214,7 +232,7 @@ func updateChannelEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
Name: req.Name,
|
||||
Metadata: req.Metadata,
|
||||
}
|
||||
if err := svc.UpdateChannel(req.key, channel); err != nil {
|
||||
if err := svc.UpdateChannel(req.token, channel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -234,7 +252,7 @@ func viewChannelEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
channel, err := svc.ViewChannel(req.key, req.id)
|
||||
channel, err := svc.ViewChannel(req.token, req.id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -258,7 +276,7 @@ func listChannelsEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
page, err := svc.ListChannels(req.key, req.offset, req.limit)
|
||||
page, err := svc.ListChannels(req.token, req.offset, req.limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -295,7 +313,7 @@ func listChannelsByThingEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
page, err := svc.ListChannelsByThing(req.key, req.id, req.offset, req.limit)
|
||||
page, err := svc.ListChannelsByThing(req.token, req.id, req.offset, req.limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -333,7 +351,7 @@ func removeChannelEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := svc.RemoveChannel(req.key, req.id); err != nil {
|
||||
if err := svc.RemoveChannel(req.token, req.id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -349,7 +367,7 @@ func connectEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := svc.Connect(cr.key, cr.chanID, cr.thingID); err != nil {
|
||||
if err := svc.Connect(cr.token, cr.chanID, cr.thingID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -365,7 +383,7 @@ func disconnectEndpoint(svc things.Service) endpoint.Endpoint {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := svc.Disconnect(cr.key, cr.chanID, cr.thingID); err != nil {
|
||||
if err := svc.Disconnect(cr.token, cr.chanID, cr.thingID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,9 @@ func TestAddThing(t *testing.T) {
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
|
||||
data := toJSON(thing)
|
||||
th := thing
|
||||
th.Key = "key"
|
||||
data := toJSON(th)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
@ -107,6 +109,14 @@ func TestAddThing(t *testing.T) {
|
||||
status: http.StatusCreated,
|
||||
location: "/things/1",
|
||||
},
|
||||
{
|
||||
desc: "add thing with existing key",
|
||||
req: data,
|
||||
contentType: contentType,
|
||||
auth: token,
|
||||
status: http.StatusUnprocessableEntity,
|
||||
location: "",
|
||||
},
|
||||
{
|
||||
desc: "add thing with empty JSON request",
|
||||
req: "{}",
|
||||
@ -280,6 +290,126 @@ func TestUpdateThing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateKey(t *testing.T) {
|
||||
svc := newService(map[string]string{token: email})
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
|
||||
th := thing
|
||||
th.Key = "key"
|
||||
sth, _ := svc.AddThing(token, th)
|
||||
|
||||
sth.Key = "new-key"
|
||||
data := toJSON(sth)
|
||||
|
||||
sth.Key = "key"
|
||||
dummyData := toJSON(sth)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
req string
|
||||
id string
|
||||
contentType string
|
||||
auth string
|
||||
status int
|
||||
}{
|
||||
{
|
||||
desc: "update key for an existing thing",
|
||||
req: data,
|
||||
id: sth.ID,
|
||||
contentType: contentType,
|
||||
auth: token,
|
||||
status: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "update thing with conflicting key",
|
||||
req: data,
|
||||
id: sth.ID,
|
||||
contentType: contentType,
|
||||
auth: token,
|
||||
status: http.StatusUnprocessableEntity,
|
||||
},
|
||||
{
|
||||
desc: "update key with empty JSON request",
|
||||
req: "{}",
|
||||
id: sth.ID,
|
||||
contentType: contentType,
|
||||
auth: token,
|
||||
status: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "update key of non-existent thing",
|
||||
req: dummyData,
|
||||
id: strconv.FormatUint(wrongID, 10),
|
||||
contentType: contentType,
|
||||
auth: token,
|
||||
status: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
desc: "update thing with invalid id",
|
||||
req: dummyData,
|
||||
id: "invalid",
|
||||
contentType: contentType,
|
||||
auth: token,
|
||||
status: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
desc: "update thing with invalid user token",
|
||||
req: data,
|
||||
id: sth.ID,
|
||||
contentType: contentType,
|
||||
auth: wrongValue,
|
||||
status: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
desc: "update thing with empty user token",
|
||||
req: data,
|
||||
id: sth.ID,
|
||||
contentType: contentType,
|
||||
auth: "",
|
||||
status: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
desc: "update thing with invalid data format",
|
||||
req: "{",
|
||||
id: sth.ID,
|
||||
contentType: contentType,
|
||||
auth: token,
|
||||
status: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "update thing with empty request",
|
||||
req: "",
|
||||
id: sth.ID,
|
||||
contentType: contentType,
|
||||
auth: token,
|
||||
status: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "update thing without content type",
|
||||
req: data,
|
||||
id: sth.ID,
|
||||
contentType: "",
|
||||
auth: token,
|
||||
status: http.StatusUnsupportedMediaType,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: ts.Client(),
|
||||
method: http.MethodPatch,
|
||||
url: fmt.Sprintf("%s/things/%s/key", ts.URL, tc.id),
|
||||
contentType: tc.contentType,
|
||||
token: tc.auth,
|
||||
body: strings.NewReader(tc.req),
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestViewThing(t *testing.T) {
|
||||
svc := newService(map[string]string{token: email})
|
||||
ts := newServer(svc)
|
||||
|
@ -15,26 +15,15 @@ type apiReq interface {
|
||||
validate() error
|
||||
}
|
||||
|
||||
type identityReq struct {
|
||||
key string
|
||||
}
|
||||
|
||||
func (req identityReq) validate() error {
|
||||
if req.key == "" {
|
||||
return things.ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type addThingReq struct {
|
||||
key string
|
||||
token string
|
||||
Name string `json:"name,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
func (req addThingReq) validate() error {
|
||||
if req.key == "" {
|
||||
if req.token == "" {
|
||||
return things.ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
@ -42,14 +31,14 @@ func (req addThingReq) validate() error {
|
||||
}
|
||||
|
||||
type updateThingReq struct {
|
||||
key string
|
||||
token string
|
||||
id string
|
||||
Name string `json:"name,omitempty"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
func (req updateThingReq) validate() error {
|
||||
if req.key == "" {
|
||||
if req.token == "" {
|
||||
return things.ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
@ -60,14 +49,32 @@ func (req updateThingReq) validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type updateKeyReq struct {
|
||||
token string
|
||||
id string
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
func (req updateKeyReq) validate() error {
|
||||
if req.token == "" {
|
||||
return things.ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
if req.id == "" || req.Key == "" {
|
||||
return things.ErrMalformedEntity
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type createChannelReq struct {
|
||||
key string
|
||||
token string
|
||||
Name string `json:"name,omitempty"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
func (req createChannelReq) validate() error {
|
||||
if req.key == "" {
|
||||
if req.token == "" {
|
||||
return things.ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
@ -75,14 +82,14 @@ func (req createChannelReq) validate() error {
|
||||
}
|
||||
|
||||
type updateChannelReq struct {
|
||||
key string
|
||||
token string
|
||||
id string
|
||||
Name string `json:"name,omitempty"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
func (req updateChannelReq) validate() error {
|
||||
if req.key == "" {
|
||||
if req.token == "" {
|
||||
return things.ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
@ -94,12 +101,12 @@ func (req updateChannelReq) validate() error {
|
||||
}
|
||||
|
||||
type viewResourceReq struct {
|
||||
key string
|
||||
id string
|
||||
token string
|
||||
id string
|
||||
}
|
||||
|
||||
func (req viewResourceReq) validate() error {
|
||||
if req.key == "" {
|
||||
if req.token == "" {
|
||||
return things.ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
@ -111,13 +118,13 @@ func (req viewResourceReq) validate() error {
|
||||
}
|
||||
|
||||
type listResourcesReq struct {
|
||||
key string
|
||||
token string
|
||||
offset uint64
|
||||
limit uint64
|
||||
}
|
||||
|
||||
func (req *listResourcesReq) validate() error {
|
||||
if req.key == "" {
|
||||
if req.token == "" {
|
||||
return things.ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
@ -129,14 +136,14 @@ func (req *listResourcesReq) validate() error {
|
||||
}
|
||||
|
||||
type listByConnectionReq struct {
|
||||
key string
|
||||
token string
|
||||
id string
|
||||
offset uint64
|
||||
limit uint64
|
||||
}
|
||||
|
||||
func (req listByConnectionReq) validate() error {
|
||||
if req.key == "" {
|
||||
if req.token == "" {
|
||||
return things.ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
@ -152,13 +159,13 @@ func (req listByConnectionReq) validate() error {
|
||||
}
|
||||
|
||||
type connectionReq struct {
|
||||
key string
|
||||
token string
|
||||
chanID string
|
||||
thingID string
|
||||
}
|
||||
|
||||
func (req connectionReq) validate() error {
|
||||
if req.key == "" {
|
||||
if req.token == "" {
|
||||
return things.ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
|
@ -17,51 +17,30 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIdentityReqValidation(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
key string
|
||||
err error
|
||||
}{
|
||||
"non-empty token": {
|
||||
key: uuid.NewV4().String(),
|
||||
err: nil,
|
||||
},
|
||||
"empty token": {
|
||||
key: "",
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
req := identityReq{key: tc.key}
|
||||
err := req.validate()
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddThingReqValidation(t *testing.T) {
|
||||
key := uuid.NewV4().String()
|
||||
token := uuid.NewV4().String()
|
||||
valid := things.Thing{}
|
||||
|
||||
cases := map[string]struct {
|
||||
thing things.Thing
|
||||
key string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
"valid thing addition request": {
|
||||
thing: valid,
|
||||
key: key,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
"missing token": {
|
||||
thing: valid,
|
||||
key: "", err: things.ErrUnauthorizedAccess,
|
||||
token: "",
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
req := addThingReq{
|
||||
key: tc.key,
|
||||
token: tc.token,
|
||||
Name: tc.thing.Name,
|
||||
Metadata: tc.thing.Metadata,
|
||||
}
|
||||
@ -72,38 +51,38 @@ func TestAddThingReqValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateThingReqValidation(t *testing.T) {
|
||||
key := uuid.NewV4().String()
|
||||
token := uuid.NewV4().String()
|
||||
valid := things.Thing{ID: "1"}
|
||||
|
||||
cases := map[string]struct {
|
||||
thing things.Thing
|
||||
id string
|
||||
key string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
"valid thing update request": {
|
||||
thing: valid,
|
||||
id: valid.ID,
|
||||
key: key,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
"missing token": {
|
||||
thing: valid,
|
||||
id: valid.ID,
|
||||
key: "",
|
||||
token: "",
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
"empty thing id": {
|
||||
thing: valid,
|
||||
id: "",
|
||||
key: key,
|
||||
token: token,
|
||||
err: things.ErrMalformedEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
req := updateThingReq{
|
||||
key: tc.key,
|
||||
token: tc.token,
|
||||
id: tc.id,
|
||||
Name: tc.thing.Name,
|
||||
Metadata: tc.thing.Metadata,
|
||||
@ -114,31 +93,79 @@ func TestUpdateThingReqValidation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateKeyReqValidation(t *testing.T) {
|
||||
token := uuid.NewV4().String()
|
||||
thing := things.Thing{ID: "1", Key: "key"}
|
||||
|
||||
cases := map[string]struct {
|
||||
token string
|
||||
id string
|
||||
key string
|
||||
err error
|
||||
}{
|
||||
"valid key update request": {
|
||||
token: token,
|
||||
id: thing.ID,
|
||||
key: thing.Key,
|
||||
err: nil,
|
||||
},
|
||||
"missing token": {
|
||||
token: "",
|
||||
id: thing.ID,
|
||||
key: thing.Key,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
"empty thing id": {
|
||||
token: token,
|
||||
id: "",
|
||||
key: thing.Key,
|
||||
err: things.ErrMalformedEntity,
|
||||
},
|
||||
"empty key": {
|
||||
token: token,
|
||||
id: thing.ID,
|
||||
key: "",
|
||||
err: things.ErrMalformedEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
req := updateKeyReq{
|
||||
token: tc.token,
|
||||
id: tc.id,
|
||||
Key: tc.key,
|
||||
}
|
||||
|
||||
err := req.validate()
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateChannelReqValidation(t *testing.T) {
|
||||
channel := things.Channel{}
|
||||
key := uuid.NewV4().String()
|
||||
token := uuid.NewV4().String()
|
||||
|
||||
cases := map[string]struct {
|
||||
channel things.Channel
|
||||
key string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
"valid channel creation request": {
|
||||
channel: channel,
|
||||
key: key,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
"missing token": {
|
||||
channel: channel,
|
||||
key: "",
|
||||
token: "",
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
req := createChannelReq{
|
||||
key: tc.key,
|
||||
Name: tc.channel.Name,
|
||||
token: tc.token,
|
||||
Name: tc.channel.Name,
|
||||
}
|
||||
|
||||
err := req.validate()
|
||||
@ -147,40 +174,40 @@ func TestCreateChannelReqValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateChannelReqValidation(t *testing.T) {
|
||||
key := uuid.NewV4().String()
|
||||
token := uuid.NewV4().String()
|
||||
channel := things.Channel{ID: "1"}
|
||||
|
||||
cases := map[string]struct {
|
||||
channel things.Channel
|
||||
id string
|
||||
key string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
"valid channel update request": {
|
||||
channel: channel,
|
||||
id: channel.ID,
|
||||
key: key,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
"missing token": {
|
||||
channel: channel,
|
||||
id: channel.ID,
|
||||
key: "",
|
||||
token: "",
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
"empty channel id": {
|
||||
channel: channel,
|
||||
id: "",
|
||||
key: key,
|
||||
token: token,
|
||||
err: things.ErrMalformedEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
req := updateChannelReq{
|
||||
key: tc.key,
|
||||
id: tc.id,
|
||||
Name: tc.channel.Name,
|
||||
token: tc.token,
|
||||
id: tc.id,
|
||||
Name: tc.channel.Name,
|
||||
}
|
||||
|
||||
err := req.validate()
|
||||
@ -189,68 +216,68 @@ func TestUpdateChannelReqValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestViewResourceReqValidation(t *testing.T) {
|
||||
key := uuid.NewV4().String()
|
||||
token := uuid.NewV4().String()
|
||||
id := uint64(1)
|
||||
|
||||
cases := map[string]struct {
|
||||
id string
|
||||
key string
|
||||
err error
|
||||
id string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
"valid resource viewing request": {
|
||||
id: strconv.FormatUint(id, 10),
|
||||
key: key,
|
||||
err: nil,
|
||||
id: strconv.FormatUint(id, 10),
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
"missing token": {
|
||||
id: strconv.FormatUint(id, 10),
|
||||
key: "",
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
id: strconv.FormatUint(id, 10),
|
||||
token: "",
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
"empty resource id": {
|
||||
id: "",
|
||||
key: key,
|
||||
err: things.ErrMalformedEntity,
|
||||
id: "",
|
||||
token: token,
|
||||
err: things.ErrMalformedEntity,
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
req := viewResourceReq{tc.key, tc.id}
|
||||
req := viewResourceReq{tc.token, tc.id}
|
||||
err := req.validate()
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestListResourcesReqValidation(t *testing.T) {
|
||||
key := uuid.NewV4().String()
|
||||
token := uuid.NewV4().String()
|
||||
value := uint64(10)
|
||||
|
||||
cases := map[string]struct {
|
||||
key string
|
||||
token string
|
||||
offset uint64
|
||||
limit uint64
|
||||
err error
|
||||
}{
|
||||
"valid listing request": {
|
||||
key: key,
|
||||
token: token,
|
||||
offset: value,
|
||||
limit: value,
|
||||
err: nil,
|
||||
},
|
||||
"missing token": {
|
||||
key: "",
|
||||
token: "",
|
||||
offset: value,
|
||||
limit: value,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
"zero limit": {
|
||||
key: key,
|
||||
token: token,
|
||||
offset: value,
|
||||
limit: 0,
|
||||
err: things.ErrMalformedEntity,
|
||||
},
|
||||
"too big limit": {
|
||||
key: key,
|
||||
token: token,
|
||||
offset: value,
|
||||
limit: 20 * value,
|
||||
err: things.ErrMalformedEntity,
|
||||
@ -259,7 +286,7 @@ func TestListResourcesReqValidation(t *testing.T) {
|
||||
|
||||
for desc, tc := range cases {
|
||||
req := listResourcesReq{
|
||||
key: tc.key,
|
||||
token: tc.token,
|
||||
offset: tc.offset,
|
||||
limit: tc.limit,
|
||||
}
|
||||
@ -271,31 +298,31 @@ func TestListResourcesReqValidation(t *testing.T) {
|
||||
|
||||
func TestConnectionReqValidation(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
key string
|
||||
token string
|
||||
chanID string
|
||||
thingID string
|
||||
err error
|
||||
}{
|
||||
"valid key": {
|
||||
key: "valid-key",
|
||||
"valid token": {
|
||||
token: "valid-token",
|
||||
chanID: "1",
|
||||
thingID: "1",
|
||||
err: nil,
|
||||
},
|
||||
"empty key": {
|
||||
key: "",
|
||||
"empty token": {
|
||||
token: "",
|
||||
chanID: "1",
|
||||
thingID: "1",
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
"empty channel id": {
|
||||
key: "valid-key",
|
||||
token: "valid-token",
|
||||
chanID: "",
|
||||
thingID: "1",
|
||||
err: things.ErrMalformedEntity,
|
||||
},
|
||||
"empty thing id": {
|
||||
key: "valid-key",
|
||||
token: "valid-token",
|
||||
chanID: "1",
|
||||
thingID: "",
|
||||
err: things.ErrMalformedEntity,
|
||||
@ -304,7 +331,7 @@ func TestConnectionReqValidation(t *testing.T) {
|
||||
|
||||
for desc, tc := range cases {
|
||||
req := connectionReq{
|
||||
key: tc.key,
|
||||
token: tc.token,
|
||||
chanID: tc.chanID,
|
||||
thingID: tc.thingID,
|
||||
}
|
||||
|
@ -52,6 +52,13 @@ func MakeHandler(svc things.Service) http.Handler {
|
||||
opts...,
|
||||
))
|
||||
|
||||
r.Patch("/things/:id/key", kithttp.NewServer(
|
||||
updateKeyEndpoint(svc),
|
||||
decodeKeyUpdate,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
))
|
||||
|
||||
r.Put("/things/:id", kithttp.NewServer(
|
||||
updateThingEndpoint(svc),
|
||||
decodeThingUpdate,
|
||||
@ -154,7 +161,7 @@ func decodeThingCreation(_ context.Context, r *http.Request) (interface{}, error
|
||||
return nil, errUnsupportedContentType
|
||||
}
|
||||
|
||||
req := addThingReq{key: r.Header.Get("Authorization")}
|
||||
req := addThingReq{token: r.Header.Get("Authorization")}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -168,8 +175,24 @@ func decodeThingUpdate(_ context.Context, r *http.Request) (interface{}, error)
|
||||
}
|
||||
|
||||
req := updateThingReq{
|
||||
key: r.Header.Get("Authorization"),
|
||||
id: bone.GetValue(r, "id"),
|
||||
token: r.Header.Get("Authorization"),
|
||||
id: bone.GetValue(r, "id"),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeKeyUpdate(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
|
||||
return nil, errUnsupportedContentType
|
||||
}
|
||||
|
||||
req := updateKeyReq{
|
||||
token: r.Header.Get("Authorization"),
|
||||
id: bone.GetValue(r, "id"),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
@ -183,7 +206,7 @@ func decodeChannelCreation(_ context.Context, r *http.Request) (interface{}, err
|
||||
return nil, errUnsupportedContentType
|
||||
}
|
||||
|
||||
req := createChannelReq{key: r.Header.Get("Authorization")}
|
||||
req := createChannelReq{token: r.Header.Get("Authorization")}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -197,8 +220,8 @@ func decodeChannelUpdate(_ context.Context, r *http.Request) (interface{}, error
|
||||
}
|
||||
|
||||
req := updateChannelReq{
|
||||
key: r.Header.Get("Authorization"),
|
||||
id: bone.GetValue(r, "id"),
|
||||
token: r.Header.Get("Authorization"),
|
||||
id: bone.GetValue(r, "id"),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
@ -209,8 +232,8 @@ func decodeChannelUpdate(_ context.Context, r *http.Request) (interface{}, error
|
||||
|
||||
func decodeView(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
req := viewResourceReq{
|
||||
key: r.Header.Get("Authorization"),
|
||||
id: bone.GetValue(r, "id"),
|
||||
token: r.Header.Get("Authorization"),
|
||||
id: bone.GetValue(r, "id"),
|
||||
}
|
||||
|
||||
return req, nil
|
||||
@ -228,7 +251,7 @@ func decodeList(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
}
|
||||
|
||||
req := listResourcesReq{
|
||||
key: r.Header.Get("Authorization"),
|
||||
token: r.Header.Get("Authorization"),
|
||||
offset: o,
|
||||
limit: l,
|
||||
}
|
||||
@ -248,7 +271,7 @@ func decodeListByConnection(_ context.Context, r *http.Request) (interface{}, er
|
||||
}
|
||||
|
||||
req := listByConnectionReq{
|
||||
key: r.Header.Get("Authorization"),
|
||||
token: r.Header.Get("Authorization"),
|
||||
id: bone.GetValue(r, "id"),
|
||||
offset: o,
|
||||
limit: l,
|
||||
@ -259,7 +282,7 @@ func decodeListByConnection(_ context.Context, r *http.Request) (interface{}, er
|
||||
|
||||
func decodeConnection(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
req := connectionReq{
|
||||
key: r.Header.Get("Authorization"),
|
||||
token: r.Header.Get("Authorization"),
|
||||
chanID: bone.GetValue(r, "chanId"),
|
||||
thingID: bone.GetValue(r, "thingId"),
|
||||
}
|
||||
@ -295,6 +318,8 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
case things.ErrNotFound:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
case things.ErrConflict:
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
case errUnsupportedContentType:
|
||||
w.WriteHeader(http.StatusUnsupportedMediaType)
|
||||
case errInvalidQueryParams:
|
||||
|
@ -29,9 +29,9 @@ func LoggingMiddleware(svc things.Service, logger log.Logger) things.Service {
|
||||
return &loggingMiddleware{logger, svc}
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) AddThing(key string, thing things.Thing) (saved things.Thing, err error) {
|
||||
func (lm *loggingMiddleware) AddThing(token string, thing things.Thing) (saved things.Thing, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method add_thing for key %s and thing %s took %s to complete", key, saved.ID, time.Since(begin))
|
||||
message := fmt.Sprintf("Method add_thing for token %s and thing %s took %s to complete", token, saved.ID, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
@ -39,12 +39,12 @@ func (lm *loggingMiddleware) AddThing(key string, thing things.Thing) (saved thi
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.AddThing(key, thing)
|
||||
return lm.svc.AddThing(token, thing)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) UpdateThing(key string, thing things.Thing) (err error) {
|
||||
func (lm *loggingMiddleware) UpdateThing(token string, thing things.Thing) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method update_thing for key %s and thing %s took %s to complete", key, thing.ID, time.Since(begin))
|
||||
message := fmt.Sprintf("Method update_thing for token %s and thing %s took %s to complete", token, thing.ID, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
@ -52,12 +52,12 @@ func (lm *loggingMiddleware) UpdateThing(key string, thing things.Thing) (err er
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.UpdateThing(key, thing)
|
||||
return lm.svc.UpdateThing(token, thing)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ViewThing(key, id string) (thing things.Thing, err error) {
|
||||
func (lm *loggingMiddleware) UpdateKey(token, id, key string) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method view_thing for key %s and thing %s took %s to complete", key, id, time.Since(begin))
|
||||
message := fmt.Sprintf("Method update_key for thing %s and key %s took %s to complete", id, key, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
@ -65,12 +65,12 @@ func (lm *loggingMiddleware) ViewThing(key, id string) (thing things.Thing, err
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.ViewThing(key, id)
|
||||
return lm.svc.UpdateKey(token, id, key)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ListThings(key string, offset, limit uint64) (_ things.ThingsPage, err error) {
|
||||
func (lm *loggingMiddleware) ViewThing(token, id string) (thing things.Thing, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method list_things for key %s took %s to complete", key, time.Since(begin))
|
||||
message := fmt.Sprintf("Method view_thing for token %s and thing %s took %s to complete", token, id, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
@ -78,10 +78,23 @@ func (lm *loggingMiddleware) ListThings(key string, offset, limit uint64) (_ thi
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.ListThings(key, offset, limit)
|
||||
return lm.svc.ViewThing(token, id)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ListThingsByChannel(key, id string, offset, limit uint64) (_ things.ThingsPage, err error) {
|
||||
func (lm *loggingMiddleware) ListThings(token string, offset, limit uint64) (_ things.ThingsPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method list_things for token %s took %s to complete", token, 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.ListThings(token, offset, limit)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ListThingsByChannel(token, id string, offset, limit uint64) (_ things.ThingsPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method list_things_by_channel for channel %s took %s to complete", id, time.Since(begin))
|
||||
if err != nil {
|
||||
@ -90,12 +103,12 @@ func (lm *loggingMiddleware) ListThingsByChannel(key, id string, offset, limit u
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.ListThingsByChannel(key, id, offset, limit)
|
||||
return lm.svc.ListThingsByChannel(token, id, offset, limit)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) RemoveThing(key, id string) (err error) {
|
||||
func (lm *loggingMiddleware) RemoveThing(token, id string) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method remove_thing for key %s and thing %s took %s to complete", key, id, time.Since(begin))
|
||||
message := fmt.Sprintf("Method remove_thing for token %s and thing %s took %s to complete", token, id, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
@ -103,12 +116,12 @@ func (lm *loggingMiddleware) RemoveThing(key, id string) (err error) {
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.RemoveThing(key, id)
|
||||
return lm.svc.RemoveThing(token, id)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) CreateChannel(key string, channel things.Channel) (saved things.Channel, err error) {
|
||||
func (lm *loggingMiddleware) CreateChannel(token string, channel things.Channel) (saved things.Channel, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method create_channel for key %s and channel %s took %s to complete", key, channel.ID, time.Since(begin))
|
||||
message := fmt.Sprintf("Method create_channel for token %s and channel %s took %s to complete", token, channel.ID, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
@ -116,12 +129,12 @@ func (lm *loggingMiddleware) CreateChannel(key string, channel things.Channel) (
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.CreateChannel(key, channel)
|
||||
return lm.svc.CreateChannel(token, channel)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) UpdateChannel(key string, channel things.Channel) (err error) {
|
||||
func (lm *loggingMiddleware) UpdateChannel(token string, channel things.Channel) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method update_channel for key %s and channel %s took %s to complete", key, channel.ID, time.Since(begin))
|
||||
message := fmt.Sprintf("Method update_channel for token %s and channel %s took %s to complete", token, channel.ID, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
@ -129,12 +142,12 @@ func (lm *loggingMiddleware) UpdateChannel(key string, channel things.Channel) (
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.UpdateChannel(key, channel)
|
||||
return lm.svc.UpdateChannel(token, channel)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ViewChannel(key, id string) (channel things.Channel, err error) {
|
||||
func (lm *loggingMiddleware) ViewChannel(token, id string) (channel things.Channel, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method view_channel for key %s and channel %s took %s to complete", key, id, time.Since(begin))
|
||||
message := fmt.Sprintf("Method view_channel for token %s and channel %s took %s to complete", token, id, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
@ -142,12 +155,12 @@ func (lm *loggingMiddleware) ViewChannel(key, id string) (channel things.Channel
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.ViewChannel(key, id)
|
||||
return lm.svc.ViewChannel(token, id)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ListChannels(key string, offset, limit uint64) (_ things.ChannelsPage, err error) {
|
||||
func (lm *loggingMiddleware) ListChannels(token string, offset, limit uint64) (_ things.ChannelsPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method list_channels for key %s took %s to complete", key, time.Since(begin))
|
||||
message := fmt.Sprintf("Method list_channels for token %s took %s to complete", token, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
@ -155,10 +168,10 @@ func (lm *loggingMiddleware) ListChannels(key string, offset, limit uint64) (_ t
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.ListChannels(key, offset, limit)
|
||||
return lm.svc.ListChannels(token, offset, limit)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) ListChannelsByThing(key, id string, offset, limit uint64) (_ things.ChannelsPage, err error) {
|
||||
func (lm *loggingMiddleware) ListChannelsByThing(token, id string, offset, limit uint64) (_ things.ChannelsPage, err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method list_channels_by_thing for thing %s took %s to complete", id, time.Since(begin))
|
||||
if err != nil {
|
||||
@ -167,12 +180,12 @@ func (lm *loggingMiddleware) ListChannelsByThing(key, id string, offset, limit u
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.ListChannelsByThing(key, id, offset, limit)
|
||||
return lm.svc.ListChannelsByThing(token, id, offset, limit)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) RemoveChannel(key, id string) (err error) {
|
||||
func (lm *loggingMiddleware) RemoveChannel(token, id string) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method remove_channel for key %s and channel %s took %s to complete", key, id, time.Since(begin))
|
||||
message := fmt.Sprintf("Method remove_channel for token %s and channel %s took %s to complete", token, id, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
@ -180,12 +193,12 @@ func (lm *loggingMiddleware) RemoveChannel(key, id string) (err error) {
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.RemoveChannel(key, id)
|
||||
return lm.svc.RemoveChannel(token, id)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) Connect(key, chanID, thingID string) (err error) {
|
||||
func (lm *loggingMiddleware) Connect(token, chanID, thingID string) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method connect for key %s, channel %s and thing %s took %s to complete", key, chanID, thingID, time.Since(begin))
|
||||
message := fmt.Sprintf("Method connect for token %s, channel %s and thing %s took %s to complete", token, chanID, thingID, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
@ -193,12 +206,12 @@ func (lm *loggingMiddleware) Connect(key, chanID, thingID string) (err error) {
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.Connect(key, chanID, thingID)
|
||||
return lm.svc.Connect(token, chanID, thingID)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) Disconnect(key, chanID, thingID string) (err error) {
|
||||
func (lm *loggingMiddleware) Disconnect(token, chanID, thingID string) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
message := fmt.Sprintf("Method disconnect for key %s, channel %s and thing %s took %s to complete", key, chanID, thingID, time.Since(begin))
|
||||
message := fmt.Sprintf("Method disconnect for token %s, channel %s and thing %s took %s to complete", token, chanID, thingID, time.Since(begin))
|
||||
if err != nil {
|
||||
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
|
||||
return
|
||||
@ -206,7 +219,7 @@ func (lm *loggingMiddleware) Disconnect(key, chanID, thingID string) (err error)
|
||||
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
|
||||
}(time.Now())
|
||||
|
||||
return lm.svc.Disconnect(key, chanID, thingID)
|
||||
return lm.svc.Disconnect(token, chanID, thingID)
|
||||
}
|
||||
|
||||
func (lm *loggingMiddleware) CanAccess(id, key string) (thing string, err error) {
|
||||
|
@ -34,130 +34,139 @@ func MetricsMiddleware(svc things.Service, counter metrics.Counter, latency metr
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) AddThing(key string, thing things.Thing) (things.Thing, error) {
|
||||
func (ms *metricsMiddleware) AddThing(token string, thing things.Thing) (things.Thing, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "add_thing").Add(1)
|
||||
ms.latency.With("method", "add_thing").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.AddThing(key, thing)
|
||||
return ms.svc.AddThing(token, thing)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) UpdateThing(key string, thing things.Thing) error {
|
||||
func (ms *metricsMiddleware) UpdateThing(token string, thing things.Thing) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "update_thing").Add(1)
|
||||
ms.latency.With("method", "update_thing").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.UpdateThing(key, thing)
|
||||
return ms.svc.UpdateThing(token, thing)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) ViewThing(key, id string) (things.Thing, error) {
|
||||
func (ms *metricsMiddleware) UpdateKey(token, id, key string) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "update_key").Add(1)
|
||||
ms.latency.With("method", "update_key").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.UpdateKey(token, id, key)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) ViewThing(token, id string) (things.Thing, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "view_thing").Add(1)
|
||||
ms.latency.With("method", "view_thing").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.ViewThing(key, id)
|
||||
return ms.svc.ViewThing(token, id)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) ListThings(key string, offset, limit uint64) (things.ThingsPage, error) {
|
||||
func (ms *metricsMiddleware) ListThings(token string, offset, limit uint64) (things.ThingsPage, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "list_things").Add(1)
|
||||
ms.latency.With("method", "list_things").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.ListThings(key, offset, limit)
|
||||
return ms.svc.ListThings(token, offset, limit)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) ListThingsByChannel(key, id string, offset, limit uint64) (things.ThingsPage, error) {
|
||||
func (ms *metricsMiddleware) ListThingsByChannel(token, id string, offset, limit uint64) (things.ThingsPage, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "list_things_by_channel").Add(1)
|
||||
ms.latency.With("method", "list_things_by_channel").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.ListThingsByChannel(key, id, offset, limit)
|
||||
return ms.svc.ListThingsByChannel(token, id, offset, limit)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) RemoveThing(key, id string) error {
|
||||
func (ms *metricsMiddleware) RemoveThing(token, id string) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "remove_thing").Add(1)
|
||||
ms.latency.With("method", "remove_thing").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.RemoveThing(key, id)
|
||||
return ms.svc.RemoveThing(token, id)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) CreateChannel(key string, channel things.Channel) (things.Channel, error) {
|
||||
func (ms *metricsMiddleware) CreateChannel(token string, channel things.Channel) (things.Channel, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "create_channel").Add(1)
|
||||
ms.latency.With("method", "create_channel").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.CreateChannel(key, channel)
|
||||
return ms.svc.CreateChannel(token, channel)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) UpdateChannel(key string, channel things.Channel) error {
|
||||
func (ms *metricsMiddleware) UpdateChannel(token string, channel things.Channel) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "update_channel").Add(1)
|
||||
ms.latency.With("method", "update_channel").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.UpdateChannel(key, channel)
|
||||
return ms.svc.UpdateChannel(token, channel)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) ViewChannel(key, id string) (things.Channel, error) {
|
||||
func (ms *metricsMiddleware) ViewChannel(token, id string) (things.Channel, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "view_channel").Add(1)
|
||||
ms.latency.With("method", "view_channel").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.ViewChannel(key, id)
|
||||
return ms.svc.ViewChannel(token, id)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) ListChannels(key string, offset, limit uint64) (things.ChannelsPage, error) {
|
||||
func (ms *metricsMiddleware) ListChannels(token string, offset, limit uint64) (things.ChannelsPage, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "list_channels").Add(1)
|
||||
ms.latency.With("method", "list_channels").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.ListChannels(key, offset, limit)
|
||||
return ms.svc.ListChannels(token, offset, limit)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) ListChannelsByThing(key, id string, offset, limit uint64) (things.ChannelsPage, error) {
|
||||
func (ms *metricsMiddleware) ListChannelsByThing(token, id string, offset, limit uint64) (things.ChannelsPage, error) {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "list_channels_by_thing").Add(1)
|
||||
ms.latency.With("method", "list_channels_by_thing").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.ListChannelsByThing(key, id, offset, limit)
|
||||
return ms.svc.ListChannelsByThing(token, id, offset, limit)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) RemoveChannel(key, id string) error {
|
||||
func (ms *metricsMiddleware) RemoveChannel(token, id string) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "remove_channel").Add(1)
|
||||
ms.latency.With("method", "remove_channel").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.RemoveChannel(key, id)
|
||||
return ms.svc.RemoveChannel(token, id)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) Connect(key, chanID, thingID string) error {
|
||||
func (ms *metricsMiddleware) Connect(token, chanID, thingID string) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "connect").Add(1)
|
||||
ms.latency.With("method", "connect").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.Connect(key, chanID, thingID)
|
||||
return ms.svc.Connect(token, chanID, thingID)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) Disconnect(key, chanID, thingID string) error {
|
||||
func (ms *metricsMiddleware) Disconnect(token, chanID, thingID string) error {
|
||||
defer func(begin time.Time) {
|
||||
ms.counter.With("method", "disconnect").Add(1)
|
||||
ms.latency.With("method", "disconnect").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return ms.svc.Disconnect(key, chanID, thingID)
|
||||
return ms.svc.Disconnect(token, chanID, thingID)
|
||||
}
|
||||
|
||||
func (ms *metricsMiddleware) CanAccess(id, key string) (string, error) {
|
||||
|
@ -51,6 +51,12 @@ func (trm *thingRepositoryMock) Save(thing things.Thing) (string, error) {
|
||||
trm.mu.Lock()
|
||||
defer trm.mu.Unlock()
|
||||
|
||||
for _, th := range trm.things {
|
||||
if th.Key == thing.Key {
|
||||
return "", things.ErrConflict
|
||||
}
|
||||
}
|
||||
|
||||
trm.counter++
|
||||
thing.ID = strconv.FormatUint(trm.counter, 10)
|
||||
trm.things[key(thing.Owner, thing.ID)] = thing
|
||||
@ -73,6 +79,29 @@ func (trm *thingRepositoryMock) Update(thing things.Thing) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (trm *thingRepositoryMock) UpdateKey(owner, id, val string) error {
|
||||
trm.mu.Lock()
|
||||
defer trm.mu.Unlock()
|
||||
|
||||
for _, th := range trm.things {
|
||||
if th.Key == val {
|
||||
return things.ErrConflict
|
||||
}
|
||||
}
|
||||
|
||||
dbKey := key(owner, id)
|
||||
|
||||
th, ok := trm.things[dbKey]
|
||||
if !ok {
|
||||
return things.ErrNotFound
|
||||
}
|
||||
|
||||
th.Key = val
|
||||
trm.things[dbKey] = th
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (trm *thingRepositoryMock) RetrieveByID(owner, id string) (things.Thing, error) {
|
||||
trm.mu.Lock()
|
||||
defer trm.mu.Unlock()
|
||||
|
@ -55,7 +55,7 @@ func migrateDB(db *sqlx.DB) error {
|
||||
`CREATE TABLE IF NOT EXISTS things (
|
||||
id UUID,
|
||||
owner VARCHAR(254),
|
||||
key CHAR(36) UNIQUE NOT NULL,
|
||||
key VARCHAR(4096) UNIQUE NOT NULL,
|
||||
name TEXT,
|
||||
metadata JSON,
|
||||
PRIMARY KEY (id, owner)
|
||||
|
@ -43,8 +43,13 @@ func (tr thingRepository) Save(thing things.Thing) (string, error) {
|
||||
_, err = tr.db.NamedExec(q, dbth)
|
||||
if err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
if ok && errInvalid == pqErr.Code.Name() {
|
||||
return "", things.ErrMalformedEntity
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid:
|
||||
return "", things.ErrMalformedEntity
|
||||
case errDuplicate:
|
||||
return "", things.ErrConflict
|
||||
}
|
||||
}
|
||||
|
||||
return "", err
|
||||
@ -83,6 +88,41 @@ func (tr thingRepository) Update(thing things.Thing) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tr thingRepository) UpdateKey(owner, id, key string) error {
|
||||
q := `UPDATE things SET key = :key WHERE owner = :owner AND id = :id;`
|
||||
dbth := dbThing{
|
||||
ID: id,
|
||||
Owner: owner,
|
||||
Key: key,
|
||||
}
|
||||
|
||||
res, err := tr.db.NamedExec(q, dbth)
|
||||
if err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid:
|
||||
return things.ErrMalformedEntity
|
||||
case errDuplicate:
|
||||
return things.ErrConflict
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
cnt, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cnt == 0 {
|
||||
return things.ErrNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tr thingRepository) RetrieveByID(owner, id string) (things.Thing, error) {
|
||||
q := `SELECT name, key, metadata FROM things WHERE id = $1 AND owner = $2;`
|
||||
|
||||
|
@ -48,6 +48,11 @@ func TestThingSave(t *testing.T) {
|
||||
},
|
||||
err: things.ErrMalformedEntity,
|
||||
},
|
||||
{
|
||||
desc: "create thing with conflicting key",
|
||||
thing: thing,
|
||||
err: things.ErrConflict,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
@ -111,6 +116,78 @@ func TestThingUpdate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateKey(t *testing.T) {
|
||||
email := "thing-update=key@example.com"
|
||||
newKey := "new-key"
|
||||
thingRepo := postgres.NewThingRepository(db, testLog)
|
||||
|
||||
existingThing := things.Thing{
|
||||
ID: uuid.New().ID(),
|
||||
Owner: email,
|
||||
Key: uuid.New().ID(),
|
||||
}
|
||||
existingID, _ := thingRepo.Save(existingThing)
|
||||
existingThing.ID = existingID
|
||||
|
||||
thing := things.Thing{
|
||||
ID: uuid.New().ID(),
|
||||
Owner: email,
|
||||
Key: uuid.New().ID(),
|
||||
}
|
||||
|
||||
id, _ := thingRepo.Save(thing)
|
||||
thing.ID = id
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
owner string
|
||||
id string
|
||||
key string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "update key of an existing thing",
|
||||
owner: thing.Owner,
|
||||
id: thing.ID,
|
||||
key: newKey,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "update key of a non-existing thing with existing user",
|
||||
owner: thing.Owner,
|
||||
id: uuid.New().ID(),
|
||||
key: newKey,
|
||||
err: things.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "update key of an existing thing with non-existing user",
|
||||
owner: wrongValue,
|
||||
id: thing.ID,
|
||||
key: newKey,
|
||||
err: things.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "update key of a non-existing thing with non-existing user",
|
||||
owner: wrongValue,
|
||||
id: uuid.New().ID(),
|
||||
key: newKey,
|
||||
err: things.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "update key with existing key value",
|
||||
owner: thing.Owner,
|
||||
id: thing.ID,
|
||||
key: existingThing.Key,
|
||||
err: things.ErrConflict,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
err := thingRepo.UpdateKey(tc.owner, tc.id, tc.key)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSingleThingRetrieval(t *testing.T) {
|
||||
email := "thing-single-retrieval@example.com"
|
||||
thingRepo := postgres.NewThingRepository(db, testLog)
|
||||
|
@ -33,8 +33,8 @@ func NewEventStoreMiddleware(svc things.Service, client *redis.Client) things.Se
|
||||
}
|
||||
}
|
||||
|
||||
func (es eventStore) AddThing(key string, thing things.Thing) (things.Thing, error) {
|
||||
sth, err := es.svc.AddThing(key, thing)
|
||||
func (es eventStore) AddThing(token string, thing things.Thing) (things.Thing, error) {
|
||||
sth, err := es.svc.AddThing(token, thing)
|
||||
if err != nil {
|
||||
return sth, err
|
||||
}
|
||||
@ -55,8 +55,8 @@ func (es eventStore) AddThing(key string, thing things.Thing) (things.Thing, err
|
||||
return sth, err
|
||||
}
|
||||
|
||||
func (es eventStore) UpdateThing(key string, thing things.Thing) error {
|
||||
if err := es.svc.UpdateThing(key, thing); err != nil {
|
||||
func (es eventStore) UpdateThing(token string, thing things.Thing) error {
|
||||
if err := es.svc.UpdateThing(token, thing); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -75,20 +75,27 @@ func (es eventStore) UpdateThing(key string, thing things.Thing) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (es eventStore) ViewThing(key, id string) (things.Thing, error) {
|
||||
return es.svc.ViewThing(key, id)
|
||||
// UpdateKey doesn't send event because key shouldn't be sent over stream.
|
||||
// Maybe we can start publishing this event at some point, without key value
|
||||
// in order to notify adapters to disconnect connected things after key update.
|
||||
func (es eventStore) UpdateKey(token, id, key string) error {
|
||||
return es.svc.UpdateKey(token, id, key)
|
||||
}
|
||||
|
||||
func (es eventStore) ListThings(key string, offset, limit uint64) (things.ThingsPage, error) {
|
||||
return es.svc.ListThings(key, offset, limit)
|
||||
func (es eventStore) ViewThing(token, id string) (things.Thing, error) {
|
||||
return es.svc.ViewThing(token, id)
|
||||
}
|
||||
|
||||
func (es eventStore) ListThingsByChannel(key, id string, offset, limit uint64) (things.ThingsPage, error) {
|
||||
return es.svc.ListThingsByChannel(key, id, offset, limit)
|
||||
func (es eventStore) ListThings(token string, offset, limit uint64) (things.ThingsPage, error) {
|
||||
return es.svc.ListThings(token, offset, limit)
|
||||
}
|
||||
|
||||
func (es eventStore) RemoveThing(key, id string) error {
|
||||
if err := es.svc.RemoveThing(key, id); err != nil {
|
||||
func (es eventStore) ListThingsByChannel(token, id string, offset, limit uint64) (things.ThingsPage, error) {
|
||||
return es.svc.ListThingsByChannel(token, id, offset, limit)
|
||||
}
|
||||
|
||||
func (es eventStore) RemoveThing(token, id string) error {
|
||||
if err := es.svc.RemoveThing(token, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -105,8 +112,8 @@ func (es eventStore) RemoveThing(key, id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (es eventStore) CreateChannel(key string, channel things.Channel) (things.Channel, error) {
|
||||
sch, err := es.svc.CreateChannel(key, channel)
|
||||
func (es eventStore) CreateChannel(token string, channel things.Channel) (things.Channel, error) {
|
||||
sch, err := es.svc.CreateChannel(token, channel)
|
||||
if err != nil {
|
||||
return sch, err
|
||||
}
|
||||
@ -127,8 +134,8 @@ func (es eventStore) CreateChannel(key string, channel things.Channel) (things.C
|
||||
return sch, err
|
||||
}
|
||||
|
||||
func (es eventStore) UpdateChannel(key string, channel things.Channel) error {
|
||||
if err := es.svc.UpdateChannel(key, channel); err != nil {
|
||||
func (es eventStore) UpdateChannel(token string, channel things.Channel) error {
|
||||
if err := es.svc.UpdateChannel(token, channel); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -147,20 +154,20 @@ func (es eventStore) UpdateChannel(key string, channel things.Channel) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (es eventStore) ViewChannel(key, id string) (things.Channel, error) {
|
||||
return es.svc.ViewChannel(key, id)
|
||||
func (es eventStore) ViewChannel(token, id string) (things.Channel, error) {
|
||||
return es.svc.ViewChannel(token, id)
|
||||
}
|
||||
|
||||
func (es eventStore) ListChannels(key string, offset, limit uint64) (things.ChannelsPage, error) {
|
||||
return es.svc.ListChannels(key, offset, limit)
|
||||
func (es eventStore) ListChannels(token string, offset, limit uint64) (things.ChannelsPage, error) {
|
||||
return es.svc.ListChannels(token, offset, limit)
|
||||
}
|
||||
|
||||
func (es eventStore) ListChannelsByThing(key, id string, offset, limit uint64) (things.ChannelsPage, error) {
|
||||
return es.svc.ListChannelsByThing(key, id, offset, limit)
|
||||
func (es eventStore) ListChannelsByThing(token, id string, offset, limit uint64) (things.ChannelsPage, error) {
|
||||
return es.svc.ListChannelsByThing(token, id, offset, limit)
|
||||
}
|
||||
|
||||
func (es eventStore) RemoveChannel(key, id string) error {
|
||||
if err := es.svc.RemoveChannel(key, id); err != nil {
|
||||
func (es eventStore) RemoveChannel(token, id string) error {
|
||||
if err := es.svc.RemoveChannel(token, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -177,8 +184,8 @@ func (es eventStore) RemoveChannel(key, id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (es eventStore) Connect(key, chanID, thingID string) error {
|
||||
if err := es.svc.Connect(key, chanID, thingID); err != nil {
|
||||
func (es eventStore) Connect(token, chanID, thingID string) error {
|
||||
if err := es.svc.Connect(token, chanID, thingID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -196,8 +203,8 @@ func (es eventStore) Connect(key, chanID, thingID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (es eventStore) Disconnect(key, chanID, thingID string) error {
|
||||
if err := es.svc.Disconnect(key, chanID, thingID); err != nil {
|
||||
func (es eventStore) Disconnect(token, chanID, thingID string) error {
|
||||
if err := es.svc.Disconnect(token, chanID, thingID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,9 @@ var (
|
||||
|
||||
// ErrNotFound indicates a non-existent entity request.
|
||||
ErrNotFound = errors.New("non-existent entity")
|
||||
|
||||
// ErrConflict indicates that entity already exists.
|
||||
ErrConflict = errors.New("entity already exists")
|
||||
)
|
||||
|
||||
// Service specifies an API that must be fullfiled by the domain service
|
||||
@ -38,6 +41,10 @@ type Service interface {
|
||||
// belongs to the user identified by the provided key.
|
||||
UpdateThing(string, Thing) error
|
||||
|
||||
// UpdateKey updates key value of the existing thing. A non-nil error is
|
||||
// returned to indicate operation failure.
|
||||
UpdateKey(string, string, string) error
|
||||
|
||||
// ViewThing retrieves data about the thing identified with the provided
|
||||
// ID, that belongs to the user identified by the provided key.
|
||||
ViewThing(string, string) (Thing, error)
|
||||
@ -124,7 +131,7 @@ func New(users mainflux.UsersServiceClient, things ThingRepository, channels Cha
|
||||
}
|
||||
}
|
||||
|
||||
func (ts *thingsService) AddThing(key string, thing Thing) (Thing, error) {
|
||||
func (ts *thingsService) AddThing(token string, thing Thing) (Thing, error) {
|
||||
if err := thing.Validate(); err != nil {
|
||||
return Thing{}, ErrMalformedEntity
|
||||
}
|
||||
@ -132,14 +139,17 @@ func (ts *thingsService) AddThing(key string, thing Thing) (Thing, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return Thing{}, ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
thing.ID = ts.idp.ID()
|
||||
thing.Owner = res.GetValue()
|
||||
thing.Key = ts.idp.ID()
|
||||
|
||||
if thing.Key == "" {
|
||||
thing.Key = ts.idp.ID()
|
||||
}
|
||||
|
||||
id, err := ts.things.Save(thing)
|
||||
if err != nil {
|
||||
@ -150,7 +160,7 @@ func (ts *thingsService) AddThing(key string, thing Thing) (Thing, error) {
|
||||
return thing, nil
|
||||
}
|
||||
|
||||
func (ts *thingsService) UpdateThing(key string, thing Thing) error {
|
||||
func (ts *thingsService) UpdateThing(token string, thing Thing) error {
|
||||
if err := thing.Validate(); err != nil {
|
||||
return ErrMalformedEntity
|
||||
}
|
||||
@ -158,7 +168,7 @@ func (ts *thingsService) UpdateThing(key string, thing Thing) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return ErrUnauthorizedAccess
|
||||
}
|
||||
@ -168,11 +178,26 @@ func (ts *thingsService) UpdateThing(key string, thing Thing) error {
|
||||
return ts.things.Update(thing)
|
||||
}
|
||||
|
||||
func (ts *thingsService) ViewThing(key, id string) (Thing, error) {
|
||||
func (ts *thingsService) UpdateKey(token, id, key string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
owner := res.GetValue()
|
||||
|
||||
return ts.things.UpdateKey(owner, id, key)
|
||||
|
||||
}
|
||||
|
||||
func (ts *thingsService) ViewThing(token, id string) (Thing, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return Thing{}, ErrUnauthorizedAccess
|
||||
}
|
||||
@ -180,11 +205,11 @@ func (ts *thingsService) ViewThing(key, id string) (Thing, error) {
|
||||
return ts.things.RetrieveByID(res.GetValue(), id)
|
||||
}
|
||||
|
||||
func (ts *thingsService) ListThings(key string, offset, limit uint64) (ThingsPage, error) {
|
||||
func (ts *thingsService) ListThings(token string, offset, limit uint64) (ThingsPage, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return ThingsPage{}, ErrUnauthorizedAccess
|
||||
}
|
||||
@ -192,11 +217,11 @@ func (ts *thingsService) ListThings(key string, offset, limit uint64) (ThingsPag
|
||||
return ts.things.RetrieveAll(res.GetValue(), offset, limit), nil
|
||||
}
|
||||
|
||||
func (ts *thingsService) ListThingsByChannel(key, channel string, offset, limit uint64) (ThingsPage, error) {
|
||||
func (ts *thingsService) ListThingsByChannel(token, channel string, offset, limit uint64) (ThingsPage, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return ThingsPage{}, ErrUnauthorizedAccess
|
||||
}
|
||||
@ -204,11 +229,11 @@ func (ts *thingsService) ListThingsByChannel(key, channel string, offset, limit
|
||||
return ts.things.RetrieveByChannel(res.GetValue(), channel, offset, limit), nil
|
||||
}
|
||||
|
||||
func (ts *thingsService) RemoveThing(key string, id string) error {
|
||||
func (ts *thingsService) RemoveThing(token, id string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return ErrUnauthorizedAccess
|
||||
}
|
||||
@ -217,11 +242,11 @@ func (ts *thingsService) RemoveThing(key string, id string) error {
|
||||
return ts.things.Remove(res.GetValue(), id)
|
||||
}
|
||||
|
||||
func (ts *thingsService) CreateChannel(key string, channel Channel) (Channel, error) {
|
||||
func (ts *thingsService) CreateChannel(token string, channel Channel) (Channel, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return Channel{}, ErrUnauthorizedAccess
|
||||
}
|
||||
@ -238,11 +263,11 @@ func (ts *thingsService) CreateChannel(key string, channel Channel) (Channel, er
|
||||
return channel, nil
|
||||
}
|
||||
|
||||
func (ts *thingsService) UpdateChannel(key string, channel Channel) error {
|
||||
func (ts *thingsService) UpdateChannel(token string, channel Channel) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return ErrUnauthorizedAccess
|
||||
}
|
||||
@ -251,11 +276,11 @@ func (ts *thingsService) UpdateChannel(key string, channel Channel) error {
|
||||
return ts.channels.Update(channel)
|
||||
}
|
||||
|
||||
func (ts *thingsService) ViewChannel(key, id string) (Channel, error) {
|
||||
func (ts *thingsService) ViewChannel(token, id string) (Channel, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return Channel{}, ErrUnauthorizedAccess
|
||||
}
|
||||
@ -263,11 +288,11 @@ func (ts *thingsService) ViewChannel(key, id string) (Channel, error) {
|
||||
return ts.channels.RetrieveByID(res.GetValue(), id)
|
||||
}
|
||||
|
||||
func (ts *thingsService) ListChannels(key string, offset, limit uint64) (ChannelsPage, error) {
|
||||
func (ts *thingsService) ListChannels(token string, offset, limit uint64) (ChannelsPage, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return ChannelsPage{}, ErrUnauthorizedAccess
|
||||
}
|
||||
@ -275,11 +300,11 @@ func (ts *thingsService) ListChannels(key string, offset, limit uint64) (Channel
|
||||
return ts.channels.RetrieveAll(res.GetValue(), offset, limit), nil
|
||||
}
|
||||
|
||||
func (ts *thingsService) ListChannelsByThing(key, thing string, offset, limit uint64) (ChannelsPage, error) {
|
||||
func (ts *thingsService) ListChannelsByThing(token, thing string, offset, limit uint64) (ChannelsPage, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return ChannelsPage{}, ErrUnauthorizedAccess
|
||||
}
|
||||
@ -287,11 +312,11 @@ func (ts *thingsService) ListChannelsByThing(key, thing string, offset, limit ui
|
||||
return ts.channels.RetrieveByThing(res.GetValue(), thing, offset, limit), nil
|
||||
}
|
||||
|
||||
func (ts *thingsService) RemoveChannel(key, id string) error {
|
||||
func (ts *thingsService) RemoveChannel(token, id string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return ErrUnauthorizedAccess
|
||||
}
|
||||
@ -300,11 +325,11 @@ func (ts *thingsService) RemoveChannel(key, id string) error {
|
||||
return ts.channels.Remove(res.GetValue(), id)
|
||||
}
|
||||
|
||||
func (ts *thingsService) Connect(key, chanID, thingID string) error {
|
||||
func (ts *thingsService) Connect(token, chanID, thingID string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return ErrUnauthorizedAccess
|
||||
}
|
||||
@ -312,11 +337,11 @@ func (ts *thingsService) Connect(key, chanID, thingID string) error {
|
||||
return ts.channels.Connect(res.GetValue(), chanID, thingID)
|
||||
}
|
||||
|
||||
func (ts *thingsService) Disconnect(key, chanID, thingID string) error {
|
||||
func (ts *thingsService) Disconnect(token, chanID, thingID string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
|
||||
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: token})
|
||||
if err != nil {
|
||||
return ErrUnauthorizedAccess
|
||||
}
|
||||
|
@ -48,25 +48,25 @@ func TestAddThing(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
thing things.Thing
|
||||
key string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "add new thing",
|
||||
thing: things.Thing{Name: "a"},
|
||||
key: token,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "add thing with wrong credentials",
|
||||
thing: things.Thing{Name: "d"},
|
||||
key: wrongValue,
|
||||
token: wrongValue,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
_, err := svc.AddThing(tc.key, tc.thing)
|
||||
_, err := svc.AddThing(tc.token, tc.thing)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
@ -79,31 +79,73 @@ func TestUpdateThing(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
thing things.Thing
|
||||
key string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "update existing thing",
|
||||
thing: saved,
|
||||
key: token,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "update thing with wrong credentials",
|
||||
thing: saved,
|
||||
key: wrongValue,
|
||||
token: wrongValue,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
{
|
||||
desc: "update non-existing thing",
|
||||
thing: other,
|
||||
key: token,
|
||||
token: token,
|
||||
err: things.ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
err := svc.UpdateThing(tc.key, tc.thing)
|
||||
err := svc.UpdateThing(tc.token, tc.thing)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateKey(t *testing.T) {
|
||||
key := "new-key"
|
||||
svc := newService(map[string]string{token: email})
|
||||
saved, err := svc.AddThing(token, thing)
|
||||
require.Nil(t, err, fmt.Sprintf("unexpected error: %s\n", err))
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
token string
|
||||
id string
|
||||
key string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "update key of an existing thing",
|
||||
token: token,
|
||||
id: saved.ID,
|
||||
key: key,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "update key with invalid credentials",
|
||||
token: wrongValue,
|
||||
id: saved.ID,
|
||||
key: key,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
{
|
||||
desc: "update key of non-existing thing",
|
||||
token: token,
|
||||
id: wrongID,
|
||||
key: wrongValue,
|
||||
err: things.ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
err := svc.UpdateKey(tc.token, tc.id, tc.key)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
@ -113,29 +155,29 @@ func TestViewThing(t *testing.T) {
|
||||
saved, _ := svc.AddThing(token, thing)
|
||||
|
||||
cases := map[string]struct {
|
||||
id string
|
||||
key string
|
||||
err error
|
||||
id string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
"view existing thing": {
|
||||
id: saved.ID,
|
||||
key: token,
|
||||
err: nil,
|
||||
id: saved.ID,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
"view thing with wrong credentials": {
|
||||
id: saved.ID,
|
||||
key: wrongValue,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
id: saved.ID,
|
||||
token: wrongValue,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
"view non-existing thing": {
|
||||
id: wrongID,
|
||||
key: token,
|
||||
err: things.ErrNotFound,
|
||||
id: wrongID,
|
||||
token: token,
|
||||
err: things.ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
_, err := svc.ViewThing(tc.key, tc.id)
|
||||
_, err := svc.ViewThing(tc.token, tc.id)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
@ -149,49 +191,49 @@ func TestListThings(t *testing.T) {
|
||||
}
|
||||
|
||||
cases := map[string]struct {
|
||||
key string
|
||||
token string
|
||||
offset uint64
|
||||
limit uint64
|
||||
size uint64
|
||||
err error
|
||||
}{
|
||||
"list all things": {
|
||||
key: token,
|
||||
token: token,
|
||||
offset: 0,
|
||||
limit: n,
|
||||
size: n,
|
||||
err: nil,
|
||||
},
|
||||
"list half": {
|
||||
key: token,
|
||||
token: token,
|
||||
offset: n / 2,
|
||||
limit: n,
|
||||
size: n / 2,
|
||||
err: nil,
|
||||
},
|
||||
"list last thing": {
|
||||
key: token,
|
||||
token: token,
|
||||
offset: n - 1,
|
||||
limit: n,
|
||||
size: 1,
|
||||
err: nil,
|
||||
},
|
||||
"list empty set": {
|
||||
key: token,
|
||||
token: token,
|
||||
offset: n + 1,
|
||||
limit: n,
|
||||
size: 0,
|
||||
err: nil,
|
||||
},
|
||||
"list with zero limit": {
|
||||
key: token,
|
||||
token: token,
|
||||
offset: 1,
|
||||
limit: 0,
|
||||
size: 0,
|
||||
err: nil,
|
||||
},
|
||||
"list with wrong credentials": {
|
||||
key: wrongValue,
|
||||
token: wrongValue,
|
||||
offset: 0,
|
||||
limit: 0,
|
||||
size: 0,
|
||||
@ -200,7 +242,7 @@ func TestListThings(t *testing.T) {
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
page, err := svc.ListThings(tc.key, tc.offset, tc.limit)
|
||||
page, err := svc.ListThings(tc.token, tc.offset, tc.limit)
|
||||
size := uint64(len(page.Things))
|
||||
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected %d got %d\n", desc, tc.size, size))
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
||||
@ -223,7 +265,7 @@ func TestListThingsByChannel(t *testing.T) {
|
||||
time.Sleep(time.Second)
|
||||
|
||||
cases := map[string]struct {
|
||||
key string
|
||||
token string
|
||||
channel string
|
||||
offset uint64
|
||||
limit uint64
|
||||
@ -231,7 +273,7 @@ func TestListThingsByChannel(t *testing.T) {
|
||||
err error
|
||||
}{
|
||||
"list all things by existing channel": {
|
||||
key: token,
|
||||
token: token,
|
||||
channel: sch.ID,
|
||||
offset: 0,
|
||||
limit: n,
|
||||
@ -239,7 +281,7 @@ func TestListThingsByChannel(t *testing.T) {
|
||||
err: nil,
|
||||
},
|
||||
"list half of things by existing channel": {
|
||||
key: token,
|
||||
token: token,
|
||||
channel: sch.ID,
|
||||
offset: n / 2,
|
||||
limit: n,
|
||||
@ -247,7 +289,7 @@ func TestListThingsByChannel(t *testing.T) {
|
||||
err: nil,
|
||||
},
|
||||
"list last thing by existing channel": {
|
||||
key: token,
|
||||
token: token,
|
||||
channel: sch.ID,
|
||||
offset: n - 1,
|
||||
limit: n,
|
||||
@ -255,7 +297,7 @@ func TestListThingsByChannel(t *testing.T) {
|
||||
err: nil,
|
||||
},
|
||||
"list empty set of things by existing channel": {
|
||||
key: token,
|
||||
token: token,
|
||||
channel: sch.ID,
|
||||
offset: n + 1,
|
||||
limit: n,
|
||||
@ -263,7 +305,7 @@ func TestListThingsByChannel(t *testing.T) {
|
||||
err: nil,
|
||||
},
|
||||
"list things by existing channel with zero limit": {
|
||||
key: token,
|
||||
token: token,
|
||||
channel: sch.ID,
|
||||
offset: 1,
|
||||
limit: 0,
|
||||
@ -271,7 +313,7 @@ func TestListThingsByChannel(t *testing.T) {
|
||||
err: nil,
|
||||
},
|
||||
"list things by existing channel with wrong credentials": {
|
||||
key: wrongValue,
|
||||
token: wrongValue,
|
||||
channel: sch.ID,
|
||||
offset: 0,
|
||||
limit: 0,
|
||||
@ -279,7 +321,7 @@ func TestListThingsByChannel(t *testing.T) {
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
"list things by non-existent channel with wrong credentials": {
|
||||
key: token,
|
||||
token: token,
|
||||
channel: "non-existent",
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
@ -289,7 +331,7 @@ func TestListThingsByChannel(t *testing.T) {
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
page, err := svc.ListThingsByChannel(tc.key, tc.channel, tc.offset, tc.limit)
|
||||
page, err := svc.ListThingsByChannel(tc.token, tc.channel, tc.offset, tc.limit)
|
||||
size := uint64(len(page.Things))
|
||||
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected %d got %d\n", desc, tc.size, size))
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
||||
@ -301,39 +343,39 @@ func TestRemoveThing(t *testing.T) {
|
||||
saved, _ := svc.AddThing(token, thing)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
key string
|
||||
err error
|
||||
desc string
|
||||
id string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "remove thing with wrong credentials",
|
||||
id: saved.ID,
|
||||
key: wrongValue,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
desc: "remove thing with wrong credentials",
|
||||
id: saved.ID,
|
||||
token: wrongValue,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
{
|
||||
desc: "remove existing thing",
|
||||
id: saved.ID,
|
||||
key: token,
|
||||
err: nil,
|
||||
desc: "remove existing thing",
|
||||
id: saved.ID,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "remove removed thing",
|
||||
id: saved.ID,
|
||||
key: token,
|
||||
err: nil,
|
||||
desc: "remove removed thing",
|
||||
id: saved.ID,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "remove non-existing thing",
|
||||
id: wrongID,
|
||||
key: token,
|
||||
err: nil,
|
||||
desc: "remove non-existing thing",
|
||||
id: wrongID,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
err := svc.RemoveThing(tc.key, tc.id)
|
||||
err := svc.RemoveThing(tc.token, tc.id)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
@ -344,25 +386,25 @@ func TestCreateChannel(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
channel things.Channel
|
||||
key string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "create channel",
|
||||
channel: channel,
|
||||
key: token,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "create channel with wrong credentials",
|
||||
channel: channel,
|
||||
key: wrongValue,
|
||||
token: wrongValue,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
_, err := svc.CreateChannel(tc.key, tc.channel)
|
||||
_, err := svc.CreateChannel(tc.token, tc.channel)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
@ -375,31 +417,31 @@ func TestUpdateChannel(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
channel things.Channel
|
||||
key string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "update existing channel",
|
||||
channel: saved,
|
||||
key: token,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "update channel with wrong credentials",
|
||||
channel: saved,
|
||||
key: wrongValue,
|
||||
token: wrongValue,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
{
|
||||
desc: "update non-existing channel",
|
||||
channel: other,
|
||||
key: token,
|
||||
token: token,
|
||||
err: things.ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
err := svc.UpdateChannel(tc.key, tc.channel)
|
||||
err := svc.UpdateChannel(tc.token, tc.channel)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
@ -409,29 +451,29 @@ func TestViewChannel(t *testing.T) {
|
||||
saved, _ := svc.CreateChannel(token, channel)
|
||||
|
||||
cases := map[string]struct {
|
||||
id string
|
||||
key string
|
||||
err error
|
||||
id string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
"view existing channel": {
|
||||
id: saved.ID,
|
||||
key: token,
|
||||
err: nil,
|
||||
id: saved.ID,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
"view channel with wrong credentials": {
|
||||
id: saved.ID,
|
||||
key: wrongValue,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
id: saved.ID,
|
||||
token: wrongValue,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
"view non-existing channel": {
|
||||
id: wrongID,
|
||||
key: token,
|
||||
err: things.ErrNotFound,
|
||||
id: wrongID,
|
||||
token: token,
|
||||
err: things.ErrNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
_, err := svc.ViewChannel(tc.key, tc.id)
|
||||
_, err := svc.ViewChannel(tc.token, tc.id)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
@ -444,49 +486,49 @@ func TestListChannels(t *testing.T) {
|
||||
svc.CreateChannel(token, channel)
|
||||
}
|
||||
cases := map[string]struct {
|
||||
key string
|
||||
token string
|
||||
offset uint64
|
||||
limit uint64
|
||||
size uint64
|
||||
err error
|
||||
}{
|
||||
"list all channels": {
|
||||
key: token,
|
||||
token: token,
|
||||
offset: 0,
|
||||
limit: n,
|
||||
size: n,
|
||||
err: nil,
|
||||
},
|
||||
"list half": {
|
||||
key: token,
|
||||
token: token,
|
||||
offset: n / 2,
|
||||
limit: n,
|
||||
size: n / 2,
|
||||
err: nil,
|
||||
},
|
||||
"list last channel": {
|
||||
key: token,
|
||||
token: token,
|
||||
offset: n - 1,
|
||||
limit: n,
|
||||
size: 1,
|
||||
err: nil,
|
||||
},
|
||||
"list empty set": {
|
||||
key: token,
|
||||
token: token,
|
||||
offset: n + 1,
|
||||
limit: n,
|
||||
size: 0,
|
||||
err: nil,
|
||||
},
|
||||
"list with zero limit": {
|
||||
key: token,
|
||||
token: token,
|
||||
offset: 1,
|
||||
limit: 0,
|
||||
size: 0,
|
||||
err: nil,
|
||||
},
|
||||
"list with wrong credentials": {
|
||||
key: wrongValue,
|
||||
token: wrongValue,
|
||||
offset: 0,
|
||||
limit: 0,
|
||||
size: 0,
|
||||
@ -495,7 +537,7 @@ func TestListChannels(t *testing.T) {
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
page, err := svc.ListChannels(tc.key, tc.offset, tc.limit)
|
||||
page, err := svc.ListChannels(tc.token, tc.offset, tc.limit)
|
||||
size := uint64(len(page.Channels))
|
||||
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected %d got %d\n", desc, tc.size, size))
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
||||
@ -518,7 +560,7 @@ func TestListChannelsByThing(t *testing.T) {
|
||||
time.Sleep(time.Second)
|
||||
|
||||
cases := map[string]struct {
|
||||
key string
|
||||
token string
|
||||
thing string
|
||||
offset uint64
|
||||
limit uint64
|
||||
@ -526,7 +568,7 @@ func TestListChannelsByThing(t *testing.T) {
|
||||
err error
|
||||
}{
|
||||
"list all channels by existing thing": {
|
||||
key: token,
|
||||
token: token,
|
||||
thing: sth.ID,
|
||||
offset: 0,
|
||||
limit: n,
|
||||
@ -534,7 +576,7 @@ func TestListChannelsByThing(t *testing.T) {
|
||||
err: nil,
|
||||
},
|
||||
"list half of channels by existing thing": {
|
||||
key: token,
|
||||
token: token,
|
||||
thing: sth.ID,
|
||||
offset: n / 2,
|
||||
limit: n,
|
||||
@ -542,7 +584,7 @@ func TestListChannelsByThing(t *testing.T) {
|
||||
err: nil,
|
||||
},
|
||||
"list last channel by existing thing": {
|
||||
key: token,
|
||||
token: token,
|
||||
thing: sth.ID,
|
||||
offset: n - 1,
|
||||
limit: n,
|
||||
@ -550,7 +592,7 @@ func TestListChannelsByThing(t *testing.T) {
|
||||
err: nil,
|
||||
},
|
||||
"list empty set of channels by existing thing": {
|
||||
key: token,
|
||||
token: token,
|
||||
thing: sth.ID,
|
||||
offset: n + 1,
|
||||
limit: n,
|
||||
@ -558,7 +600,7 @@ func TestListChannelsByThing(t *testing.T) {
|
||||
err: nil,
|
||||
},
|
||||
"list channels by existing thing with zero limit": {
|
||||
key: token,
|
||||
token: token,
|
||||
thing: sth.ID,
|
||||
offset: 1,
|
||||
limit: 0,
|
||||
@ -566,7 +608,7 @@ func TestListChannelsByThing(t *testing.T) {
|
||||
err: nil,
|
||||
},
|
||||
"list channels by existing thing with wrong credentials": {
|
||||
key: wrongValue,
|
||||
token: wrongValue,
|
||||
thing: sth.ID,
|
||||
offset: 0,
|
||||
limit: 0,
|
||||
@ -574,7 +616,7 @@ func TestListChannelsByThing(t *testing.T) {
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
"list channels by non-existent thing": {
|
||||
key: token,
|
||||
token: token,
|
||||
thing: "non-existent",
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
@ -584,7 +626,7 @@ func TestListChannelsByThing(t *testing.T) {
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
page, err := svc.ListChannelsByThing(tc.key, tc.thing, tc.offset, tc.limit)
|
||||
page, err := svc.ListChannelsByThing(tc.token, tc.thing, tc.offset, tc.limit)
|
||||
size := uint64(len(page.Channels))
|
||||
assert.Equal(t, tc.size, size, fmt.Sprintf("%s: expected %d got %d\n", desc, tc.size, size))
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
||||
@ -596,39 +638,39 @@ func TestRemoveChannel(t *testing.T) {
|
||||
saved, _ := svc.CreateChannel(token, channel)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
key string
|
||||
err error
|
||||
desc string
|
||||
id string
|
||||
token string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "remove channel with wrong credentials",
|
||||
id: saved.ID,
|
||||
key: wrongValue,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
desc: "remove channel with wrong credentials",
|
||||
id: saved.ID,
|
||||
token: wrongValue,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
{
|
||||
desc: "remove existing channel",
|
||||
id: saved.ID,
|
||||
key: token,
|
||||
err: nil,
|
||||
desc: "remove existing channel",
|
||||
id: saved.ID,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "remove removed channel",
|
||||
id: saved.ID,
|
||||
key: token,
|
||||
err: nil,
|
||||
desc: "remove removed channel",
|
||||
id: saved.ID,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "remove non-existing channel",
|
||||
id: saved.ID,
|
||||
key: token,
|
||||
err: nil,
|
||||
desc: "remove non-existing channel",
|
||||
id: saved.ID,
|
||||
token: token,
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
err := svc.RemoveChannel(tc.key, tc.id)
|
||||
err := svc.RemoveChannel(tc.token, tc.id)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
@ -641,35 +683,35 @@ func TestConnect(t *testing.T) {
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
key string
|
||||
token string
|
||||
chanID string
|
||||
thingID string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "connect thing",
|
||||
key: token,
|
||||
token: token,
|
||||
chanID: sch.ID,
|
||||
thingID: sth.ID,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "connect thing with wrong credentials",
|
||||
key: wrongValue,
|
||||
token: wrongValue,
|
||||
chanID: sch.ID,
|
||||
thingID: sth.ID,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
{
|
||||
desc: "connect thing to non-existing channel",
|
||||
key: token,
|
||||
token: token,
|
||||
chanID: wrongID,
|
||||
thingID: sth.ID,
|
||||
err: things.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "connect non-existing thing to channel",
|
||||
key: token,
|
||||
token: token,
|
||||
chanID: sch.ID,
|
||||
thingID: wrongID,
|
||||
err: things.ErrNotFound,
|
||||
@ -677,7 +719,7 @@ func TestConnect(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
err := svc.Connect(tc.key, tc.chanID, tc.thingID)
|
||||
err := svc.Connect(tc.token, tc.chanID, tc.thingID)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
@ -691,42 +733,42 @@ func TestDisconnect(t *testing.T) {
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
key string
|
||||
token string
|
||||
chanID string
|
||||
thingID string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "disconnect connected thing",
|
||||
key: token,
|
||||
token: token,
|
||||
chanID: sch.ID,
|
||||
thingID: sth.ID,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "disconnect disconnected thing",
|
||||
key: token,
|
||||
token: token,
|
||||
chanID: sch.ID,
|
||||
thingID: sth.ID,
|
||||
err: things.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "disconnect with wrong credentials",
|
||||
key: wrongValue,
|
||||
token: wrongValue,
|
||||
chanID: sch.ID,
|
||||
thingID: sth.ID,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
{
|
||||
desc: "disconnect from non-existing channel",
|
||||
key: token,
|
||||
token: token,
|
||||
chanID: wrongID,
|
||||
thingID: sth.ID,
|
||||
err: things.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "disconnect non-existing thing",
|
||||
key: token,
|
||||
token: token,
|
||||
chanID: sch.ID,
|
||||
thingID: wrongID,
|
||||
err: things.ErrNotFound,
|
||||
@ -734,7 +776,7 @@ func TestDisconnect(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
err := svc.Disconnect(tc.key, tc.chanID, tc.thingID)
|
||||
err := svc.Disconnect(tc.token, tc.chanID, tc.thingID)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
||||
}
|
||||
|
||||
@ -748,29 +790,29 @@ func TestCanAccess(t *testing.T) {
|
||||
svc.Connect(token, sch.ID, sth.ID)
|
||||
|
||||
cases := map[string]struct {
|
||||
key string
|
||||
token string
|
||||
channel string
|
||||
err error
|
||||
}{
|
||||
"allowed access": {
|
||||
key: sth.Key,
|
||||
token: sth.Key,
|
||||
channel: sch.ID,
|
||||
err: nil,
|
||||
},
|
||||
"not-connected cannot access": {
|
||||
key: wrongValue,
|
||||
token: wrongValue,
|
||||
channel: sch.ID,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
"access to non-existing channel": {
|
||||
key: sth.Key,
|
||||
token: sth.Key,
|
||||
channel: wrongID,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
_, err := svc.CanAccess(tc.channel, tc.key)
|
||||
_, err := svc.CanAccess(tc.channel, tc.token)
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
||||
}
|
||||
}
|
||||
@ -781,24 +823,24 @@ func TestIdentify(t *testing.T) {
|
||||
sth, _ := svc.AddThing(token, thing)
|
||||
|
||||
cases := map[string]struct {
|
||||
key string
|
||||
id string
|
||||
err error
|
||||
token string
|
||||
id string
|
||||
err error
|
||||
}{
|
||||
"identify existing thing": {
|
||||
key: sth.Key,
|
||||
id: sth.ID,
|
||||
err: nil,
|
||||
token: sth.Key,
|
||||
id: sth.ID,
|
||||
err: nil,
|
||||
},
|
||||
"identify non-existing thing": {
|
||||
key: wrongValue,
|
||||
id: wrongID,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
token: wrongValue,
|
||||
id: wrongID,
|
||||
err: things.ErrUnauthorizedAccess,
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
id, err := svc.Identify(tc.key)
|
||||
id, err := svc.Identify(tc.token)
|
||||
assert.Equal(t, tc.id, id, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.id, id))
|
||||
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", desc, tc.err, err))
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ paths:
|
||||
description: JSON-formatted document describing the new thing.
|
||||
in: body
|
||||
schema:
|
||||
$ref: "#/definitions/ThingReq"
|
||||
$ref: "#/definitions/CreateThingReq"
|
||||
required: true
|
||||
responses:
|
||||
201:
|
||||
@ -120,7 +120,7 @@ paths:
|
||||
description: JSON-formatted document describing the updated thing.
|
||||
in: body
|
||||
schema:
|
||||
$ref: "#/definitions/ThingReq"
|
||||
$ref: "#/definitions/UpdateThingReq"
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
@ -154,6 +154,37 @@ paths:
|
||||
description: Missing or invalid access token provided.
|
||||
500:
|
||||
$ref: "#/responses/ServiceError"
|
||||
/things/{thingId}/key:
|
||||
patch:
|
||||
summary: Updates thing key
|
||||
description: |
|
||||
Update is performed by replacing current key with a new one.
|
||||
tags:
|
||||
- things
|
||||
parameters:
|
||||
- $ref: "#/parameters/Authorization"
|
||||
- $ref: "#/parameters/ThingId"
|
||||
- name: key
|
||||
description: JSON-formatted document describing updated key.
|
||||
in: body
|
||||
schema:
|
||||
$ref: "#/definitions/UpdateKeyReq"
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
description: Thing key updated.
|
||||
400:
|
||||
description: Failed due to malformed JSON.
|
||||
403:
|
||||
description: Missing or invalid access token provided.
|
||||
404:
|
||||
description: Thing does not exist.
|
||||
409:
|
||||
description: Specified key already exists.
|
||||
415:
|
||||
description: Missing or invalid content type.
|
||||
500:
|
||||
$ref: "#/responses/ServiceError"
|
||||
/channels:
|
||||
post:
|
||||
summary: Creates new channel
|
||||
@ -461,14 +492,33 @@ definitions:
|
||||
- id
|
||||
- type
|
||||
- key
|
||||
ThingReq:
|
||||
CreateThingReq:
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
description: |
|
||||
Thing key that is used for thing auth. If there is
|
||||
not one provided service will generate one in UUID
|
||||
format.
|
||||
name:
|
||||
type: string
|
||||
description: Free-form thing name.
|
||||
metadata:
|
||||
type: object
|
||||
description: Custom thing's data in JSON format.
|
||||
UpdateThingReq:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: Free-form thing name.
|
||||
metadata:
|
||||
type: object
|
||||
description: Custom thing's data in JSON format.
|
||||
UpdateKeyReq:
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
description: Arbitrary, string-encoded thing's data.
|
||||
required:
|
||||
- type
|
||||
description: Thing key that is used for thing auth.
|
||||
|
@ -39,6 +39,10 @@ type ThingRepository interface {
|
||||
// returned to indicate operation failure.
|
||||
Update(Thing) error
|
||||
|
||||
// UpdateKey updates key value of the existing thing. A non-nil error is
|
||||
// returned to indicate operation failure.
|
||||
UpdateKey(string, string, string) error
|
||||
|
||||
// RetrieveByID retrieves the thing having the provided identifier, that is owned
|
||||
// by the specified user.
|
||||
RetrieveByID(string, string) (Thing, error)
|
||||
|
Loading…
x
Reference in New Issue
Block a user