1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-04-27 13:48:49 +08:00

NOISSUE - Implement errors package in senml transformer, readers and writers (#1108)

* Implement errors package in senml transformer, readers and writers

Signed-off-by: Ivan Milošević <iva@blokovi.com>

* Remove unused const
Return wrapped error in postgres writer

Signed-off-by: Ivan Milošević <iva@blokovi.com>

* fix default db host in postgres writer

Signed-off-by: Ivan Milošević <iva@blokovi.com>

* fix capital letters in errors messages

Signed-off-by: Ivan Milošević <iva@blokovi.com>

* use svcName instead of postgres for Promethius initialization

Signed-off-by: Ivan Milošević <iva@blokovi.com>
This commit is contained in:
Ivan Milošević 2020-04-13 12:57:53 +02:00 committed by GitHub
parent e438be4250
commit 880e193b0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 92 additions and 47 deletions

View File

@ -31,7 +31,7 @@ import (
) )
const ( const (
svcName = "postgres-writer" svcName = "postgres-reader"
sep = "," sep = ","
defLogLevel = "error" defLogLevel = "error"
@ -112,7 +112,7 @@ func main() {
}() }()
err = <-errs err = <-errs
logger.Error(fmt.Sprintf("Postgres writer service terminated: %s", err)) logger.Error(fmt.Sprintf("Postgres reader service terminated: %s", err))
} }
func loadConfig() config { func loadConfig() config {
@ -213,14 +213,14 @@ func newService(db *sqlx.DB, logger logger.Logger) readers.MessageRepository {
svc = api.MetricsMiddleware( svc = api.MetricsMiddleware(
svc, svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "postgres", Namespace: svcName,
Subsystem: "message_writer", Subsystem: "message_reader",
Name: "request_count", Name: "request_count",
Help: "Number of requests received.", Help: "Number of requests received.",
}, []string{"method"}), }, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "postgres", Namespace: svcName,
Subsystem: "message_writer", Subsystem: "message_reader",
Name: "request_latency_microseconds", Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.", Help: "Total duration of requests in microseconds.",
}, []string{"method"}), }, []string{"method"}),

View File

@ -30,7 +30,7 @@ const (
defLogLevel = "error" defLogLevel = "error"
defNatsURL = "nats://localhost:4222" defNatsURL = "nats://localhost:4222"
defPort = "8180" defPort = "8180"
defDBHost = "postgres" defDBHost = "localhost"
defDBPort = "5432" defDBPort = "5432"
defDBUser = "mainflux" defDBUser = "mainflux"
defDBPass = "mainflux" defDBPass = "mainflux"
@ -139,13 +139,13 @@ func newService(db *sqlx.DB, logger logger.Logger) writers.MessageRepository {
svc = api.MetricsMiddleware( svc = api.MetricsMiddleware(
svc, svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "postgres", Namespace: svcName,
Subsystem: "message_writer", Subsystem: "message_writer",
Name: "request_count", Name: "request_count",
Help: "Number of requests received.", Help: "Number of requests received.",
}, []string{"method"}), }, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "postgres", Namespace: svcName,
Subsystem: "message_writer", Subsystem: "message_writer",
Name: "request_latency_microseconds", Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.", Help: "Total duration of requests in microseconds.",

View File

@ -30,3 +30,7 @@ func (res pageRes) Code() int {
func (res pageRes) Empty() bool { func (res pageRes) Empty() bool {
return false return false
} }
type errorRes struct {
Err string `json:"error"`
}

View File

@ -6,7 +6,6 @@ package api
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
@ -14,6 +13,7 @@ import (
kithttp "github.com/go-kit/kit/transport/http" kithttp "github.com/go-kit/kit/transport/http"
"github.com/go-zoo/bone" "github.com/go-zoo/bone"
"github.com/mainflux/mainflux" "github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/errors"
"github.com/mainflux/mainflux/readers" "github.com/mainflux/mainflux/readers"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
@ -111,15 +111,22 @@ func encodeResponse(_ context.Context, w http.ResponseWriter, response interface
} }
func encodeError(_ context.Context, err error, w http.ResponseWriter) { func encodeError(_ context.Context, err error, w http.ResponseWriter) {
switch err { switch {
case nil: case errors.Contains(err, nil):
case errInvalidRequest: case errors.Contains(err, errInvalidRequest):
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
case errUnauthorizedAccess: case errors.Contains(err, errUnauthorizedAccess):
w.WriteHeader(http.StatusForbidden) w.WriteHeader(http.StatusForbidden)
default: default:
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
} }
errorVal, ok := err.(errors.Error)
if ok {
w.Header().Set("Content-Type", contentType)
if err := json.NewEncoder(w).Encode(errorRes{Err: errorVal.Msg()}); err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
}
} }
func authorize(r *http.Request, chanID string) error { func authorize(r *http.Request, chanID string) error {

View File

@ -7,10 +7,13 @@ import (
"fmt" "fmt"
"github.com/gocql/gocql" "github.com/gocql/gocql"
"github.com/mainflux/mainflux/errors"
"github.com/mainflux/mainflux/readers" "github.com/mainflux/mainflux/readers"
"github.com/mainflux/mainflux/transformers/senml" "github.com/mainflux/mainflux/transformers/senml"
) )
var errReadMessages = errors.New("faled to read messages from cassandra database")
var _ readers.MessageRepository = (*cassandraRepository)(nil) var _ readers.MessageRepository = (*cassandraRepository)(nil)
type cassandraRepository struct { type cassandraRepository struct {
@ -59,13 +62,13 @@ func (cr cassandraRepository) ReadAll(chanID string, offset, limit uint64, query
&msg.Name, &msg.Unit, &msg.Value, &msg.StringValue, &msg.BoolValue, &msg.Name, &msg.Unit, &msg.Value, &msg.StringValue, &msg.BoolValue,
&msg.DataValue, &msg.Sum, &msg.Time, &msg.UpdateTime) &msg.DataValue, &msg.Sum, &msg.Time, &msg.UpdateTime)
if err != nil { if err != nil {
return readers.MessagesPage{}, err return readers.MessagesPage{}, errors.Wrap(errReadMessages, err)
} }
page.Messages = append(page.Messages, msg) page.Messages = append(page.Messages, msg)
} }
if err := cr.session.Query(countCQL, vals[:len(vals)-1]...).Scan(&page.Total); err != nil { if err := cr.session.Query(countCQL, vals[:len(vals)-1]...).Scan(&page.Total); err != nil {
return readers.MessagesPage{}, err return readers.MessagesPage{}, errors.Wrap(errReadMessages, err)
} }
return page, nil return page, nil

View File

@ -8,6 +8,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/mainflux/mainflux/errors"
"github.com/mainflux/mainflux/readers" "github.com/mainflux/mainflux/readers"
influxdata "github.com/influxdata/influxdb/client/v2" influxdata "github.com/influxdata/influxdb/client/v2"
@ -16,6 +17,8 @@ import (
const countCol = "count" const countCol = "count"
var errReadMessages = errors.New("faled to read messages from influxdb database")
var _ readers.MessageRepository = (*influxRepository)(nil) var _ readers.MessageRepository = (*influxRepository)(nil)
type influxRepository struct { type influxRepository struct {
@ -43,10 +46,10 @@ func (repo *influxRepository) ReadAll(chanID string, offset, limit uint64, query
resp, err := repo.client.Query(q) resp, err := repo.client.Query(q)
if err != nil { if err != nil {
return readers.MessagesPage{}, err return readers.MessagesPage{}, errors.Wrap(errReadMessages, err)
} }
if resp.Error() != nil { if resp.Error() != nil {
return readers.MessagesPage{}, resp.Error() return readers.MessagesPage{}, errors.Wrap(errReadMessages, resp.Error())
} }
if len(resp.Results) < 1 || len(resp.Results[0].Series) < 1 { if len(resp.Results) < 1 || len(resp.Results[0].Series) < 1 {
@ -60,7 +63,7 @@ func (repo *influxRepository) ReadAll(chanID string, offset, limit uint64, query
total, err := repo.count(condition) total, err := repo.count(condition)
if err != nil { if err != nil {
return readers.MessagesPage{}, err return readers.MessagesPage{}, errors.Wrap(errReadMessages, err)
} }
return readers.MessagesPage{ return readers.MessagesPage{

View File

@ -6,6 +6,7 @@ package mongodb
import ( import (
"context" "context"
"github.com/mainflux/mainflux/errors"
"github.com/mainflux/mainflux/readers" "github.com/mainflux/mainflux/readers"
"github.com/mainflux/mainflux/transformers/senml" "github.com/mainflux/mainflux/transformers/senml"
"go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson"
@ -15,6 +16,8 @@ import (
const collection = "mainflux" const collection = "mainflux"
var errReadMessages = errors.New("faled to read messages from mongodb database")
var _ readers.MessageRepository = (*mongoRepository)(nil) var _ readers.MessageRepository = (*mongoRepository)(nil)
type mongoRepository struct { type mongoRepository struct {
@ -54,7 +57,7 @@ func (repo mongoRepository) ReadAll(chanID string, offset, limit uint64, query m
filter := fmtCondition(chanID, query) filter := fmtCondition(chanID, query)
cursor, err := col.Find(context.Background(), filter, options.Find().SetSort(sortMap).SetLimit(int64(limit)).SetSkip(int64(offset))) cursor, err := col.Find(context.Background(), filter, options.Find().SetSort(sortMap).SetLimit(int64(limit)).SetSkip(int64(offset)))
if err != nil { if err != nil {
return readers.MessagesPage{}, err return readers.MessagesPage{}, errors.Wrap(errReadMessages, err)
} }
defer cursor.Close(context.Background()) defer cursor.Close(context.Background())
@ -62,7 +65,7 @@ func (repo mongoRepository) ReadAll(chanID string, offset, limit uint64, query m
for cursor.Next(context.Background()) { for cursor.Next(context.Background()) {
var m message var m message
if err := cursor.Decode(&m); err != nil { if err := cursor.Decode(&m); err != nil {
return readers.MessagesPage{}, err return readers.MessagesPage{}, errors.Wrap(errReadMessages, err)
} }
msg := senml.Message{ msg := senml.Message{
@ -93,7 +96,7 @@ func (repo mongoRepository) ReadAll(chanID string, offset, limit uint64, query m
total, err := col.CountDocuments(context.Background(), filter) total, err := col.CountDocuments(context.Background(), filter)
if err != nil { if err != nil {
return readers.MessagesPage{}, err return readers.MessagesPage{}, errors.Wrap(errReadMessages, err)
} }
if total < 0 { if total < 0 {
return readers.MessagesPage{}, nil return readers.MessagesPage{}, nil

View File

@ -4,17 +4,17 @@
package postgres package postgres
import ( import (
"errors"
"fmt" "fmt"
"github.com/jmoiron/sqlx" // required for DB access "github.com/jmoiron/sqlx" // required for DB access
"github.com/mainflux/mainflux/errors"
"github.com/mainflux/mainflux/readers" "github.com/mainflux/mainflux/readers"
"github.com/mainflux/mainflux/transformers/senml" "github.com/mainflux/mainflux/transformers/senml"
) )
const errInvalid = "invalid_text_representation" const errInvalid = "invalid_text_representation"
var errInvalidMessage = errors.New("invalid message representation") var errReadMessages = errors.New("faled to read messages from postgres database")
var _ readers.MessageRepository = (*postgresRepository)(nil) var _ readers.MessageRepository = (*postgresRepository)(nil)
@ -46,7 +46,7 @@ func (tr postgresRepository) ReadAll(chanID string, offset, limit uint64, query
rows, err := tr.db.NamedQuery(q, params) rows, err := tr.db.NamedQuery(q, params)
if err != nil { if err != nil {
return readers.MessagesPage{}, err return readers.MessagesPage{}, errors.Wrap(errReadMessages, err)
} }
defer rows.Close() defer rows.Close()
@ -58,7 +58,7 @@ func (tr postgresRepository) ReadAll(chanID string, offset, limit uint64, query
for rows.Next() { for rows.Next() {
dbm := dbMessage{Channel: chanID} dbm := dbMessage{Channel: chanID}
if err := rows.StructScan(&dbm); err != nil { if err := rows.StructScan(&dbm); err != nil {
return readers.MessagesPage{}, err return readers.MessagesPage{}, errors.Wrap(errReadMessages, err)
} }
msg := toMessage(dbm) msg := toMessage(dbm)
@ -74,7 +74,7 @@ func (tr postgresRepository) ReadAll(chanID string, offset, limit uint64, query
} }
if err := tr.db.QueryRow(q, qParams...).Scan(&page.Total); err != nil { if err := tr.db.QueryRow(q, qParams...).Scan(&page.Total); err != nil {
return readers.MessagesPage{}, err return readers.MessagesPage{}, errors.Wrap(errReadMessages, err)
} }
return page, nil return page, nil

View File

@ -5,10 +5,16 @@ package senml
import ( import (
"github.com/mainflux/mainflux/broker" "github.com/mainflux/mainflux/broker"
"github.com/mainflux/mainflux/errors"
"github.com/mainflux/mainflux/transformers" "github.com/mainflux/mainflux/transformers"
"github.com/mainflux/senml" "github.com/mainflux/senml"
) )
var (
errDecode = errors.New("failed to decode senml")
errNormalize = errors.New("faled to normalize senml")
)
var formats = map[string]senml.Format{ var formats = map[string]senml.Format{
JSON: senml.JSON, JSON: senml.JSON,
CBOR: senml.CBOR, CBOR: senml.CBOR,
@ -29,12 +35,12 @@ func (n transformer) Transform(msg broker.Message) (interface{}, error) {
raw, err := senml.Decode(msg.Payload, format) raw, err := senml.Decode(msg.Payload, format)
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(errDecode, err)
} }
normalized, err := senml.Normalize(raw) normalized, err := senml.Normalize(raw)
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(errNormalize, err)
} }
msgs := make([]Message, len(normalized.Records)) msgs := make([]Message, len(normalized.Records))

View File

@ -9,6 +9,7 @@ import (
"testing" "testing"
"github.com/mainflux/mainflux/broker" "github.com/mainflux/mainflux/broker"
"github.com/mainflux/mainflux/errors"
"github.com/mainflux/mainflux/transformers/senml" "github.com/mainflux/mainflux/transformers/senml"
mfsenml "github.com/mainflux/senml" mfsenml "github.com/mainflux/senml"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -102,6 +103,6 @@ func TestTransform(t *testing.T) {
for _, tc := range cases { for _, tc := range cases {
msgs, err := tr.Transform(tc.msg) msgs, err := tr.Transform(tc.msg)
assert.Equal(t, tc.msgs, msgs, fmt.Sprintf("%s expected %v, got %v", tc.desc, tc.msgs, msgs)) assert.Equal(t, tc.msgs, msgs, fmt.Sprintf("%s expected %v, got %v", tc.desc, tc.msgs, msgs))
assert.Equal(t, tc.err, err, fmt.Sprintf("%s expected %s, got %s", tc.desc, tc.err, err)) assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s expected %s, got %s", tc.desc, tc.err, err))
} }
} }

View File

@ -5,10 +5,13 @@ package cassandra
import ( import (
"github.com/gocql/gocql" "github.com/gocql/gocql"
"github.com/mainflux/mainflux/errors"
"github.com/mainflux/mainflux/transformers/senml" "github.com/mainflux/mainflux/transformers/senml"
"github.com/mainflux/mainflux/writers" "github.com/mainflux/mainflux/writers"
) )
var errSaveMessage = errors.New("faled to save message to cassandra database")
var _ writers.MessageRepository = (*cassandraRepository)(nil) var _ writers.MessageRepository = (*cassandraRepository)(nil)
type cassandraRepository struct { type cassandraRepository struct {
@ -32,7 +35,7 @@ func (cr *cassandraRepository) Save(messages ...senml.Message) error {
msg.Protocol, msg.Name, msg.Unit, msg.Value, msg.StringValue, msg.Protocol, msg.Name, msg.Unit, msg.Value, msg.StringValue,
msg.BoolValue, msg.DataValue, msg.Sum, msg.Time, msg.UpdateTime).Exec() msg.BoolValue, msg.DataValue, msg.Sum, msg.Time, msg.UpdateTime).Exec()
if err != nil { if err != nil {
return err return errors.Wrap(errSaveMessage, err)
} }
} }

View File

@ -8,6 +8,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/mainflux/mainflux/errors"
"github.com/mainflux/mainflux/transformers/senml" "github.com/mainflux/mainflux/transformers/senml"
"github.com/mainflux/mainflux/writers" "github.com/mainflux/mainflux/writers"
@ -16,6 +17,8 @@ import (
const pointName = "messages" const pointName = "messages"
var errSaveMessage = errors.New("faled to save message to influxdb database")
var _ writers.MessageRepository = (*influxRepo)(nil) var _ writers.MessageRepository = (*influxRepo)(nil)
type influxRepo struct { type influxRepo struct {
@ -39,7 +42,7 @@ func New(client influxdata.Client, database string) writers.MessageRepository {
func (repo *influxRepo) Save(messages ...senml.Message) error { func (repo *influxRepo) Save(messages ...senml.Message) error {
pts, err := influxdata.NewBatchPoints(repo.cfg) pts, err := influxdata.NewBatchPoints(repo.cfg)
if err != nil { if err != nil {
return err return errors.Wrap(errSaveMessage, err)
} }
for _, msg := range messages { for _, msg := range messages {
@ -50,12 +53,14 @@ func (repo *influxRepo) Save(messages ...senml.Message) error {
pt, err := influxdata.NewPoint(pointName, tgs, flds, t) pt, err := influxdata.NewPoint(pointName, tgs, flds, t)
if err != nil { if err != nil {
return err return errors.Wrap(errSaveMessage, err)
} }
pts.AddPoint(pt) pts.AddPoint(pt)
} }
if err := repo.client.Write(pts); err != nil {
return repo.client.Write(pts) return errors.Wrap(errSaveMessage, err)
}
return nil
} }
func (repo *influxRepo) tagsOf(msg *senml.Message) tags { func (repo *influxRepo) tagsOf(msg *senml.Message) tags {

View File

@ -8,12 +8,15 @@ import (
"go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo"
"github.com/mainflux/mainflux/errors"
"github.com/mainflux/mainflux/transformers/senml" "github.com/mainflux/mainflux/transformers/senml"
"github.com/mainflux/mainflux/writers" "github.com/mainflux/mainflux/writers"
) )
const collectionName string = "mainflux" const collectionName string = "mainflux"
var errSaveMessage = errors.New("faled to save message to mongodb database")
var _ writers.MessageRepository = (*mongoRepo)(nil) var _ writers.MessageRepository = (*mongoRepo)(nil)
type mongoRepo struct { type mongoRepo struct {
@ -73,5 +76,8 @@ func (repo *mongoRepo) Save(messages ...senml.Message) error {
} }
_, err := coll.InsertMany(context.Background(), msgs) _, err := coll.InsertMany(context.Background(), msgs)
return err if err != nil {
return errors.Wrap(errSaveMessage, err)
}
return nil
} }

View File

@ -5,21 +5,23 @@ package postgres
import ( import (
"context" "context"
"errors"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/lib/pq" // required for DB access "github.com/lib/pq" // required for DB access
"github.com/mainflux/mainflux/errors"
"github.com/mainflux/mainflux/transformers/senml" "github.com/mainflux/mainflux/transformers/senml"
"github.com/mainflux/mainflux/writers" "github.com/mainflux/mainflux/writers"
) )
const errInvalid = "invalid_text_representation" const errInvalid = "invalid_text_representation"
// ErrInvalidMessage indicates that service received message that var (
// doesn't fit required format. // ErrInvalidMessage indicates that service received message that
var ErrInvalidMessage = errors.New("invalid message representation") // doesn't fit required format.
ErrInvalidMessage = errors.New("invalid message representation")
errSaveMessage = errors.New("faled to save message to postgress database")
)
var _ writers.MessageRepository = (*postgresRepo)(nil) var _ writers.MessageRepository = (*postgresRepo)(nil)
@ -42,13 +44,13 @@ func (pr postgresRepo) Save(messages ...senml.Message) error {
tx, err := pr.db.BeginTxx(context.Background(), nil) tx, err := pr.db.BeginTxx(context.Background(), nil)
if err != nil { if err != nil {
return err return errors.Wrap(errSaveMessage, err)
} }
for _, msg := range messages { for _, msg := range messages {
dbth, err := toDBMessage(msg) dbth, err := toDBMessage(msg)
if err != nil { if err != nil {
return err return errors.Wrap(errSaveMessage, err)
} }
if _, err := tx.NamedExec(q, dbth); err != nil { if _, err := tx.NamedExec(q, dbth); err != nil {
@ -56,15 +58,17 @@ func (pr postgresRepo) Save(messages ...senml.Message) error {
if ok { if ok {
switch pqErr.Code.Name() { switch pqErr.Code.Name() {
case errInvalid: case errInvalid:
return ErrInvalidMessage return errors.Wrap(errSaveMessage, ErrInvalidMessage)
} }
} }
return err return errors.Wrap(errSaveMessage, err)
} }
} }
if err := tx.Commit(); err != nil {
return tx.Commit() return errors.Wrap(errSaveMessage, err)
}
return nil
} }
type dbMessage struct { type dbMessage struct {