diff --git a/pkg/handlers/adminapi/api.go b/pkg/handlers/adminapi/api.go
index 1dc3efa0343..5f8d2960b2e 100644
--- a/pkg/handlers/adminapi/api.go
+++ b/pkg/handlers/adminapi/api.go
@@ -53,16 +53,19 @@ func NewAdminAPI(handlerConfig handlers.HandlerConfig) *adminops.MymoveAPI {
adminAPI.ServeError = handlers.ServeCustomError
+ transportationOfficeFetcher := transportationoffice.NewTransportationOfficesFetcher()
+ userRolesCreator := usersroles.NewUsersRolesCreator()
+ newRolesFetcher := roles.NewRolesFetcher()
+
adminAPI.RequestedOfficeUsersIndexRequestedOfficeUsersHandler = IndexRequestedOfficeUsersHandler{
handlerConfig,
requestedofficeusers.NewRequestedOfficeUsersListFetcher(queryBuilder),
query.NewQueryFilter,
pagination.NewPagination,
+ transportationOfficeFetcher,
+ newRolesFetcher,
}
- userRolesCreator := usersroles.NewUsersRolesCreator()
- newRolesFetcher := roles.NewRolesFetcher()
-
adminAPI.RequestedOfficeUsersGetRequestedOfficeUserHandler = GetRequestedOfficeUserHandler{
handlerConfig,
requestedofficeusers.NewRequestedOfficeUserFetcher(queryBuilder),
@@ -124,7 +127,6 @@ func NewAdminAPI(handlerConfig handlers.HandlerConfig) *adminops.MymoveAPI {
pagination.NewPagination,
}
- transportationOfficeFetcher := transportationoffice.NewTransportationOfficesFetcher()
adminAPI.TransportationOfficesGetOfficeByIDHandler = GetOfficeByIdHandler{
handlerConfig,
transportationOfficeFetcher,
diff --git a/pkg/handlers/adminapi/requested_office_users.go b/pkg/handlers/adminapi/requested_office_users.go
index 5561fd7abf1..1571e8dbbc0 100644
--- a/pkg/handlers/adminapi/requested_office_users.go
+++ b/pkg/handlers/adminapi/requested_office_users.go
@@ -3,12 +3,14 @@ package adminapi
import (
"bytes"
"encoding/json"
+ "errors"
"fmt"
"io"
"net/http"
"strings"
"github.com/go-openapi/runtime/middleware"
+ "github.com/gobuffalo/pop/v6"
"github.com/gofrs/uuid"
"github.com/spf13/viper"
"go.uber.org/zap"
@@ -154,15 +156,29 @@ type IndexRequestedOfficeUsersHandler struct {
services.RequestedOfficeUserListFetcher
services.NewQueryFilter
services.NewPagination
+ services.TransportationOfficesFetcher
+ services.RoleAssociater
}
-var requestedOfficeUserFilterConverters = map[string]func(string) []services.QueryFilter{
- "search": func(content string) []services.QueryFilter {
- nameSearch := fmt.Sprintf("%s%%", content)
- return []services.QueryFilter{
- query.NewQueryFilter("email", "ILIKE", fmt.Sprintf("%%%s%%", content)),
- query.NewQueryFilter("first_name", "ILIKE", nameSearch),
- query.NewQueryFilter("last_name", "ILIKE", nameSearch),
+var requestedOfficeUserFilterConverters = map[string]func(string) func(*pop.Query){
+ "search": func(content string) func(*pop.Query) {
+ return func(query *pop.Query) {
+ nameSearch := fmt.Sprintf("%%%s%%", content)
+ query.Where("office_users.email ILIKE ? AND office_users.status = 'REQUESTED' OR office_users.first_name ILIKE ? AND office_users.status = 'REQUESTED' OR office_users.last_name ILIKE ? AND office_users.status = 'REQUESTED'", nameSearch, nameSearch, nameSearch)
+ }
+ },
+
+ "offices": func(content string) func(*pop.Query) {
+ return func(query *pop.Query) {
+ nameSearch := fmt.Sprintf("%%%s%%", content)
+ query.Where("transportation_offices.name ILIKE ? AND office_users.status = 'REQUESTED'", nameSearch)
+ }
+ },
+
+ "rolesSearch": func(content string) func(*pop.Query) {
+ return func(query *pop.Query) {
+ nameSearch := fmt.Sprintf("%%%s%%", content)
+ query.Where("roles.role_name ILIKE ? AND office_users.status = 'REQUESTED'", nameSearch)
}
},
}
@@ -172,27 +188,25 @@ func (h IndexRequestedOfficeUsersHandler) Handle(params requested_office_users.I
return h.AuditableAppContextFromRequestWithErrors(params.HTTPRequest,
func(appCtx appcontext.AppContext) (middleware.Responder, error) {
- // adding in filters for when a search or filtering is done
- queryFilters := generateQueryFilters(appCtx.Logger(), params.Filter, requestedOfficeUserFilterConverters)
+ var filtersMap map[string]string
+ if params.Filter != nil && *params.Filter != "" {
+ err := json.Unmarshal([]byte(*params.Filter), &filtersMap)
+ if err != nil {
+ return handlers.ResponseForError(appCtx.Logger(), errors.New("invalid filter format")), err
+ }
+ }
- // We only want users that are in a REQUESTED status
- queryFilters = append(queryFilters, query.NewQueryFilter("status", "=", "REQUESTED"))
+ var filterFuncs []func(*pop.Query)
+ for key, filterFunc := range requestedOfficeUserFilterConverters {
+ if filterValue, exists := filtersMap[key]; exists {
+ filterFuncs = append(filterFuncs, filterFunc(filterValue))
+ }
+ }
- // adding in pagination for the UI
pagination := h.NewPagination(params.Page, params.PerPage)
ordering := query.NewQueryOrder(params.Sort, params.Order)
- // need to also get the user's roles
- queryAssociations := query.NewQueryAssociationsPreload([]services.QueryAssociation{
- query.NewQueryAssociation("User.Roles"),
- })
-
- officeUsers, err := h.RequestedOfficeUserListFetcher.FetchRequestedOfficeUsersList(appCtx, queryFilters, queryAssociations, pagination, ordering)
- if err != nil {
- return handlers.ResponseForError(appCtx.Logger(), err), err
- }
-
- totalOfficeUsersCount, err := h.RequestedOfficeUserListFetcher.FetchRequestedOfficeUsersCount(appCtx, queryFilters)
+ officeUsers, count, err := h.RequestedOfficeUserListFetcher.FetchRequestedOfficeUsersList(appCtx, filterFuncs, pagination, ordering)
if err != nil {
return handlers.ResponseForError(appCtx.Logger(), err), err
}
@@ -205,7 +219,7 @@ func (h IndexRequestedOfficeUsersHandler) Handle(params requested_office_users.I
payload[i] = payloadForRequestedOfficeUserModel(s)
}
- return requested_office_users.NewIndexRequestedOfficeUsersOK().WithContentRange(fmt.Sprintf("requested office users %d-%d/%d", pagination.Offset(), pagination.Offset()+queriedOfficeUsersCount, totalOfficeUsersCount)).WithPayload(payload), nil
+ return requested_office_users.NewIndexRequestedOfficeUsersOK().WithContentRange(fmt.Sprintf("requested office users %d-%d/%d", pagination.Offset(), pagination.Offset()+queriedOfficeUsersCount, count)).WithPayload(payload), nil
})
}
diff --git a/pkg/handlers/adminapi/requested_office_users_test.go b/pkg/handlers/adminapi/requested_office_users_test.go
index d84c87ad69c..38f1c9c8afd 100644
--- a/pkg/handlers/adminapi/requested_office_users_test.go
+++ b/pkg/handlers/adminapi/requested_office_users_test.go
@@ -25,9 +25,7 @@ import (
)
func (suite *HandlerSuite) TestIndexRequestedOfficeUsersHandler() {
- // test that everything is wired up
suite.Run("requested users result in ok response", func() {
- // building two office user with requested status
requestedOfficeUsers := models.OfficeUsers{
factory.BuildOfficeUserWithRoles(suite.DB(), factory.GetTraitRequestedOfficeUser(), []roles.RoleType{roles.RoleTypeQae}),
factory.BuildOfficeUserWithRoles(suite.DB(), factory.GetTraitRequestedOfficeUser(), []roles.RoleType{roles.RoleTypeQae})}
@@ -45,16 +43,377 @@ func (suite *HandlerSuite) TestIndexRequestedOfficeUsersHandler() {
response := handler.Handle(params)
- // should get an ok response
suite.IsType(&requestedofficeuserop.IndexRequestedOfficeUsersOK{}, response)
okResponse := response.(*requestedofficeuserop.IndexRequestedOfficeUsersOK)
suite.Len(okResponse.Payload, 2)
- suite.Equal(requestedOfficeUsers[0].ID.String(), okResponse.Payload[0].ID.String())
+ requestedOfficeUser1Id := requestedOfficeUsers[0].ID.String()
+ requestedOfficeUser2Id := requestedOfficeUsers[1].ID.String()
+ payloadRequestedUser1Id := okResponse.Payload[0].ID.String()
+ payloadRequestedUser2Id := okResponse.Payload[1].ID.String()
+
+ // requested office users should exist in response no matter the ordering that has been applied
+ user1ExistsInResponse := requestedOfficeUser1Id == payloadRequestedUser1Id || requestedOfficeUser1Id == payloadRequestedUser2Id
+ user2ExistsInResponse := requestedOfficeUser2Id == payloadRequestedUser1Id || requestedOfficeUser2Id == payloadRequestedUser2Id
+ suite.True(user1ExistsInResponse)
+ suite.True(user2ExistsInResponse)
+ })
+
+ suite.Run("able to search by name & email", func() {
+ requestedStatus := models.OfficeUserStatusREQUESTED
+ officeUser1 := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ FirstName: "Angelina",
+ LastName: "Jolie",
+ Email: "laraCroft@mail.mil",
+ Status: &requestedStatus,
+ },
+ },
+ }, []roles.RoleType{roles.RoleTypeTOO})
+ officeUser2 := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ FirstName: "Billy",
+ LastName: "Bob",
+ Email: "bigBob@mail.mil",
+ Status: &requestedStatus,
+ },
+ },
+ }, []roles.RoleType{roles.RoleTypeTIO})
+ officeUser3 := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ FirstName: "Nick",
+ LastName: "Cage",
+ Email: "conAirKilluh@mail.mil",
+ Status: &requestedStatus,
+ },
+ },
+ }, []roles.RoleType{roles.RoleTypeServicesCounselor})
+
+ // partial first name search
+ filterJSON := "{\"search\":\"Angel\"}"
+ params := requestedofficeuserop.IndexRequestedOfficeUsersParams{
+ HTTPRequest: suite.setupAuthenticatedRequest("GET", "/requested_office_users"),
+ Filter: &filterJSON,
+ }
+
+ queryBuilder := query.NewQueryBuilder()
+ handler := IndexRequestedOfficeUsersHandler{
+ HandlerConfig: suite.HandlerConfig(),
+ NewQueryFilter: query.NewQueryFilter,
+ RequestedOfficeUserListFetcher: requestedofficeusers.NewRequestedOfficeUsersListFetcher(queryBuilder),
+ NewPagination: pagination.NewPagination,
+ }
+
+ response := handler.Handle(params)
+
+ suite.IsType(&requestedofficeuserop.IndexRequestedOfficeUsersOK{}, response)
+ okResponse := response.(*requestedofficeuserop.IndexRequestedOfficeUsersOK)
+ suite.Len(okResponse.Payload, 1)
+ suite.Equal(officeUser1.ID.String(), okResponse.Payload[0].ID.String())
+
+ // search by first name
+ filterJSON = "{\"search\":\"Bill\"}"
+ params = requestedofficeuserop.IndexRequestedOfficeUsersParams{
+ HTTPRequest: suite.setupAuthenticatedRequest("GET", "/requested_office_users"),
+ Filter: &filterJSON,
+ }
+ response = handler.Handle(params)
+
+ suite.IsType(&requestedofficeuserop.IndexRequestedOfficeUsersOK{}, response)
+ okResponse = response.(*requestedofficeuserop.IndexRequestedOfficeUsersOK)
+ suite.Len(okResponse.Payload, 1)
+ suite.Equal(officeUser2.ID.String(), okResponse.Payload[0].ID.String())
+
+ // email search
+ filterJSON = "{\"search\":\"conAir\"}"
+ params = requestedofficeuserop.IndexRequestedOfficeUsersParams{
+ HTTPRequest: suite.setupAuthenticatedRequest("GET", "/requested_office_users"),
+ Filter: &filterJSON,
+ }
+ response = handler.Handle(params)
+
+ suite.IsType(&requestedofficeuserop.IndexRequestedOfficeUsersOK{}, response)
+ okResponse = response.(*requestedofficeuserop.IndexRequestedOfficeUsersOK)
+ suite.Len(okResponse.Payload, 1)
+ suite.Equal(officeUser3.ID.String(), okResponse.Payload[0].ID.String())
+ })
+
+ suite.Run("test the return of sorted requested office users in asc order", func() {
+ requestedStatus := models.OfficeUserStatusREQUESTED
+ officeUser1 := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ FirstName: "Angelina",
+ LastName: "Jolie",
+ Email: "laraCroft@mail.mil",
+ Status: &requestedStatus,
+ },
+ },
+ {
+ Model: models.TransportationOffice{
+ Name: "PPPO Kirtland AFB - USAF",
+ },
+ },
+ }, []roles.RoleType{roles.RoleTypeTOO})
+ officeUser2 := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ FirstName: "Billy",
+ LastName: "Bob",
+ Email: "bigBob@mail.mil",
+ Status: &requestedStatus,
+ },
+ },
+ {
+ Model: models.TransportationOffice{
+ Name: "PPPO Fort Knox - USA",
+ },
+ },
+ }, []roles.RoleType{roles.RoleTypeTIO})
+ officeUser3 := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ FirstName: "Nick",
+ LastName: "Cage",
+ Email: "conAirKilluh@mail.mil",
+ Status: &requestedStatus,
+ },
+ },
+ {
+ Model: models.TransportationOffice{
+ Name: "PPPO Detroit Arsenal - USA",
+ },
+ },
+ }, []roles.RoleType{roles.RoleTypeServicesCounselor})
+
+ sortColumn := "transportation_office_id"
+ params := requestedofficeuserop.IndexRequestedOfficeUsersParams{
+ HTTPRequest: suite.setupAuthenticatedRequest("GET", "/requested_office_users"),
+ Sort: &sortColumn,
+ Order: models.BoolPointer(true),
+ }
+
+ queryBuilder := query.NewQueryBuilder()
+ handler := IndexRequestedOfficeUsersHandler{
+ HandlerConfig: suite.HandlerConfig(),
+ NewQueryFilter: query.NewQueryFilter,
+ RequestedOfficeUserListFetcher: requestedofficeusers.NewRequestedOfficeUsersListFetcher(queryBuilder),
+ NewPagination: pagination.NewPagination,
+ }
+
+ response := handler.Handle(params)
+
+ suite.IsType(&requestedofficeuserop.IndexRequestedOfficeUsersOK{}, response)
+ okResponse := response.(*requestedofficeuserop.IndexRequestedOfficeUsersOK)
+ suite.Len(okResponse.Payload, 3)
+ suite.Equal(officeUser3.ID.String(), okResponse.Payload[0].ID.String())
+ suite.Equal(officeUser2.ID.String(), okResponse.Payload[1].ID.String())
+ suite.Equal(officeUser1.ID.String(), okResponse.Payload[2].ID.String())
+
+ // sort by transportation office name in desc order
+ sortColumn = "transportation_office_id"
+ params = requestedofficeuserop.IndexRequestedOfficeUsersParams{
+ HTTPRequest: suite.setupAuthenticatedRequest("GET", "/requested_office_users"),
+ Sort: &sortColumn,
+ Order: models.BoolPointer(false),
+ }
+
+ queryBuilder = query.NewQueryBuilder()
+ handler = IndexRequestedOfficeUsersHandler{
+ HandlerConfig: suite.HandlerConfig(),
+ NewQueryFilter: query.NewQueryFilter,
+ RequestedOfficeUserListFetcher: requestedofficeusers.NewRequestedOfficeUsersListFetcher(queryBuilder),
+ NewPagination: pagination.NewPagination,
+ }
+
+ response = handler.Handle(params)
+
+ suite.IsType(&requestedofficeuserop.IndexRequestedOfficeUsersOK{}, response)
+ okResponse = response.(*requestedofficeuserop.IndexRequestedOfficeUsersOK)
+ suite.Len(okResponse.Payload, 3)
+ suite.Equal(officeUser1.ID.String(), okResponse.Payload[0].ID.String())
+ suite.Equal(officeUser2.ID.String(), okResponse.Payload[1].ID.String())
+ suite.Equal(officeUser3.ID.String(), okResponse.Payload[2].ID.String())
+
+ // sort by first name in asc order
+ sortColumn = "first_name"
+ params = requestedofficeuserop.IndexRequestedOfficeUsersParams{
+ HTTPRequest: suite.setupAuthenticatedRequest("GET", "/requested_office_users"),
+ Sort: &sortColumn,
+ Order: models.BoolPointer(true),
+ }
+
+ queryBuilder = query.NewQueryBuilder()
+ handler = IndexRequestedOfficeUsersHandler{
+ HandlerConfig: suite.HandlerConfig(),
+ NewQueryFilter: query.NewQueryFilter,
+ RequestedOfficeUserListFetcher: requestedofficeusers.NewRequestedOfficeUsersListFetcher(queryBuilder),
+ NewPagination: pagination.NewPagination,
+ }
+
+ response = handler.Handle(params)
+
+ suite.IsType(&requestedofficeuserop.IndexRequestedOfficeUsersOK{}, response)
+ okResponse = response.(*requestedofficeuserop.IndexRequestedOfficeUsersOK)
+ suite.Len(okResponse.Payload, 3)
+ suite.Equal(officeUser1.ID.String(), okResponse.Payload[0].ID.String())
+ suite.Equal(officeUser2.ID.String(), okResponse.Payload[1].ID.String())
+ suite.Equal(officeUser3.ID.String(), okResponse.Payload[2].ID.String())
+ })
+
+ suite.Run("able to search by transportation office", func() {
+ transportationOffice := factory.BuildTransportationOffice(suite.DB(), []factory.Customization{
+ {
+ Model: models.TransportationOffice{
+ Name: "Tinker",
+ },
+ },
+ }, nil)
+ requestedStatus := models.OfficeUserStatusREQUESTED
+ officeUser1 := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ TransportationOfficeID: transportationOffice.ID,
+ Status: &requestedStatus,
+ },
+ },
+ {
+ Model: transportationOffice,
+ LinkOnly: true,
+ Type: &factory.TransportationOffices.CounselingOffice,
+ },
+ }, []roles.RoleType{roles.RoleTypeServicesCounselor})
+ factory.BuildOfficeUserWithRoles(suite.DB(), factory.GetTraitRequestedOfficeUser(), []roles.RoleType{roles.RoleTypeTOO})
+ factory.BuildOfficeUserWithRoles(suite.DB(), factory.GetTraitRequestedOfficeUser(), []roles.RoleType{roles.RoleTypeTIO})
+
+ filterJSON := "{\"offices\":\"Tinker\"}"
+ params := requestedofficeuserop.IndexRequestedOfficeUsersParams{
+ HTTPRequest: suite.setupAuthenticatedRequest("GET", "/requested_office_users"),
+ Filter: &filterJSON,
+ }
+
+ queryBuilder := query.NewQueryBuilder()
+ handler := IndexRequestedOfficeUsersHandler{
+ HandlerConfig: suite.HandlerConfig(),
+ NewQueryFilter: query.NewQueryFilter,
+ RequestedOfficeUserListFetcher: requestedofficeusers.NewRequestedOfficeUsersListFetcher(queryBuilder),
+ NewPagination: pagination.NewPagination,
+ }
+
+ response := handler.Handle(params)
+
+ suite.IsType(&requestedofficeuserop.IndexRequestedOfficeUsersOK{}, response)
+ okResponse := response.(*requestedofficeuserop.IndexRequestedOfficeUsersOK)
+ suite.Len(okResponse.Payload, 1)
+ suite.Equal(officeUser1.ID.String(), okResponse.Payload[0].ID.String())
+ })
+
+ suite.Run("able to search by role", func() {
+ requestedStatus := models.OfficeUserStatusREQUESTED
+ officeUser1 := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ Status: &requestedStatus,
+ },
+ },
+ }, []roles.RoleType{roles.RoleTypeServicesCounselor})
+
+ filterJSON := "{\"rolesSearch\":\"services\"}"
+ params := requestedofficeuserop.IndexRequestedOfficeUsersParams{
+ HTTPRequest: suite.setupAuthenticatedRequest("GET", "/requested_office_users"),
+ Filter: &filterJSON,
+ }
+
+ queryBuilder := query.NewQueryBuilder()
+ handler := IndexRequestedOfficeUsersHandler{
+ HandlerConfig: suite.HandlerConfig(),
+ NewQueryFilter: query.NewQueryFilter,
+ RequestedOfficeUserListFetcher: requestedofficeusers.NewRequestedOfficeUsersListFetcher(queryBuilder),
+ NewPagination: pagination.NewPagination,
+ }
+
+ response := handler.Handle(params)
+
+ suite.IsType(&requestedofficeuserop.IndexRequestedOfficeUsersOK{}, response)
+ okResponse := response.(*requestedofficeuserop.IndexRequestedOfficeUsersOK)
+ suite.Len(okResponse.Payload, 1)
+ suite.Equal(officeUser1.ID.String(), okResponse.Payload[0].ID.String())
+ })
+
+ suite.Run("return error when querying for unhandled data", func() {
+ requestedStatus := models.OfficeUserStatusREQUESTED
+ factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ Status: &requestedStatus,
+ },
+ },
+ }, []roles.RoleType{roles.RoleTypeServicesCounselor})
+
+ sortColumn := "unknown_column"
+ params := requestedofficeuserop.IndexRequestedOfficeUsersParams{
+ HTTPRequest: suite.setupAuthenticatedRequest("GET", "/requested_office_users"),
+ Sort: &sortColumn,
+ Order: models.BoolPointer(true),
+ }
+
+ queryBuilder := query.NewQueryBuilder()
+ handler := IndexRequestedOfficeUsersHandler{
+ HandlerConfig: suite.HandlerConfig(),
+ NewQueryFilter: query.NewQueryFilter,
+ RequestedOfficeUserListFetcher: requestedofficeusers.NewRequestedOfficeUsersListFetcher(queryBuilder),
+ NewPagination: pagination.NewPagination,
+ }
+
+ response := handler.Handle(params)
+
+ suite.IsType(&handlers.ErrResponse{}, response)
+ errResponse := response.(*handlers.ErrResponse)
+ suite.Equal(http.StatusInternalServerError, errResponse.Code)
+ errMsg := errResponse.Err.Error()
+ suite.Equal(errMsg, "Unhandled data error encountered")
+ })
+
+ suite.Run("should error when a param filter format is incorrect", func() {
+ requestedStatus := models.OfficeUserStatusREQUESTED
+ factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ Status: &requestedStatus,
+ },
+ },
+ }, []roles.RoleType{roles.RoleTypeServicesCounselor})
+
+ // Invalid format for filter params
+ filterJSON := "test{\"unknown\":\"value\"}test"
+ params := requestedofficeuserop.IndexRequestedOfficeUsersParams{
+ HTTPRequest: suite.setupAuthenticatedRequest("GET", "/requested_office_users"),
+ Filter: &filterJSON,
+ }
+
+ queryBuilder := query.NewQueryBuilder()
+ handler := IndexRequestedOfficeUsersHandler{
+ HandlerConfig: suite.HandlerConfig(),
+ NewQueryFilter: query.NewQueryFilter,
+ RequestedOfficeUserListFetcher: requestedofficeusers.NewRequestedOfficeUsersListFetcher(queryBuilder),
+ NewPagination: pagination.NewPagination,
+ }
+
+ response := handler.Handle(params)
+
+ expectedError := models.ErrInvalidFilterFormat
+ expectedResponse := &handlers.ErrResponse{
+ Code: http.StatusInternalServerError,
+ Err: expectedError,
+ }
+
+ suite.Equal(expectedResponse, response)
})
}
func (suite *HandlerSuite) TestGetRequestedOfficeUserHandler() {
- // test that everything is wired up
suite.Run("integration test ok response", func() {
requestedOfficeUser := factory.BuildOfficeUserWithRoles(suite.DB(), factory.GetTraitRequestedOfficeUser(), []roles.RoleType{roles.RoleTypeQae})
params := requestedofficeuserop.GetRequestedOfficeUserParams{
diff --git a/pkg/handlers/ghcapi/tranportation_offices.go b/pkg/handlers/ghcapi/tranportation_offices.go
index 5df15bb6aaa..b08f45cfb04 100644
--- a/pkg/handlers/ghcapi/tranportation_offices.go
+++ b/pkg/handlers/ghcapi/tranportation_offices.go
@@ -23,7 +23,7 @@ func (h GetTransportationOfficesHandler) Handle(params transportationofficeop.Ge
// B-21022: forPpm param is set true. This is used by PPM closeout widget. Need to ensure certain offices are included/excluded
// if location has ppm closedout enabled.
- transportationOffices, err := h.TransportationOfficesFetcher.GetTransportationOffices(appCtx, params.Search, true)
+ transportationOffices, err := h.TransportationOfficesFetcher.GetTransportationOffices(appCtx, params.Search, true, false)
if err != nil {
appCtx.Logger().Error("Error searching for Transportation Offices: ", zap.Error(err))
@@ -44,7 +44,7 @@ func (h GetTransportationOfficesOpenHandler) Handle(params transportationofficeo
return h.AuditableAppContextFromRequestWithErrors(params.HTTPRequest,
func(appCtx appcontext.AppContext) (middleware.Responder, error) {
- transportationOffices, err := h.TransportationOfficesFetcher.GetTransportationOffices(appCtx, params.Search, false)
+ transportationOffices, err := h.TransportationOfficesFetcher.GetTransportationOffices(appCtx, params.Search, false, false)
if err != nil {
appCtx.Logger().Error("Error searching for Transportation Offices: ", zap.Error(err))
return transportationofficeop.NewGetTransportationOfficesOpenInternalServerError(), err
diff --git a/pkg/handlers/internalapi/transportation_offices.go b/pkg/handlers/internalapi/transportation_offices.go
index f1f7eb706eb..7f8529fe479 100644
--- a/pkg/handlers/internalapi/transportation_offices.go
+++ b/pkg/handlers/internalapi/transportation_offices.go
@@ -51,7 +51,7 @@ func (h GetTransportationOfficesHandler) Handle(params transportationofficeop.Ge
return transportationofficeop.NewGetTransportationOfficesForbidden(), noServiceMemberIDErr
}
- transportationOffices, err := h.TransportationOfficesFetcher.GetTransportationOffices(appCtx, params.Search, true)
+ transportationOffices, err := h.TransportationOfficesFetcher.GetTransportationOffices(appCtx, params.Search, true, false)
if err != nil {
appCtx.Logger().Error("Error searching for Transportation Offices: ", zap.Error(err))
return transportationofficeop.NewGetTransportationOfficesInternalServerError(), err
diff --git a/pkg/models/errors.go b/pkg/models/errors.go
index 2a1cdeeff0e..78011442f48 100644
--- a/pkg/models/errors.go
+++ b/pkg/models/errors.go
@@ -63,3 +63,6 @@ var ErrMissingDestinationAddress = errors.New("DESTINATION_ADDRESS_MISSING")
// ErrUnsupportedShipmentType is used if the shipment type is not supported by a method
var ErrUnsupportedShipmentType = errors.New("UNSUPPORTED_SHIPMENT_TYPE")
+
+// ErrInvalidFilterFormat is used if the param filter is not in the expected format
+var ErrInvalidFilterFormat = errors.New("invalid filter format")
diff --git a/pkg/models/roles/roles.go b/pkg/models/roles/roles.go
index 10fedc99b6e..dd326a3f895 100644
--- a/pkg/models/roles/roles.go
+++ b/pkg/models/roles/roles.go
@@ -94,3 +94,25 @@ func FetchRolesForUser(db *pop.Connection, userID uuid.UUID) (Roles, error) {
All(&roles)
return roles, err
}
+
+// Fetch like roles based on the search parameter
+func FindRoles(db *pop.Connection, search string) (Roles, error) {
+ var rolesList Roles
+
+ // The % operator filters out strings that are below this similarity threshold
+ err := db.Q().RawQuery("SET pg_trgm.similarity_threshold = 0.03").Exec()
+ if err != nil {
+ return rolesList, err
+ }
+
+ sqlQuery := `select * from roles where role_name % $1`
+
+ query := db.Q().RawQuery(sqlQuery, search)
+ if err := query.All(&rolesList); err != nil {
+ if err != nil {
+ return rolesList, err
+ }
+ }
+
+ return rolesList, nil
+}
diff --git a/pkg/models/roles/roles_test.go b/pkg/models/roles/roles_test.go
index 9ec5a0e7d86..6bcf0c07704 100644
--- a/pkg/models/roles/roles_test.go
+++ b/pkg/models/roles/roles_test.go
@@ -3,10 +3,12 @@ package roles_test
import (
"testing"
+ "github.com/gofrs/uuid"
"github.com/stretchr/testify/suite"
"github.com/transcom/mymove/pkg/factory"
"github.com/transcom/mymove/pkg/models"
+ "github.com/transcom/mymove/pkg/models/roles"
m "github.com/transcom/mymove/pkg/models/roles"
"github.com/transcom/mymove/pkg/testingsuite"
)
@@ -66,3 +68,36 @@ func (suite *RolesSuite) TestFetchRolesForUser() {
suite.NoError(err)
suite.Equal(1, len(userRoles), userRoles)
}
+
+func (suite *RolesSuite) TestFindRoles() {
+ id1, _ := uuid.NewV4()
+ role1 := roles.Role{
+ ID: id1,
+ RoleName: "Task Invoicing Officer",
+ RoleType: "role1",
+ }
+
+ id2, _ := uuid.NewV4()
+ role2 := roles.Role{
+ ID: id2,
+ RoleName: "Task Ordering Officer",
+ RoleType: "role2",
+ }
+
+ id3, _ := uuid.NewV4()
+ role3 := roles.Role{
+ ID: id3,
+ RoleName: "Contracting Officer",
+ RoleType: "role3",
+ }
+
+ // Create roles
+ rs := roles.Roles{role1, role2, role3}
+ err := suite.DB().Create(rs)
+ suite.NoError(err)
+
+ userRoles, err := m.FindRoles(suite.DB(), "Ta")
+
+ suite.NoError(err)
+ suite.GreaterOrEqual(len(userRoles), 2)
+}
diff --git a/pkg/services/mocks/RequestedOfficeUserListFetcher.go b/pkg/services/mocks/RequestedOfficeUserListFetcher.go
index 98a226808eb..98211d4b57b 100644
--- a/pkg/services/mocks/RequestedOfficeUserListFetcher.go
+++ b/pkg/services/mocks/RequestedOfficeUserListFetcher.go
@@ -8,6 +8,8 @@ import (
models "github.com/transcom/mymove/pkg/models"
+ pop "github.com/gobuffalo/pop/v6"
+
services "github.com/transcom/mymove/pkg/services"
)
@@ -44,34 +46,41 @@ func (_m *RequestedOfficeUserListFetcher) FetchRequestedOfficeUsersCount(appCtx
return r0, r1
}
-// FetchRequestedOfficeUsersList provides a mock function with given fields: appCtx, filters, associations, pagination, ordering
-func (_m *RequestedOfficeUserListFetcher) FetchRequestedOfficeUsersList(appCtx appcontext.AppContext, filters []services.QueryFilter, associations services.QueryAssociations, pagination services.Pagination, ordering services.QueryOrder) (models.OfficeUsers, error) {
- ret := _m.Called(appCtx, filters, associations, pagination, ordering)
+// FetchRequestedOfficeUsersList provides a mock function with given fields: appCtx, filterFuncs, pagination, ordering
+func (_m *RequestedOfficeUserListFetcher) FetchRequestedOfficeUsersList(appCtx appcontext.AppContext, filterFuncs []func(*pop.Query), pagination services.Pagination, ordering services.QueryOrder) (models.OfficeUsers, int, error) {
+ ret := _m.Called(appCtx, filterFuncs, pagination, ordering)
if len(ret) == 0 {
panic("no return value specified for FetchRequestedOfficeUsersList")
}
var r0 models.OfficeUsers
- var r1 error
- if rf, ok := ret.Get(0).(func(appcontext.AppContext, []services.QueryFilter, services.QueryAssociations, services.Pagination, services.QueryOrder) (models.OfficeUsers, error)); ok {
- return rf(appCtx, filters, associations, pagination, ordering)
+ var r1 int
+ var r2 error
+ if rf, ok := ret.Get(0).(func(appcontext.AppContext, []func(*pop.Query), services.Pagination, services.QueryOrder) (models.OfficeUsers, int, error)); ok {
+ return rf(appCtx, filterFuncs, pagination, ordering)
}
- if rf, ok := ret.Get(0).(func(appcontext.AppContext, []services.QueryFilter, services.QueryAssociations, services.Pagination, services.QueryOrder) models.OfficeUsers); ok {
- r0 = rf(appCtx, filters, associations, pagination, ordering)
+ if rf, ok := ret.Get(0).(func(appcontext.AppContext, []func(*pop.Query), services.Pagination, services.QueryOrder) models.OfficeUsers); ok {
+ r0 = rf(appCtx, filterFuncs, pagination, ordering)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(models.OfficeUsers)
}
}
- if rf, ok := ret.Get(1).(func(appcontext.AppContext, []services.QueryFilter, services.QueryAssociations, services.Pagination, services.QueryOrder) error); ok {
- r1 = rf(appCtx, filters, associations, pagination, ordering)
+ if rf, ok := ret.Get(1).(func(appcontext.AppContext, []func(*pop.Query), services.Pagination, services.QueryOrder) int); ok {
+ r1 = rf(appCtx, filterFuncs, pagination, ordering)
} else {
- r1 = ret.Error(1)
+ r1 = ret.Get(1).(int)
}
- return r0, r1
+ if rf, ok := ret.Get(2).(func(appcontext.AppContext, []func(*pop.Query), services.Pagination, services.QueryOrder) error); ok {
+ r2 = rf(appCtx, filterFuncs, pagination, ordering)
+ } else {
+ r2 = ret.Error(2)
+ }
+
+ return r0, r1, r2
}
// NewRequestedOfficeUserListFetcher creates a new instance of RequestedOfficeUserListFetcher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
diff --git a/pkg/services/mocks/TransportationOfficesFetcher.go b/pkg/services/mocks/TransportationOfficesFetcher.go
index ad777d72b0a..d0c017b3e19 100644
--- a/pkg/services/mocks/TransportationOfficesFetcher.go
+++ b/pkg/services/mocks/TransportationOfficesFetcher.go
@@ -106,9 +106,9 @@ func (_m *TransportationOfficesFetcher) GetTransportationOffice(appCtx appcontex
return r0, r1
}
-// GetTransportationOffices provides a mock function with given fields: appCtx, search, forPpm
-func (_m *TransportationOfficesFetcher) GetTransportationOffices(appCtx appcontext.AppContext, search string, forPpm bool) (*models.TransportationOffices, error) {
- ret := _m.Called(appCtx, search, forPpm)
+// GetTransportationOffices provides a mock function with given fields: appCtx, search, forPpm, forAdminOfficeUserReqFilter
+func (_m *TransportationOfficesFetcher) GetTransportationOffices(appCtx appcontext.AppContext, search string, forPpm bool, forAdminOfficeUserReqFilter bool) (*models.TransportationOffices, error) {
+ ret := _m.Called(appCtx, search, forPpm, forAdminOfficeUserReqFilter)
if len(ret) == 0 {
panic("no return value specified for GetTransportationOffices")
@@ -116,19 +116,19 @@ func (_m *TransportationOfficesFetcher) GetTransportationOffices(appCtx appconte
var r0 *models.TransportationOffices
var r1 error
- if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, bool) (*models.TransportationOffices, error)); ok {
- return rf(appCtx, search, forPpm)
+ if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, bool, bool) (*models.TransportationOffices, error)); ok {
+ return rf(appCtx, search, forPpm, forAdminOfficeUserReqFilter)
}
- if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, bool) *models.TransportationOffices); ok {
- r0 = rf(appCtx, search, forPpm)
+ if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, bool, bool) *models.TransportationOffices); ok {
+ r0 = rf(appCtx, search, forPpm, forAdminOfficeUserReqFilter)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.TransportationOffices)
}
}
- if rf, ok := ret.Get(1).(func(appcontext.AppContext, string, bool) error); ok {
- r1 = rf(appCtx, search, forPpm)
+ if rf, ok := ret.Get(1).(func(appcontext.AppContext, string, bool, bool) error); ok {
+ r1 = rf(appCtx, search, forPpm, forAdminOfficeUserReqFilter)
} else {
r1 = ret.Error(1)
}
diff --git a/pkg/services/requested_office_users.go b/pkg/services/requested_office_users.go
index 574d5915c83..ad917128256 100644
--- a/pkg/services/requested_office_users.go
+++ b/pkg/services/requested_office_users.go
@@ -1,6 +1,7 @@
package services
import (
+ "github.com/gobuffalo/pop/v6"
"github.com/gobuffalo/validate/v3"
"github.com/gofrs/uuid"
@@ -13,7 +14,7 @@ import (
//
//go:generate mockery --name RequestedOfficeUserListFetcher
type RequestedOfficeUserListFetcher interface {
- FetchRequestedOfficeUsersList(appCtx appcontext.AppContext, filters []QueryFilter, associations QueryAssociations, pagination Pagination, ordering QueryOrder) (models.OfficeUsers, error)
+ FetchRequestedOfficeUsersList(appCtx appcontext.AppContext, filterFuncs []func(*pop.Query), pagination Pagination, ordering QueryOrder) (models.OfficeUsers, int, error)
FetchRequestedOfficeUsersCount(appCtx appcontext.AppContext, filters []QueryFilter) (int, error)
}
diff --git a/pkg/services/requested_office_users/requested_office_user_list_fetcher_test.go b/pkg/services/requested_office_users/requested_office_user_list_fetcher_test.go
index b3271f90a9c..9192bf73d6e 100644
--- a/pkg/services/requested_office_users/requested_office_user_list_fetcher_test.go
+++ b/pkg/services/requested_office_users/requested_office_user_list_fetcher_test.go
@@ -1,26 +1,17 @@
package adminuser
import (
- "errors"
- "reflect"
-
- "github.com/gofrs/uuid"
-
"github.com/transcom/mymove/pkg/appcontext"
+ "github.com/transcom/mymove/pkg/factory"
"github.com/transcom/mymove/pkg/models"
+ "github.com/transcom/mymove/pkg/models/roles"
"github.com/transcom/mymove/pkg/services"
"github.com/transcom/mymove/pkg/services/pagination"
"github.com/transcom/mymove/pkg/services/query"
)
type testRequestedOfficeUsersListQueryBuilder struct {
- fakeFetchMany func(appCtx appcontext.AppContext, model interface{}) error
- fakeCount func(appCtx appcontext.AppContext, model interface{}) (int, error)
-}
-
-func (t *testRequestedOfficeUsersListQueryBuilder) FetchMany(appCtx appcontext.AppContext, model interface{}, _ []services.QueryFilter, _ services.QueryAssociations, _ services.Pagination, _ services.QueryOrder) error {
- m := t.fakeFetchMany(appCtx, model)
- return m
+ fakeCount func(appCtx appcontext.AppContext, model interface{}) (int, error)
}
func (t *testRequestedOfficeUsersListQueryBuilder) Count(appCtx appcontext.AppContext, model interface{}, _ []services.QueryFilter) (int, error) {
@@ -33,50 +24,119 @@ func defaultPagination() services.Pagination {
return pagination.NewPagination(&page, &perPage)
}
-func defaultAssociations() services.QueryAssociations {
- return query.NewQueryAssociations([]services.QueryAssociation{})
-}
-
func defaultOrdering() services.QueryOrder {
return query.NewQueryOrder(nil, nil)
}
func (suite *RequestedOfficeUsersServiceSuite) TestFetchRequestedOfficeUserList() {
suite.Run("if the users are successfully fetched, they should be returned", func() {
- id, err := uuid.NewV4()
+ requestedStatus := models.OfficeUserStatusREQUESTED
+ officeUser1 := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ Status: &requestedStatus,
+ },
+ },
+ }, []roles.RoleType{roles.RoleTypeTOO})
+ builder := &testRequestedOfficeUsersListQueryBuilder{}
+
+ fetcher := NewRequestedOfficeUsersListFetcher(builder)
+
+ requestedOfficeUsers, _, err := fetcher.FetchRequestedOfficeUsersList(suite.AppContextForTest(), nil, defaultPagination(), defaultOrdering())
+
suite.NoError(err)
- fakeFetchMany := func(_ appcontext.AppContext, model interface{}) error {
- value := reflect.ValueOf(model).Elem()
- requestedStatus := models.OfficeUserStatusREQUESTED
- value.Set(reflect.Append(value, reflect.ValueOf(models.OfficeUser{ID: id, Status: &requestedStatus})))
- return nil
- }
- builder := &testRequestedOfficeUsersListQueryBuilder{
- fakeFetchMany: fakeFetchMany,
- }
+ suite.Equal(officeUser1.ID, requestedOfficeUsers[0].ID)
+ })
+
+ suite.Run("if there are no requested office users, we don't receive any requested office users", func() {
+ builder := &testRequestedOfficeUsersListQueryBuilder{}
fetcher := NewRequestedOfficeUsersListFetcher(builder)
- requestedOfficeUsers, err := fetcher.FetchRequestedOfficeUsersList(suite.AppContextForTest(), nil, defaultAssociations(), defaultPagination(), defaultOrdering())
+ requestedOfficeUsers, _, err := fetcher.FetchRequestedOfficeUsersList(suite.AppContextForTest(), nil, defaultPagination(), defaultOrdering())
suite.NoError(err)
- suite.Equal(id, requestedOfficeUsers[0].ID)
+ suite.Equal(models.OfficeUsers(nil), requestedOfficeUsers)
})
- suite.Run("if there is an error, we get it with no requested office users", func() {
- fakeFetchMany := func(_ appcontext.AppContext, _ interface{}) error {
- return errors.New("Fetch error")
- }
- builder := &testRequestedOfficeUsersListQueryBuilder{
- fakeFetchMany: fakeFetchMany,
- }
+ suite.Run("should sort and order requested office users", func() {
+ requestedStatus := models.OfficeUserStatusREQUESTED
+ officeUser1 := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ FirstName: "Angelina",
+ LastName: "Jolie",
+ Email: "laraCroft@mail.mil",
+ Status: &requestedStatus,
+ },
+ },
+ {
+ Model: models.TransportationOffice{
+ Name: "PPPO Kirtland AFB - USAF",
+ },
+ },
+ }, []roles.RoleType{roles.RoleTypeTOO})
+ officeUser2 := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ FirstName: "Billy",
+ LastName: "Bob",
+ Email: "bigBob@mail.mil",
+ Status: &requestedStatus,
+ },
+ },
+ {
+ Model: models.TransportationOffice{
+ Name: "PPPO Fort Knox - USA",
+ },
+ },
+ }, []roles.RoleType{roles.RoleTypeTIO})
+ officeUser3 := factory.BuildOfficeUserWithRoles(suite.DB(), []factory.Customization{
+ {
+ Model: models.OfficeUser{
+ FirstName: "Nick",
+ LastName: "Cage",
+ Email: "conAirKilluh@mail.mil",
+ Status: &requestedStatus,
+ },
+ },
+ {
+ Model: models.TransportationOffice{
+ Name: "PPPO Detroit Arsenal - USA",
+ },
+ },
+ }, []roles.RoleType{roles.RoleTypeServicesCounselor})
+
+ builder := &testRequestedOfficeUsersListQueryBuilder{}
fetcher := NewRequestedOfficeUsersListFetcher(builder)
- requestedOfficeUsers, err := fetcher.FetchRequestedOfficeUsersList(suite.AppContextForTest(), []services.QueryFilter{}, defaultAssociations(), defaultPagination(), defaultOrdering())
+ column := "transportation_office_id"
+ ordering := query.NewQueryOrder(&column, models.BoolPointer(true))
+
+ requestedOfficeUsers, _, err := fetcher.FetchRequestedOfficeUsersList(suite.AppContextForTest(), nil, defaultPagination(), ordering)
+
+ suite.NoError(err)
+ suite.Len(requestedOfficeUsers, 3)
+ suite.Equal(officeUser3.ID.String(), requestedOfficeUsers[0].ID.String())
+ suite.Equal(officeUser2.ID.String(), requestedOfficeUsers[1].ID.String())
+ suite.Equal(officeUser1.ID.String(), requestedOfficeUsers[2].ID.String())
+
+ ordering = query.NewQueryOrder(&column, models.BoolPointer(false))
+
+ requestedOfficeUsers, _, err = fetcher.FetchRequestedOfficeUsersList(suite.AppContextForTest(), nil, defaultPagination(), ordering)
+
+ suite.NoError(err)
+ suite.Len(requestedOfficeUsers, 3)
+ suite.Equal(officeUser1.ID.String(), requestedOfficeUsers[0].ID.String())
+ suite.Equal(officeUser2.ID.String(), requestedOfficeUsers[1].ID.String())
+ suite.Equal(officeUser3.ID.String(), requestedOfficeUsers[2].ID.String())
+
+ column = "unknown_column"
+
+ requestedOfficeUsers, _, err = fetcher.FetchRequestedOfficeUsersList(suite.AppContextForTest(), nil, defaultPagination(), ordering)
suite.Error(err)
- suite.Equal(err.Error(), "Fetch error")
- suite.Equal(models.OfficeUsers(nil), requestedOfficeUsers)
+ suite.Len(requestedOfficeUsers, 0)
})
}
diff --git a/pkg/services/requested_office_users/requested_office_users_list_fetcher.go b/pkg/services/requested_office_users/requested_office_users_list_fetcher.go
index 29004b8485e..3c883225b3b 100644
--- a/pkg/services/requested_office_users/requested_office_users_list_fetcher.go
+++ b/pkg/services/requested_office_users/requested_office_users_list_fetcher.go
@@ -1,13 +1,17 @@
package adminuser
import (
+ "fmt"
+ "sort"
+
+ "github.com/gobuffalo/pop/v6"
+
"github.com/transcom/mymove/pkg/appcontext"
"github.com/transcom/mymove/pkg/models"
"github.com/transcom/mymove/pkg/services"
)
type requestedOfficeUsersListQueryBuilder interface {
- FetchMany(appCtx appcontext.AppContext, model interface{}, filters []services.QueryFilter, associations services.QueryAssociations, pagination services.Pagination, ordering services.QueryOrder) error
Count(appCtx appcontext.AppContext, model interface{}, filters []services.QueryFilter) (int, error)
}
@@ -16,10 +20,57 @@ type requestedOfficeUserListFetcher struct {
}
// FetchAdminUserList uses the passed query builder to fetch a list of office users
-func (o *requestedOfficeUserListFetcher) FetchRequestedOfficeUsersList(appCtx appcontext.AppContext, filters []services.QueryFilter, associations services.QueryAssociations, pagination services.Pagination, ordering services.QueryOrder) (models.OfficeUsers, error) {
+func (o *requestedOfficeUserListFetcher) FetchRequestedOfficeUsersList(appCtx appcontext.AppContext, filterFuncs []func(*pop.Query), pagination services.Pagination, ordering services.QueryOrder) (models.OfficeUsers, int, error) {
+ var query *pop.Query
var requestedUsers models.OfficeUsers
- err := o.builder.FetchMany(appCtx, &requestedUsers, filters, associations, pagination, ordering)
- return requestedUsers, err
+
+ query = appCtx.DB().Q().EagerPreload(
+ "User.Roles",
+ "TransportationOffice").
+ Join("users", "users.id = office_users.user_id").
+ Join("users_roles", "users.id = users_roles.user_id").
+ Join("roles", "users_roles.role_id = roles.id").
+ Join("transportation_offices", "office_users.transportation_office_id = transportation_offices.id")
+
+ for _, filterFunc := range filterFuncs {
+ filterFunc(query)
+ }
+
+ query = query.Where("status = ?", models.OfficeUserStatusREQUESTED)
+ query.GroupBy("office_users.id")
+
+ var order = "desc"
+ if ordering.SortOrder() != nil && *ordering.SortOrder() {
+ order = "asc"
+ }
+
+ var orderTerm = "id"
+ if ordering.Column() != nil {
+ orderTerm = *ordering.Column()
+ }
+
+ query.Order(fmt.Sprintf("%s %s", orderTerm, order))
+ query.Select("office_users.*")
+
+ err := query.Paginate(pagination.Page(), pagination.PerPage()).All(&requestedUsers)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ if orderTerm == "transportation_office_id" {
+ if order == "desc" {
+ sort.Slice(requestedUsers, func(i, j int) bool {
+ return requestedUsers[i].TransportationOffice.Name > requestedUsers[j].TransportationOffice.Name
+ })
+ } else {
+ sort.Slice(requestedUsers, func(i, j int) bool {
+ return requestedUsers[i].TransportationOffice.Name < requestedUsers[j].TransportationOffice.Name
+ })
+ }
+ }
+
+ count := query.Paginator.TotalEntriesSize
+ return requestedUsers, count, nil
}
// FetchAdminUserList uses the passed query builder to fetch a list of office users
diff --git a/pkg/services/transportation_office.go b/pkg/services/transportation_office.go
index 9ec90364ba0..7255991f376 100644
--- a/pkg/services/transportation_office.go
+++ b/pkg/services/transportation_office.go
@@ -9,7 +9,7 @@ import (
//go:generate mockery --name TransportationOfficesFetcher
type TransportationOfficesFetcher interface {
- GetTransportationOffices(appCtx appcontext.AppContext, search string, forPpm bool) (*models.TransportationOffices, error)
+ GetTransportationOffices(appCtx appcontext.AppContext, search string, forPpm bool, forAdminOfficeUserReqFilter bool) (*models.TransportationOffices, error)
GetTransportationOffice(appCtx appcontext.AppContext, transportationOfficeID uuid.UUID, includeOnlyPPMCloseoutOffices bool) (*models.TransportationOffice, error)
GetAllGBLOCs(appCtx appcontext.AppContext) (*models.GBLOCs, error)
GetCounselingOffices(appCtx appcontext.AppContext, dutyLocationID uuid.UUID, serviceMemberID uuid.UUID) (*models.TransportationOffices, error)
diff --git a/pkg/services/transportation_office/transportation_office_fetcher.go b/pkg/services/transportation_office/transportation_office_fetcher.go
index 8ccd09694f3..8fde2e98093 100644
--- a/pkg/services/transportation_office/transportation_office_fetcher.go
+++ b/pkg/services/transportation_office/transportation_office_fetcher.go
@@ -46,8 +46,8 @@ func (o transportationOfficesFetcher) GetTransportationOffice(appCtx appcontext.
return &transportationOffice, nil
}
-func (o transportationOfficesFetcher) GetTransportationOffices(appCtx appcontext.AppContext, search string, forPpm bool) (*models.TransportationOffices, error) {
- officeList, err := FindTransportationOffice(appCtx, search, forPpm)
+func (o transportationOfficesFetcher) GetTransportationOffices(appCtx appcontext.AppContext, search string, forPpm bool, forAdminOfficeUserReqFilter bool) (*models.TransportationOffices, error) {
+ officeList, err := FindTransportationOffice(appCtx, search, forPpm, forAdminOfficeUserReqFilter)
if err != nil {
switch err {
@@ -61,9 +61,15 @@ func (o transportationOfficesFetcher) GetTransportationOffices(appCtx appcontext
return &officeList, nil
}
-func FindTransportationOffice(appCtx appcontext.AppContext, search string, forPpm bool) (models.TransportationOffices, error) {
+func FindTransportationOffice(appCtx appcontext.AppContext, search string, forPpm bool, forAdminOfficeUserReqFilter bool) (models.TransportationOffices, error) {
var officeList []models.TransportationOffice
+ // Changing return limit for Admin Requested Office Users Transportation Office Filter implementation
+ var limit = 5
+ if forAdminOfficeUserReqFilter {
+ limit = 50
+ }
+
// The % operator filters out strings that are below this similarity threshold
err := appCtx.DB().Q().RawQuery("SET pg_trgm.similarity_threshold = 0.03").Exec()
if err != nil {
@@ -80,13 +86,13 @@ func FindTransportationOffice(appCtx appcontext.AppContext, search string, forPp
}
sqlQuery += `
order by sim desc
- limit 5)
+ limit $2)
select office.*
from names n inner join transportation_offices office on n.transportation_office_id = office.id
group by office.id
order by max(n.sim) desc, office.name
- limit 5`
- query := appCtx.DB().Q().RawQuery(sqlQuery, search)
+ limit $2`
+ query := appCtx.DB().Q().RawQuery(sqlQuery, search, limit)
if err := query.All(&officeList); err != nil {
if errors.Cause(err).Error() != models.RecordNotFoundErrorString {
return officeList, err
diff --git a/pkg/services/transportation_office/transportation_office_fetcher_test.go b/pkg/services/transportation_office/transportation_office_fetcher_test.go
index 59cb98150ae..64c377deb30 100644
--- a/pkg/services/transportation_office/transportation_office_fetcher_test.go
+++ b/pkg/services/transportation_office/transportation_office_fetcher_test.go
@@ -42,7 +42,7 @@ func (suite *TransportationOfficeServiceSuite) Test_SearchTransportationOffice()
},
},
}, nil)
- office, err := FindTransportationOffice(suite.AppContextForTest(), "LRC Fort Knox", true)
+ office, err := FindTransportationOffice(suite.AppContextForTest(), "LRC Fort Knox", true, false)
suite.NoError(err)
suite.Equal(transportationOffice.Name, office[0].Name)
@@ -53,7 +53,7 @@ func (suite *TransportationOfficeServiceSuite) Test_SearchTransportationOffice()
func (suite *TransportationOfficeServiceSuite) Test_SearchWithNoTransportationOffices() {
- office, err := FindTransportationOffice(suite.AppContextForTest(), "LRC Fort Knox", true)
+ office, err := FindTransportationOffice(suite.AppContextForTest(), "LRC Fort Knox", true, false)
suite.NoError(err)
suite.Len(office, 0)
}
@@ -87,7 +87,7 @@ func (suite *TransportationOfficeServiceSuite) Test_SortedTransportationOffices(
},
}, nil)
- office, err := FindTransportationOffice(suite.AppContextForTest(), "JPPSO", true)
+ office, err := FindTransportationOffice(suite.AppContextForTest(), "JPPSO", true, false)
suite.NoError(err)
suite.Equal(transportationOffice1.Name, office[0].Name)
diff --git a/src/pages/Admin/RequestedOfficeUsers/RequestedOfficeUserList.jsx b/src/pages/Admin/RequestedOfficeUsers/RequestedOfficeUserList.jsx
index 9aeb51ea0f9..b1f1dc5bec1 100644
--- a/src/pages/Admin/RequestedOfficeUsers/RequestedOfficeUserList.jsx
+++ b/src/pages/Admin/RequestedOfficeUsers/RequestedOfficeUserList.jsx
@@ -1,5 +1,16 @@
import React from 'react';
-import { Datagrid, DateField, Filter, List, ReferenceField, TextField, TextInput, TopToolbar } from 'react-admin';
+import {
+ ArrayField,
+ Datagrid,
+ DateField,
+ Filter,
+ List,
+ ReferenceField,
+ TextField,
+ TextInput,
+ TopToolbar,
+ useRecordContext,
+} from 'react-admin';
import AdminPagination from 'scenes/SystemAdmin/shared/AdminPagination';
@@ -7,15 +18,36 @@ import AdminPagination from 'scenes/SystemAdmin/shared/AdminPagination';
const ListActions = () => {
return ;
};
-
-const RequestedOfficeUserListFilter = () => (
-
-
+const RequestedOfficeUserListFilter = (props) => (
+
+
+
+
);
const defaultSort = { field: 'createdAt', order: 'DESC' };
+const RolesTextField = (user) => {
+ const { roles } = user;
+
+ let roleStr = '';
+ for (let i = 0; i < roles.length; i += 1) {
+ roleStr += roles[i].roleName;
+
+ if (i < roles.length - 1) {
+ roleStr += ', ';
+ }
+ }
+
+ return roleStr;
+};
+
+const RolesField = () => {
+ const record = useRecordContext();
+ return {RolesTextField(record)}
;
+};
+
const RequestedOfficeUserList = () => (
}
@@ -34,6 +66,9 @@ const RequestedOfficeUserList = () => (
+
+
+
);
diff --git a/src/pages/Admin/RequestedOfficeUsers/RequestedOfficeUserShow.module.scss b/src/pages/Admin/RequestedOfficeUsers/RequestedOfficeUserShow.module.scss
index 974aa69bf7f..d2b9adfc325 100644
--- a/src/pages/Admin/RequestedOfficeUsers/RequestedOfficeUserShow.module.scss
+++ b/src/pages/Admin/RequestedOfficeUsers/RequestedOfficeUserShow.module.scss
@@ -52,4 +52,6 @@
margin-left: 15px;
margin-right: 15px;
margin-bottom: 10px;
-}
\ No newline at end of file
+}
+
+ul { list-style-type: none; }
\ No newline at end of file