diff --git a/api/http/common.go b/api/http/common.go index 6240794a6..46c177638 100644 --- a/api/http/common.go +++ b/api/http/common.go @@ -75,7 +75,7 @@ const ( DefTotal = uint64(100) DefOffset = 0 DefOrder = "updated_at" - DefDir = "asc" + DefDir = "desc" DefLimit = 10 DefLevel = 0 DefStartLevel = 1 diff --git a/channels/postgres/channels.go b/channels/postgres/channels.go index 3462fd505..d6bf21456 100644 --- a/channels/postgres/channels.go +++ b/channels/postgres/channels.go @@ -1340,14 +1340,22 @@ func PageQuery(pm channels.Page) (string, error) { } func applyOrdering(emq string, pm channels.Page) string { + var orderBy string switch pm.Order { - case "name", "created_at", "updated_at": - emq = fmt.Sprintf("%s ORDER BY %s", emq, pm.Order) - if pm.Dir == api.AscDir || pm.Dir == api.DescDir { - emq = fmt.Sprintf("%s %s", emq, pm.Dir) - } + case "name": + orderBy = "name" + case "created_at": + orderBy = "created_at" + case "updated_at": + orderBy = "COALESCE(updated_at, created_at)" + default: + return emq } - return emq + + if pm.Dir == api.AscDir || pm.Dir == api.DescDir { + return fmt.Sprintf("%s ORDER BY %s %s, id %s", emq, orderBy, pm.Dir, pm.Dir) + } + return fmt.Sprintf("%s ORDER BY %s", emq, orderBy) } func applyLimitOffset(query string) string { diff --git a/channels/postgres/setup_test.go b/channels/postgres/setup_test.go index a379d57fe..cd525873b 100644 --- a/channels/postgres/setup_test.go +++ b/channels/postgres/setup_test.go @@ -12,7 +12,6 @@ import ( "time" chpostgres "github.com/absmach/supermq/channels/postgres" - "github.com/absmach/supermq/pkg/postgres" pgclient "github.com/absmach/supermq/pkg/postgres" "github.com/jmoiron/sqlx" "github.com/ory/dockertest/v3" @@ -22,7 +21,7 @@ import ( var ( db *sqlx.DB - database postgres.Database + database pgclient.Database tracer = otel.Tracer("repo_tests") ) @@ -84,7 +83,7 @@ func TestMain(m *testing.M) { log.Fatalf("Could not setup test DB connection: %s", err) } - database = postgres.NewDatabase(db, dbConfig, tracer) + database = pgclient.NewDatabase(db, dbConfig, tracer) code := m.Run() diff --git a/clients/postgres/clients.go b/clients/postgres/clients.go index 1ba63c6f4..f5865646c 100644 --- a/clients/postgres/clients.go +++ b/clients/postgres/clients.go @@ -1275,14 +1275,24 @@ func PageQuery(pm clients.Page) (string, error) { } func applyOrdering(emq string, pm clients.Page) string { + var orderBy string switch pm.Order { - case "name", "identity", "created_at", "updated_at": - emq = fmt.Sprintf("%s ORDER BY %s", emq, pm.Order) - if pm.Dir == api.AscDir || pm.Dir == api.DescDir { - emq = fmt.Sprintf("%s %s", emq, pm.Dir) - } + case "name": + orderBy = "name" + case "identity": + orderBy = "identity" + case "created_at": + orderBy = "created_at" + case "updated_at": + orderBy = "COALESCE(updated_at, created_at)" + default: + return emq } - return emq + + if pm.Dir == api.AscDir || pm.Dir == api.DescDir { + return fmt.Sprintf("%s ORDER BY %s %s, id %s", emq, orderBy, pm.Dir, pm.Dir) + } + return fmt.Sprintf("%s ORDER BY %s", emq, orderBy) } func applyLimitOffset(query string) string { diff --git a/domains/postgres/domains.go b/domains/postgres/domains.go index 7be915385..174ec4492 100644 --- a/domains/postgres/domains.go +++ b/domains/postgres/domains.go @@ -521,14 +521,22 @@ func (repo domainRepo) processRows(rows *sqlx.Rows) ([]domains.Domain, error) { } func applyOrdering(emq string, pm domains.Page) string { + var orderBy string switch pm.Order { - case "name", "created_at", "updated_at": - emq = fmt.Sprintf("%s ORDER BY d.%s", emq, pm.Order) - if pm.Dir == api.AscDir || pm.Dir == api.DescDir { - emq = fmt.Sprintf("%s %s", emq, pm.Dir) - } + case "name": + orderBy = "d.name" + case "created_at": + orderBy = "d.created_at" + case "updated_at": + orderBy = "COALESCE(d.updated_at, d.created_at)" + default: + return emq } - return emq + + if pm.Dir == api.AscDir || pm.Dir == api.DescDir { + return fmt.Sprintf("%s ORDER BY %s %s, d.id %s", emq, orderBy, pm.Dir, pm.Dir) + } + return fmt.Sprintf("%s ORDER BY %s", emq, orderBy) } type dbDomain struct { diff --git a/groups/api/http/decode.go b/groups/api/http/decode.go index 56c0ad3d5..6831cb1bc 100644 --- a/groups/api/http/decode.go +++ b/groups/api/http/decode.go @@ -288,7 +288,7 @@ func decodePageMeta(r *http.Request) (groups.PageMeta, error) { if err != nil { return groups.PageMeta{}, errors.Wrap(apiutil.ErrValidation, err) } - dir, err := apiutil.ReadStringQuery(r, api.DirKey, api.DescDir) + dir, err := apiutil.ReadStringQuery(r, api.DirKey, api.DefDir) if err != nil { return groups.PageMeta{}, errors.Wrap(apiutil.ErrValidation, err) } diff --git a/groups/postgres/groups.go b/groups/postgres/groups.go index ee76e65df..33c9ce845 100644 --- a/groups/postgres/groups.go +++ b/groups/postgres/groups.go @@ -412,12 +412,22 @@ func (repo groupRepository) RetrieveAll(ctx context.Context, pm groups.PageMeta) query := buildQuery(pm) orderClause := "" + var orderBy string switch pm.Order { - case "name", "created_at", "updated_at": - orderClause = fmt.Sprintf("ORDER BY g.%s", pm.Order) - if pm.Dir == api.AscDir || pm.Dir == api.DescDir { - orderClause = fmt.Sprintf("%s %s", orderClause, pm.Dir) + case "name": + orderBy = "g.name" + case "created_at": + orderBy = "g.created_at" + case "updated_at": + orderBy = "COALESCE(g.updated_at, g.created_at)" + } + + if orderBy != "" { + dir := pm.Dir + if dir != api.AscDir && dir != api.DescDir { + dir = api.DescDir } + orderClause = fmt.Sprintf("ORDER BY %s %s, g.id %s", orderBy, dir, dir) } q := fmt.Sprintf(`SELECT DISTINCT g.id, g.domain_id, tags, COALESCE(g.parent_id, '') AS parent_id, g.name, g.description, diff --git a/pkg/sdk/channels_test.go b/pkg/sdk/channels_test.go index a57910ef9..05b47d9e9 100644 --- a/pkg/sdk/channels_test.go +++ b/pkg/sdk/channels_test.go @@ -398,7 +398,7 @@ func TestListChannels(t *testing.T) { channelsPageMeta: channels.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", Offset: offset, Limit: limit, }, @@ -425,7 +425,7 @@ func TestListChannels(t *testing.T) { channelsPageMeta: channels.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", Offset: offset, Limit: limit, }, @@ -443,7 +443,7 @@ func TestListChannels(t *testing.T) { channelsPageMeta: channels.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", }, svcRes: channels.ChannelsPage{}, svcErr: nil, @@ -459,7 +459,7 @@ func TestListChannels(t *testing.T) { channelsPageMeta: channels.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", Offset: offset, Limit: 10, }, @@ -487,7 +487,7 @@ func TestListChannels(t *testing.T) { channelsPageMeta: channels.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", }, svcRes: channels.ChannelsPage{}, svcErr: nil, @@ -504,7 +504,7 @@ func TestListChannels(t *testing.T) { channelsPageMeta: channels.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", Offset: offset, Limit: 1, }, @@ -533,7 +533,7 @@ func TestListChannels(t *testing.T) { channelsPageMeta: channels.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", Offset: offset, Limit: 10, Metadata: channels.Metadata{"name": "client_89"}, @@ -565,7 +565,7 @@ func TestListChannels(t *testing.T) { channelsPageMeta: channels.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", }, svcRes: channels.ChannelsPage{}, svcErr: nil, @@ -581,7 +581,7 @@ func TestListChannels(t *testing.T) { channelsPageMeta: channels.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", Offset: 0, Limit: 10, }, diff --git a/pkg/sdk/clients_test.go b/pkg/sdk/clients_test.go index 3e160838b..0f5f03a9e 100644 --- a/pkg/sdk/clients_test.go +++ b/pkg/sdk/clients_test.go @@ -363,7 +363,7 @@ func TestListClients(t *testing.T) { svcReq: clients.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", Offset: 0, Limit: 100, }, @@ -395,7 +395,7 @@ func TestListClients(t *testing.T) { svcReq: clients.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", Offset: 0, Limit: 100, }, @@ -415,7 +415,7 @@ func TestListClients(t *testing.T) { svcReq: clients.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", }, svcRes: clients.ClientsPage{}, svcErr: nil, @@ -434,7 +434,7 @@ func TestListClients(t *testing.T) { svcReq: clients.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", }, svcRes: clients.ClientsPage{}, svcErr: nil, @@ -453,7 +453,7 @@ func TestListClients(t *testing.T) { svcReq: clients.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", Offset: 0, Limit: 100, Status: clients.DisabledStatus, @@ -488,7 +488,7 @@ func TestListClients(t *testing.T) { svcReq: clients.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", Offset: 0, Limit: 100, Tag: "tag1", @@ -525,7 +525,7 @@ func TestListClients(t *testing.T) { svcReq: clients.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", }, svcRes: clients.ClientsPage{}, svcErr: nil, @@ -543,7 +543,7 @@ func TestListClients(t *testing.T) { svcReq: clients.Page{ Actions: []string{}, Order: "updated_at", - Dir: "asc", + Dir: "desc", Offset: 0, Limit: 100, }, diff --git a/readers/messages.go b/readers/messages.go index 1d9662831..a6accb4b1 100644 --- a/readers/messages.go +++ b/readers/messages.go @@ -43,6 +43,8 @@ type MessagesPage struct { type PageMetadata struct { Offset uint64 `json:"offset"` Limit uint64 `json:"limit"` + Order string `json:"order,omitempty"` + Dir string `json:"dir,omitempty"` Subtopic string `json:"subtopic,omitempty"` Publisher string `json:"publisher,omitempty"` Protocol string `json:"protocol,omitempty"` diff --git a/users/postgres/users.go b/users/postgres/users.go index 0f8d65530..a6abf223f 100644 --- a/users/postgres/users.go +++ b/users/postgres/users.go @@ -119,11 +119,21 @@ func (repo *userRepo) RetrieveAll(ctx context.Context, pm users.Page) (users.Use orderClause := "" switch pm.Order { - case "first_name", "last_name", "username", "email", "created_at", "updated_at": - orderClause = fmt.Sprintf("ORDER BY u.%s", pm.Order) - if pm.Dir == api.AscDir || pm.Dir == api.DescDir { - orderClause = fmt.Sprintf("%s %s", orderClause, pm.Dir) - } + case "first_name": + orderClause = "ORDER BY u.first_name" + case "last_name": + orderClause = "ORDER BY u.last_name" + case "username": + orderClause = "ORDER BY u.username" + case "email": + orderClause = "ORDER BY u.email" + case "created_at": + orderClause = "ORDER BY u.created_at" + case "updated_at": + orderClause = "ORDER BY COALESCE(u.updated_at, u.created_at)" + } + if orderClause != "" && (pm.Dir == api.AscDir || pm.Dir == api.DescDir) { + orderClause = fmt.Sprintf("%s %s, u.id %s", orderClause, pm.Dir, pm.Dir) } q := fmt.Sprintf(`SELECT u.id, u.tags, u.email, u.metadata, u.status, u.role, u.first_name, u.last_name, u.username,