This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 4a6f1e6  Add sort by privilege level to TO and use it in TP (#3682)
4a6f1e6 is described below

commit 4a6f1e62b8fdb4d9fed59ce006e5c6eb9ed07228
Author: Alex Luckerman <[email protected]>
AuthorDate: Wed Aug 7 15:41:25 2019 -0600

    Add sort by privilege level to TO and use it in TP (#3682)
    
    * Add sort by priv_level to TO and use in TP
    
    * Add support for sorting roles by priv_level
    * Change TP to request roles ordered by priv_level
    * Add support to TO Go to order by descending order
    * Change TP to request roles in descending order
    
    * Change TO sort direction parameter
    
    * Require valid orderby before applying sortOrder
    
    * Make requested PR changes
    
    * Fix comment and raise API version to 1.4
    
    * Update CHANGELOG.md
    
    * Fix roles sortBy TO API test
    
    * Fix Go comment
    
    * Remove db-admin binary
---
 CHANGELOG.md                                       |  1 +
 traffic_ops/client/role.go                         | 36 +++++++++++++++++-----
 traffic_ops/testing/api/v14/roles_test.go          | 33 +++++++++++++++++++-
 .../traffic_ops_golang/dbhelpers/db_helpers.go     | 12 +++++++-
 traffic_ops/traffic_ops_golang/role/roles.go       |  6 ++--
 .../app/src/modules/private/roles/list/index.js    |  2 +-
 6 files changed, 76 insertions(+), 14 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ce9f36a..3b51a6f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -38,6 +38,7 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
 - Added /#!/sso page to Traffic Portal to catch redirects back from OAuth 
provider and POST token into the API.
 - In Traffic Portal, server table columns can now be rearranged and their 
visibility toggled on/off as desired by the user. Hidden table columns are 
excluded from the table search. These settings are persisted in the browser.
 - Added pagination support to some Traffic Ops endpoints via three new query 
parameters, limit and offset/page
+- Traffic Ops now supports a "sortOrder" query parameter on some endpoints to 
return API responses in descending order
 
 ### Changed
 - Traffic Router, added TLS certificate validation on certificates imported 
from Traffic Ops
diff --git a/traffic_ops/client/role.go b/traffic_ops/client/role.go
index 5e0c185..5e415ab 100644
--- a/traffic_ops/client/role.go
+++ b/traffic_ops/client/role.go
@@ -26,7 +26,7 @@ import (
 )
 
 const (
-       API_v13_ROLES = "/api/1.3/roles"
+       API_v14_ROLES = "/api/1.4/roles"
 )
 
 // Create a Role
@@ -38,7 +38,7 @@ func (to *Session) CreateRole(region tc.Role) (tc.Alerts, 
ReqInf, int, error) {
        if err != nil {
                return tc.Alerts{}, reqInf, 0, err
        }
-       resp, remoteAddr, errClient := to.rawRequest(http.MethodPost, 
API_v13_ROLES, reqBody)
+       resp, remoteAddr, errClient := to.rawRequest(http.MethodPost, 
API_v14_ROLES, reqBody)
        if resp != nil {
                defer resp.Body.Close()
                var alerts tc.Alerts
@@ -59,7 +59,7 @@ func (to *Session) UpdateRoleByID(id int, region tc.Role) 
(tc.Alerts, ReqInf, in
        if err != nil {
                return tc.Alerts{}, reqInf, 0, err
        }
-       route := fmt.Sprintf("%s/?id=%d", API_v13_ROLES, id)
+       route := fmt.Sprintf("%s/?id=%d", API_v14_ROLES, id)
        resp, remoteAddr, errClient := to.rawRequest(http.MethodPut, route, 
reqBody)
        if resp != nil {
                defer resp.Body.Close()
@@ -74,7 +74,7 @@ func (to *Session) UpdateRoleByID(id int, region tc.Role) 
(tc.Alerts, ReqInf, in
 
 // Returns a list of roles
 func (to *Session) GetRoles() ([]tc.Role, ReqInf, int, error) {
-       resp, remoteAddr, errClient := to.rawRequest(http.MethodGet, 
API_v13_ROLES, nil)
+       resp, remoteAddr, errClient := to.rawRequest(http.MethodGet, 
API_v14_ROLES, nil)
        reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
        if resp != nil {
                defer resp.Body.Close()
@@ -90,7 +90,7 @@ func (to *Session) GetRoles() ([]tc.Role, ReqInf, int, error) 
{
 
 // GET a Role by the Role id
 func (to *Session) GetRoleByID(id int) ([]tc.Role, ReqInf, int, error) {
-       route := fmt.Sprintf("%s/?id=%d", API_v13_ROLES, id)
+       route := fmt.Sprintf("%s/?id=%d", API_v14_ROLES, id)
        resp, remoteAddr, errClient := to.rawRequest(http.MethodGet, route, nil)
        reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
        if resp != nil {
@@ -107,8 +107,28 @@ func (to *Session) GetRoleByID(id int) ([]tc.Role, ReqInf, 
int, error) {
 
 // GET a Role by the Role name
 func (to *Session) GetRoleByName(name string) ([]tc.Role, ReqInf, int, error) {
-       url := fmt.Sprintf("%s?name=%s", API_v13_ROLES, url.QueryEscape(name))
-       resp, remoteAddr, errClient := to.rawRequest(http.MethodGet, url, nil)
+       route := fmt.Sprintf("%s?name=%s", API_v14_ROLES, url.QueryEscape(name))
+       resp, remoteAddr, errClient := to.rawRequest(http.MethodGet, route, nil)
+       reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
+       if resp != nil {
+               defer resp.Body.Close()
+
+               var data tc.RolesResponse
+               if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
+                       return data.Response, reqInf, resp.StatusCode, err
+               }
+               return data.Response, reqInf, resp.StatusCode, errClient
+       }
+       return []tc.Role{}, reqInf, 0, errClient
+}
+
+// GetRoleByQueryParams gets a Role by the Role query parameters
+func (to *Session) GetRoleByQueryParams(queryParams map[string]string) 
([]tc.Role, ReqInf, int, error) {
+       route := fmt.Sprintf("%s?", API_v14_ROLES)
+       for param, val := range queryParams {
+               route += fmt.Sprintf("%s=%s&", url.QueryEscape(param), 
url.QueryEscape(val))
+       }
+       resp, remoteAddr, errClient := to.rawRequest(http.MethodGet, route, nil)
        reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
        if resp != nil {
                defer resp.Body.Close()
@@ -124,7 +144,7 @@ func (to *Session) GetRoleByName(name string) ([]tc.Role, 
ReqInf, int, error) {
 
 // DELETE a Role by ID
 func (to *Session) DeleteRoleByID(id int) (tc.Alerts, ReqInf, int, error) {
-       route := fmt.Sprintf("%s/?id=%d", API_v13_ROLES, id)
+       route := fmt.Sprintf("%s/?id=%d", API_v14_ROLES, id)
        resp, remoteAddr, errClient := to.rawRequest(http.MethodDelete, route, 
nil)
        reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: 
remoteAddr}
        if resp != nil {
diff --git a/traffic_ops/testing/api/v14/roles_test.go 
b/traffic_ops/testing/api/v14/roles_test.go
index 0fd588b..e34c0ce 100644
--- a/traffic_ops/testing/api/v14/roles_test.go
+++ b/traffic_ops/testing/api/v14/roles_test.go
@@ -34,11 +34,12 @@ func TestRoles(t *testing.T) {
        WithObjs(t, []TCObj{Parameters, Roles}, func() {
                UpdateTestRoles(t)
                GetTestRoles(t)
+               VerifyGetRolesOrder(t)
        })
 }
 
 func CreateTestRoles(t *testing.T) {
-       expectedAlerts := []tc.Alerts{tc.Alerts{[]tc.Alert{tc.Alert{"role was 
created.", "success"}}}, tc.Alerts{[]tc.Alert{tc.Alert{"can not add 
non-existent capabilities: [invalid-capability]", "error"}}}}
+       expectedAlerts := []tc.Alerts{tc.Alerts{[]tc.Alert{tc.Alert{"role was 
created.", "success"}}}, tc.Alerts{[]tc.Alert{tc.Alert{"can not add 
non-existent capabilities: [invalid-capability]", "error"}}}, 
tc.Alerts{[]tc.Alert{tc.Alert{"role was created.", "success"}}}}
        for i, role := range testData.Roles {
                var alerts tc.Alerts
                alerts, _, status, err := TOSession.CreateRole(role)
@@ -105,6 +106,36 @@ func GetTestRoles(t *testing.T) {
 
 }
 
+func VerifyGetRolesOrder(t *testing.T) {
+       params := map[string]string{
+               "orderby":   "name",
+               "sortOrder": "desc",
+       }
+       descResp, _, status, err := TOSession.GetRoleByQueryParams(params)
+       log.Debugln("Status Code: ", status)
+       if err != nil {
+               t.Errorf("cannot GET Role by role: %v - %v\n", err, descResp)
+       }
+       params["sortOrder"] = "asc"
+       ascResp, _, status, err := TOSession.GetRoleByQueryParams(params)
+       log.Debugln("Status Code: ", status)
+       if err != nil {
+               t.Errorf("cannot GET Role by role: %v - %v\n", err, ascResp)
+       }
+
+       if reflect.DeepEqual(descResp, ascResp) {
+               t.Errorf("Role responses for descending and ascending are the 
same: %v - %v\n", descResp, ascResp)
+       }
+
+       // reverse the descending-sorted response and compare it to the 
ascending-sorted one
+       for start, end := 0, len(descResp)-1; start < end; start, end = 
start+1, end-1 {
+               descResp[start], descResp[end] = descResp[end], descResp[start]
+       }
+       if !reflect.DeepEqual(descResp, ascResp) {
+               t.Errorf("Role responses are not equal after reversal: %v - 
%v\n", descResp, ascResp)
+       }
+}
+
 func DeleteTestRoles(t *testing.T) {
 
        role := testData.Roles[roleGood]
diff --git a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go 
b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
index dfd6cff..7fbb06d 100644
--- a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
+++ b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go
@@ -63,8 +63,18 @@ func BuildWhereAndOrderByAndPagination(parameters 
map[string]string, queryParams
                if colInfo, ok := queryParamsToSQLCols[orderby]; ok {
                        log.Debugln("orderby column ", colInfo)
                        orderBy += " " + colInfo.Column
+
+                       // if orderby is specified and valid, also check for 
sortOrder
+                       if sortOrder, exists := parameters["sortOrder"]; exists 
{
+                               log.Debugln("sortOrder: ", sortOrder)
+                               if sortOrder == "desc" {
+                                       orderBy += " DESC"
+                               } else if sortOrder != "asc" {
+                                       log.Debugln("sortOrder value must be 
desc or asc. Invalid value provided: ", sortOrder)
+                               }
+                       }
                } else {
-                       log.Debugln("Incorrect name for orderby: ", orderby)
+                       log.Debugln("This column is not configured to support 
orderby: ", orderby)
                }
        }
 
diff --git a/traffic_ops/traffic_ops_golang/role/roles.go 
b/traffic_ops/traffic_ops_golang/role/roles.go
index 1f4b580..68621aa 100644
--- a/traffic_ops/traffic_ops_golang/role/roles.go
+++ b/traffic_ops/traffic_ops_golang/role/roles.go
@@ -50,9 +50,9 @@ func (v *TORole) NewReadObj() interface{}       { return 
&TORole{} }
 func (v *TORole) SelectQuery() string           { return selectQuery() }
 func (v *TORole) ParamColumns() map[string]dbhelpers.WhereColumnInfo {
        return map[string]dbhelpers.WhereColumnInfo{
-               "name": dbhelpers.WhereColumnInfo{"name", nil},
-               "id":   dbhelpers.WhereColumnInfo{"id", api.IsInt},
-       }
+               "name":      dbhelpers.WhereColumnInfo{"name", nil},
+               "id":        dbhelpers.WhereColumnInfo{"id", api.IsInt},
+               "privLevel": dbhelpers.WhereColumnInfo{"priv_level", api.IsInt}}
 }
 func (v *TORole) UpdateQuery() string { return updateQuery() }
 func (v *TORole) DeleteQuery() string { return deleteQuery() }
diff --git a/traffic_portal/app/src/modules/private/roles/list/index.js 
b/traffic_portal/app/src/modules/private/roles/list/index.js
index f991001..a85bde3 100644
--- a/traffic_portal/app/src/modules/private/roles/list/index.js
+++ b/traffic_portal/app/src/modules/private/roles/list/index.js
@@ -28,7 +28,7 @@ module.exports = 
angular.module('trafficPortal.private.roles.list', [])
                                                controller: 
'TableRolesController',
                                                resolve: {
                                                        roles: 
function(roleService) {
-                                                               return 
roleService.getRoles({ orderby: 'priv_level DESC' });
+                                                               return 
roleService.getRoles({ orderby: 'privLevel', sortOrder : 'desc' });
                                                        }
                                                }
                                        }

Reply via email to