mirror of
https://github.com/absmach/supermq.git
synced 2026-06-23 04:40:19 +00:00
NOISSUE - Update Errors (#374)
* update MG errors Signed-off-by: Arvindh <arvindh91@gmail.com> * update MG errors Signed-off-by: Arvindh <arvindh91@gmail.com> * sync with supermq main Signed-off-by: Arvindh <arvindh91@gmail.com> * update MG errors Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com>
This commit is contained in:
@@ -100,9 +100,6 @@ jobs:
|
||||
internal:
|
||||
- "internal/**"
|
||||
|
||||
pkg-errors:
|
||||
- "pkg/errors/**"
|
||||
|
||||
pkg-events:
|
||||
- "pkg/events/**"
|
||||
- "pkg/messaging/**"
|
||||
@@ -161,11 +158,6 @@ jobs:
|
||||
run: |
|
||||
go test --race -v -count=1 -coverprofile=coverage/internal.out ./internal/...
|
||||
|
||||
- name: Run pkg errors tests
|
||||
if: steps.changes.outputs.pkg-errors == 'true' || steps.changes.outputs.workflow == 'true'
|
||||
run: |
|
||||
go test --race -v -count=1 -coverprofile=coverage/pkg-errors.out ./pkg/errors/...
|
||||
|
||||
- name: Run pkg sdk tests
|
||||
if: steps.changes.outputs.pkg-sdk == 'true' || steps.changes.outputs.workflow == 'true'
|
||||
run: |
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/absmach/magistrala/alarms"
|
||||
"github.com/absmach/magistrala/internal/testsutil"
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -195,12 +195,12 @@ func decodeAlarmReq(_ context.Context, r *http.Request) (any, error) {
|
||||
|
||||
func decodeUpdateAlarmReq(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return alarmReq{}, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return alarmReq{}, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := alarmReq{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req.Alarm); err != nil {
|
||||
return alarmReq{}, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
|
||||
return alarmReq{}, errors.Wrap(apiutil.ErrMalformedRequestBody, err)
|
||||
}
|
||||
|
||||
req.Alarm.ID = chi.URLParam(r, "alarmID")
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
|
||||
"github.com/absmach/magistrala/alarms"
|
||||
"github.com/absmach/magistrala/alarms/mocks"
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/authn"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
repoerr "github.com/absmach/supermq/pkg/errors/repository"
|
||||
"github.com/absmach/supermq/pkg/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -89,11 +89,11 @@ var (
|
||||
CACert: "newca",
|
||||
}
|
||||
|
||||
missingIDRes = toJSON(apiutil.ErrorRes{Err: apiutil.ErrMissingID.Error(), Msg: apiutil.ErrValidation.Error()})
|
||||
missingKeyRes = toJSON(apiutil.ErrorRes{Err: apiutil.ErrBearerKey.Error(), Msg: apiutil.ErrValidation.Error()})
|
||||
bsErrorRes = toJSON(apiutil.ErrorRes{Msg: bootstrap.ErrBootstrap.Error()})
|
||||
extKeyRes = toJSON(apiutil.ErrorRes{Msg: bootstrap.ErrExternalKey.Error()})
|
||||
extSecKeyRes = toJSON(apiutil.ErrorRes{Msg: bootstrap.ErrExternalKeySecure.Error()})
|
||||
missingIDRes = toJSON(apiutil.ErrMissingID)
|
||||
missingKeyRes = toJSON(apiutil.ErrBearerKey)
|
||||
unknownExternalIDErrorRes = toJSON(svcerr.ErrNotFound)
|
||||
extKeyRes = toJSON(bootstrap.ErrExternalKey)
|
||||
extSecKeyRes = toJSON(bootstrap.ErrExternalKeySecure)
|
||||
)
|
||||
|
||||
type testRequest struct {
|
||||
@@ -257,7 +257,7 @@ func TestAdd(t *testing.T) {
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
contentType: contentType,
|
||||
status: http.StatusConflict,
|
||||
status: http.StatusBadRequest,
|
||||
location: "",
|
||||
err: svcerr.ErrConflict,
|
||||
},
|
||||
@@ -267,7 +267,7 @@ func TestAdd(t *testing.T) {
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
contentType: contentType,
|
||||
status: http.StatusConflict,
|
||||
status: http.StatusBadRequest,
|
||||
location: "",
|
||||
err: svcerr.ErrConflict,
|
||||
},
|
||||
@@ -277,7 +277,7 @@ func TestAdd(t *testing.T) {
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
contentType: contentType,
|
||||
status: http.StatusConflict,
|
||||
status: http.StatusBadRequest,
|
||||
location: "",
|
||||
err: svcerr.ErrConflict,
|
||||
},
|
||||
@@ -1190,9 +1190,9 @@ func TestBootstrap(t *testing.T) {
|
||||
externalID: unknown,
|
||||
externalKey: c.ExternalKey,
|
||||
status: http.StatusNotFound,
|
||||
res: bsErrorRes,
|
||||
res: unknownExternalIDErrorRes,
|
||||
secure: false,
|
||||
err: bootstrap.ErrBootstrap,
|
||||
err: svcerr.ErrNotFound,
|
||||
},
|
||||
{
|
||||
desc: "bootstrap a Client with an empty ID",
|
||||
@@ -1201,7 +1201,7 @@ func TestBootstrap(t *testing.T) {
|
||||
status: http.StatusBadRequest,
|
||||
res: missingIDRes,
|
||||
secure: false,
|
||||
err: errors.Wrap(bootstrap.ErrBootstrap, svcerr.ErrMalformedEntity),
|
||||
err: apiutil.ErrMissingID,
|
||||
},
|
||||
{
|
||||
desc: "bootstrap a Client with unknown key",
|
||||
@@ -1210,16 +1210,16 @@ func TestBootstrap(t *testing.T) {
|
||||
status: http.StatusForbidden,
|
||||
res: extKeyRes,
|
||||
secure: false,
|
||||
err: errors.Wrap(bootstrap.ErrExternalKey, errors.New("")),
|
||||
err: bootstrap.ErrExternalKey,
|
||||
},
|
||||
{
|
||||
desc: "bootstrap a Client with an empty key",
|
||||
externalID: c.ExternalID,
|
||||
externalKey: "",
|
||||
status: http.StatusBadRequest,
|
||||
status: http.StatusUnauthorized,
|
||||
res: missingKeyRes,
|
||||
secure: false,
|
||||
err: errors.Wrap(bootstrap.ErrBootstrap, svcerr.ErrAuthentication),
|
||||
err: apiutil.ErrBearerKey,
|
||||
},
|
||||
{
|
||||
desc: "bootstrap known Client",
|
||||
|
||||
+11
-12
@@ -11,7 +11,6 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
mgapi "github.com/absmach/magistrala/api"
|
||||
"github.com/absmach/magistrala/bootstrap"
|
||||
"github.com/absmach/supermq"
|
||||
api "github.com/absmach/supermq/api/http"
|
||||
@@ -43,7 +42,7 @@ var (
|
||||
// MakeHandler returns a HTTP handler for API endpoints.
|
||||
func MakeHandler(svc bootstrap.Service, authn smqauthn.AuthNMiddleware, reader bootstrap.ConfigReader, logger *slog.Logger, instanceID string) http.Handler {
|
||||
opts := []kithttp.ServerOption{
|
||||
kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, mgapi.EncodeError)),
|
||||
kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)),
|
||||
}
|
||||
|
||||
r := chi.NewRouter()
|
||||
@@ -129,14 +128,14 @@ func MakeHandler(svc bootstrap.Service, authn smqauthn.AuthNMiddleware, reader b
|
||||
|
||||
func decodeAddRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := addReq{
|
||||
token: apiutil.ExtractBearerToken(r),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
|
||||
return nil, errors.Wrap(apiutil.ErrMalformedRequestBody, err)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
@@ -144,14 +143,14 @@ func decodeAddRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
|
||||
func decodeUpdateRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := updateReq{
|
||||
id: chi.URLParam(r, "configID"),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
|
||||
return nil, errors.Wrap(apiutil.ErrMalformedRequestBody, err)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
@@ -159,14 +158,14 @@ func decodeUpdateRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
|
||||
func decodeUpdateCertRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := updateCertReq{
|
||||
clientID: chi.URLParam(r, "certID"),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
|
||||
return nil, errors.Wrap(apiutil.ErrMalformedRequestBody, err)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
@@ -174,7 +173,7 @@ func decodeUpdateCertRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
|
||||
func decodeUpdateConnRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := updateConnReq{
|
||||
@@ -182,7 +181,7 @@ func decodeUpdateConnRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
id: chi.URLParam(r, "connID"),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
|
||||
return nil, errors.Wrap(apiutil.ErrMalformedRequestBody, err)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
@@ -224,7 +223,7 @@ func decodeBootstrapRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
|
||||
func decodeStateRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := changeStateReq{
|
||||
@@ -232,7 +231,7 @@ func decodeStateRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
id: chi.URLParam(r, "clientID"),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
|
||||
return nil, errors.Wrap(apiutil.ErrMalformedRequestBody, err)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
|
||||
@@ -104,7 +104,7 @@ func (cr configRepository) RetrieveByID(ctx context.Context, domainID, id string
|
||||
}
|
||||
|
||||
if !row.Next() {
|
||||
return bootstrap.Config{}, errors.Wrap(repoerr.ErrNotFound, sql.ErrNoRows)
|
||||
return bootstrap.Config{}, repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
if err := row.StructScan(&dbcfg); err != nil {
|
||||
@@ -205,7 +205,7 @@ func (cr configRepository) RetrieveByExternalID(ctx context.Context, externalID
|
||||
}
|
||||
|
||||
if !row.Next() {
|
||||
return bootstrap.Config{}, errors.Wrap(repoerr.ErrNotFound, sql.ErrNoRows)
|
||||
return bootstrap.Config{}, repoerr.ErrNotFound
|
||||
}
|
||||
|
||||
if err := row.StructScan(&dbcfg); err != nil {
|
||||
|
||||
@@ -24,19 +24,19 @@ var (
|
||||
ErrClients = errors.New("failed to receive response from Clients service")
|
||||
|
||||
// ErrExternalKey indicates a non-existent bootstrap configuration for given external key.
|
||||
ErrExternalKey = errors.New("failed to get bootstrap configuration for given external key")
|
||||
ErrExternalKey = errors.NewAuthZError("failed to get bootstrap configuration for given external key")
|
||||
|
||||
// ErrExternalKeySecure indicates error in getting bootstrap configuration for given encrypted external key.
|
||||
ErrExternalKeySecure = errors.New("failed to get bootstrap configuration for given encrypted external key")
|
||||
ErrExternalKeySecure = errors.NewAuthZError("failed to get bootstrap configuration for given encrypted external key")
|
||||
|
||||
// ErrBootstrap indicates error in getting bootstrap configuration.
|
||||
ErrBootstrap = errors.New("failed to read bootstrap configuration")
|
||||
|
||||
// ErrAddBootstrap indicates error in adding bootstrap configuration.
|
||||
ErrAddBootstrap = errors.New("failed to add bootstrap configuration")
|
||||
ErrAddBootstrap = errors.NewServiceError("failed to add bootstrap configuration")
|
||||
|
||||
// ErrBootstrapState indicates an invalid bootstrap state.
|
||||
ErrBootstrapState = errors.New("invalid bootstrap state")
|
||||
ErrBootstrapState = errors.NewRequestError("invalid bootstrap state")
|
||||
|
||||
// ErrNotInSameDomain indicates entities are not in the same domain.
|
||||
errNotInSameDomain = errors.New("entities are not in the same domain")
|
||||
|
||||
@@ -38,10 +38,10 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
notFoundRes = toJSON(apiutil.ErrorRes{Msg: svcerr.ErrNotFound.Error()})
|
||||
unauthRes = toJSON(apiutil.ErrorRes{Msg: svcerr.ErrAuthentication.Error()})
|
||||
invalidRes = toJSON(apiutil.ErrorRes{Err: apiutil.ErrInvalidQueryParams.Error(), Msg: apiutil.ErrValidation.Error()})
|
||||
missingTokRes = toJSON(apiutil.ErrorRes{Err: apiutil.ErrBearerToken.Error(), Msg: apiutil.ErrValidation.Error()})
|
||||
notFoundRes = toJSON(svcerr.ErrNotFound)
|
||||
unauthRes = toJSON(svcerr.ErrAuthentication)
|
||||
invalidRes = toJSON(apiutil.ErrInvalidQueryParams)
|
||||
missingTokRes = toJSON(apiutil.ErrBearerToken)
|
||||
)
|
||||
|
||||
type testRequest struct {
|
||||
@@ -119,7 +119,7 @@ func TestCreate(t *testing.T) {
|
||||
req: data,
|
||||
contentType: contentType,
|
||||
auth: token,
|
||||
status: http.StatusConflict,
|
||||
status: http.StatusBadRequest,
|
||||
location: "",
|
||||
err: svcerr.ErrConflict,
|
||||
},
|
||||
|
||||
@@ -83,12 +83,12 @@ func MakeHandler(svc notifiers.Service, logger *slog.Logger, instanceID string)
|
||||
|
||||
func decodeCreate(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := createSubReq{token: apiutil.ExtractBearerToken(r)}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
|
||||
return nil, errors.Wrap(apiutil.ErrMalformedRequestBody, err)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
|
||||
@@ -42,7 +42,7 @@ func (repo subscriptionsRepo) Save(ctx context.Context, sub notifiers.Subscripti
|
||||
row, err := repo.db.NamedQueryContext(ctx, q, dbSub)
|
||||
if err != nil {
|
||||
if pqErr, ok := err.(*pgconn.PgError); ok && pqErr.Code == pgerrcode.UniqueViolation {
|
||||
return "", errors.Wrap(repoerr.ErrConflict, err)
|
||||
return "", errors.Wrap(notifiers.ErrSubscriptionsAlreadyExists, err)
|
||||
}
|
||||
return "", errors.Wrap(repoerr.ErrCreateEntity, err)
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func TestSave(t *testing.T) {
|
||||
desc: "save duplicate",
|
||||
sub: sub2,
|
||||
id: "",
|
||||
err: repoerr.ErrConflict,
|
||||
err: notifiers.ErrSubscriptionsAlreadyExists,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,13 @@ import (
|
||||
"github.com/absmach/supermq/pkg/messaging"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrMessage indicates an error converting a message to SuperMQ message.
|
||||
var ErrMessage = errors.New("failed to convert to SuperMQ message")
|
||||
ErrMessage = errors.New("failed to convert to SuperMQ message")
|
||||
|
||||
// ErrSubscriptionsAlreadyExists indicates subscription already exists.
|
||||
ErrSubscriptionsAlreadyExists = errors.NewRequestError("subscription already exists")
|
||||
)
|
||||
var _ consumers.AsyncConsumer = (*notifierService)(nil)
|
||||
|
||||
// Service reprents a notification service.
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
smqauthn "github.com/absmach/supermq/pkg/authn"
|
||||
authnmocks "github.com/absmach/supermq/pkg/authn/mocks"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
repoerr "github.com/absmach/supermq/pkg/errors/repository"
|
||||
svcerr "github.com/absmach/supermq/pkg/errors/service"
|
||||
"github.com/absmach/supermq/pkg/messaging"
|
||||
"github.com/absmach/supermq/pkg/uuid"
|
||||
@@ -66,7 +65,7 @@ func TestCreateSubscription(t *testing.T) {
|
||||
token: exampleUser1,
|
||||
sub: notifiers.Subscription{Contact: exampleUser1, Topic: "valid.topic"},
|
||||
id: "",
|
||||
err: repoerr.ErrConflict,
|
||||
err: notifiers.ErrSubscriptionsAlreadyExists,
|
||||
authenticateErr: nil,
|
||||
userID: validID,
|
||||
},
|
||||
|
||||
@@ -137,6 +137,9 @@ func (pr postgresRepo) insertJSON(ctx context.Context, msgs smqjson.Messages) er
|
||||
}
|
||||
|
||||
if _, err = tx.NamedExec(q, dbmsg); err != nil {
|
||||
if preErr, ok := err.(*pgconn.PrepareError); ok {
|
||||
err = preErr.Unwrap()
|
||||
}
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pgErr.Code {
|
||||
|
||||
@@ -139,6 +139,9 @@ func (tr timescaleRepo) insertJSON(ctx context.Context, msgs smqjson.Messages) e
|
||||
return errors.Wrap(errSaveMessage, err)
|
||||
}
|
||||
if _, err = tx.NamedExec(q, dbmsg); err != nil {
|
||||
if preErr, ok := err.(*pgconn.PrepareError); ok {
|
||||
err = preErr.Unwrap()
|
||||
}
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pgErr.Code {
|
||||
|
||||
@@ -1002,7 +1002,7 @@
|
||||
"uid": "PBFA97CFB590B2093"
|
||||
},
|
||||
"exemplar": true,
|
||||
"expr": "http_adapter_api_request_count{}",
|
||||
"expr": "ws_adapter_api_request_count{}",
|
||||
"interval": "",
|
||||
"legendFormat": "{{method}}",
|
||||
"refId": "A"
|
||||
@@ -1107,7 +1107,7 @@
|
||||
},
|
||||
"editorMode": "code",
|
||||
"exemplar": false,
|
||||
"expr": "label_replace(label_replace(label_replace(http_adapter_api_request_latency_microseconds, \"quantile\", \"50th percentile\", \"quantile\", \"0.5\"), \"quantile\", \"90th percentile\", \"quantile\", \"0.9\"), \"quantile\", \"99th percentile\", \"quantile\", \"0.99\")",
|
||||
"expr": "label_replace(label_replace(label_replace(ws_adapter_api_request_latency_microseconds, \"quantile\", \"50th percentile\", \"quantile\", \"0.5\"), \"quantile\", \"90th percentile\", \"quantile\", \"0.9\"), \"quantile\", \"99th percentile\", \"quantile\", \"0.99\")",
|
||||
"format": "time_series",
|
||||
"instant": false,
|
||||
"interval": "",
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"gopkg.in/gomail.v2"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Errors
|
||||
|
||||
`errors` package serve to build an arbitrary long error chain in order to capture errors returned from nested service calls.
|
||||
|
||||
`errors` package contains the custom Go `error` interface implementation, `Error`. You use the `Error` interface to **wrap** two errors in a containing error as well as to test recursively if a given error **contains** some other error.
|
||||
@@ -1,5 +0,0 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package errors contains Magistrala errors definitions.
|
||||
package errors
|
||||
@@ -1,128 +0,0 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Error specifies an API that must be fullfiled by error type.
|
||||
type Error interface {
|
||||
// Error implements the error interface.
|
||||
Error() string
|
||||
|
||||
// Msg returns error message.
|
||||
Msg() string
|
||||
|
||||
// Err returns wrapped error.
|
||||
Err() Error
|
||||
|
||||
// MarshalJSON returns a marshaled error.
|
||||
MarshalJSON() ([]byte, error)
|
||||
}
|
||||
|
||||
var _ Error = (*customError)(nil)
|
||||
|
||||
// customError represents a Magistrala error.
|
||||
type customError struct {
|
||||
msg string
|
||||
err Error
|
||||
}
|
||||
|
||||
// New returns an Error that formats as the given text.
|
||||
func New(text string) Error {
|
||||
return &customError{
|
||||
msg: text,
|
||||
err: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (ce *customError) Error() string {
|
||||
if ce == nil {
|
||||
return ""
|
||||
}
|
||||
if ce.err == nil {
|
||||
return ce.msg
|
||||
}
|
||||
return ce.msg + " : " + ce.err.Error()
|
||||
}
|
||||
|
||||
func (ce *customError) Msg() string {
|
||||
return ce.msg
|
||||
}
|
||||
|
||||
func (ce *customError) Err() Error {
|
||||
return ce.err
|
||||
}
|
||||
|
||||
func (ce *customError) MarshalJSON() ([]byte, error) {
|
||||
var val string
|
||||
if e := ce.Err(); e != nil {
|
||||
val = e.Msg()
|
||||
}
|
||||
return json.Marshal(&struct {
|
||||
Err string `json:"error"`
|
||||
Msg string `json:"message"`
|
||||
}{
|
||||
Err: val,
|
||||
Msg: ce.Msg(),
|
||||
})
|
||||
}
|
||||
|
||||
// Contains inspects if e2 error is contained in any layer of e1 error.
|
||||
func Contains(e1, e2 error) bool {
|
||||
if e1 == nil || e2 == nil {
|
||||
return e2 == e1
|
||||
}
|
||||
ce, ok := e1.(Error)
|
||||
if ok {
|
||||
if ce.Msg() == e2.Error() {
|
||||
return true
|
||||
}
|
||||
return Contains(ce.Err(), e2)
|
||||
}
|
||||
return e1.Error() == e2.Error()
|
||||
}
|
||||
|
||||
// Wrap returns an Error that wrap err with wrapper.
|
||||
func Wrap(wrapper, err error) error {
|
||||
if wrapper == nil || err == nil {
|
||||
return wrapper
|
||||
}
|
||||
if w, ok := wrapper.(Error); ok {
|
||||
return &customError{
|
||||
msg: w.Msg(),
|
||||
err: cast(err),
|
||||
}
|
||||
}
|
||||
return &customError{
|
||||
msg: wrapper.Error(),
|
||||
err: cast(err),
|
||||
}
|
||||
}
|
||||
|
||||
// Unwrap returns the wrapper and the error by separating the Wrapper from the error.
|
||||
func Unwrap(err error) (error, error) {
|
||||
if ce, ok := err.(Error); ok {
|
||||
if ce.Err() == nil {
|
||||
return nil, New(ce.Msg())
|
||||
}
|
||||
return New(ce.Msg()), ce.Err()
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func cast(err error) Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if e, ok := err.(Error); ok {
|
||||
return e
|
||||
}
|
||||
return &customError{
|
||||
msg: err.Error(),
|
||||
err: nil,
|
||||
}
|
||||
}
|
||||
@@ -1,352 +0,0 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package errors_test
|
||||
|
||||
import (
|
||||
nerrors "errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const level = 10
|
||||
|
||||
var (
|
||||
err0 = errors.New("0")
|
||||
err1 = errors.New("1")
|
||||
err2 = errors.New("2")
|
||||
nat = nerrors.New("native error")
|
||||
)
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
err error
|
||||
msg string
|
||||
bytes []byte
|
||||
bytesErr error
|
||||
}{
|
||||
{
|
||||
desc: "level 0 wrapped error",
|
||||
err: err0,
|
||||
msg: "0",
|
||||
bytes: []byte(`{"error":"","message":"0"}`),
|
||||
bytesErr: nil,
|
||||
},
|
||||
{
|
||||
desc: "level 1 wrapped error",
|
||||
err: wrap(1),
|
||||
msg: message(1),
|
||||
bytes: []byte(`{"error":"0","message":"1"}`),
|
||||
bytesErr: nil,
|
||||
},
|
||||
{
|
||||
desc: "level 2 wrapped error",
|
||||
err: wrap(2),
|
||||
msg: message(2),
|
||||
bytes: []byte(`{"error":"1","message":"2"}`),
|
||||
bytesErr: nil,
|
||||
},
|
||||
{
|
||||
desc: fmt.Sprintf("level %d wrapped error", level),
|
||||
err: wrap(level),
|
||||
msg: message(level),
|
||||
bytes: []byte(`{"error":"9","message":"` + strconv.Itoa(level) + `"}`),
|
||||
bytesErr: nil,
|
||||
},
|
||||
{
|
||||
desc: "nil error",
|
||||
err: errors.New(""),
|
||||
msg: "",
|
||||
bytes: []byte(`{"error":"","message":""}`),
|
||||
bytesErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
errMsg := c.err.Error()
|
||||
assert.Equal(t, c.msg, errMsg)
|
||||
err := c.err.(errors.Error)
|
||||
data, derr := err.MarshalJSON()
|
||||
assert.Equal(t, c.bytesErr, derr)
|
||||
assert.Equal(t, c.bytes, data)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContains(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
container error
|
||||
contained error
|
||||
contains bool
|
||||
}{
|
||||
{
|
||||
desc: "nil contains nil",
|
||||
container: nil,
|
||||
contained: nil,
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
desc: "nil contains non-nil",
|
||||
container: nil,
|
||||
contained: err0,
|
||||
contains: false,
|
||||
},
|
||||
{
|
||||
desc: "non-nil contains nil",
|
||||
container: err0,
|
||||
contained: nil,
|
||||
contains: false,
|
||||
},
|
||||
{
|
||||
desc: "non-nil contains non-nil",
|
||||
container: err0,
|
||||
contained: err1,
|
||||
contains: false,
|
||||
},
|
||||
{
|
||||
desc: "res of errors.Wrap(err1, err0) contains err0",
|
||||
container: errors.Wrap(err1, err0),
|
||||
contained: err0,
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
desc: "res of errors.Wrap(err1, err0) contains err1",
|
||||
container: errors.Wrap(err1, err0),
|
||||
contained: err1,
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
desc: "res of errors.Wrap(err2, errors.Wrap(err1, err0)) contains err1",
|
||||
container: errors.Wrap(err2, errors.Wrap(err1, err0)),
|
||||
contained: err1,
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
desc: fmt.Sprintf("level %d wrapped error contains", level),
|
||||
container: wrap(level),
|
||||
contained: errors.New(strconv.Itoa(level / 2)),
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
desc: "superset wrapper error contains subset wrapper error",
|
||||
container: wrap(level),
|
||||
contained: wrap(level / 2),
|
||||
contains: false,
|
||||
},
|
||||
{
|
||||
desc: "native error contains error",
|
||||
container: nat,
|
||||
contained: err0,
|
||||
contains: false,
|
||||
},
|
||||
{
|
||||
desc: "res of errors.Wrap(err1, errors.New('')) contains err1",
|
||||
container: errors.Wrap(err1, nat),
|
||||
contained: err1,
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
desc: "error contains native error",
|
||||
container: err0,
|
||||
contained: nat,
|
||||
contains: false,
|
||||
},
|
||||
{
|
||||
desc: "res of errors.Wrap(errors.New(''), err0) contains err0",
|
||||
container: errors.Wrap(nat, err0),
|
||||
contained: err0,
|
||||
contains: true,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
contains := errors.Contains(c.container, c.contained)
|
||||
assert.Equal(t, c.contains, contains)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrap(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
wrapper error
|
||||
wrapped error
|
||||
contained error
|
||||
contains bool
|
||||
}{
|
||||
{
|
||||
desc: "err 1 wraps err 2",
|
||||
wrapper: err1,
|
||||
wrapped: err0,
|
||||
contained: err0,
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
desc: "err2 wraps err1 wraps err0 and contains err0",
|
||||
wrapper: err2,
|
||||
wrapped: errors.Wrap(err1, err0),
|
||||
contained: err0,
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
desc: "err2 wraps err1 wraps err0 and contains err1",
|
||||
wrapper: err2,
|
||||
wrapped: errors.Wrap(err1, err0),
|
||||
contained: err1,
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
desc: "nil wraps nil",
|
||||
wrapper: nil,
|
||||
wrapped: nil,
|
||||
contained: nil,
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
desc: "err0 wraps nil",
|
||||
wrapper: err0,
|
||||
wrapped: nil,
|
||||
contained: nil,
|
||||
contains: false,
|
||||
},
|
||||
{
|
||||
desc: "nil wraps err0",
|
||||
wrapper: nil,
|
||||
wrapped: err0,
|
||||
contained: err0,
|
||||
contains: false,
|
||||
},
|
||||
{
|
||||
desc: "err0 wraps native error",
|
||||
wrapper: err0,
|
||||
wrapped: nat,
|
||||
contained: nat,
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
desc: "nil wraps native error",
|
||||
wrapper: nil,
|
||||
wrapped: nat,
|
||||
contained: nat,
|
||||
contains: false,
|
||||
},
|
||||
{
|
||||
desc: "native error wraps err0",
|
||||
wrapper: nat,
|
||||
wrapped: err0,
|
||||
contained: err0,
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
desc: "native error wraps nil",
|
||||
wrapper: nat,
|
||||
wrapped: nil,
|
||||
contained: nil,
|
||||
contains: false,
|
||||
},
|
||||
{
|
||||
desc: "err0 wraps err1 wraps native error",
|
||||
wrapper: err0,
|
||||
wrapped: errors.Wrap(err1, nat),
|
||||
contained: nat,
|
||||
contains: true,
|
||||
},
|
||||
{
|
||||
desc: "native error wraps err1 wraps err0",
|
||||
wrapper: nat,
|
||||
wrapped: errors.Wrap(err1, err0),
|
||||
contained: err0,
|
||||
contains: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
err := errors.Wrap(c.wrapper, c.wrapped)
|
||||
contains := errors.Contains(err, c.contained)
|
||||
assert.Equal(t, c.contains, contains)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnwrap(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
err error
|
||||
wrapper error
|
||||
wrapped error
|
||||
}{
|
||||
{
|
||||
desc: "err 1 wraped err 2",
|
||||
err: errors.Wrap(err1, err2),
|
||||
wrapper: err1,
|
||||
wrapped: err2,
|
||||
},
|
||||
{
|
||||
desc: "err2 wraps err1 wraps err0",
|
||||
err: errors.Wrap(err2, errors.Wrap(err1, err0)),
|
||||
wrapper: err2,
|
||||
wrapped: errors.Wrap(err1, err0),
|
||||
},
|
||||
{
|
||||
desc: "nil wraps nil",
|
||||
err: errors.Wrap(nil, nil),
|
||||
wrapper: nil,
|
||||
wrapped: nil,
|
||||
},
|
||||
{
|
||||
desc: "err0 wraps nil",
|
||||
err: errors.Wrap(err0, nil),
|
||||
wrapper: nil,
|
||||
wrapped: err0,
|
||||
},
|
||||
{
|
||||
desc: "nil wraps err0",
|
||||
err: errors.Wrap(nil, err0),
|
||||
wrapper: nil,
|
||||
wrapped: nil,
|
||||
},
|
||||
{
|
||||
desc: "nil wraps native error",
|
||||
err: errors.Wrap(nil, nat),
|
||||
wrapper: nil,
|
||||
wrapped: nil,
|
||||
},
|
||||
{
|
||||
desc: "native error wraps nil",
|
||||
err: errors.Wrap(nat, nil),
|
||||
wrapper: nil,
|
||||
wrapped: nat,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
wrapper, wrapped := errors.Unwrap(c.err)
|
||||
assert.Equal(t, c.wrapper, wrapper)
|
||||
assert.Equal(t, c.wrapped, wrapped)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func wrap(level int) error {
|
||||
if level == 0 {
|
||||
return errors.New(strconv.Itoa(level))
|
||||
}
|
||||
return errors.Wrap(errors.New(strconv.Itoa(level)), wrap(level-1))
|
||||
}
|
||||
|
||||
// message generates error message of wrap() generated wrapper error.
|
||||
func message(level int) string {
|
||||
if level == 0 {
|
||||
return "0"
|
||||
}
|
||||
return strconv.Itoa(level) + " : " + message(level-1)
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package repository
|
||||
|
||||
import "github.com/absmach/magistrala/pkg/errors"
|
||||
|
||||
// Wrapper for Repository errors.
|
||||
var (
|
||||
// ErrMalformedEntity indicates a malformed entity specification.
|
||||
ErrMalformedEntity = errors.New("malformed entity specification")
|
||||
|
||||
// ErrNotFound indicates a non-existent entity request.
|
||||
ErrNotFound = errors.New("entity not found")
|
||||
|
||||
// ErrConflict indicates that entity already exists.
|
||||
ErrConflict = errors.New("entity already exists")
|
||||
|
||||
// ErrCreateEntity indicates error in creating entity or entities.
|
||||
ErrCreateEntity = errors.New("failed to create entity in the db")
|
||||
|
||||
// ErrViewEntity indicates error in viewing entity or entities.
|
||||
ErrViewEntity = errors.New("view entity failed")
|
||||
|
||||
// ErrUpdateEntity indicates error in updating entity or entities.
|
||||
ErrUpdateEntity = errors.New("update entity failed")
|
||||
|
||||
// ErrRemoveEntity indicates error in removing entity.
|
||||
ErrRemoveEntity = errors.New("failed to remove entity")
|
||||
|
||||
// ErrFailedOpDB indicates a failure in a database operation.
|
||||
ErrFailedOpDB = errors.New("operation on db element failed")
|
||||
|
||||
// ErrFailedToRetrieveAllGroups failed to retrieve groups.
|
||||
ErrFailedToRetrieveAllGroups = errors.New("failed to retrieve all groups")
|
||||
|
||||
// ErrMissingNames indicates missing first and last names.
|
||||
ErrMissingNames = errors.New("missing first or last name")
|
||||
)
|
||||
@@ -1,123 +0,0 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type errorRes struct {
|
||||
Err string `json:"error"`
|
||||
Msg string `json:"message"`
|
||||
}
|
||||
|
||||
// Failed to read response body.
|
||||
var errRespBody = New("failed to read response body")
|
||||
|
||||
// SDKError is an error type for Magistrala SDK.
|
||||
type SDKError interface {
|
||||
Error
|
||||
StatusCode() int
|
||||
}
|
||||
|
||||
var _ SDKError = (*sdkError)(nil)
|
||||
|
||||
type sdkError struct {
|
||||
*customError
|
||||
statusCode int
|
||||
}
|
||||
|
||||
func (ce *sdkError) Error() string {
|
||||
if ce == nil {
|
||||
return ""
|
||||
}
|
||||
if ce.customError == nil {
|
||||
return http.StatusText(ce.statusCode)
|
||||
}
|
||||
return fmt.Sprintf("Status: %s: %s", http.StatusText(ce.statusCode), ce.customError.Error())
|
||||
}
|
||||
|
||||
func (ce *sdkError) StatusCode() int {
|
||||
return ce.statusCode
|
||||
}
|
||||
|
||||
// NewSDKError returns an SDK Error that formats as the given text.
|
||||
func NewSDKError(err error) SDKError {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if e, ok := err.(Error); ok {
|
||||
return &sdkError{
|
||||
statusCode: 0,
|
||||
customError: &customError{
|
||||
msg: e.Msg(),
|
||||
err: cast(e.Err()),
|
||||
},
|
||||
}
|
||||
}
|
||||
return &sdkError{
|
||||
customError: &customError{
|
||||
msg: err.Error(),
|
||||
err: nil,
|
||||
},
|
||||
statusCode: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSDKErrorWithStatus returns an SDK Error setting the status code.
|
||||
func NewSDKErrorWithStatus(err error, statusCode int) SDKError {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if e, ok := err.(Error); ok {
|
||||
return &sdkError{
|
||||
statusCode: statusCode,
|
||||
customError: &customError{
|
||||
msg: e.Msg(),
|
||||
err: cast(e.Err()),
|
||||
},
|
||||
}
|
||||
}
|
||||
return &sdkError{
|
||||
statusCode: statusCode,
|
||||
customError: &customError{
|
||||
msg: err.Error(),
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CheckError will check the HTTP response status code and matches it with the given status codes.
|
||||
// Since multiple status codes can be valid, we can pass multiple status codes to the function.
|
||||
// The function then checks for errors in the HTTP response.
|
||||
func CheckError(resp *http.Response, expectedStatusCodes ...int) SDKError {
|
||||
if resp == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, expectedStatusCode := range expectedStatusCodes {
|
||||
if resp.StatusCode == expectedStatusCode {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return NewSDKErrorWithStatus(Wrap(errRespBody, err), resp.StatusCode)
|
||||
}
|
||||
var content errorRes
|
||||
if err := json.Unmarshal(body, &content); err != nil {
|
||||
return NewSDKErrorWithStatus(err, resp.StatusCode)
|
||||
}
|
||||
if content.Err == "" {
|
||||
return NewSDKErrorWithStatus(New(content.Msg), resp.StatusCode)
|
||||
}
|
||||
|
||||
return NewSDKErrorWithStatus(Wrap(New(content.Msg), New(content.Err)), resp.StatusCode)
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package errors_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var body = []byte(`{"error":"error","message":"message"}`)
|
||||
|
||||
func TestNewSDKError(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "nil error",
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "non nil error",
|
||||
err: err0,
|
||||
},
|
||||
{
|
||||
desc: "non nil error with wrapped error",
|
||||
err: errors.Wrap(err0, err1),
|
||||
},
|
||||
{
|
||||
desc: "native error",
|
||||
err: nat,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
sdk := errors.NewSDKError(c.err)
|
||||
if c.err != nil {
|
||||
assert.Equal(t, sdk.StatusCode(), 0)
|
||||
assert.Equal(t, sdk.Error(), fmt.Sprintf("Status: %s: %s", http.StatusText(0), c.err.Error()))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSDKErrorWithStatus(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
err error
|
||||
sc int
|
||||
}{
|
||||
{
|
||||
desc: "nil error with 0 status code",
|
||||
err: nil,
|
||||
sc: 0,
|
||||
},
|
||||
{
|
||||
desc: "nil error with 404 status code",
|
||||
err: nil,
|
||||
sc: 404,
|
||||
},
|
||||
{
|
||||
desc: "non nil error with 0 status code",
|
||||
err: err0,
|
||||
sc: 0,
|
||||
},
|
||||
{
|
||||
desc: "non nil error with 404 status code",
|
||||
err: err0,
|
||||
sc: 404,
|
||||
},
|
||||
{
|
||||
desc: "non nil error with wrapped error and 0 status code",
|
||||
err: errors.Wrap(err0, err1),
|
||||
sc: 0,
|
||||
},
|
||||
{
|
||||
desc: "non nil error with wrapped error and 404 status code",
|
||||
err: errors.Wrap(err0, err1),
|
||||
sc: 404,
|
||||
},
|
||||
{
|
||||
desc: "native error with 0 status code",
|
||||
err: nat,
|
||||
sc: 0,
|
||||
},
|
||||
{
|
||||
desc: "native error with 404 status code",
|
||||
err: nat,
|
||||
sc: 404,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
sdk := errors.NewSDKErrorWithStatus(c.err, c.sc)
|
||||
if c.err != nil {
|
||||
assert.Equal(t, sdk.StatusCode(), c.sc)
|
||||
assert.Equal(t, sdk.Error(), fmt.Sprintf("Status: %s: %s", http.StatusText(c.sc), c.err.Error()))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckError(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
resp *http.Response
|
||||
codes []int
|
||||
err errors.SDKError
|
||||
}{
|
||||
{
|
||||
desc: "nil response",
|
||||
resp: nil,
|
||||
codes: []int{http.StatusOK},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "nil response with 404 status code",
|
||||
resp: nil,
|
||||
codes: []int{http.StatusNotFound},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "valid response with 200 status code",
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewReader(body)),
|
||||
},
|
||||
codes: []int{http.StatusOK},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "valid response with 404 status code",
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusNotFound,
|
||||
Body: io.NopCloser(bytes.NewReader(body)),
|
||||
},
|
||||
codes: []int{http.StatusNotFound},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "invalid response with 200 status code",
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusNotFound,
|
||||
Body: io.NopCloser(bytes.NewReader(body)),
|
||||
},
|
||||
codes: []int{http.StatusOK},
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(errors.New("message"), errors.New("error")), http.StatusNotFound),
|
||||
},
|
||||
{
|
||||
desc: "invalid response with 404 status code",
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewReader(body)),
|
||||
},
|
||||
codes: []int{http.StatusNotFound},
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(errors.New("message"), errors.New("error")), http.StatusOK),
|
||||
},
|
||||
{
|
||||
desc: "valid response with 200 status code and 404 status code",
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewReader(body)),
|
||||
},
|
||||
codes: []int{http.StatusOK, http.StatusNotFound},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "error in JSON marshalling",
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusNotFound,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(`"error":`))),
|
||||
},
|
||||
codes: []int{http.StatusOK},
|
||||
err: errors.NewSDKErrorWithStatus(errors.New("invalid character ':' after top-level value"), http.StatusNotFound),
|
||||
},
|
||||
{
|
||||
desc: "empty error message",
|
||||
resp: &http.Response{
|
||||
StatusCode: http.StatusNotFound,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(`{"error":"","message":""}`))),
|
||||
},
|
||||
codes: []int{http.StatusOK},
|
||||
err: errors.NewSDKErrorWithStatus(errors.New(""), http.StatusNotFound),
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
sdk := errors.CheckError(c.resp, c.codes...)
|
||||
assert.Equal(t, sdk, c.err)
|
||||
if c.err != nil {
|
||||
assert.Equal(t, sdk, c.err)
|
||||
assert.Equal(t, sdk.StatusCode(), c.resp.StatusCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package service
|
||||
|
||||
import "github.com/absmach/magistrala/pkg/errors"
|
||||
|
||||
// Wrapper for Service errors.
|
||||
var (
|
||||
// ErrAuthentication indicates failure occurred while authenticating the entity.
|
||||
ErrAuthentication = errors.New("failed to perform authentication over the entity")
|
||||
|
||||
// ErrAuthorization indicates failure occurred while authorizing the entity.
|
||||
ErrAuthorization = errors.New("failed to perform authorization over the entity")
|
||||
|
||||
// ErrDomainAuthorization indicates failure occurred while authorizing the domain.
|
||||
ErrDomainAuthorization = errors.New("failed to perform authorization over the domain")
|
||||
|
||||
// ErrLogin indicates wrong login credentials.
|
||||
ErrLogin = errors.New("invalid user id or secret")
|
||||
|
||||
// ErrMalformedEntity indicates a malformed entity specification.
|
||||
ErrMalformedEntity = errors.New("malformed entity specification")
|
||||
|
||||
// ErrNotFound indicates a non-existent entity request.
|
||||
ErrNotFound = errors.New("entity not found")
|
||||
|
||||
// ErrConflict indicates that entity already exists.
|
||||
ErrConflict = errors.New("entity already exists")
|
||||
|
||||
// ErrCreateEntity indicates error in creating entity or entities.
|
||||
ErrCreateEntity = errors.New("failed to create entity")
|
||||
|
||||
// ErrRemoveEntity indicates error in removing entity.
|
||||
ErrRemoveEntity = errors.New("failed to remove entity")
|
||||
|
||||
// ErrViewEntity indicates error in viewing entity or entities.
|
||||
ErrViewEntity = errors.New("view entity failed")
|
||||
|
||||
// ErrUpdateEntity indicates error in updating entity or entities.
|
||||
ErrUpdateEntity = errors.New("update entity failed")
|
||||
|
||||
// ErrInvalidStatus indicates an invalid status.
|
||||
ErrInvalidStatus = errors.New("invalid status")
|
||||
|
||||
// ErrInvalidRole indicates that an invalid role.
|
||||
ErrInvalidRole = errors.New("invalid client role")
|
||||
|
||||
// ErrInvalidPolicy indicates that an invalid policy.
|
||||
ErrInvalidPolicy = errors.New("invalid policy")
|
||||
|
||||
// ErrEnableClient indicates error in enabling client.
|
||||
ErrEnableClient = errors.New("failed to enable client")
|
||||
|
||||
// ErrDisableClient indicates error in disabling client.
|
||||
ErrDisableClient = errors.New("failed to disable client")
|
||||
|
||||
// ErrAddPolicies indicates error in adding policies.
|
||||
ErrAddPolicies = errors.New("failed to add policies")
|
||||
|
||||
// ErrDeletePolicies indicates error in removing policies.
|
||||
ErrDeletePolicies = errors.New("failed to remove policies")
|
||||
|
||||
// ErrSearch indicates error in searching clients.
|
||||
ErrSearch = errors.New("failed to search clients")
|
||||
|
||||
// ErrInvitationAlreadyRejected indicates that the invitation is already rejected.
|
||||
ErrInvitationAlreadyRejected = errors.New("invitation already rejected")
|
||||
|
||||
// ErrInvitationAlreadyAccepted indicates that the invitation is already accepted.
|
||||
ErrInvitationAlreadyAccepted = errors.New("invitation already accepted")
|
||||
|
||||
// ErrParentGroupAuthorization indicates failure occurred while authorizing the parent group.
|
||||
ErrParentGroupAuthorization = errors.New("failed to authorize parent group")
|
||||
|
||||
// ErrMissingUsername indicates that the user's names are missing.
|
||||
ErrMissingUsername = errors.New("missing usernames")
|
||||
)
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package errors
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrMalformedEntity indicates a malformed entity specification.
|
||||
ErrMalformedEntity = New("malformed entity specification")
|
||||
|
||||
// ErrUnsupportedContentType indicates invalid content type.
|
||||
ErrUnsupportedContentType = errors.New("invalid content type")
|
||||
|
||||
// ErrUnidentified indicates unidentified error.
|
||||
ErrUnidentified = errors.New("unidentified error")
|
||||
|
||||
// ErrEmptyPath indicates empty file path.
|
||||
ErrEmptyPath = errors.New("empty file path")
|
||||
|
||||
// ErrStatusAlreadyAssigned indicated that the client or group has already been assigned the status.
|
||||
ErrStatusAlreadyAssigned = errors.New("status already assigned")
|
||||
|
||||
// ErrRollbackTx indicates failed to rollback transaction.
|
||||
ErrRollbackTx = errors.New("failed to rollback transaction")
|
||||
|
||||
// ErrAuthentication indicates failure occurred while authenticating the entity.
|
||||
ErrAuthentication = errors.New("failed to perform authentication over the entity")
|
||||
|
||||
// ErrAuthorization indicates failure occurred while authorizing the entity.
|
||||
ErrAuthorization = errors.New("failed to perform authorization over the entity")
|
||||
)
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidRecurringType = errors.New("invalid recurring type")
|
||||
ErrStartDateTimeInPast = errors.New("start_datetime must be greater than or equal to current time")
|
||||
ErrInvalidRecurringType = errors.NewRequestError("invalid recurring type")
|
||||
ErrStartDateTimeInPast = errors.NewRequestError("start_datetime must be greater than or equal to current time")
|
||||
)
|
||||
|
||||
// Type can be daily, weekly or monthly.
|
||||
|
||||
@@ -224,7 +224,7 @@ func TestAddBootstrap(t *testing.T) {
|
||||
svcReq: bootstrapConfig,
|
||||
svcRes: bootstrap.Config{},
|
||||
svcErr: svcerr.ErrConflict,
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrConflict, http.StatusConflict),
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrConflict, http.StatusBadRequest),
|
||||
},
|
||||
{
|
||||
desc: "add empty config",
|
||||
@@ -234,7 +234,7 @@ func TestAddBootstrap(t *testing.T) {
|
||||
svcReq: bootstrap.Config{},
|
||||
svcRes: bootstrap.Config{},
|
||||
svcErr: nil,
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest),
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingID, http.StatusBadRequest),
|
||||
},
|
||||
{
|
||||
desc: "add with non-existent client Id",
|
||||
@@ -487,7 +487,7 @@ func TestWhiteList(t *testing.T) {
|
||||
state: -1,
|
||||
svcReq: bootstrap.Active,
|
||||
svcErr: nil,
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, bootstrap.ErrBootstrapState), http.StatusBadRequest),
|
||||
err: errors.NewSDKErrorWithStatus(bootstrap.ErrBootstrapState, http.StatusBadRequest),
|
||||
},
|
||||
{
|
||||
desc: "whitelist with empty client Id",
|
||||
@@ -586,9 +586,9 @@ func TestViewBootstrap(t *testing.T) {
|
||||
token: validToken,
|
||||
id: invalid,
|
||||
svcResp: bootstrap.Config{},
|
||||
svcErr: svcerr.ErrViewEntity,
|
||||
svcErr: svcerr.ErrNotFound,
|
||||
response: sdk.BootstrapConfig{},
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrViewEntity, http.StatusBadRequest),
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound),
|
||||
},
|
||||
{
|
||||
desc: "view with response that cannot be unmarshalled",
|
||||
@@ -1203,7 +1203,7 @@ func TestBoostrap(t *testing.T) {
|
||||
svcErr: nil,
|
||||
readerResp: bootstrap.Config{},
|
||||
readerErr: nil,
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerKey), http.StatusBadRequest),
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerKey, http.StatusUnauthorized),
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
|
||||
@@ -101,7 +101,7 @@ func TestCreateSubscription(t *testing.T) {
|
||||
svcReq: notifiers.Subscription{},
|
||||
svcRes: "",
|
||||
svcErr: nil,
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized),
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
|
||||
},
|
||||
{
|
||||
desc: "create new subscription with invalid token",
|
||||
@@ -124,7 +124,7 @@ func TestCreateSubscription(t *testing.T) {
|
||||
svcReq: notifiers.Subscription{},
|
||||
svcErr: nil,
|
||||
svcRes: "",
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrInvalidTopic), http.StatusBadRequest),
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrInvalidTopic, http.StatusBadRequest),
|
||||
},
|
||||
{
|
||||
desc: "create new subscription with empty contact",
|
||||
@@ -137,7 +137,7 @@ func TestCreateSubscription(t *testing.T) {
|
||||
svcReq: notifiers.Subscription{},
|
||||
svcErr: nil,
|
||||
svcRes: "",
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrInvalidContact), http.StatusBadRequest),
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrInvalidContact, http.StatusBadRequest),
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
@@ -209,7 +209,7 @@ func TestViewSubscription(t *testing.T) {
|
||||
svcRes: notifiers.Subscription{},
|
||||
svcErr: nil,
|
||||
response: sdk.Subscription{},
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized),
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
@@ -368,7 +368,7 @@ func TestListSubscription(t *testing.T) {
|
||||
svcRes: notifiers.Page{},
|
||||
svcErr: nil,
|
||||
response: sdk.SubscriptionPage{},
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized),
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -431,14 +431,14 @@ func TestDeleteSubscription(t *testing.T) {
|
||||
subID: subID,
|
||||
token: "",
|
||||
svcErr: nil,
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized),
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
|
||||
},
|
||||
{
|
||||
desc: "delete subscription with empty subID",
|
||||
subID: "",
|
||||
token: validToken,
|
||||
svcErr: nil,
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest),
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingID, http.StatusBadRequest),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ func TestReadMessages(t *testing.T) {
|
||||
authzErr: svcerr.ErrAuthorization,
|
||||
repoRes: readers.MessagesPage{},
|
||||
response: sdk.MessagesPage{},
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrAuthorization, svcerr.ErrAuthorization), http.StatusUnauthorized),
|
||||
err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden),
|
||||
},
|
||||
{
|
||||
desc: "read messages with empty token",
|
||||
@@ -161,7 +161,7 @@ func TestReadMessages(t *testing.T) {
|
||||
authnErr: svcerr.ErrAuthentication,
|
||||
repoRes: readers.MessagesPage{},
|
||||
response: sdk.MessagesPage{},
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerToken), http.StatusUnauthorized),
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerToken, http.StatusUnauthorized),
|
||||
},
|
||||
{
|
||||
desc: "read messages with empty channel ID",
|
||||
@@ -179,7 +179,7 @@ func TestReadMessages(t *testing.T) {
|
||||
repoRes: readers.MessagesPage{},
|
||||
repoErr: nil,
|
||||
response: sdk.MessagesPage{},
|
||||
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest),
|
||||
err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingID, http.StatusBadRequest),
|
||||
},
|
||||
{
|
||||
desc: "read messages with invalid message page metadata",
|
||||
|
||||
@@ -97,7 +97,7 @@ func TestProvision(t *testing.T) {
|
||||
token: validToken,
|
||||
domainID: validID,
|
||||
data: fmt.Sprintf(`{"name": "test", "external_id": "%s"}`, validID),
|
||||
status: http.StatusBadRequest,
|
||||
status: http.StatusUnauthorized,
|
||||
contentType: validContenType,
|
||||
svcErr: nil,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
package api
|
||||
|
||||
import apiutil "github.com/absmach/supermq/api/http/util"
|
||||
import (
|
||||
apiutil "github.com/absmach/supermq/api/http/util"
|
||||
)
|
||||
|
||||
type provisionReq struct {
|
||||
token string
|
||||
|
||||
@@ -55,7 +55,7 @@ func MakeHandler(svc provision.Service, logger *slog.Logger, instanceID string)
|
||||
|
||||
func decodeProvisionRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if r.Header.Get("Content-Type") != contentType {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := provisionReq{
|
||||
@@ -63,7 +63,7 @@ func decodeProvisionRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
domainID: chi.URLParam(r, "domainID"),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
|
||||
return nil, errors.Wrap(apiutil.ErrMalformedRequestBody, err)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
@@ -71,7 +71,7 @@ func decodeProvisionRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
|
||||
func decodeMappingRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if r.Header.Get("Content-Type") != contentType {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := mappingReq{
|
||||
|
||||
@@ -798,7 +798,7 @@ func TestUpdateRuleTagsEndpoint(t *testing.T) {
|
||||
contentType: contentType,
|
||||
data: fmt.Sprintf(`{"tags":["%s"}`, newTag),
|
||||
status: http.StatusBadRequest,
|
||||
err: errors.ErrMalformedEntity,
|
||||
err: apiutil.ErrMalformedRequestBody,
|
||||
},
|
||||
{
|
||||
desc: "update rule with empty id",
|
||||
|
||||
+8
-8
@@ -112,11 +112,11 @@ func MakeHandler(svc re.Service, authn smqauthn.AuthNMiddleware, mux *chi.Mux, l
|
||||
|
||||
func decodeAddRuleRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
var rule re.Rule
|
||||
if err := json.NewDecoder(r.Body).Decode(&rule); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
|
||||
return nil, errors.Wrap(apiutil.ErrMalformedRequestBody, err)
|
||||
}
|
||||
return addRuleReq{Rule: rule}, nil
|
||||
}
|
||||
@@ -128,11 +128,11 @@ func decodeViewRuleRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
|
||||
func decodeUpdateRuleRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
var rule re.Rule
|
||||
if err := json.NewDecoder(r.Body).Decode(&rule); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
|
||||
return nil, errors.Wrap(apiutil.ErrMalformedRequestBody, err)
|
||||
}
|
||||
rule.ID = chi.URLParam(r, ruleIdKey)
|
||||
|
||||
@@ -141,14 +141,14 @@ func decodeUpdateRuleRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
|
||||
func decodeUpdateRuleTags(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := updateRuleTagsReq{
|
||||
id: chi.URLParam(r, ruleIdKey),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
|
||||
return nil, errors.Wrap(apiutil.ErrMalformedRequestBody, err)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
@@ -156,14 +156,14 @@ func decodeUpdateRuleTags(_ context.Context, r *http.Request) (any, error) {
|
||||
|
||||
func decodeUpdateRuleScheduleRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := updateRuleScheduleReq{
|
||||
id: chi.URLParam(r, ruleIdKey),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
|
||||
return nil, errors.Wrap(apiutil.ErrMalformedRequestBody, err)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
grpcReadersV1 "github.com/absmach/magistrala/api/grpc/readers/v1"
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
svcerr "github.com/absmach/supermq/pkg/errors/service"
|
||||
"github.com/absmach/supermq/pkg/transformers/senml"
|
||||
readers "github.com/absmach/supermq/readers"
|
||||
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
"time"
|
||||
|
||||
grpcReadersV1 "github.com/absmach/magistrala/api/grpc/readers/v1"
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
grpcapi "github.com/absmach/magistrala/readers/api/grpc"
|
||||
apiutil "github.com/absmach/supermq/api/http/util"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
"github.com/absmach/supermq/pkg/transformers/senml"
|
||||
"github.com/absmach/supermq/readers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -235,7 +235,7 @@ func TestReadAll(t *testing.T) {
|
||||
token: "",
|
||||
authResponse: false,
|
||||
authnErr: svcerr.ErrAuthentication,
|
||||
status: http.StatusBadRequest,
|
||||
status: http.StatusUnauthorized,
|
||||
err: svcerr.ErrAuthentication,
|
||||
},
|
||||
{
|
||||
@@ -645,7 +645,7 @@ func TestReadAll(t *testing.T) {
|
||||
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
|
||||
token: "",
|
||||
authResponse: false,
|
||||
status: http.StatusBadRequest,
|
||||
status: http.StatusUnauthorized,
|
||||
err: svcerr.ErrAuthorization,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -51,7 +51,7 @@ const (
|
||||
// MakeHandler returns a HTTP handler for API endpoints.
|
||||
func MakeHandler(svc readers.MessageRepository, authn smqauthn.Authentication, clients grpcClientsV1.ClientsServiceClient, channels grpcChannelsV1.ChannelsServiceClient, svcName, instanceID string) http.Handler {
|
||||
opts := []kithttp.ServerOption{
|
||||
kithttp.ServerErrorEncoder(encodeError),
|
||||
kithttp.ServerErrorEncoder(api.EncodeError),
|
||||
}
|
||||
|
||||
mux := chi.NewRouter()
|
||||
@@ -209,43 +209,6 @@ func encodeResponse(_ context.Context, w http.ResponseWriter, response any) erro
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
|
||||
switch {
|
||||
case errors.Contains(err, nil):
|
||||
w.WriteHeader(http.StatusOK)
|
||||
case errors.Contains(err, apiutil.ErrInvalidQueryParams),
|
||||
errors.Contains(err, svcerr.ErrMalformedEntity),
|
||||
errors.Contains(err, apiutil.ErrValidation),
|
||||
errors.Contains(err, apiutil.ErrMissingID),
|
||||
errors.Contains(err, apiutil.ErrLimitSize),
|
||||
errors.Contains(err, apiutil.ErrOffsetSize),
|
||||
errors.Contains(err, apiutil.ErrInvalidComparator),
|
||||
errors.Contains(err, apiutil.ErrInvalidAggregation),
|
||||
errors.Contains(err, apiutil.ErrInvalidInterval),
|
||||
errors.Contains(err, apiutil.ErrMissingFrom),
|
||||
errors.Contains(err, apiutil.ErrMissingTo),
|
||||
errors.Contains(err, apiutil.ErrMissingDomainID):
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
case errors.Contains(err, svcerr.ErrAuthentication),
|
||||
errors.Contains(err, apiutil.ErrBearerToken):
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
case errors.Contains(err, svcerr.ErrAuthorization):
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
case errors.Contains(err, readers.ErrReadMessages):
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
default:
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if errorVal, ok := err.(errors.Error); ok {
|
||||
if err := json.NewEncoder(w).Encode(errorVal); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func authnAuthz(ctx context.Context, req listMessagesReq, authn smqauthn.Authentication, clients grpcClientsV1.ClientsServiceClient, channels grpcChannelsV1.ChannelsServiceClient) error {
|
||||
clientID, clientType, err := authenticate(ctx, req, authn, clients)
|
||||
if err != nil {
|
||||
|
||||
@@ -59,6 +59,9 @@ func (tr postgresRepository) ReadAll(chanID string, rpm readers.PageMetadata) (r
|
||||
}
|
||||
rows, err := tr.db.NamedQuery(q, params)
|
||||
if err != nil {
|
||||
if preErr, ok := err.(*pgconn.PrepareError); ok {
|
||||
err = preErr.Unwrap()
|
||||
}
|
||||
if pgErr, ok := err.(*pgconn.PgError); ok {
|
||||
if pgErr.Code == pgerrcode.UndefinedTable {
|
||||
return readers.MessagesPage{}, nil
|
||||
|
||||
@@ -120,6 +120,9 @@ func (tr timescaleRepository) ReadAll(chanID string, rpm readers.PageMetadata) (
|
||||
|
||||
rows, err := tr.db.NamedQuery(q, params)
|
||||
if err != nil {
|
||||
if preErr, ok := err.(*pgconn.PrepareError); ok {
|
||||
err = preErr.Unwrap()
|
||||
}
|
||||
if pgErr, ok := err.(*pgconn.PgError); ok {
|
||||
if pgErr.Code == pgerrcode.UndefinedTable {
|
||||
return readers.MessagesPage{}, nil
|
||||
|
||||
@@ -1125,7 +1125,7 @@ func TestViewReportTemplateEndpoint(t *testing.T) {
|
||||
id: validID,
|
||||
contentType: contentType,
|
||||
svcErr: svcerr.ErrViewEntity,
|
||||
status: http.StatusBadRequest,
|
||||
status: http.StatusUnprocessableEntity,
|
||||
err: svcerr.ErrViewEntity,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ func MakeHandler(svc reports.Service, authn smqauthn.AuthNMiddleware, mux *chi.M
|
||||
|
||||
func decodeGenerateReportRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
a, err := apiutil.ReadStringQuery(r, actionKey, defAction)
|
||||
@@ -159,7 +159,7 @@ func decodeGenerateReportRequest(_ context.Context, r *http.Request) (any, error
|
||||
|
||||
func decodeAddReportConfigRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
var config reports.ReportConfig
|
||||
if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
|
||||
@@ -175,7 +175,7 @@ func decodeViewReportConfigRequest(_ context.Context, r *http.Request) (any, err
|
||||
|
||||
func decodeUpdateReportConfigRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
var config reports.ReportConfig
|
||||
if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
|
||||
@@ -187,14 +187,14 @@ func decodeUpdateReportConfigRequest(_ context.Context, r *http.Request) (any, e
|
||||
|
||||
func decodeUpdateReportScheduleRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := updateReportScheduleReq{
|
||||
id: chi.URLParam(r, reportIdKey),
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
|
||||
return nil, errors.Wrap(apiutil.ErrMalformedRequestBody, err)
|
||||
}
|
||||
|
||||
return req, nil
|
||||
@@ -214,7 +214,7 @@ func decodeDeleteReportConfigRequest(_ context.Context, r *http.Request) (any, e
|
||||
|
||||
func decodeUpdateReportTemplateRequest(_ context.Context, r *http.Request) (any, error) {
|
||||
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
|
||||
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
|
||||
return nil, apiutil.ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
req := updateReportTemplateReq{}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
repoerr "github.com/absmach/supermq/pkg/errors/repository"
|
||||
)
|
||||
|
||||
var _ errors.Mapper = (*duplicateErrors)(nil)
|
||||
|
||||
type duplicateErrors struct{}
|
||||
|
||||
// GetError maps constraint names to known errors.
|
||||
func (d duplicateErrors) GetError(constraint string) (error, bool) {
|
||||
switch constraint {
|
||||
case "report_config_pkey":
|
||||
return repoerr.ErrConflict, true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
func NewDuplicateErrors() errors.Mapper {
|
||||
return duplicateErrors{}
|
||||
}
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/magistrala/pkg/schedule"
|
||||
"github.com/absmach/magistrala/reports"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
)
|
||||
|
||||
// dbReport represents the database structure for a Report.
|
||||
|
||||
@@ -10,19 +10,26 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
"github.com/absmach/magistrala/reports"
|
||||
api "github.com/absmach/supermq/api/http"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
repoerr "github.com/absmach/supermq/pkg/errors/repository"
|
||||
"github.com/absmach/supermq/pkg/postgres"
|
||||
)
|
||||
|
||||
type PostgresRepository struct {
|
||||
DB postgres.Database
|
||||
eh errors.Handler
|
||||
}
|
||||
|
||||
func NewRepository(db postgres.Database) reports.Repository {
|
||||
return &PostgresRepository{DB: db}
|
||||
errHandlerOptions := []errors.HandlerOption{
|
||||
postgres.WithDuplicateErrors(NewDuplicateErrors()),
|
||||
}
|
||||
return &PostgresRepository{
|
||||
DB: db,
|
||||
eh: postgres.NewErrorHandler(errHandlerOptions...),
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *PostgresRepository) AddReportConfig(ctx context.Context, cfg reports.ReportConfig) (reports.ReportConfig, error) {
|
||||
@@ -36,24 +43,24 @@ func (repo *PostgresRepository) AddReportConfig(ctx context.Context, cfg reports
|
||||
`
|
||||
dbr, err := reportToDb(cfg)
|
||||
if err != nil {
|
||||
return reports.ReportConfig{}, err
|
||||
return reports.ReportConfig{}, repo.eh.HandleError(repoerr.ErrCreateEntity, err)
|
||||
}
|
||||
row, err := repo.DB.NamedQueryContext(ctx, q, dbr)
|
||||
if err != nil {
|
||||
return reports.ReportConfig{}, err
|
||||
return reports.ReportConfig{}, repo.eh.HandleError(repoerr.ErrCreateEntity, err)
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
var dbReport dbReport
|
||||
if row.Next() {
|
||||
if err := row.StructScan(&dbReport); err != nil {
|
||||
return reports.ReportConfig{}, err
|
||||
return reports.ReportConfig{}, repo.eh.HandleError(repoerr.ErrCreateEntity, err)
|
||||
}
|
||||
}
|
||||
|
||||
report, err := dbToReport(dbReport)
|
||||
if err != nil {
|
||||
return reports.ReportConfig{}, err
|
||||
return reports.ReportConfig{}, repo.eh.HandleError(repoerr.ErrCreateEntity, err)
|
||||
}
|
||||
|
||||
return report, nil
|
||||
@@ -72,6 +79,9 @@ func (repo *PostgresRepository) ViewReportConfig(ctx context.Context, id string)
|
||||
}
|
||||
var dbr dbReport
|
||||
if err := row.StructScan(&dbr); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return reports.ReportConfig{}, repoerr.ErrNotFound
|
||||
}
|
||||
return reports.ReportConfig{}, err
|
||||
}
|
||||
rpt, err := dbToReport(dbr)
|
||||
@@ -163,10 +173,14 @@ func (repo *PostgresRepository) UpdateReportConfig(ctx context.Context, cfg repo
|
||||
defer row.Close()
|
||||
|
||||
var dbReport dbReport
|
||||
if row.Next() {
|
||||
if err := row.StructScan(&dbReport); err != nil {
|
||||
if !row.Next() {
|
||||
if err := row.Err(); err != nil {
|
||||
return reports.ReportConfig{}, err
|
||||
}
|
||||
return reports.ReportConfig{}, repoerr.ErrNotFound
|
||||
}
|
||||
if err := row.StructScan(&dbReport); err != nil {
|
||||
return reports.ReportConfig{}, err
|
||||
}
|
||||
rpt, err := dbToReport(dbReport)
|
||||
if err != nil {
|
||||
@@ -196,11 +210,15 @@ func (repo *PostgresRepository) UpdateReportSchedule(ctx context.Context, cfg re
|
||||
defer row.Close()
|
||||
|
||||
var dbReport dbReport
|
||||
if row.Next() {
|
||||
if !row.Next() {
|
||||
if err := row.Err(); err != nil {
|
||||
return reports.ReportConfig{}, postgres.HandleError(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
return reports.ReportConfig{}, repoerr.ErrNotFound
|
||||
}
|
||||
if err := row.StructScan(&dbReport); err != nil {
|
||||
return reports.ReportConfig{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
}
|
||||
report, err := dbToReport(dbReport)
|
||||
if err != nil {
|
||||
return reports.ReportConfig{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
@@ -320,11 +338,15 @@ func (repo *PostgresRepository) UpdateReportDue(ctx context.Context, id string,
|
||||
defer row.Close()
|
||||
|
||||
var dbReport dbReport
|
||||
if row.Next() {
|
||||
if !row.Next() {
|
||||
if err := row.Err(); err != nil {
|
||||
return reports.ReportConfig{}, postgres.HandleError(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
return reports.ReportConfig{}, repoerr.ErrNotFound
|
||||
}
|
||||
if err := row.StructScan(&dbReport); err != nil {
|
||||
return reports.ReportConfig{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
}
|
||||
}
|
||||
report, err := dbToReport(dbReport)
|
||||
if err != nil {
|
||||
return reports.ReportConfig{}, errors.Wrap(repoerr.ErrUpdateEntity, err)
|
||||
|
||||
@@ -292,6 +292,7 @@ func TestUpdateReportConfigStatus(t *testing.T) {
|
||||
CreatedBy: generateUUID(t),
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
UpdatedBy: generateUUID(t),
|
||||
Metrics: []reports.ReqMetric{},
|
||||
}
|
||||
|
||||
saved, err := repo.AddReportConfig(context.Background(), reportConfig)
|
||||
@@ -361,6 +362,7 @@ func TestRemoveReportConfig(t *testing.T) {
|
||||
Status: reports.EnabledStatus,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
Metrics: []reports.ReqMetric{},
|
||||
}
|
||||
|
||||
saved, err := repo.AddReportConfig(context.Background(), reportConfig)
|
||||
@@ -414,6 +416,7 @@ func TestListReportsConfig(t *testing.T) {
|
||||
Status: reports.EnabledStatus,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
Metrics: []reports.ReqMetric{},
|
||||
}
|
||||
_, err := repo.AddReportConfig(context.Background(), reportConfig)
|
||||
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
||||
@@ -589,7 +592,7 @@ func TestUpdateReportDue(t *testing.T) {
|
||||
saved, err := repo.AddReportConfig(context.Background(), reportConfig)
|
||||
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err))
|
||||
|
||||
newDue := time.Now().UTC().Add(24 * time.Hour)
|
||||
newDue := time.Now().UTC().Add(24 * time.Hour).Truncate(time.Microsecond)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
|
||||
Reference in New Issue
Block a user