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:
Arvindh
2025-12-31 21:27:06 +05:30
committed by GitHub
parent c9b3107ad9
commit 67180a55f7
50 changed files with 188 additions and 1133 deletions
-8
View File
@@ -100,9 +100,6 @@ jobs:
internal: internal:
- "internal/**" - "internal/**"
pkg-errors:
- "pkg/errors/**"
pkg-events: pkg-events:
- "pkg/events/**" - "pkg/events/**"
- "pkg/messaging/**" - "pkg/messaging/**"
@@ -161,11 +158,6 @@ jobs:
run: | run: |
go test --race -v -count=1 -coverprofile=coverage/internal.out ./internal/... 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 - name: Run pkg sdk tests
if: steps.changes.outputs.pkg-sdk == 'true' || steps.changes.outputs.workflow == 'true' if: steps.changes.outputs.pkg-sdk == 'true' || steps.changes.outputs.workflow == 'true'
run: | run: |
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"github.com/absmach/magistrala/alarms" "github.com/absmach/magistrala/alarms"
"github.com/absmach/magistrala/internal/testsutil" "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/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
+2 -2
View File
@@ -195,12 +195,12 @@ func decodeAlarmReq(_ context.Context, r *http.Request) (any, error) {
func decodeUpdateAlarmReq(_ 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) { if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
return alarmReq{}, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) return alarmReq{}, apiutil.ErrUnsupportedContentType
} }
req := alarmReq{} req := alarmReq{}
if err := json.NewDecoder(r.Body).Decode(&req.Alarm); err != nil { 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") req.Alarm.ID = chi.URLParam(r, "alarmID")
+1 -1
View File
@@ -12,8 +12,8 @@ import (
"github.com/absmach/magistrala/alarms" "github.com/absmach/magistrala/alarms"
"github.com/absmach/magistrala/alarms/mocks" "github.com/absmach/magistrala/alarms/mocks"
"github.com/absmach/magistrala/pkg/errors"
"github.com/absmach/supermq/pkg/authn" "github.com/absmach/supermq/pkg/authn"
"github.com/absmach/supermq/pkg/errors"
repoerr "github.com/absmach/supermq/pkg/errors/repository" repoerr "github.com/absmach/supermq/pkg/errors/repository"
"github.com/absmach/supermq/pkg/uuid" "github.com/absmach/supermq/pkg/uuid"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
+14 -14
View File
@@ -89,11 +89,11 @@ var (
CACert: "newca", CACert: "newca",
} }
missingIDRes = toJSON(apiutil.ErrorRes{Err: apiutil.ErrMissingID.Error(), Msg: apiutil.ErrValidation.Error()}) missingIDRes = toJSON(apiutil.ErrMissingID)
missingKeyRes = toJSON(apiutil.ErrorRes{Err: apiutil.ErrBearerKey.Error(), Msg: apiutil.ErrValidation.Error()}) missingKeyRes = toJSON(apiutil.ErrBearerKey)
bsErrorRes = toJSON(apiutil.ErrorRes{Msg: bootstrap.ErrBootstrap.Error()}) unknownExternalIDErrorRes = toJSON(svcerr.ErrNotFound)
extKeyRes = toJSON(apiutil.ErrorRes{Msg: bootstrap.ErrExternalKey.Error()}) extKeyRes = toJSON(bootstrap.ErrExternalKey)
extSecKeyRes = toJSON(apiutil.ErrorRes{Msg: bootstrap.ErrExternalKeySecure.Error()}) extSecKeyRes = toJSON(bootstrap.ErrExternalKeySecure)
) )
type testRequest struct { type testRequest struct {
@@ -257,7 +257,7 @@ func TestAdd(t *testing.T) {
domainID: domainID, domainID: domainID,
token: validToken, token: validToken,
contentType: contentType, contentType: contentType,
status: http.StatusConflict, status: http.StatusBadRequest,
location: "", location: "",
err: svcerr.ErrConflict, err: svcerr.ErrConflict,
}, },
@@ -267,7 +267,7 @@ func TestAdd(t *testing.T) {
domainID: domainID, domainID: domainID,
token: validToken, token: validToken,
contentType: contentType, contentType: contentType,
status: http.StatusConflict, status: http.StatusBadRequest,
location: "", location: "",
err: svcerr.ErrConflict, err: svcerr.ErrConflict,
}, },
@@ -277,7 +277,7 @@ func TestAdd(t *testing.T) {
domainID: domainID, domainID: domainID,
token: validToken, token: validToken,
contentType: contentType, contentType: contentType,
status: http.StatusConflict, status: http.StatusBadRequest,
location: "", location: "",
err: svcerr.ErrConflict, err: svcerr.ErrConflict,
}, },
@@ -1190,9 +1190,9 @@ func TestBootstrap(t *testing.T) {
externalID: unknown, externalID: unknown,
externalKey: c.ExternalKey, externalKey: c.ExternalKey,
status: http.StatusNotFound, status: http.StatusNotFound,
res: bsErrorRes, res: unknownExternalIDErrorRes,
secure: false, secure: false,
err: bootstrap.ErrBootstrap, err: svcerr.ErrNotFound,
}, },
{ {
desc: "bootstrap a Client with an empty ID", desc: "bootstrap a Client with an empty ID",
@@ -1201,7 +1201,7 @@ func TestBootstrap(t *testing.T) {
status: http.StatusBadRequest, status: http.StatusBadRequest,
res: missingIDRes, res: missingIDRes,
secure: false, secure: false,
err: errors.Wrap(bootstrap.ErrBootstrap, svcerr.ErrMalformedEntity), err: apiutil.ErrMissingID,
}, },
{ {
desc: "bootstrap a Client with unknown key", desc: "bootstrap a Client with unknown key",
@@ -1210,16 +1210,16 @@ func TestBootstrap(t *testing.T) {
status: http.StatusForbidden, status: http.StatusForbidden,
res: extKeyRes, res: extKeyRes,
secure: false, secure: false,
err: errors.Wrap(bootstrap.ErrExternalKey, errors.New("")), err: bootstrap.ErrExternalKey,
}, },
{ {
desc: "bootstrap a Client with an empty key", desc: "bootstrap a Client with an empty key",
externalID: c.ExternalID, externalID: c.ExternalID,
externalKey: "", externalKey: "",
status: http.StatusBadRequest, status: http.StatusUnauthorized,
res: missingKeyRes, res: missingKeyRes,
secure: false, secure: false,
err: errors.Wrap(bootstrap.ErrBootstrap, svcerr.ErrAuthentication), err: apiutil.ErrBearerKey,
}, },
{ {
desc: "bootstrap known Client", desc: "bootstrap known Client",
+11 -12
View File
@@ -11,7 +11,6 @@ import (
"net/url" "net/url"
"strings" "strings"
mgapi "github.com/absmach/magistrala/api"
"github.com/absmach/magistrala/bootstrap" "github.com/absmach/magistrala/bootstrap"
"github.com/absmach/supermq" "github.com/absmach/supermq"
api "github.com/absmach/supermq/api/http" api "github.com/absmach/supermq/api/http"
@@ -43,7 +42,7 @@ var (
// MakeHandler returns a HTTP handler for API endpoints. // 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 { func MakeHandler(svc bootstrap.Service, authn smqauthn.AuthNMiddleware, reader bootstrap.ConfigReader, logger *slog.Logger, instanceID string) http.Handler {
opts := []kithttp.ServerOption{ opts := []kithttp.ServerOption{
kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, mgapi.EncodeError)), kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)),
} }
r := chi.NewRouter() 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) { func decodeAddRequest(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), contentType) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) return nil, apiutil.ErrUnsupportedContentType
} }
req := addReq{ req := addReq{
token: apiutil.ExtractBearerToken(r), token: apiutil.ExtractBearerToken(r),
} }
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 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 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) { func decodeUpdateRequest(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), contentType) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) return nil, apiutil.ErrUnsupportedContentType
} }
req := updateReq{ req := updateReq{
id: chi.URLParam(r, "configID"), id: chi.URLParam(r, "configID"),
} }
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 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 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) { func decodeUpdateCertRequest(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), contentType) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) return nil, apiutil.ErrUnsupportedContentType
} }
req := updateCertReq{ req := updateCertReq{
clientID: chi.URLParam(r, "certID"), clientID: chi.URLParam(r, "certID"),
} }
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 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 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) { func decodeUpdateConnRequest(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), contentType) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) return nil, apiutil.ErrUnsupportedContentType
} }
req := updateConnReq{ req := updateConnReq{
@@ -182,7 +181,7 @@ func decodeUpdateConnRequest(_ context.Context, r *http.Request) (any, error) {
id: chi.URLParam(r, "connID"), id: chi.URLParam(r, "connID"),
} }
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 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 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) { func decodeStateRequest(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), contentType) { if !strings.Contains(r.Header.Get("Content-Type"), contentType) {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) return nil, apiutil.ErrUnsupportedContentType
} }
req := changeStateReq{ req := changeStateReq{
@@ -232,7 +231,7 @@ func decodeStateRequest(_ context.Context, r *http.Request) (any, error) {
id: chi.URLParam(r, "clientID"), id: chi.URLParam(r, "clientID"),
} }
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 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 return req, nil
+4 -4
View File
@@ -104,7 +104,7 @@ func (cr configRepository) RetrieveByID(ctx context.Context, domainID, id string
} }
if !row.Next() { if !row.Next() {
return bootstrap.Config{}, errors.Wrap(repoerr.ErrNotFound, sql.ErrNoRows) return bootstrap.Config{}, repoerr.ErrNotFound
} }
if err := row.StructScan(&dbcfg); err != nil { if err := row.StructScan(&dbcfg); err != nil {
@@ -205,7 +205,7 @@ func (cr configRepository) RetrieveByExternalID(ctx context.Context, externalID
} }
if !row.Next() { if !row.Next() {
return bootstrap.Config{}, errors.Wrap(repoerr.ErrNotFound, sql.ErrNoRows) return bootstrap.Config{}, repoerr.ErrNotFound
} }
if err := row.StructScan(&dbcfg); err != nil { if err := row.StructScan(&dbcfg); err != nil {
@@ -275,7 +275,7 @@ func (cr configRepository) Update(ctx context.Context, cfg bootstrap.Config) err
} }
func (cr configRepository) UpdateCert(ctx context.Context, domainID, clientID, clientCert, clientKey, caCert string) (bootstrap.Config, error) { func (cr configRepository) UpdateCert(ctx context.Context, domainID, clientID, clientCert, clientKey, caCert string) (bootstrap.Config, error) {
q := `UPDATE configs SET client_cert = :client_cert, client_key = :client_key, ca_cert = :ca_cert WHERE magistrala_client = :magistrala_client AND domain_id = :domain_id q := `UPDATE configs SET client_cert = :client_cert, client_key = :client_key, ca_cert = :ca_cert WHERE magistrala_client = :magistrala_client AND domain_id = :domain_id
RETURNING magistrala_client, client_cert, client_key, ca_cert` RETURNING magistrala_client, client_cert, client_key, ca_cert`
dbcfg := dbConfig{ dbcfg := dbConfig{
@@ -436,7 +436,7 @@ func (cr configRepository) UpdateChannel(ctx context.Context, c bootstrap.Channe
return errors.Wrap(repoerr.ErrUpdateEntity, err) return errors.Wrap(repoerr.ErrUpdateEntity, err)
} }
q := `UPDATE channels SET name = :name, metadata = :metadata, updated_at = :updated_at, updated_by = :updated_by q := `UPDATE channels SET name = :name, metadata = :metadata, updated_at = :updated_at, updated_by = :updated_by
WHERE magistrala_channel = :magistrala_channel` WHERE magistrala_channel = :magistrala_channel`
if _, err = cr.db.NamedExecContext(ctx, q, dbch); err != nil { if _, err = cr.db.NamedExecContext(ctx, q, dbch); err != nil {
return errors.Wrap(errUpdateChannels, err) return errors.Wrap(errUpdateChannels, err)
+4 -4
View File
@@ -24,19 +24,19 @@ var (
ErrClients = errors.New("failed to receive response from Clients service") ErrClients = errors.New("failed to receive response from Clients service")
// ErrExternalKey indicates a non-existent bootstrap configuration for given external key. // 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 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 indicates error in getting bootstrap configuration.
ErrBootstrap = errors.New("failed to read bootstrap configuration") ErrBootstrap = errors.New("failed to read bootstrap configuration")
// ErrAddBootstrap indicates error in adding 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 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 indicates entities are not in the same domain.
errNotInSameDomain = errors.New("entities are not in the same domain") errNotInSameDomain = errors.New("entities are not in the same domain")
+5 -5
View File
@@ -38,10 +38,10 @@ const (
) )
var ( var (
notFoundRes = toJSON(apiutil.ErrorRes{Msg: svcerr.ErrNotFound.Error()}) notFoundRes = toJSON(svcerr.ErrNotFound)
unauthRes = toJSON(apiutil.ErrorRes{Msg: svcerr.ErrAuthentication.Error()}) unauthRes = toJSON(svcerr.ErrAuthentication)
invalidRes = toJSON(apiutil.ErrorRes{Err: apiutil.ErrInvalidQueryParams.Error(), Msg: apiutil.ErrValidation.Error()}) invalidRes = toJSON(apiutil.ErrInvalidQueryParams)
missingTokRes = toJSON(apiutil.ErrorRes{Err: apiutil.ErrBearerToken.Error(), Msg: apiutil.ErrValidation.Error()}) missingTokRes = toJSON(apiutil.ErrBearerToken)
) )
type testRequest struct { type testRequest struct {
@@ -119,7 +119,7 @@ func TestCreate(t *testing.T) {
req: data, req: data,
contentType: contentType, contentType: contentType,
auth: token, auth: token,
status: http.StatusConflict, status: http.StatusBadRequest,
location: "", location: "",
err: svcerr.ErrConflict, err: svcerr.ErrConflict,
}, },
+2 -2
View File
@@ -83,12 +83,12 @@ func MakeHandler(svc notifiers.Service, logger *slog.Logger, instanceID string)
func decodeCreate(_ context.Context, r *http.Request) (any, error) { func decodeCreate(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), contentType) { 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)} req := createSubReq{token: apiutil.ExtractBearerToken(r)}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 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 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) row, err := repo.db.NamedQueryContext(ctx, q, dbSub)
if err != nil { if err != nil {
if pqErr, ok := err.(*pgconn.PgError); ok && pqErr.Code == pgerrcode.UniqueViolation { 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) return "", errors.Wrap(repoerr.ErrCreateEntity, err)
} }
@@ -60,7 +60,7 @@ func TestSave(t *testing.T) {
desc: "save duplicate", desc: "save duplicate",
sub: sub2, sub: sub2,
id: "", id: "",
err: repoerr.ErrConflict, err: notifiers.ErrSubscriptionsAlreadyExists,
}, },
} }
+6 -2
View File
@@ -15,9 +15,13 @@ import (
"github.com/absmach/supermq/pkg/messaging" "github.com/absmach/supermq/pkg/messaging"
) )
// ErrMessage indicates an error converting a message to SuperMQ message. var (
var ErrMessage = errors.New("failed to convert to SuperMQ message") // ErrMessage indicates an error converting a message 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) var _ consumers.AsyncConsumer = (*notifierService)(nil)
// Service reprents a notification service. // Service reprents a notification service.
+1 -2
View File
@@ -16,7 +16,6 @@ import (
smqauthn "github.com/absmach/supermq/pkg/authn" smqauthn "github.com/absmach/supermq/pkg/authn"
authnmocks "github.com/absmach/supermq/pkg/authn/mocks" authnmocks "github.com/absmach/supermq/pkg/authn/mocks"
"github.com/absmach/supermq/pkg/errors" "github.com/absmach/supermq/pkg/errors"
repoerr "github.com/absmach/supermq/pkg/errors/repository"
svcerr "github.com/absmach/supermq/pkg/errors/service" svcerr "github.com/absmach/supermq/pkg/errors/service"
"github.com/absmach/supermq/pkg/messaging" "github.com/absmach/supermq/pkg/messaging"
"github.com/absmach/supermq/pkg/uuid" "github.com/absmach/supermq/pkg/uuid"
@@ -66,7 +65,7 @@ func TestCreateSubscription(t *testing.T) {
token: exampleUser1, token: exampleUser1,
sub: notifiers.Subscription{Contact: exampleUser1, Topic: "valid.topic"}, sub: notifiers.Subscription{Contact: exampleUser1, Topic: "valid.topic"},
id: "", id: "",
err: repoerr.ErrConflict, err: notifiers.ErrSubscriptionsAlreadyExists,
authenticateErr: nil, authenticateErr: nil,
userID: validID, userID: validID,
}, },
+3
View File
@@ -137,6 +137,9 @@ func (pr postgresRepo) insertJSON(ctx context.Context, msgs smqjson.Messages) er
} }
if _, err = tx.NamedExec(q, dbmsg); err != nil { if _, err = tx.NamedExec(q, dbmsg); err != nil {
if preErr, ok := err.(*pgconn.PrepareError); ok {
err = preErr.Unwrap()
}
pgErr, ok := err.(*pgconn.PgError) pgErr, ok := err.(*pgconn.PgError)
if ok { if ok {
switch pgErr.Code { switch pgErr.Code {
+3
View File
@@ -139,6 +139,9 @@ func (tr timescaleRepo) insertJSON(ctx context.Context, msgs smqjson.Messages) e
return errors.Wrap(errSaveMessage, err) return errors.Wrap(errSaveMessage, err)
} }
if _, err = tx.NamedExec(q, dbmsg); err != nil { if _, err = tx.NamedExec(q, dbmsg); err != nil {
if preErr, ok := err.(*pgconn.PrepareError); ok {
err = preErr.Unwrap()
}
pgErr, ok := err.(*pgconn.PgError) pgErr, ok := err.(*pgconn.PgError)
if ok { if ok {
switch pgErr.Code { switch pgErr.Code {
@@ -1002,7 +1002,7 @@
"uid": "PBFA97CFB590B2093" "uid": "PBFA97CFB590B2093"
}, },
"exemplar": true, "exemplar": true,
"expr": "http_adapter_api_request_count{}", "expr": "ws_adapter_api_request_count{}",
"interval": "", "interval": "",
"legendFormat": "{{method}}", "legendFormat": "{{method}}",
"refId": "A" "refId": "A"
@@ -1107,7 +1107,7 @@
}, },
"editorMode": "code", "editorMode": "code",
"exemplar": false, "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", "format": "time_series",
"instant": false, "instant": false,
"interval": "", "interval": "",
+1 -1
View File
@@ -12,7 +12,7 @@ import (
"strings" "strings"
"text/template" "text/template"
"github.com/absmach/magistrala/pkg/errors" "github.com/absmach/supermq/pkg/errors"
"gopkg.in/gomail.v2" "gopkg.in/gomail.v2"
) )
-5
View File
@@ -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.
-5
View File
@@ -1,5 +0,0 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Package errors contains Magistrala errors definitions.
package errors
-128
View File
@@ -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,
}
}
-352
View File
@@ -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)
}
-39
View File
@@ -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")
)
-123
View File
@@ -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)
}
-206
View File
@@ -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)
}
})
}
}
-78
View File
@@ -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")
)
-32
View File
@@ -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")
)
+1 -1
View File
@@ -10,7 +10,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/absmach/magistrala/pkg/errors" "github.com/absmach/supermq/pkg/errors"
) )
var ( var (
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/absmach/magistrala/pkg/errors" "github.com/absmach/supermq/pkg/errors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
+2 -2
View File
@@ -19,8 +19,8 @@ const (
) )
var ( var (
ErrInvalidRecurringType = errors.New("invalid recurring type") ErrInvalidRecurringType = errors.NewRequestError("invalid recurring type")
ErrStartDateTimeInPast = errors.New("start_datetime must be greater than or equal to current time") ErrStartDateTimeInPast = errors.NewRequestError("start_datetime must be greater than or equal to current time")
) )
// Type can be daily, weekly or monthly. // Type can be daily, weekly or monthly.
+6 -6
View File
@@ -224,7 +224,7 @@ func TestAddBootstrap(t *testing.T) {
svcReq: bootstrapConfig, svcReq: bootstrapConfig,
svcRes: bootstrap.Config{}, svcRes: bootstrap.Config{},
svcErr: svcerr.ErrConflict, svcErr: svcerr.ErrConflict,
err: errors.NewSDKErrorWithStatus(svcerr.ErrConflict, http.StatusConflict), err: errors.NewSDKErrorWithStatus(svcerr.ErrConflict, http.StatusBadRequest),
}, },
{ {
desc: "add empty config", desc: "add empty config",
@@ -234,7 +234,7 @@ func TestAddBootstrap(t *testing.T) {
svcReq: bootstrap.Config{}, svcReq: bootstrap.Config{},
svcRes: bootstrap.Config{}, svcRes: bootstrap.Config{},
svcErr: nil, 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", desc: "add with non-existent client Id",
@@ -487,7 +487,7 @@ func TestWhiteList(t *testing.T) {
state: -1, state: -1,
svcReq: bootstrap.Active, svcReq: bootstrap.Active,
svcErr: nil, 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", desc: "whitelist with empty client Id",
@@ -586,9 +586,9 @@ func TestViewBootstrap(t *testing.T) {
token: validToken, token: validToken,
id: invalid, id: invalid,
svcResp: bootstrap.Config{}, svcResp: bootstrap.Config{},
svcErr: svcerr.ErrViewEntity, svcErr: svcerr.ErrNotFound,
response: sdk.BootstrapConfig{}, 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", desc: "view with response that cannot be unmarshalled",
@@ -1203,7 +1203,7 @@ func TestBoostrap(t *testing.T) {
svcErr: nil, svcErr: nil,
readerResp: bootstrap.Config{}, readerResp: bootstrap.Config{},
readerErr: nil, readerErr: nil,
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrBearerKey), http.StatusBadRequest), err: errors.NewSDKErrorWithStatus(apiutil.ErrBearerKey, http.StatusUnauthorized),
}, },
} }
for _, tc := range cases { for _, tc := range cases {
+7 -7
View File
@@ -101,7 +101,7 @@ func TestCreateSubscription(t *testing.T) {
svcReq: notifiers.Subscription{}, svcReq: notifiers.Subscription{},
svcRes: "", svcRes: "",
svcErr: nil, 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", desc: "create new subscription with invalid token",
@@ -124,7 +124,7 @@ func TestCreateSubscription(t *testing.T) {
svcReq: notifiers.Subscription{}, svcReq: notifiers.Subscription{},
svcErr: nil, svcErr: nil,
svcRes: "", 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", desc: "create new subscription with empty contact",
@@ -137,7 +137,7 @@ func TestCreateSubscription(t *testing.T) {
svcReq: notifiers.Subscription{}, svcReq: notifiers.Subscription{},
svcErr: nil, svcErr: nil,
svcRes: "", svcRes: "",
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrInvalidContact), http.StatusBadRequest), err: errors.NewSDKErrorWithStatus(apiutil.ErrInvalidContact, http.StatusBadRequest),
}, },
} }
for _, tc := range cases { for _, tc := range cases {
@@ -209,7 +209,7 @@ func TestViewSubscription(t *testing.T) {
svcRes: notifiers.Subscription{}, svcRes: notifiers.Subscription{},
svcErr: nil, svcErr: nil,
response: sdk.Subscription{}, 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 { for _, tc := range cases {
@@ -368,7 +368,7 @@ func TestListSubscription(t *testing.T) {
svcRes: notifiers.Page{}, svcRes: notifiers.Page{},
svcErr: nil, svcErr: nil,
response: sdk.SubscriptionPage{}, 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, subID: subID,
token: "", token: "",
svcErr: nil, 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", desc: "delete subscription with empty subID",
subID: "", subID: "",
token: validToken, token: validToken,
svcErr: nil, svcErr: nil,
err: errors.NewSDKErrorWithStatus(errors.Wrap(apiutil.ErrValidation, apiutil.ErrMissingID), http.StatusBadRequest), err: errors.NewSDKErrorWithStatus(apiutil.ErrMissingID, http.StatusBadRequest),
}, },
} }
+3 -3
View File
@@ -143,7 +143,7 @@ func TestReadMessages(t *testing.T) {
authzErr: svcerr.ErrAuthorization, authzErr: svcerr.ErrAuthorization,
repoRes: readers.MessagesPage{}, repoRes: readers.MessagesPage{},
response: sdk.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", desc: "read messages with empty token",
@@ -161,7 +161,7 @@ func TestReadMessages(t *testing.T) {
authnErr: svcerr.ErrAuthentication, authnErr: svcerr.ErrAuthentication,
repoRes: readers.MessagesPage{}, repoRes: readers.MessagesPage{},
response: sdk.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", desc: "read messages with empty channel ID",
@@ -179,7 +179,7 @@ func TestReadMessages(t *testing.T) {
repoRes: readers.MessagesPage{}, repoRes: readers.MessagesPage{},
repoErr: nil, repoErr: nil,
response: sdk.MessagesPage{}, 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", desc: "read messages with invalid message page metadata",
+1 -1
View File
@@ -97,7 +97,7 @@ func TestProvision(t *testing.T) {
token: validToken, token: validToken,
domainID: validID, domainID: validID,
data: fmt.Sprintf(`{"name": "test", "external_id": "%s"}`, validID), data: fmt.Sprintf(`{"name": "test", "external_id": "%s"}`, validID),
status: http.StatusBadRequest, status: http.StatusUnauthorized,
contentType: validContenType, contentType: validContenType,
svcErr: nil, svcErr: nil,
}, },
+3 -1
View File
@@ -3,7 +3,9 @@
package api package api
import apiutil "github.com/absmach/supermq/api/http/util" import (
apiutil "github.com/absmach/supermq/api/http/util"
)
type provisionReq struct { type provisionReq struct {
token string token string
+3 -3
View File
@@ -55,7 +55,7 @@ func MakeHandler(svc provision.Service, logger *slog.Logger, instanceID string)
func decodeProvisionRequest(_ context.Context, r *http.Request) (any, error) { func decodeProvisionRequest(_ context.Context, r *http.Request) (any, error) {
if r.Header.Get("Content-Type") != contentType { if r.Header.Get("Content-Type") != contentType {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) return nil, apiutil.ErrUnsupportedContentType
} }
req := provisionReq{ req := provisionReq{
@@ -63,7 +63,7 @@ func decodeProvisionRequest(_ context.Context, r *http.Request) (any, error) {
domainID: chi.URLParam(r, "domainID"), domainID: chi.URLParam(r, "domainID"),
} }
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 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 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) { func decodeMappingRequest(_ context.Context, r *http.Request) (any, error) {
if r.Header.Get("Content-Type") != contentType { if r.Header.Get("Content-Type") != contentType {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) return nil, apiutil.ErrUnsupportedContentType
} }
req := mappingReq{ req := mappingReq{
+1 -1
View File
@@ -798,7 +798,7 @@ func TestUpdateRuleTagsEndpoint(t *testing.T) {
contentType: contentType, contentType: contentType,
data: fmt.Sprintf(`{"tags":["%s"}`, newTag), data: fmt.Sprintf(`{"tags":["%s"}`, newTag),
status: http.StatusBadRequest, status: http.StatusBadRequest,
err: errors.ErrMalformedEntity, err: apiutil.ErrMalformedRequestBody,
}, },
{ {
desc: "update rule with empty id", desc: "update rule with empty id",
+8 -8
View File
@@ -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) { func decodeAddRuleRequest(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) { 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 var rule re.Rule
if err := json.NewDecoder(r.Body).Decode(&rule); err != nil { 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 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) { func decodeUpdateRuleRequest(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) { 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 var rule re.Rule
if err := json.NewDecoder(r.Body).Decode(&rule); err != nil { 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) 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) { func decodeUpdateRuleTags(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) { if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) return nil, apiutil.ErrUnsupportedContentType
} }
req := updateRuleTagsReq{ req := updateRuleTagsReq{
id: chi.URLParam(r, ruleIdKey), id: chi.URLParam(r, ruleIdKey),
} }
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 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 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) { func decodeUpdateRuleScheduleRequest(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) { if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) return nil, apiutil.ErrUnsupportedContentType
} }
req := updateRuleScheduleReq{ req := updateRuleScheduleReq{
id: chi.URLParam(r, ruleIdKey), id: chi.URLParam(r, ruleIdKey),
} }
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 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 return req, nil
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"time" "time"
grpcReadersV1 "github.com/absmach/magistrala/api/grpc/readers/v1" 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" svcerr "github.com/absmach/supermq/pkg/errors/service"
"github.com/absmach/supermq/pkg/transformers/senml" "github.com/absmach/supermq/pkg/transformers/senml"
readers "github.com/absmach/supermq/readers" readers "github.com/absmach/supermq/readers"
+1 -1
View File
@@ -12,9 +12,9 @@ import (
"time" "time"
grpcReadersV1 "github.com/absmach/magistrala/api/grpc/readers/v1" grpcReadersV1 "github.com/absmach/magistrala/api/grpc/readers/v1"
"github.com/absmach/magistrala/pkg/errors"
grpcapi "github.com/absmach/magistrala/readers/api/grpc" grpcapi "github.com/absmach/magistrala/readers/api/grpc"
apiutil "github.com/absmach/supermq/api/http/util" 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/pkg/transformers/senml"
"github.com/absmach/supermq/readers" "github.com/absmach/supermq/readers"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
+2 -2
View File
@@ -235,7 +235,7 @@ func TestReadAll(t *testing.T) {
token: "", token: "",
authResponse: false, authResponse: false,
authnErr: svcerr.ErrAuthentication, authnErr: svcerr.ErrAuthentication,
status: http.StatusBadRequest, status: http.StatusUnauthorized,
err: svcerr.ErrAuthentication, 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), url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: "", token: "",
authResponse: false, authResponse: false,
status: http.StatusBadRequest, status: http.StatusUnauthorized,
err: svcerr.ErrAuthorization, err: svcerr.ErrAuthorization,
}, },
{ {
+1 -38
View File
@@ -51,7 +51,7 @@ const (
// MakeHandler returns a HTTP handler for API endpoints. // 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 { func MakeHandler(svc readers.MessageRepository, authn smqauthn.Authentication, clients grpcClientsV1.ClientsServiceClient, channels grpcChannelsV1.ChannelsServiceClient, svcName, instanceID string) http.Handler {
opts := []kithttp.ServerOption{ opts := []kithttp.ServerOption{
kithttp.ServerErrorEncoder(encodeError), kithttp.ServerErrorEncoder(api.EncodeError),
} }
mux := chi.NewRouter() mux := chi.NewRouter()
@@ -209,43 +209,6 @@ func encodeResponse(_ context.Context, w http.ResponseWriter, response any) erro
return json.NewEncoder(w).Encode(response) 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 { 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) clientID, clientType, err := authenticate(ctx, req, authn, clients)
if err != nil { if err != nil {
+3
View File
@@ -59,6 +59,9 @@ func (tr postgresRepository) ReadAll(chanID string, rpm readers.PageMetadata) (r
} }
rows, err := tr.db.NamedQuery(q, params) rows, err := tr.db.NamedQuery(q, params)
if err != nil { if err != nil {
if preErr, ok := err.(*pgconn.PrepareError); ok {
err = preErr.Unwrap()
}
if pgErr, ok := err.(*pgconn.PgError); ok { if pgErr, ok := err.(*pgconn.PgError); ok {
if pgErr.Code == pgerrcode.UndefinedTable { if pgErr.Code == pgerrcode.UndefinedTable {
return readers.MessagesPage{}, nil return readers.MessagesPage{}, nil
+3
View File
@@ -120,6 +120,9 @@ func (tr timescaleRepository) ReadAll(chanID string, rpm readers.PageMetadata) (
rows, err := tr.db.NamedQuery(q, params) rows, err := tr.db.NamedQuery(q, params)
if err != nil { if err != nil {
if preErr, ok := err.(*pgconn.PrepareError); ok {
err = preErr.Unwrap()
}
if pgErr, ok := err.(*pgconn.PgError); ok { if pgErr, ok := err.(*pgconn.PgError); ok {
if pgErr.Code == pgerrcode.UndefinedTable { if pgErr.Code == pgerrcode.UndefinedTable {
return readers.MessagesPage{}, nil return readers.MessagesPage{}, nil
+1 -1
View File
@@ -1125,7 +1125,7 @@ func TestViewReportTemplateEndpoint(t *testing.T) {
id: validID, id: validID,
contentType: contentType, contentType: contentType,
svcErr: svcerr.ErrViewEntity, svcErr: svcerr.ErrViewEntity,
status: http.StatusBadRequest, status: http.StatusUnprocessableEntity,
err: svcerr.ErrViewEntity, err: svcerr.ErrViewEntity,
}, },
} }
+6 -6
View File
@@ -135,7 +135,7 @@ func MakeHandler(svc reports.Service, authn smqauthn.AuthNMiddleware, mux *chi.M
func decodeGenerateReportRequest(_ context.Context, r *http.Request) (any, error) { func decodeGenerateReportRequest(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) { 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) 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) { func decodeAddReportConfigRequest(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) { 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 var config reports.ReportConfig
if err := json.NewDecoder(r.Body).Decode(&config); err != nil { 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) { func decodeUpdateReportConfigRequest(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) { 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 var config reports.ReportConfig
if err := json.NewDecoder(r.Body).Decode(&config); err != nil { 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) { func decodeUpdateReportScheduleRequest(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) { if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) return nil, apiutil.ErrUnsupportedContentType
} }
req := updateReportScheduleReq{ req := updateReportScheduleReq{
id: chi.URLParam(r, reportIdKey), id: chi.URLParam(r, reportIdKey),
} }
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 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 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) { func decodeUpdateReportTemplateRequest(_ context.Context, r *http.Request) (any, error) {
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) { if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) return nil, apiutil.ErrUnsupportedContentType
} }
req := updateReportTemplateReq{} req := updateReportTemplateReq{}
+27
View File
@@ -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{}
}
+1 -1
View File
@@ -8,9 +8,9 @@ import (
"encoding/json" "encoding/json"
"time" "time"
"github.com/absmach/magistrala/pkg/errors"
"github.com/absmach/magistrala/pkg/schedule" "github.com/absmach/magistrala/pkg/schedule"
"github.com/absmach/magistrala/reports" "github.com/absmach/magistrala/reports"
"github.com/absmach/supermq/pkg/errors"
) )
// dbReport represents the database structure for a Report. // dbReport represents the database structure for a Report.
+40 -18
View File
@@ -10,19 +10,26 @@ import (
"strings" "strings"
"time" "time"
"github.com/absmach/magistrala/pkg/errors"
"github.com/absmach/magistrala/reports" "github.com/absmach/magistrala/reports"
api "github.com/absmach/supermq/api/http" api "github.com/absmach/supermq/api/http"
"github.com/absmach/supermq/pkg/errors"
repoerr "github.com/absmach/supermq/pkg/errors/repository" repoerr "github.com/absmach/supermq/pkg/errors/repository"
"github.com/absmach/supermq/pkg/postgres" "github.com/absmach/supermq/pkg/postgres"
) )
type PostgresRepository struct { type PostgresRepository struct {
DB postgres.Database DB postgres.Database
eh errors.Handler
} }
func NewRepository(db postgres.Database) reports.Repository { 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) { 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) dbr, err := reportToDb(cfg)
if err != nil { if err != nil {
return reports.ReportConfig{}, err return reports.ReportConfig{}, repo.eh.HandleError(repoerr.ErrCreateEntity, err)
} }
row, err := repo.DB.NamedQueryContext(ctx, q, dbr) row, err := repo.DB.NamedQueryContext(ctx, q, dbr)
if err != nil { if err != nil {
return reports.ReportConfig{}, err return reports.ReportConfig{}, repo.eh.HandleError(repoerr.ErrCreateEntity, err)
} }
defer row.Close() defer row.Close()
var dbReport dbReport var dbReport dbReport
if row.Next() { if row.Next() {
if err := row.StructScan(&dbReport); err != nil { if err := row.StructScan(&dbReport); err != nil {
return reports.ReportConfig{}, err return reports.ReportConfig{}, repo.eh.HandleError(repoerr.ErrCreateEntity, err)
} }
} }
report, err := dbToReport(dbReport) report, err := dbToReport(dbReport)
if err != nil { if err != nil {
return reports.ReportConfig{}, err return reports.ReportConfig{}, repo.eh.HandleError(repoerr.ErrCreateEntity, err)
} }
return report, nil return report, nil
@@ -72,6 +79,9 @@ func (repo *PostgresRepository) ViewReportConfig(ctx context.Context, id string)
} }
var dbr dbReport var dbr dbReport
if err := row.StructScan(&dbr); err != nil { if err := row.StructScan(&dbr); err != nil {
if err == sql.ErrNoRows {
return reports.ReportConfig{}, repoerr.ErrNotFound
}
return reports.ReportConfig{}, err return reports.ReportConfig{}, err
} }
rpt, err := dbToReport(dbr) rpt, err := dbToReport(dbr)
@@ -163,10 +173,14 @@ func (repo *PostgresRepository) UpdateReportConfig(ctx context.Context, cfg repo
defer row.Close() defer row.Close()
var dbReport dbReport var dbReport dbReport
if row.Next() { if !row.Next() {
if err := row.StructScan(&dbReport); err != nil { if err := row.Err(); err != nil {
return reports.ReportConfig{}, err return reports.ReportConfig{}, err
} }
return reports.ReportConfig{}, repoerr.ErrNotFound
}
if err := row.StructScan(&dbReport); err != nil {
return reports.ReportConfig{}, err
} }
rpt, err := dbToReport(dbReport) rpt, err := dbToReport(dbReport)
if err != nil { if err != nil {
@@ -196,10 +210,14 @@ func (repo *PostgresRepository) UpdateReportSchedule(ctx context.Context, cfg re
defer row.Close() defer row.Close()
var dbReport dbReport var dbReport dbReport
if row.Next() { if !row.Next() {
if err := row.StructScan(&dbReport); err != nil { if err := row.Err(); err != nil {
return reports.ReportConfig{}, errors.Wrap(repoerr.ErrUpdateEntity, err) 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) report, err := dbToReport(dbReport)
if err != nil { if err != nil {
@@ -320,10 +338,14 @@ func (repo *PostgresRepository) UpdateReportDue(ctx context.Context, id string,
defer row.Close() defer row.Close()
var dbReport dbReport var dbReport dbReport
if row.Next() { if !row.Next() {
if err := row.StructScan(&dbReport); err != nil { if err := row.Err(); err != nil {
return reports.ReportConfig{}, errors.Wrap(repoerr.ErrUpdateEntity, err) 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) report, err := dbToReport(dbReport)
if err != nil { if err != nil {
@@ -335,8 +357,8 @@ func (repo *PostgresRepository) UpdateReportDue(ctx context.Context, id string,
func (repo *PostgresRepository) UpdateReportTemplate(ctx context.Context, domainID, reportID string, template reports.ReportTemplate) error { func (repo *PostgresRepository) UpdateReportTemplate(ctx context.Context, domainID, reportID string, template reports.ReportTemplate) error {
q := ` q := `
UPDATE report_config UPDATE report_config
SET report_template = :report_template, updated_at = :updated_at SET report_template = :report_template, updated_at = :updated_at
WHERE id = :id AND domain_id = :domain_id` WHERE id = :id AND domain_id = :domain_id`
dbr := dbReport{ dbr := dbReport{
@@ -357,8 +379,8 @@ func (repo *PostgresRepository) UpdateReportTemplate(ctx context.Context, domain
func (repo *PostgresRepository) ViewReportTemplate(ctx context.Context, domainID, reportID string) (reports.ReportTemplate, error) { func (repo *PostgresRepository) ViewReportTemplate(ctx context.Context, domainID, reportID string) (reports.ReportTemplate, error) {
q := ` q := `
SELECT COALESCE(report_template, '') as report_template SELECT COALESCE(report_template, '') as report_template
FROM report_config FROM report_config
WHERE id = $1 AND domain_id = $2` WHERE id = $1 AND domain_id = $2`
var template reports.ReportTemplate var template reports.ReportTemplate
+4 -1
View File
@@ -292,6 +292,7 @@ func TestUpdateReportConfigStatus(t *testing.T) {
CreatedBy: generateUUID(t), CreatedBy: generateUUID(t),
UpdatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(),
UpdatedBy: generateUUID(t), UpdatedBy: generateUUID(t),
Metrics: []reports.ReqMetric{},
} }
saved, err := repo.AddReportConfig(context.Background(), reportConfig) saved, err := repo.AddReportConfig(context.Background(), reportConfig)
@@ -361,6 +362,7 @@ func TestRemoveReportConfig(t *testing.T) {
Status: reports.EnabledStatus, Status: reports.EnabledStatus,
CreatedAt: time.Now().UTC(), CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(),
Metrics: []reports.ReqMetric{},
} }
saved, err := repo.AddReportConfig(context.Background(), reportConfig) saved, err := repo.AddReportConfig(context.Background(), reportConfig)
@@ -414,6 +416,7 @@ func TestListReportsConfig(t *testing.T) {
Status: reports.EnabledStatus, Status: reports.EnabledStatus,
CreatedAt: time.Now().UTC(), CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(),
Metrics: []reports.ReqMetric{},
} }
_, err := repo.AddReportConfig(context.Background(), reportConfig) _, err := repo.AddReportConfig(context.Background(), reportConfig)
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err)) 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) saved, err := repo.AddReportConfig(context.Background(), reportConfig)
require.Nil(t, err, fmt.Sprintf("unexpected error: %s", err)) 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 { cases := []struct {
desc string desc string