NOISSUE - Handle Domain & Channel Route not available error during creation (#3180)

Signed-off-by: Arvindh <arvindh91@gmail.com>
This commit is contained in:
Arvindh
2025-10-09 18:36:06 +05:30
committed by GitHub
parent b0e9e43b9e
commit 6f40c7fa1e
8 changed files with 49 additions and 4 deletions
+1
View File
@@ -192,6 +192,7 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) {
switch {
case errors.Contains(err, errors.ErrEmailAlreadyExists),
errors.Contains(err, errors.ErrUsernameNotAvailable),
errors.Contains(err, errors.ErrRouteNotAvailable),
errors.Contains(err, errors.ErrChannelRouteNotAvailable),
errors.Contains(err, errors.ErrDomainRouteNotAvailable):
w.WriteHeader(http.StatusBadRequest)
+15 -2
View File
@@ -22,6 +22,7 @@ import (
"github.com/absmach/supermq/pkg/roles"
rolesPostgres "github.com/absmach/supermq/pkg/roles/repo/postgres"
"github.com/jackc/pgtype"
"github.com/jackc/pgx/v5/pgconn"
"github.com/lib/pq"
)
@@ -29,6 +30,8 @@ const (
rolesTableNamePrefix = "channels"
entityTableName = "channels"
entityIDColumnName = "id"
pgDuplicateErrCode = "23505"
)
var _ channels.Repository = (*channelRepository)(nil)
@@ -64,7 +67,7 @@ func (cr *channelRepository) Save(ctx context.Context, chs ...channels.Channel)
row, err := cr.db.NamedQueryContext(ctx, q, dbchs)
if err != nil {
return []channels.Channel{}, postgres.HandleError(repoerr.ErrCreateEntity, err)
return []channels.Channel{}, handleSaveError(repoerr.ErrCreateEntity, err)
}
defer row.Close()
@@ -86,6 +89,16 @@ func (cr *channelRepository) Save(ctx context.Context, chs ...channels.Channel)
return reChs, nil
}
func handleSaveError(wrapper, err error) error {
if pqErr, ok := err.(*pgconn.PgError); ok && pqErr.Code == pgDuplicateErrCode {
switch pqErr.ConstraintName {
case "unique_domain_route_not_null":
return errors.ErrRouteNotAvailable
}
}
return postgres.HandleError(wrapper, err)
}
func (cr *channelRepository) Update(ctx context.Context, channel channels.Channel) (channels.Channel, error) {
var query []string
var upq string
@@ -733,7 +746,7 @@ indirect_child_groups AS (
groups indirect_child_groups ON indirect_child_groups.path <@ dlgws.path
WHERE
indirect_child_groups.domain_id = '%s'
AND NOT EXISTS (
AND NOT EXISTS (
SELECT 1
FROM direct_groups_with_subgroup dgws
WHERE dgws.id = indirect_child_groups.id
+1 -1
View File
@@ -172,7 +172,7 @@ func TestSave(t *testing.T) {
Route: duplicateRoute,
},
resp: []channels.Channel{},
err: repoerr.ErrConflict,
err: errors.ErrRouteNotAvailable,
},
}
+3
View File
@@ -78,6 +78,9 @@ func (svc service) CreateChannels(ctx context.Context, session authn.Session, ch
savedChs, err := svc.repo.Save(ctx, reChs...)
if err != nil {
if errors.Contains(err, errors.ErrRouteNotAvailable) {
return []Channel{}, []roles.RoleProvision{}, errors.ErrRouteNotAvailable
}
return []Channel{}, []roles.RoleProvision{}, errors.Wrap(svcerr.ErrCreateEntity, err)
}
chIDs := []string{}
+14 -1
View File
@@ -20,6 +20,7 @@ import (
"github.com/absmach/supermq/pkg/roles"
rolesPostgres "github.com/absmach/supermq/pkg/roles/repo/postgres"
"github.com/jackc/pgtype"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jmoiron/sqlx"
"github.com/lib/pq"
)
@@ -30,6 +31,8 @@ const (
rolesTableNamePrefix = "domains"
entityTableName = "domains"
entityIDColumnName = "id"
pgDuplicateErrCode = "23505"
)
type domainRepo struct {
@@ -59,7 +62,7 @@ func (repo domainRepo) SaveDomain(ctx context.Context, d domains.Domain) (dd dom
row, err := repo.db.NamedQueryContext(ctx, q, dbd)
if err != nil {
return domains.Domain{}, postgres.HandleError(repoerr.ErrCreateEntity, err)
return domains.Domain{}, handleSaveError(repoerr.ErrCreateEntity, err)
}
defer row.Close()
@@ -80,6 +83,16 @@ func (repo domainRepo) SaveDomain(ctx context.Context, d domains.Domain) (dd dom
return domain, nil
}
func handleSaveError(wrapper, err error) error {
if pqErr, ok := err.(*pgconn.PgError); ok && pqErr.Code == pgDuplicateErrCode {
switch pqErr.ConstraintName {
case "domains_route_key":
return errors.ErrRouteNotAvailable
}
}
return postgres.HandleError(wrapper, err)
}
// RetrieveDomainByIDWithRoles retrieves Domain by its unique ID along with member roles.
func (repo domainRepo) RetrieveDomainByIDWithRoles(ctx context.Context, id string, memberID string) (domains.Domain, error) {
q := `
+9
View File
@@ -89,6 +89,15 @@ func Migration() (*migrate.MemoryMigrationSource, error) {
`ALTER TABLE invitations ALTER COLUMN rejected_at TYPE TIMESTAMP;`,
},
},
{
Id: "domain_5",
Up: []string{
`ALTER TABLE domains RENAME CONSTRAINT domains_alias_key TO domains_route_key;`,
},
Down: []string{
`ALTER TABLE domains RENAME CONSTRAINT domains_route_key TO domains_alias_key;`,
},
},
},
}
+3
View File
@@ -66,6 +66,9 @@ func (svc service) CreateDomain(ctx context.Context, session authn.Session, d Do
// Domain is created in repo first, because Roles table have foreign key relation with Domain ID
dom, err := svc.repo.SaveDomain(ctx, d)
if err != nil {
if errors.Contains(err, errors.ErrRouteNotAvailable) {
return Domain{}, []roles.RoleProvision{}, errors.ErrRouteNotAvailable
}
return Domain{}, []roles.RoleProvision{}, errors.Wrap(svcerr.ErrCreateEntity, err)
}
defer func() {
+3
View File
@@ -45,4 +45,7 @@ var (
// ErrChannelRouteNotAvailable indicates that the channel route is not available.
ErrChannelRouteNotAvailable = New("channel route not available")
// ErrRouteNotAvailable indicates that the username is not available.
ErrRouteNotAvailable = New("route not available")
)