1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-04-27 13:48:49 +08:00
Aleksandar Novaković ad5c66fad2 NOISSUE - Refactor SDK and things service (#420)
* Refactor Mainflux go SDK

Add structures instead of string parameters. Add offset and limit
parameters to things and channels methods. Add better configuration
support.

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>

* Add new public errors with better error handling

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>

* Update SDK to use uint instread of string id

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>

* Update cli to use new SDK API

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>

* Remove TLS termination from nginx configuration

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>

* Update SDK documentation and structures

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>

* Refactor things service

Decouple HTTP layer from business logic. Remove ID number validation
check. Remove models from HTTP requests and responses.

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>

* Reformat tests for things service

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>

* Increase test coverage for things service

Signed-off-by: Aleksandar Novakovic <aleksandar.novakovic@mainflux.com>
2018-10-24 10:21:03 +01:00

332 lines
9.1 KiB
Go

//
// Copyright (c) 2018
// Mainflux
//
// SPDX-License-Identifier: Apache-2.0
//
package things
import (
"context"
"errors"
"time"
"github.com/mainflux/mainflux"
)
var (
// ErrConflict indicates usage of the existing email during account
// registration.
ErrConflict = errors.New("email already taken")
// ErrMalformedEntity indicates malformed entity specification (e.g.
// invalid username or password).
ErrMalformedEntity = errors.New("malformed entity specification")
// ErrUnauthorizedAccess indicates missing or invalid credentials provided
// when accessing a protected resource.
ErrUnauthorizedAccess = errors.New("missing or invalid credentials provided")
// ErrNotFound indicates a non-existent entity request.
ErrNotFound = errors.New("non-existent entity")
)
// Service specifies an API that must be fullfiled by the domain service
// implementation, and all of its decorators (e.g. logging & metrics).
type Service interface {
// AddThing adds new thing to the user identified by the provided key.
AddThing(string, Thing) (Thing, error)
// UpdateThing updates the thing identified by the provided ID, that
// belongs to the user identified by the provided key.
UpdateThing(string, Thing) error
// ViewThing retrieves data about the thing identified with the provided
// ID, that belongs to the user identified by the provided key.
ViewThing(string, uint64) (Thing, error)
// ListThings retrieves data about subset of things that belongs to the
// user identified by the provided key.
ListThings(string, uint64, uint64) ([]Thing, error)
// RemoveThing removes the thing identified with the provided ID, that
// belongs to the user identified by the provided key.
RemoveThing(string, uint64) error
// CreateChannel adds new channel to the user identified by the provided key.
CreateChannel(string, Channel) (Channel, error)
// UpdateChannel updates the channel identified by the provided ID, that
// belongs to the user identified by the provided key.
UpdateChannel(string, Channel) error
// ViewChannel retrieves data about the channel identified by the provided
// ID, that belongs to the user identified by the provided key.
ViewChannel(string, uint64) (Channel, error)
// ListChannels retrieves data about subset of channels that belongs to the
// user identified by the provided key.
ListChannels(string, uint64, uint64) ([]Channel, error)
// RemoveChannel removes the thing identified by the provided ID, that
// belongs to the user identified by the provided key.
RemoveChannel(string, uint64) error
// Connect adds thing to the channel's list of connected things.
Connect(string, uint64, uint64) error
// Disconnect removes thing from the channel's list of connected
// things.
Disconnect(string, uint64, uint64) error
// CanAccess determines whether the channel can be accessed using the
// provided key and returns thing's id if access is allowed.
CanAccess(uint64, string) (uint64, error)
// Identify returns thing ID for given thing key.
Identify(string) (uint64, error)
}
var _ Service = (*thingsService)(nil)
type thingsService struct {
users mainflux.UsersServiceClient
things ThingRepository
channels ChannelRepository
channelCache ChannelCache
thingCache ThingCache
idp IdentityProvider
}
// New instantiates the things service implementation.
func New(users mainflux.UsersServiceClient, things ThingRepository, channels ChannelRepository, ccache ChannelCache, tcache ThingCache, idp IdentityProvider) Service {
return &thingsService{
users: users,
things: things,
channels: channels,
channelCache: ccache,
thingCache: tcache,
idp: idp,
}
}
func (ts *thingsService) AddThing(key string, thing Thing) (Thing, error) {
if err := thing.Validate(); err != nil {
return Thing{}, ErrMalformedEntity
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
if err != nil {
return Thing{}, ErrUnauthorizedAccess
}
thing.Owner = res.GetValue()
thing.Key = ts.idp.ID()
id, err := ts.things.Save(thing)
if err != nil {
return Thing{}, err
}
thing.ID = id
return thing, nil
}
func (ts *thingsService) UpdateThing(key string, thing Thing) error {
if err := thing.Validate(); err != nil {
return ErrMalformedEntity
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
if err != nil {
return ErrUnauthorizedAccess
}
thing.Owner = res.GetValue()
return ts.things.Update(thing)
}
func (ts *thingsService) ViewThing(key string, id uint64) (Thing, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
if err != nil {
return Thing{}, ErrUnauthorizedAccess
}
return ts.things.RetrieveByID(res.GetValue(), id)
}
func (ts *thingsService) ListThings(key string, offset, limit uint64) ([]Thing, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
if err != nil {
return nil, ErrUnauthorizedAccess
}
return ts.things.RetrieveAll(res.GetValue(), offset, limit), nil
}
func (ts *thingsService) RemoveThing(key string, id uint64) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
if err != nil {
return ErrUnauthorizedAccess
}
ts.thingCache.Remove(id)
return ts.things.Remove(res.GetValue(), id)
}
func (ts *thingsService) CreateChannel(key 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})
if err != nil {
return Channel{}, ErrUnauthorizedAccess
}
channel.Owner = res.GetValue()
id, err := ts.channels.Save(channel)
if err != nil {
return Channel{}, err
}
channel.ID = id
return channel, nil
}
func (ts *thingsService) UpdateChannel(key string, channel Channel) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
if err != nil {
return ErrUnauthorizedAccess
}
channel.Owner = res.GetValue()
return ts.channels.Update(channel)
}
func (ts *thingsService) ViewChannel(key string, id uint64) (Channel, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
if err != nil {
return Channel{}, ErrUnauthorizedAccess
}
return ts.channels.RetrieveByID(res.GetValue(), id)
}
func (ts *thingsService) ListChannels(key string, offset, limit uint64) ([]Channel, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
if err != nil {
return nil, ErrUnauthorizedAccess
}
return ts.channels.RetrieveAll(res.GetValue(), offset, limit), nil
}
func (ts *thingsService) RemoveChannel(key string, id uint64) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
if err != nil {
return ErrUnauthorizedAccess
}
ts.channelCache.Remove(id)
return ts.channels.Remove(res.GetValue(), id)
}
func (ts *thingsService) Connect(key string, chanID, thingID uint64) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
if err != nil {
return ErrUnauthorizedAccess
}
return ts.channels.Connect(res.GetValue(), chanID, thingID)
}
func (ts *thingsService) Disconnect(key string, chanID, thingID uint64) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := ts.users.Identify(ctx, &mainflux.Token{Value: key})
if err != nil {
return ErrUnauthorizedAccess
}
ts.channelCache.Disconnect(chanID, thingID)
return ts.channels.Disconnect(res.GetValue(), chanID, thingID)
}
func (ts *thingsService) CanAccess(chanID uint64, key string) (uint64, error) {
thingID, err := ts.hasThing(chanID, key)
if err == nil {
return thingID, nil
}
thingID, err = ts.channels.HasThing(chanID, key)
if err != nil {
return 0, ErrUnauthorizedAccess
}
ts.thingCache.Save(key, thingID)
ts.channelCache.Connect(chanID, thingID)
return thingID, nil
}
func (ts *thingsService) Identify(key string) (uint64, error) {
id, err := ts.thingCache.ID(key)
if err == nil {
return id, nil
}
id, err = ts.things.RetrieveByKey(key)
if err != nil {
return 0, ErrUnauthorizedAccess
}
ts.thingCache.Save(key, id)
return id, nil
}
func (ts *thingsService) hasThing(chanID uint64, key string) (uint64, error) {
thingID, err := ts.thingCache.ID(key)
if err != nil {
return 0, err
}
if connected := ts.channelCache.HasThing(chanID, thingID); !connected {
return 0, ErrUnauthorizedAccess
}
return thingID, nil
}