Skip to content

Commit

Permalink
Merge branch 'main' into revert-13415-B-20256-MAIN-Prevent-Prime-user…
Browse files Browse the repository at this point in the history
…s-from-accessing-PPTAS
  • Loading branch information
pambecker authored Aug 14, 2024
2 parents 6b64fbc + c28caf0 commit 75948c2
Show file tree
Hide file tree
Showing 24 changed files with 279 additions and 37 deletions.
20 changes: 20 additions & 0 deletions pkg/gen/ghcapi/embedded_spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkg/gen/ghcapi/ghcoperations/move/search_moves.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkg/gen/ghcmessages/search_move.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/handlers/ghcapi/customer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ func (suite *HandlerSuite) TestSearchCustomersHandler() {
suite.Run("Successful customer search by DOD ID", func() {
req := setupTestData()
customer := factory.BuildServiceMember(suite.DB(), nil, nil)
customers := models.ServiceMembers{customer}
customers := models.ServiceMemberSearchResults{customer.ToSearchResult()}

mockSearcher := mocks.CustomerSearcher{}

Expand Down Expand Up @@ -347,7 +347,7 @@ func (suite *HandlerSuite) TestSearchCustomersHandler() {
suite.Run("Successful customer search by name", func() {
req := setupTestData()
customer := factory.BuildServiceMember(suite.DB(), nil, nil)
customers := models.ServiceMembers{customer}
customers := models.ServiceMemberSearchResults{customer.ToSearchResult()}

mockSearcher := mocks.CustomerSearcher{}

Expand Down
2 changes: 1 addition & 1 deletion pkg/handlers/ghcapi/internal/payloads/model_to_payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -2232,7 +2232,7 @@ func ShipmentsPaymentSITBalance(shipmentsSITBalance []services.ShipmentPaymentSI
return payload
}

func SearchCustomers(customers models.ServiceMembers) *ghcmessages.SearchCustomers {
func SearchCustomers(customers models.ServiceMemberSearchResults) *ghcmessages.SearchCustomers {
searchCustomers := make(ghcmessages.SearchCustomers, len(customers))
for i, customer := range customers {
searchCustomers[i] = &ghcmessages.SearchCustomer{
Expand Down
1 change: 1 addition & 0 deletions pkg/handlers/ghcapi/move.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func (h SearchMovesHandler) Handle(params moveop.SearchMovesParams) middleware.R
DodID: params.Body.DodID,
Emplid: params.Body.Emplid,
CustomerName: params.Body.CustomerName,
PaymentRequestCode: params.Body.PaymentRequestCode,
DestinationPostalCode: params.Body.DestinationPostalCode,
OriginPostalCode: params.Body.OriginPostalCode,
Status: params.Body.Status,
Expand Down
60 changes: 60 additions & 0 deletions pkg/models/service_member.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,72 @@ type ServiceMember struct {
CacValidated bool `json:"cac_validated" db:"cac_validated"`
}

// This model should be used whenever the customer name search is used. Had to create new struct so that Pop is aware of the "total_sim" field used in search queries.
// Since this isn't an actual column, but one created by a subquery, couldn't add it to original ServiceMember struct without errors.
type ServiceMemberSearchResult struct {
ID uuid.UUID `json:"id" db:"id"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
UserID uuid.UUID `json:"user_id" db:"user_id"`
User User `belongs_to:"user" fk_id:"user_id"`
Edipi *string `json:"edipi" db:"edipi"`
Emplid *string `json:"emplid" db:"emplid"`
Affiliation *ServiceMemberAffiliation `json:"affiliation" db:"affiliation"`
FirstName *string `json:"first_name" db:"first_name"`
MiddleName *string `json:"middle_name" db:"middle_name"`
LastName *string `json:"last_name" db:"last_name"`
Suffix *string `json:"suffix" db:"suffix"`
Telephone *string `json:"telephone" db:"telephone"`
SecondaryTelephone *string `json:"secondary_telephone" db:"secondary_telephone"`
PersonalEmail *string `json:"personal_email" db:"personal_email"`
PhoneIsPreferred *bool `json:"phone_is_preferred" db:"phone_is_preferred"`
EmailIsPreferred *bool `json:"email_is_preferred" db:"email_is_preferred"`
ResidentialAddressID *uuid.UUID `json:"residential_address_id" db:"residential_address_id"`
ResidentialAddress *Address `belongs_to:"address" fk_id:"residential_address_id"`
BackupMailingAddressID *uuid.UUID `json:"backup_mailing_address_id" db:"backup_mailing_address_id"`
BackupMailingAddress *Address `belongs_to:"address" fk_id:"backup_mailing_address_id"`
Orders Orders `has_many:"orders" fk_id:"service_member_id" order_by:"created_at desc" `
BackupContacts BackupContacts `has_many:"backup_contacts" fk_id:"service_member_id"`
CacValidated bool `json:"cac_validated" db:"cac_validated"`
TotalSim *float32 `db:"total_sim"`
}

// TableName overrides the table name used by Pop.
func (s ServiceMember) TableName() string {
return "service_members"
}

// Convenience function to convert to search result type, used in go tests
func (s ServiceMember) ToSearchResult() ServiceMemberSearchResult {
return ServiceMemberSearchResult{
ID: s.ID,
CreatedAt: s.CreatedAt,
UpdatedAt: s.UpdatedAt,
UserID: s.UserID,
User: s.User,
Edipi: s.Edipi,
Emplid: s.Emplid,
Affiliation: s.Affiliation,
FirstName: s.FirstName,
LastName: s.LastName,
Suffix: s.Suffix,
Telephone: s.Telephone,
SecondaryTelephone: s.SecondaryTelephone,
PersonalEmail: s.PersonalEmail,
EmailIsPreferred: s.EmailIsPreferred,
ResidentialAddressID: s.ResidentialAddressID,
ResidentialAddress: s.ResidentialAddress,
BackupMailingAddressID: s.BackupMailingAddressID,
BackupMailingAddress: s.BackupMailingAddress,
Orders: s.Orders,
BackupContacts: s.BackupContacts,
CacValidated: s.CacValidated,
TotalSim: nil,
}
}

type ServiceMembers []ServiceMember
type ServiceMemberSearchResults []ServiceMemberSearchResult

// Validate gets run every time you call a "pop.Validate*" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method.
func (s *ServiceMember) Validate(_ *pop.Connection) (*validate.Errors, error) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/services/customer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type CustomerUpdater interface {

//go:generate mockery --name CustomerSearcher
type CustomerSearcher interface {
SearchCustomers(appCtx appcontext.AppContext, params *SearchCustomersParams) (models.ServiceMembers, int, error)
SearchCustomers(appCtx appcontext.AppContext, params *SearchCustomersParams) (models.ServiceMemberSearchResults, int, error)
}

type SearchCustomersParams struct {
Expand Down
10 changes: 5 additions & 5 deletions pkg/services/mocks/CustomerSearcher.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/services/move.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ type SearchMovesParams struct {
DodID *string
Emplid *string
CustomerName *string
PaymentRequestCode *string
DestinationPostalCode *string
OriginPostalCode *string
Status []string
Expand Down
25 changes: 19 additions & 6 deletions pkg/services/move/move_searcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ type QueryOption func(*pop.Query)

// SearchMoves returns a list of results for a QAE/CSR move search query
func (s moveSearcher) SearchMoves(appCtx appcontext.AppContext, params *services.SearchMovesParams) (models.Moves, int, error) {
if params.Locator == nil && params.DodID == nil && params.CustomerName == nil {
if params.Locator == nil && params.DodID == nil && params.CustomerName == nil && params.PaymentRequestCode == nil {
verrs := validate.NewErrors()
verrs.Add("search key", "move locator, DOD ID, or customer name must be provided")
verrs.Add("search key", "move locator, DOD ID, customer name, or payment request number must be provided")
return models.Moves{}, 0, apperror.NewInvalidInputError(uuid.Nil, nil, verrs, "")
}
if params.Locator != nil && params.DodID != nil {
Expand Down Expand Up @@ -87,10 +87,11 @@ func (s moveSearcher) SearchMoves(appCtx appcontext.AppContext, params *services
shipmentsCountQuery := shipmentsCountFilter(params.ShipmentsCount)
scheduledPickupDateQuery := scheduledPickupDateFilter(params.PickupDate)
scheduledDeliveryDateQuery := scheduledDeliveryDateFilter(params.DeliveryDate)
orderQuery := sortOrder(params.Sort, params.Order, params.CustomerName)
orderQuery := sortOrder(params.Sort, params.Order, params.CustomerName, params.PaymentRequestCode)
paymentRequestQuery := paymentRequestCodeFilter(params.PaymentRequestCode)

options := [11]QueryOption{customerNameQuery, locatorQuery, dodIDQuery, branchQuery, orderQuery, originPostalCodeQuery,
destinationPostalCodeQuery, statusQuery, shipmentsCountQuery, scheduledPickupDateQuery, scheduledDeliveryDateQuery}
options := [12]QueryOption{customerNameQuery, locatorQuery, dodIDQuery, branchQuery, orderQuery, originPostalCodeQuery,
destinationPostalCodeQuery, statusQuery, shipmentsCountQuery, scheduledPickupDateQuery, scheduledDeliveryDateQuery, paymentRequestQuery}

for _, option := range options {
if option != nil {
Expand Down Expand Up @@ -182,6 +183,16 @@ func shipmentsCountFilter(shipmentsCount *int64) QueryOption {
}
}

func paymentRequestCodeFilter(paymentRequestCode *string) QueryOption {
return func(query *pop.Query) {
if paymentRequestCode != nil {
query.Join("payment_requests", "payment_requests.move_id = moves.id")
query.Where("payment_requests.payment_request_number = ?", *paymentRequestCode)
query.GroupBy("moves.id", "service_members.id", "origin_addresses.id", "new_addresses.id", "payment_requests.id")
}
}
}

func scheduledPickupDateFilter(pickupDate *time.Time) QueryOption {
return func(query *pop.Query) {
if pickupDate != nil {
Expand All @@ -202,7 +213,7 @@ func scheduledDeliveryDateFilter(deliveryDate *time.Time) QueryOption {
}
}

func sortOrder(sort *string, order *string, customerNameSearch *string) QueryOption {
func sortOrder(sort *string, order *string, customerNameSearch *string, paymentRequestSearch *string) QueryOption {
return func(query *pop.Query) {
if sort != nil && order != nil {
sortTerm := parameters[*sort]
Expand All @@ -213,6 +224,8 @@ func sortOrder(sort *string, order *string, customerNameSearch *string) QueryOpt
}
} else if customerNameSearch != nil {
query.Order("similarity(searchable_full_name(first_name, last_name), f_unaccent(lower(?))) DESC", *customerNameSearch)
} else if paymentRequestSearch != nil {
query.Order("similarity(payment_requests.payment_request_number, ?)", paymentRequestSearch)
} else {
query.Order("moves.created_at DESC")
}
Expand Down
23 changes: 11 additions & 12 deletions pkg/services/office_user/customer/customer_searcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,21 @@ func NewCustomerSearcher() services.CustomerSearcher {

type QueryOption func(*pop.Query)

func (s customerSearcher) SearchCustomers(appCtx appcontext.AppContext, params *services.SearchCustomersParams) (models.ServiceMembers, int, error) {
func (s customerSearcher) SearchCustomers(appCtx appcontext.AppContext, params *services.SearchCustomersParams) (models.ServiceMemberSearchResults, int, error) {
if params.DodID == nil && params.CustomerName == nil {
verrs := validate.NewErrors()
verrs.Add("search key", "DOD ID or customer name must be provided")
return models.ServiceMembers{}, 0, apperror.NewInvalidInputError(uuid.Nil, nil, verrs, "")
return models.ServiceMemberSearchResults{}, 0, apperror.NewInvalidInputError(uuid.Nil, nil, verrs, "")
}

if params.CustomerName != nil && params.DodID != nil {
verrs := validate.NewErrors()
verrs.Add("search key", "search by multiple keys is not supported")
return models.ServiceMembers{}, 0, apperror.NewInvalidInputError(uuid.Nil, nil, verrs, "")
return models.ServiceMemberSearchResults{}, 0, apperror.NewInvalidInputError(uuid.Nil, nil, verrs, "")
}

if !appCtx.Session().Roles.HasRole(roles.RoleTypeServicesCounselor) && !appCtx.Session().Roles.HasRole(roles.RoleTypeHQ) {
return models.ServiceMembers{}, 0, apperror.NewForbiddenError("not authorized to preform this search")
return models.ServiceMemberSearchResults{}, 0, apperror.NewForbiddenError("not authorized to preform this search")
}

err := appCtx.DB().RawQuery("SET pg_trgm.similarity_threshold = 0.1").Exec()
Expand Down Expand Up @@ -70,7 +70,8 @@ func (s customerSearcher) SearchCustomers(appCtx appcontext.AppContext, params *
service_members.suffix,
service_members.telephone,
service_members.updated_at,
service_members.user_id
service_members.user_id,
similarity(service_members.last_name, f_unaccent(lower($1))) + similarity(service_members.first_name, f_unaccent(lower($1))) as total_sim
FROM service_members AS service_members
JOIN users ON users.id = service_members.user_id
LEFT JOIN orders ON orders.service_member_id = service_members.id`
Expand All @@ -91,12 +92,10 @@ func (s customerSearcher) SearchCustomers(appCtx appcontext.AppContext, params *
}
query = appCtx.DB().RawQuery(rawquery, params.DodID)
} else {
rawquery += ` f_unaccent(lower($1)) % searchable_full_name(first_name, last_name)) ) distinct_customers`
rawquery += ` f_unaccent(lower($1)) % searchable_full_name(first_name, last_name)) ) distinct_customers ORDER BY total_sim DESC`
if params.Sort != nil && params.Order != nil {
sortTerm := parameters[*params.Sort]
rawquery += ` ORDER BY ` + sortTerm + ` ` + *params.Order
} else {
rawquery += ` ORDER BY distinct_customers.last_name ASC`
rawquery += `, ` + sortTerm + ` ` + *params.Order
}
query = appCtx.DB().RawQuery(rawquery, params.CustomerName)
}
Expand All @@ -111,11 +110,11 @@ func (s customerSearcher) SearchCustomers(appCtx appcontext.AppContext, params *
}
}

var customers models.ServiceMembers
var customers models.ServiceMemberSearchResults
err = query.Paginate(int(params.Page), int(params.PerPage)).All(&customers)

if err != nil {
return models.ServiceMembers{}, 0, apperror.NewQueryError("Customer", err, "")
return models.ServiceMemberSearchResults{}, 0, apperror.NewQueryError("Customer", err, "")
}

return customers, query.Paginator.TotalEntriesSize, nil
Expand All @@ -138,7 +137,7 @@ func customerNameSearch(customerName *string) QueryOption {
}

var parameters = map[string]string{
"customerName": "distinct_customers.last_name, distinct_customers.first_name",
"customerName": "distinct_customers.last_name",
"dodID": "distinct_customers.edipi",
"emplid": "distinct_customers.emplid",
"branch": "distinct_customers.affiliation",
Expand Down
Loading

0 comments on commit 75948c2

Please sign in to comment.