From 8c8662c78b569cf049a79e76b6fbfd66929e96e3 Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Sat, 25 Jan 2025 01:22:39 +0000 Subject: [PATCH 01/19] check address on dest address update --- pkg/handlers/primeapi/api.go | 2 + pkg/handlers/primeapi/mto_shipment.go | 55 +++++++++++++++++++ pkg/handlers/primeapi/mto_shipment_test.go | 8 +++ .../shipment_address_update_requester.go | 3 + 4 files changed, 68 insertions(+) diff --git a/pkg/handlers/primeapi/api.go b/pkg/handlers/primeapi/api.go index c3bb0327668..6394ed6c30c 100644 --- a/pkg/handlers/primeapi/api.go +++ b/pkg/handlers/primeapi/api.go @@ -51,6 +51,7 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primeoperations.MymoveAP uploadCreator := upload.NewUploadCreator(handlerConfig.FileStorer()) ppmEstimator := ppmshipment.NewEstimatePPM(handlerConfig.DTODPlanner(), &paymentrequesthelper.RequestPaymentHelper{}) serviceItemUpdater := mtoserviceitem.NewMTOServiceItemUpdater(handlerConfig.HHGPlanner(), queryBuilder, moveRouter, shipmentFetcher, addressCreator, portLocationFetcher) + vLocation := address.NewVLocation() userUploader, err := uploader.NewUserUploader(handlerConfig.FileStorer(), uploader.MaxCustomerUserUploadFileSizeLimit) if err != nil { @@ -111,6 +112,7 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primeoperations.MymoveAP primeAPI.MtoShipmentUpdateShipmentDestinationAddressHandler = UpdateShipmentDestinationAddressHandler{ handlerConfig, shipmentaddressupdate.NewShipmentAddressUpdateRequester(handlerConfig.HHGPlanner(), addressCreator, moveRouter), + vLocation, } addressUpdater := address.NewAddressUpdater() diff --git a/pkg/handlers/primeapi/mto_shipment.go b/pkg/handlers/primeapi/mto_shipment.go index a93967aea89..0ca32768da8 100644 --- a/pkg/handlers/primeapi/mto_shipment.go +++ b/pkg/handlers/primeapi/mto_shipment.go @@ -1,6 +1,9 @@ package primeapi import ( + "context" + "strings" + "github.com/go-openapi/runtime/middleware" "github.com/gofrs/uuid" "go.uber.org/zap" @@ -19,6 +22,7 @@ import ( type UpdateShipmentDestinationAddressHandler struct { handlers.HandlerConfig services.ShipmentAddressUpdateRequester + services.VLocation } // Handle creates the address update request for non-SIT @@ -32,6 +36,57 @@ func (h UpdateShipmentDestinationAddressHandler) Handle(params mtoshipmentops.Up eTag := params.IfMatch + /** Feature Flag - Alaska - Determines if AK can be included/excluded **/ + isAlaskaEnabled := false + akFeatureFlagName := "enable_alaska" + flag, err := h.FeatureFlagFetcher().GetBooleanFlagForUser(context.TODO(), appCtx, akFeatureFlagName, map[string]string{}) + if err != nil { + appCtx.Logger().Error("Error fetching feature flag", zap.String("featureFlagKey", akFeatureFlagName), zap.Error(err)) + } else { + isAlaskaEnabled = flag.Match + } + + /** Feature Flag - Hawaii - Determines if HI can be included/excluded **/ + isHawaiiEnabled := false + hiFeatureFlagName := "enable_hawaii" + flag, err = h.FeatureFlagFetcher().GetBooleanFlagForUser(context.TODO(), appCtx, hiFeatureFlagName, map[string]string{}) + if err != nil { + appCtx.Logger().Error("Error fetching feature flag", zap.String("featureFlagKey", hiFeatureFlagName), zap.Error(err)) + } else { + isHawaiiEnabled = flag.Match + } + + // build states to exlude filter list + statesToExclude := make([]string, 0) + if !isAlaskaEnabled { + statesToExclude = append(statesToExclude, "AK") + } + if !isHawaiiEnabled { + statesToExclude = append(statesToExclude, "HI") + } + + addressSearch := addressUpdate.NewAddress.City + ", " + addressUpdate.NewAddress.State + " " + addressUpdate.NewAddress.PostalCode + + locationList, err := h.GetLocationsByZipCityState(appCtx, addressSearch, statesToExclude) + if err != nil { + appCtx.Logger().Error("Error searching for address: ", zap.Error(err)) + return mtoshipmentops.NewUpdateShipmentDestinationAddressInternalServerError(), err + } else if len(*locationList) == 0 { + err := apperror.NewBadDataError("invalid address provided") + appCtx.Logger().Error("Error: ", zap.Error(err)) + return mtoshipmentops.NewUpdateShipmentDestinationAddressUnprocessableEntity(), err + } else if len(*locationList) > 1 { + var results []string + + for _, address := range *locationList { + results = append(results, address.CityName+" "+address.StateName+" "+address.UsprZipID) + } + joinedResult := strings.Join(results[:], "\n") + err := apperror.NewBadDataError("multiple locations found choose one of the following: " + joinedResult) + appCtx.Logger().Error("Error: ", zap.Error(err)) + return mtoshipmentops.NewUpdateShipmentDestinationAddressUnprocessableEntity(), err + } + response, err := h.ShipmentAddressUpdateRequester.RequestShipmentDeliveryAddressUpdate(appCtx, shipmentID, addressUpdate.NewAddress, addressUpdate.ContractorRemarks, eTag) if err != nil { diff --git a/pkg/handlers/primeapi/mto_shipment_test.go b/pkg/handlers/primeapi/mto_shipment_test.go index 917e10cdfc6..aca42147f95 100644 --- a/pkg/handlers/primeapi/mto_shipment_test.go +++ b/pkg/handlers/primeapi/mto_shipment_test.go @@ -59,9 +59,11 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { suite.Run("POST failure - 422 Unprocessable Entity Error", func() { subtestData := makeSubtestData() mockCreator := mocks.ShipmentAddressUpdateRequester{} + vLocationServices := address.NewVLocation() handler := UpdateShipmentDestinationAddressHandler{ suite.HandlerConfig(), &mockCreator, + vLocationServices, } // InvalidInputError should generate an UnprocessableEntity response error // Need verrs incorporated to satisfy swagger validation @@ -91,9 +93,11 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { suite.Run("POST failure - 409 Request conflict reponse Error", func() { subtestData := makeSubtestData() mockCreator := mocks.ShipmentAddressUpdateRequester{} + vLocationServices := address.NewVLocation() handler := UpdateShipmentDestinationAddressHandler{ suite.HandlerConfig(), &mockCreator, + vLocationServices, } // NewConflictError should generate a RequestConflict response error err := apperror.NewConflictError(uuid.Nil, "unable to create ShipmentAddressUpdate") @@ -121,9 +125,11 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { subtestData := makeSubtestData() mockCreator := mocks.ShipmentAddressUpdateRequester{} + vLocationServices := address.NewVLocation() handler := UpdateShipmentDestinationAddressHandler{ suite.HandlerConfig(), &mockCreator, + vLocationServices, } // NewNotFoundError should generate a RequestNotFound response error err := apperror.NewNotFoundError(uuid.Nil, "unable to create ShipmentAddressUpdate") @@ -151,9 +157,11 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { subtestData := makeSubtestData() mockCreator := mocks.ShipmentAddressUpdateRequester{} + vLocationServices := address.NewVLocation() handler := UpdateShipmentDestinationAddressHandler{ suite.HandlerConfig(), &mockCreator, + vLocationServices, } // NewQueryError should generate an InternalServerError response error err := apperror.NewQueryError("", nil, "unable to reach database") diff --git a/pkg/services/shipment_address_update/shipment_address_update_requester.go b/pkg/services/shipment_address_update/shipment_address_update_requester.go index 1a80f27538c..6cd87c837e0 100644 --- a/pkg/services/shipment_address_update/shipment_address_update_requester.go +++ b/pkg/services/shipment_address_update/shipment_address_update_requester.go @@ -281,6 +281,9 @@ func (f *shipmentAddressUpdateRequester) RequestShipmentDeliveryAddressUpdate(ap if eTag != etag.GenerateEtag(shipment.UpdatedAt) { return nil, apperror.NewPreconditionFailedError(shipmentID, nil) } + + // check if the provided address is valid + isInternationalShipment := shipment.MarketCode == models.MarketCodeInternational shipmentHasApprovedDestSIT := f.doesShipmentContainApprovedDestinationSIT(shipment) From 30622f76401be5377730969edc3189b28f967af5 Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Mon, 27 Jan 2025 20:02:30 +0000 Subject: [PATCH 02/19] print error message when lookup fails, optional exactMatch bool --- pkg/handlers/primeapi/mto_shipment.go | 28 ++++++++++++++-------- pkg/services/address.go | 2 +- pkg/services/address/address_lookup.go | 32 +++++++++++++++++++++----- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/pkg/handlers/primeapi/mto_shipment.go b/pkg/handlers/primeapi/mto_shipment.go index 0ca32768da8..4b91c343fac 100644 --- a/pkg/handlers/primeapi/mto_shipment.go +++ b/pkg/handlers/primeapi/mto_shipment.go @@ -2,6 +2,7 @@ package primeapi import ( "context" + "fmt" "strings" "github.com/go-openapi/runtime/middleware" @@ -67,24 +68,31 @@ func (h UpdateShipmentDestinationAddressHandler) Handle(params mtoshipmentops.Up addressSearch := addressUpdate.NewAddress.City + ", " + addressUpdate.NewAddress.State + " " + addressUpdate.NewAddress.PostalCode - locationList, err := h.GetLocationsByZipCityState(appCtx, addressSearch, statesToExclude) + locationList, err := h.GetLocationsByZipCityState(appCtx, addressSearch, statesToExclude, true) if err != nil { - appCtx.Logger().Error("Error searching for address: ", zap.Error(err)) - return mtoshipmentops.NewUpdateShipmentDestinationAddressInternalServerError(), err + serverError := apperror.NewInternalServerError("Error searching for address") + errStr := serverError.Error() // we do this because InternalServerError wants a *string + appCtx.Logger().Warn(serverError.Error()) + payload := payloads.InternalServerError(&errStr, h.GetTraceIDFromRequest(params.HTTPRequest)) + return mtoshipmentops.NewUpdateShipmentDestinationAddressInternalServerError().WithPayload(payload), serverError } else if len(*locationList) == 0 { - err := apperror.NewBadDataError("invalid address provided") - appCtx.Logger().Error("Error: ", zap.Error(err)) - return mtoshipmentops.NewUpdateShipmentDestinationAddressUnprocessableEntity(), err + unprocessableErr := apperror.NewUnprocessableEntityError( + fmt.Sprintf("primeapi.UpdateShipmentDestinationAddress: could not find the provided location: %s", addressSearch)) + appCtx.Logger().Warn(unprocessableErr.Error()) + payload := payloads.ValidationError(unprocessableErr.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) + return mtoshipmentops.NewUpdateShipmentDestinationAddressUnprocessableEntity().WithPayload(payload), unprocessableErr } else if len(*locationList) > 1 { var results []string for _, address := range *locationList { results = append(results, address.CityName+" "+address.StateName+" "+address.UsprZipID) } - joinedResult := strings.Join(results[:], "\n") - err := apperror.NewBadDataError("multiple locations found choose one of the following: " + joinedResult) - appCtx.Logger().Error("Error: ", zap.Error(err)) - return mtoshipmentops.NewUpdateShipmentDestinationAddressUnprocessableEntity(), err + joinedResult := strings.Join(results[:], ", ") + unprocessableErr := apperror.NewUnprocessableEntityError( + fmt.Sprintf("primeapi.UpdateShipmentDestinationAddress: multiple locations found choose one of the following: %s", joinedResult)) + appCtx.Logger().Warn(unprocessableErr.Error()) + payload := payloads.ValidationError(unprocessableErr.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) + return mtoshipmentops.NewUpdateShipmentDestinationAddressUnprocessableEntity().WithPayload(payload), unprocessableErr } response, err := h.ShipmentAddressUpdateRequester.RequestShipmentDeliveryAddressUpdate(appCtx, shipmentID, addressUpdate.NewAddress, addressUpdate.ContractorRemarks, eTag) diff --git a/pkg/services/address.go b/pkg/services/address.go index a1b25f17448..4537083bad3 100644 --- a/pkg/services/address.go +++ b/pkg/services/address.go @@ -15,5 +15,5 @@ type AddressUpdater interface { //go:generate mockery --name VLocation type VLocation interface { - GetLocationsByZipCityState(appCtx appcontext.AppContext, search string, exclusionStateFilters []string) (*models.VLocations, error) + GetLocationsByZipCityState(appCtx appcontext.AppContext, search string, exclusionStateFilters []string, exactMatch ...bool) (*models.VLocations, error) } diff --git a/pkg/services/address/address_lookup.go b/pkg/services/address/address_lookup.go index a258ab29dfb..1c12c4ed277 100644 --- a/pkg/services/address/address_lookup.go +++ b/pkg/services/address/address_lookup.go @@ -6,6 +6,7 @@ import ( "regexp" "strings" + "github.com/gobuffalo/pop/v6" "github.com/gofrs/uuid" "github.com/pkg/errors" @@ -22,8 +23,14 @@ func NewVLocation() services.VLocation { return &vLocation{} } -func (o vLocation) GetLocationsByZipCityState(appCtx appcontext.AppContext, search string, exclusionStateFilters []string) (*models.VLocations, error) { - locationList, err := FindLocationsByZipCity(appCtx, search, exclusionStateFilters) +func (o vLocation) GetLocationsByZipCityState(appCtx appcontext.AppContext, search string, exclusionStateFilters []string, exactMatch ...bool) (*models.VLocations, error) { + exact := false + + if len(exactMatch) > 0 { + exact = true + } + + locationList, err := FindLocationsByZipCity(appCtx, search, exclusionStateFilters, exact) if err != nil { switch err { @@ -42,7 +49,7 @@ func (o vLocation) GetLocationsByZipCityState(appCtx appcontext.AppContext, sear // to determine when the state and postal code need to be parsed from the search string // If there is only one result and no comma and the search string is all numbers we then search // using the entered postal code rather than city name -func FindLocationsByZipCity(appCtx appcontext.AppContext, search string, exclusionStateFilters []string) (models.VLocations, error) { +func FindLocationsByZipCity(appCtx appcontext.AppContext, search string, exclusionStateFilters []string, exactMatch bool) (models.VLocations, error) { var locationList []models.VLocation searchSlice := strings.Split(search, ",") city := "" @@ -67,8 +74,14 @@ func FindLocationsByZipCity(appCtx appcontext.AppContext, search string, exclusi } sqlQuery := `SELECT vl.city_name, vl.state, vl.usprc_county_nm, vl.uspr_zip_id, vl.uprc_id - FROM v_locations vl where vl.uspr_zip_id like ? AND - vl.city_name like upper(?) AND vl.state like upper(?)` + FROM v_locations vl where vl.uspr_zip_id like ? AND + vl.city_name like upper(?) AND vl.state like upper(?)` + + if exactMatch { + sqlQuery = `SELECT vl.city_name, vl.state, vl.usprc_county_nm, vl.uspr_zip_id, vl.uprc_id + FROM v_locations vl where vl.uspr_zip_id = ? AND + vl.city_name = upper(?) AND vl.state = upper(?)` + } // apply filter to exclude specific states if provided for _, value := range exclusionStateFilters { @@ -76,8 +89,15 @@ func FindLocationsByZipCity(appCtx appcontext.AppContext, search string, exclusi } sqlQuery += ` limit 30` + var query *pop.Query + + // we only want to add an extra % to the strings if we are using the LIKE in the query + if exactMatch { + query = appCtx.DB().RawQuery(sqlQuery, postalCode, city, state) + } else { + query = appCtx.DB().RawQuery(sqlQuery, fmt.Sprintf("%s%%", postalCode), fmt.Sprintf("%s%%", city), fmt.Sprintf("%s%%", state)) + } - query := appCtx.DB().RawQuery(sqlQuery, fmt.Sprintf("%s%%", postalCode), fmt.Sprintf("%s%%", city), fmt.Sprintf("%s%%", state)) if err := query.All(&locationList); err != nil { if errors.Cause(err).Error() != models.RecordNotFoundErrorString { return locationList, err From 25005a1228a36fef8aea8ac7eb824b377b6ea508 Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Mon, 27 Jan 2025 22:51:35 +0000 Subject: [PATCH 03/19] add getLocations endpoint to prime api --- pkg/gen/primeapi/configure_mymove.go | 6 + pkg/gen/primeapi/embedded_spec.go | 378 +++++++++++++++++ .../get_location_by_zip_city_state.go | 58 +++ ...t_location_by_zip_city_state_parameters.go | 71 ++++ ...et_location_by_zip_city_state_responses.go | 242 +++++++++++ ...t_location_by_zip_city_state_urlbuilder.go | 99 +++++ .../primeapi/primeoperations/mymove_api.go | 13 + .../primeclient/addresses/addresses_client.go | 81 ++++ ...t_location_by_zip_city_state_parameters.go | 148 +++++++ ...et_location_by_zip_city_state_responses.go | 397 ++++++++++++++++++ pkg/gen/primeclient/mymove_client.go | 5 + pkg/gen/primemessages/v_location.go | 302 +++++++++++++ pkg/gen/primemessages/v_locations.go | 78 ++++ pkg/handlers/primeapi/addresses.go | 62 +++ pkg/handlers/primeapi/api.go | 5 + .../primeapi/payloads/model_to_payload.go | 28 ++ .../payloads/model_to_payload_test.go | 27 ++ .../primeapi/payloads/payload_to_model.go | 16 + .../payloads/payload_to_model_test.go | 25 ++ swagger-def/prime.yaml | 29 ++ swagger/prime.yaml | 164 ++++++++ 21 files changed, 2234 insertions(+) create mode 100644 pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state.go create mode 100644 pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state_parameters.go create mode 100644 pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state_responses.go create mode 100644 pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state_urlbuilder.go create mode 100644 pkg/gen/primeclient/addresses/addresses_client.go create mode 100644 pkg/gen/primeclient/addresses/get_location_by_zip_city_state_parameters.go create mode 100644 pkg/gen/primeclient/addresses/get_location_by_zip_city_state_responses.go create mode 100644 pkg/gen/primemessages/v_location.go create mode 100644 pkg/gen/primemessages/v_locations.go create mode 100644 pkg/handlers/primeapi/addresses.go diff --git a/pkg/gen/primeapi/configure_mymove.go b/pkg/gen/primeapi/configure_mymove.go index c538a478d02..6def1f8afbc 100644 --- a/pkg/gen/primeapi/configure_mymove.go +++ b/pkg/gen/primeapi/configure_mymove.go @@ -11,6 +11,7 @@ import ( "github.com/go-openapi/runtime/middleware" "github.com/transcom/mymove/pkg/gen/primeapi/primeoperations" + "github.com/transcom/mymove/pkg/gen/primeapi/primeoperations/addresses" "github.com/transcom/mymove/pkg/gen/primeapi/primeoperations/move_task_order" "github.com/transcom/mymove/pkg/gen/primeapi/primeoperations/mto_service_item" "github.com/transcom/mymove/pkg/gen/primeapi/primeoperations/mto_shipment" @@ -100,6 +101,11 @@ func configureAPI(api *primeoperations.MymoveAPI) http.Handler { return middleware.NotImplemented("operation move_task_order.DownloadMoveOrder has not yet been implemented") }) } + if api.AddressesGetLocationByZipCityStateHandler == nil { + api.AddressesGetLocationByZipCityStateHandler = addresses.GetLocationByZipCityStateHandlerFunc(func(params addresses.GetLocationByZipCityStateParams) middleware.Responder { + return middleware.NotImplemented("operation addresses.GetLocationByZipCityState has not yet been implemented") + }) + } if api.MoveTaskOrderGetMoveTaskOrderHandler == nil { api.MoveTaskOrderGetMoveTaskOrderHandler = move_task_order.GetMoveTaskOrderHandlerFunc(func(params move_task_order.GetMoveTaskOrderParams) middleware.Responder { return middleware.NotImplemented("operation move_task_order.GetMoveTaskOrder has not yet been implemented") diff --git a/pkg/gen/primeapi/embedded_spec.go b/pkg/gen/primeapi/embedded_spec.go index 78a49944297..28de2c3759f 100644 --- a/pkg/gen/primeapi/embedded_spec.go +++ b/pkg/gen/primeapi/embedded_spec.go @@ -36,6 +36,44 @@ func init() { }, "basePath": "/prime/v1", "paths": { + "/addresses/zip-city-lookup/{search}": { + "get": { + "description": "Find by API using full/partial postal code or city name that returns an us_post_region_cities json object containing city, state, county and postal code.", + "tags": [ + "addresses" + ], + "summary": "Returns city, state, postal code, and county associated with the specified full/partial postal code or city state string", + "operationId": "getLocationByZipCityState", + "parameters": [ + { + "type": "string", + "name": "search", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "the requested list of city, state, county, and postal code matches", + "schema": { + "$ref": "#/definitions/VLocations" + } + }, + "400": { + "$ref": "#/responses/InvalidRequest" + }, + "403": { + "$ref": "#/responses/PermissionDenied" + }, + "404": { + "$ref": "#/responses/NotFound" + }, + "500": { + "$ref": "#/responses/ServerError" + } + } + } + }, "/move-task-orders/{moveID}": { "get": { "description": "### Functionality\nThis endpoint gets an individual MoveTaskOrder by ID.\n\nIt will provide information about the Customer and any associated MTOShipments, MTOServiceItems and PaymentRequests.\n", @@ -4479,6 +4517,151 @@ func init() { } } }, + "VLocation": { + "description": "A postal code, city, and state lookup", + "type": "object", + "properties": { + "city": { + "type": "string", + "title": "City", + "example": "Anytown" + }, + "county": { + "type": "string", + "title": "County", + "x-nullable": true, + "example": "LOS ANGELES" + }, + "postalCode": { + "type": "string", + "format": "zip", + "title": "ZIP", + "pattern": "^(\\d{5}?)$", + "example": "90210" + }, + "state": { + "type": "string", + "title": "State", + "enum": [ + "AL", + "AK", + "AR", + "AZ", + "CA", + "CO", + "CT", + "DC", + "DE", + "FL", + "GA", + "HI", + "IA", + "ID", + "IL", + "IN", + "KS", + "KY", + "LA", + "MA", + "MD", + "ME", + "MI", + "MN", + "MO", + "MS", + "MT", + "NC", + "ND", + "NE", + "NH", + "NJ", + "NM", + "NV", + "NY", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VA", + "VT", + "WA", + "WI", + "WV", + "WY" + ], + "x-display-value": { + "AK": "AK", + "AL": "AL", + "AR": "AR", + "AZ": "AZ", + "CA": "CA", + "CO": "CO", + "CT": "CT", + "DC": "DC", + "DE": "DE", + "FL": "FL", + "GA": "GA", + "HI": "HI", + "IA": "IA", + "ID": "ID", + "IL": "IL", + "IN": "IN", + "KS": "KS", + "KY": "KY", + "LA": "LA", + "MA": "MA", + "MD": "MD", + "ME": "ME", + "MI": "MI", + "MN": "MN", + "MO": "MO", + "MS": "MS", + "MT": "MT", + "NC": "NC", + "ND": "ND", + "NE": "NE", + "NH": "NH", + "NJ": "NJ", + "NM": "NM", + "NV": "NV", + "NY": "NY", + "OH": "OH", + "OK": "OK", + "OR": "OR", + "PA": "PA", + "RI": "RI", + "SC": "SC", + "SD": "SD", + "TN": "TN", + "TX": "TX", + "UT": "UT", + "VA": "VA", + "VT": "VT", + "WA": "WA", + "WI": "WI", + "WV": "WV", + "WY": "WY" + } + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" + } + } + }, + "VLocations": { + "type": "array", + "items": { + "$ref": "#/definitions/VLocation" + } + }, "ValidationError": { "allOf": [ { @@ -4613,6 +4796,56 @@ func init() { }, "basePath": "/prime/v1", "paths": { + "/addresses/zip-city-lookup/{search}": { + "get": { + "description": "Find by API using full/partial postal code or city name that returns an us_post_region_cities json object containing city, state, county and postal code.", + "tags": [ + "addresses" + ], + "summary": "Returns city, state, postal code, and county associated with the specified full/partial postal code or city state string", + "operationId": "getLocationByZipCityState", + "parameters": [ + { + "type": "string", + "name": "search", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "the requested list of city, state, county, and postal code matches", + "schema": { + "$ref": "#/definitions/VLocations" + } + }, + "400": { + "description": "The request payload is invalid.", + "schema": { + "$ref": "#/definitions/ClientError" + } + }, + "403": { + "description": "The request was denied.", + "schema": { + "$ref": "#/definitions/ClientError" + } + }, + "404": { + "description": "The requested resource wasn't found.", + "schema": { + "$ref": "#/definitions/ClientError" + } + }, + "500": { + "description": "A server error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, "/move-task-orders/{moveID}": { "get": { "description": "### Functionality\nThis endpoint gets an individual MoveTaskOrder by ID.\n\nIt will provide information about the Customer and any associated MTOShipments, MTOServiceItems and PaymentRequests.\n", @@ -9433,6 +9666,151 @@ func init() { } } }, + "VLocation": { + "description": "A postal code, city, and state lookup", + "type": "object", + "properties": { + "city": { + "type": "string", + "title": "City", + "example": "Anytown" + }, + "county": { + "type": "string", + "title": "County", + "x-nullable": true, + "example": "LOS ANGELES" + }, + "postalCode": { + "type": "string", + "format": "zip", + "title": "ZIP", + "pattern": "^(\\d{5}?)$", + "example": "90210" + }, + "state": { + "type": "string", + "title": "State", + "enum": [ + "AL", + "AK", + "AR", + "AZ", + "CA", + "CO", + "CT", + "DC", + "DE", + "FL", + "GA", + "HI", + "IA", + "ID", + "IL", + "IN", + "KS", + "KY", + "LA", + "MA", + "MD", + "ME", + "MI", + "MN", + "MO", + "MS", + "MT", + "NC", + "ND", + "NE", + "NH", + "NJ", + "NM", + "NV", + "NY", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VA", + "VT", + "WA", + "WI", + "WV", + "WY" + ], + "x-display-value": { + "AK": "AK", + "AL": "AL", + "AR": "AR", + "AZ": "AZ", + "CA": "CA", + "CO": "CO", + "CT": "CT", + "DC": "DC", + "DE": "DE", + "FL": "FL", + "GA": "GA", + "HI": "HI", + "IA": "IA", + "ID": "ID", + "IL": "IL", + "IN": "IN", + "KS": "KS", + "KY": "KY", + "LA": "LA", + "MA": "MA", + "MD": "MD", + "ME": "ME", + "MI": "MI", + "MN": "MN", + "MO": "MO", + "MS": "MS", + "MT": "MT", + "NC": "NC", + "ND": "ND", + "NE": "NE", + "NH": "NH", + "NJ": "NJ", + "NM": "NM", + "NV": "NV", + "NY": "NY", + "OH": "OH", + "OK": "OK", + "OR": "OR", + "PA": "PA", + "RI": "RI", + "SC": "SC", + "SD": "SD", + "TN": "TN", + "TX": "TX", + "UT": "UT", + "VA": "VA", + "VT": "VT", + "WA": "WA", + "WI": "WI", + "WV": "WV", + "WY": "WY" + } + }, + "usPostRegionCitiesID": { + "type": "string", + "format": "uuid", + "example": "c56a4180-65aa-42ec-a945-5fd21dec0538" + } + } + }, + "VLocations": { + "type": "array", + "items": { + "$ref": "#/definitions/VLocation" + } + }, "ValidationError": { "allOf": [ { diff --git a/pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state.go b/pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state.go new file mode 100644 index 00000000000..d202a9066f8 --- /dev/null +++ b/pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state.go @@ -0,0 +1,58 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package addresses + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// GetLocationByZipCityStateHandlerFunc turns a function with the right signature into a get location by zip city state handler +type GetLocationByZipCityStateHandlerFunc func(GetLocationByZipCityStateParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetLocationByZipCityStateHandlerFunc) Handle(params GetLocationByZipCityStateParams) middleware.Responder { + return fn(params) +} + +// GetLocationByZipCityStateHandler interface for that can handle valid get location by zip city state params +type GetLocationByZipCityStateHandler interface { + Handle(GetLocationByZipCityStateParams) middleware.Responder +} + +// NewGetLocationByZipCityState creates a new http.Handler for the get location by zip city state operation +func NewGetLocationByZipCityState(ctx *middleware.Context, handler GetLocationByZipCityStateHandler) *GetLocationByZipCityState { + return &GetLocationByZipCityState{Context: ctx, Handler: handler} +} + +/* + GetLocationByZipCityState swagger:route GET /addresses/zip-city-lookup/{search} addresses getLocationByZipCityState + +Returns city, state, postal code, and county associated with the specified full/partial postal code or city state string + +Find by API using full/partial postal code or city name that returns an us_post_region_cities json object containing city, state, county and postal code. +*/ +type GetLocationByZipCityState struct { + Context *middleware.Context + Handler GetLocationByZipCityStateHandler +} + +func (o *GetLocationByZipCityState) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetLocationByZipCityStateParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state_parameters.go b/pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state_parameters.go new file mode 100644 index 00000000000..0e8106fb581 --- /dev/null +++ b/pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state_parameters.go @@ -0,0 +1,71 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package addresses + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" +) + +// NewGetLocationByZipCityStateParams creates a new GetLocationByZipCityStateParams object +// +// There are no default values defined in the spec. +func NewGetLocationByZipCityStateParams() GetLocationByZipCityStateParams { + + return GetLocationByZipCityStateParams{} +} + +// GetLocationByZipCityStateParams contains all the bound params for the get location by zip city state operation +// typically these are obtained from a http.Request +// +// swagger:parameters getLocationByZipCityState +type GetLocationByZipCityStateParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: path + */ + Search string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetLocationByZipCityStateParams() beforehand. +func (o *GetLocationByZipCityStateParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + rSearch, rhkSearch, _ := route.Params.GetOK("search") + if err := o.bindSearch(rSearch, rhkSearch, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindSearch binds and validates parameter Search from path. +func (o *GetLocationByZipCityStateParams) bindSearch(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.Search = raw + + return nil +} diff --git a/pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state_responses.go b/pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state_responses.go new file mode 100644 index 00000000000..96eca32d7a9 --- /dev/null +++ b/pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state_responses.go @@ -0,0 +1,242 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package addresses + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/transcom/mymove/pkg/gen/primemessages" +) + +// GetLocationByZipCityStateOKCode is the HTTP code returned for type GetLocationByZipCityStateOK +const GetLocationByZipCityStateOKCode int = 200 + +/* +GetLocationByZipCityStateOK the requested list of city, state, county, and postal code matches + +swagger:response getLocationByZipCityStateOK +*/ +type GetLocationByZipCityStateOK struct { + + /* + In: Body + */ + Payload primemessages.VLocations `json:"body,omitempty"` +} + +// NewGetLocationByZipCityStateOK creates GetLocationByZipCityStateOK with default headers values +func NewGetLocationByZipCityStateOK() *GetLocationByZipCityStateOK { + + return &GetLocationByZipCityStateOK{} +} + +// WithPayload adds the payload to the get location by zip city state o k response +func (o *GetLocationByZipCityStateOK) WithPayload(payload primemessages.VLocations) *GetLocationByZipCityStateOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get location by zip city state o k response +func (o *GetLocationByZipCityStateOK) SetPayload(payload primemessages.VLocations) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetLocationByZipCityStateOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + payload := o.Payload + if payload == nil { + // return empty array + payload = primemessages.VLocations{} + } + + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +// GetLocationByZipCityStateBadRequestCode is the HTTP code returned for type GetLocationByZipCityStateBadRequest +const GetLocationByZipCityStateBadRequestCode int = 400 + +/* +GetLocationByZipCityStateBadRequest The request payload is invalid. + +swagger:response getLocationByZipCityStateBadRequest +*/ +type GetLocationByZipCityStateBadRequest struct { + + /* + In: Body + */ + Payload *primemessages.ClientError `json:"body,omitempty"` +} + +// NewGetLocationByZipCityStateBadRequest creates GetLocationByZipCityStateBadRequest with default headers values +func NewGetLocationByZipCityStateBadRequest() *GetLocationByZipCityStateBadRequest { + + return &GetLocationByZipCityStateBadRequest{} +} + +// WithPayload adds the payload to the get location by zip city state bad request response +func (o *GetLocationByZipCityStateBadRequest) WithPayload(payload *primemessages.ClientError) *GetLocationByZipCityStateBadRequest { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get location by zip city state bad request response +func (o *GetLocationByZipCityStateBadRequest) SetPayload(payload *primemessages.ClientError) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetLocationByZipCityStateBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(400) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetLocationByZipCityStateForbiddenCode is the HTTP code returned for type GetLocationByZipCityStateForbidden +const GetLocationByZipCityStateForbiddenCode int = 403 + +/* +GetLocationByZipCityStateForbidden The request was denied. + +swagger:response getLocationByZipCityStateForbidden +*/ +type GetLocationByZipCityStateForbidden struct { + + /* + In: Body + */ + Payload *primemessages.ClientError `json:"body,omitempty"` +} + +// NewGetLocationByZipCityStateForbidden creates GetLocationByZipCityStateForbidden with default headers values +func NewGetLocationByZipCityStateForbidden() *GetLocationByZipCityStateForbidden { + + return &GetLocationByZipCityStateForbidden{} +} + +// WithPayload adds the payload to the get location by zip city state forbidden response +func (o *GetLocationByZipCityStateForbidden) WithPayload(payload *primemessages.ClientError) *GetLocationByZipCityStateForbidden { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get location by zip city state forbidden response +func (o *GetLocationByZipCityStateForbidden) SetPayload(payload *primemessages.ClientError) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetLocationByZipCityStateForbidden) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(403) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetLocationByZipCityStateNotFoundCode is the HTTP code returned for type GetLocationByZipCityStateNotFound +const GetLocationByZipCityStateNotFoundCode int = 404 + +/* +GetLocationByZipCityStateNotFound The requested resource wasn't found. + +swagger:response getLocationByZipCityStateNotFound +*/ +type GetLocationByZipCityStateNotFound struct { + + /* + In: Body + */ + Payload *primemessages.ClientError `json:"body,omitempty"` +} + +// NewGetLocationByZipCityStateNotFound creates GetLocationByZipCityStateNotFound with default headers values +func NewGetLocationByZipCityStateNotFound() *GetLocationByZipCityStateNotFound { + + return &GetLocationByZipCityStateNotFound{} +} + +// WithPayload adds the payload to the get location by zip city state not found response +func (o *GetLocationByZipCityStateNotFound) WithPayload(payload *primemessages.ClientError) *GetLocationByZipCityStateNotFound { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get location by zip city state not found response +func (o *GetLocationByZipCityStateNotFound) SetPayload(payload *primemessages.ClientError) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetLocationByZipCityStateNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(404) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetLocationByZipCityStateInternalServerErrorCode is the HTTP code returned for type GetLocationByZipCityStateInternalServerError +const GetLocationByZipCityStateInternalServerErrorCode int = 500 + +/* +GetLocationByZipCityStateInternalServerError A server error occurred. + +swagger:response getLocationByZipCityStateInternalServerError +*/ +type GetLocationByZipCityStateInternalServerError struct { + + /* + In: Body + */ + Payload *primemessages.Error `json:"body,omitempty"` +} + +// NewGetLocationByZipCityStateInternalServerError creates GetLocationByZipCityStateInternalServerError with default headers values +func NewGetLocationByZipCityStateInternalServerError() *GetLocationByZipCityStateInternalServerError { + + return &GetLocationByZipCityStateInternalServerError{} +} + +// WithPayload adds the payload to the get location by zip city state internal server error response +func (o *GetLocationByZipCityStateInternalServerError) WithPayload(payload *primemessages.Error) *GetLocationByZipCityStateInternalServerError { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get location by zip city state internal server error response +func (o *GetLocationByZipCityStateInternalServerError) SetPayload(payload *primemessages.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetLocationByZipCityStateInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state_urlbuilder.go b/pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state_urlbuilder.go new file mode 100644 index 00000000000..1ea3bc879de --- /dev/null +++ b/pkg/gen/primeapi/primeoperations/addresses/get_location_by_zip_city_state_urlbuilder.go @@ -0,0 +1,99 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package addresses + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// GetLocationByZipCityStateURL generates an URL for the get location by zip city state operation +type GetLocationByZipCityStateURL struct { + Search string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetLocationByZipCityStateURL) WithBasePath(bp string) *GetLocationByZipCityStateURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetLocationByZipCityStateURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetLocationByZipCityStateURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/addresses/zip-city-lookup/{search}" + + search := o.Search + if search != "" { + _path = strings.Replace(_path, "{search}", search, -1) + } else { + return nil, errors.New("search is required on GetLocationByZipCityStateURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/prime/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetLocationByZipCityStateURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetLocationByZipCityStateURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetLocationByZipCityStateURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetLocationByZipCityStateURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetLocationByZipCityStateURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetLocationByZipCityStateURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/pkg/gen/primeapi/primeoperations/mymove_api.go b/pkg/gen/primeapi/primeoperations/mymove_api.go index b9e44b2190c..6ded41a6c0d 100644 --- a/pkg/gen/primeapi/primeoperations/mymove_api.go +++ b/pkg/gen/primeapi/primeoperations/mymove_api.go @@ -19,6 +19,7 @@ import ( "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" + "github.com/transcom/mymove/pkg/gen/primeapi/primeoperations/addresses" "github.com/transcom/mymove/pkg/gen/primeapi/primeoperations/move_task_order" "github.com/transcom/mymove/pkg/gen/primeapi/primeoperations/mto_service_item" "github.com/transcom/mymove/pkg/gen/primeapi/primeoperations/mto_shipment" @@ -79,6 +80,9 @@ func NewMymoveAPI(spec *loads.Document) *MymoveAPI { MoveTaskOrderDownloadMoveOrderHandler: move_task_order.DownloadMoveOrderHandlerFunc(func(params move_task_order.DownloadMoveOrderParams) middleware.Responder { return middleware.NotImplemented("operation move_task_order.DownloadMoveOrder has not yet been implemented") }), + AddressesGetLocationByZipCityStateHandler: addresses.GetLocationByZipCityStateHandlerFunc(func(params addresses.GetLocationByZipCityStateParams) middleware.Responder { + return middleware.NotImplemented("operation addresses.GetLocationByZipCityState has not yet been implemented") + }), MoveTaskOrderGetMoveTaskOrderHandler: move_task_order.GetMoveTaskOrderHandlerFunc(func(params move_task_order.GetMoveTaskOrderParams) middleware.Responder { return middleware.NotImplemented("operation move_task_order.GetMoveTaskOrder has not yet been implemented") }), @@ -177,6 +181,8 @@ type MymoveAPI struct { MtoShipmentDeleteMTOShipmentHandler mto_shipment.DeleteMTOShipmentHandler // MoveTaskOrderDownloadMoveOrderHandler sets the operation handler for the download move order operation MoveTaskOrderDownloadMoveOrderHandler move_task_order.DownloadMoveOrderHandler + // AddressesGetLocationByZipCityStateHandler sets the operation handler for the get location by zip city state operation + AddressesGetLocationByZipCityStateHandler addresses.GetLocationByZipCityStateHandler // MoveTaskOrderGetMoveTaskOrderHandler sets the operation handler for the get move task order operation MoveTaskOrderGetMoveTaskOrderHandler move_task_order.GetMoveTaskOrderHandler // MoveTaskOrderListMovesHandler sets the operation handler for the list moves operation @@ -310,6 +316,9 @@ func (o *MymoveAPI) Validate() error { if o.MoveTaskOrderDownloadMoveOrderHandler == nil { unregistered = append(unregistered, "move_task_order.DownloadMoveOrderHandler") } + if o.AddressesGetLocationByZipCityStateHandler == nil { + unregistered = append(unregistered, "addresses.GetLocationByZipCityStateHandler") + } if o.MoveTaskOrderGetMoveTaskOrderHandler == nil { unregistered = append(unregistered, "move_task_order.GetMoveTaskOrderHandler") } @@ -475,6 +484,10 @@ func (o *MymoveAPI) initHandlerCache() { if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } + o.handlers["GET"]["/addresses/zip-city-lookup/{search}"] = addresses.NewGetLocationByZipCityState(o.context, o.AddressesGetLocationByZipCityStateHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } o.handlers["GET"]["/move-task-orders/{moveID}"] = move_task_order.NewGetMoveTaskOrder(o.context, o.MoveTaskOrderGetMoveTaskOrderHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) diff --git a/pkg/gen/primeclient/addresses/addresses_client.go b/pkg/gen/primeclient/addresses/addresses_client.go new file mode 100644 index 00000000000..64fddbf9f02 --- /dev/null +++ b/pkg/gen/primeclient/addresses/addresses_client.go @@ -0,0 +1,81 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package addresses + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" +) + +// New creates a new addresses API client. +func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientService { + return &Client{transport: transport, formats: formats} +} + +/* +Client for addresses API +*/ +type Client struct { + transport runtime.ClientTransport + formats strfmt.Registry +} + +// ClientOption is the option for Client methods +type ClientOption func(*runtime.ClientOperation) + +// ClientService is the interface for Client methods +type ClientService interface { + GetLocationByZipCityState(params *GetLocationByZipCityStateParams, opts ...ClientOption) (*GetLocationByZipCityStateOK, error) + + SetTransport(transport runtime.ClientTransport) +} + +/* +GetLocationByZipCityState returns city state postal code and county associated with the specified full partial postal code or city state string + +Find by API using full/partial postal code or city name that returns an us_post_region_cities json object containing city, state, county and postal code. +*/ +func (a *Client) GetLocationByZipCityState(params *GetLocationByZipCityStateParams, opts ...ClientOption) (*GetLocationByZipCityStateOK, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewGetLocationByZipCityStateParams() + } + op := &runtime.ClientOperation{ + ID: "getLocationByZipCityState", + Method: "GET", + PathPattern: "/addresses/zip-city-lookup/{search}", + ProducesMediaTypes: []string{"application/json"}, + ConsumesMediaTypes: []string{"application/json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &GetLocationByZipCityStateReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*GetLocationByZipCityStateOK) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for getLocationByZipCityState: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + +// SetTransport changes the transport on the client +func (a *Client) SetTransport(transport runtime.ClientTransport) { + a.transport = transport +} diff --git a/pkg/gen/primeclient/addresses/get_location_by_zip_city_state_parameters.go b/pkg/gen/primeclient/addresses/get_location_by_zip_city_state_parameters.go new file mode 100644 index 00000000000..494619925b4 --- /dev/null +++ b/pkg/gen/primeclient/addresses/get_location_by_zip_city_state_parameters.go @@ -0,0 +1,148 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package addresses + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" +) + +// NewGetLocationByZipCityStateParams creates a new GetLocationByZipCityStateParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewGetLocationByZipCityStateParams() *GetLocationByZipCityStateParams { + return &GetLocationByZipCityStateParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewGetLocationByZipCityStateParamsWithTimeout creates a new GetLocationByZipCityStateParams object +// with the ability to set a timeout on a request. +func NewGetLocationByZipCityStateParamsWithTimeout(timeout time.Duration) *GetLocationByZipCityStateParams { + return &GetLocationByZipCityStateParams{ + timeout: timeout, + } +} + +// NewGetLocationByZipCityStateParamsWithContext creates a new GetLocationByZipCityStateParams object +// with the ability to set a context for a request. +func NewGetLocationByZipCityStateParamsWithContext(ctx context.Context) *GetLocationByZipCityStateParams { + return &GetLocationByZipCityStateParams{ + Context: ctx, + } +} + +// NewGetLocationByZipCityStateParamsWithHTTPClient creates a new GetLocationByZipCityStateParams object +// with the ability to set a custom HTTPClient for a request. +func NewGetLocationByZipCityStateParamsWithHTTPClient(client *http.Client) *GetLocationByZipCityStateParams { + return &GetLocationByZipCityStateParams{ + HTTPClient: client, + } +} + +/* +GetLocationByZipCityStateParams contains all the parameters to send to the API endpoint + + for the get location by zip city state operation. + + Typically these are written to a http.Request. +*/ +type GetLocationByZipCityStateParams struct { + + // Search. + Search string + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the get location by zip city state params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetLocationByZipCityStateParams) WithDefaults() *GetLocationByZipCityStateParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the get location by zip city state params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *GetLocationByZipCityStateParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the get location by zip city state params +func (o *GetLocationByZipCityStateParams) WithTimeout(timeout time.Duration) *GetLocationByZipCityStateParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the get location by zip city state params +func (o *GetLocationByZipCityStateParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the get location by zip city state params +func (o *GetLocationByZipCityStateParams) WithContext(ctx context.Context) *GetLocationByZipCityStateParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the get location by zip city state params +func (o *GetLocationByZipCityStateParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the get location by zip city state params +func (o *GetLocationByZipCityStateParams) WithHTTPClient(client *http.Client) *GetLocationByZipCityStateParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the get location by zip city state params +func (o *GetLocationByZipCityStateParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithSearch adds the search to the get location by zip city state params +func (o *GetLocationByZipCityStateParams) WithSearch(search string) *GetLocationByZipCityStateParams { + o.SetSearch(search) + return o +} + +// SetSearch adds the search to the get location by zip city state params +func (o *GetLocationByZipCityStateParams) SetSearch(search string) { + o.Search = search +} + +// WriteToRequest writes these params to a swagger request +func (o *GetLocationByZipCityStateParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + + // path param search + if err := r.SetPathParam("search", o.Search); err != nil { + return err + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/gen/primeclient/addresses/get_location_by_zip_city_state_responses.go b/pkg/gen/primeclient/addresses/get_location_by_zip_city_state_responses.go new file mode 100644 index 00000000000..a077d9cc5d5 --- /dev/null +++ b/pkg/gen/primeclient/addresses/get_location_by_zip_city_state_responses.go @@ -0,0 +1,397 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package addresses + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + "io" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" + + "github.com/transcom/mymove/pkg/gen/primemessages" +) + +// GetLocationByZipCityStateReader is a Reader for the GetLocationByZipCityState structure. +type GetLocationByZipCityStateReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *GetLocationByZipCityStateReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 200: + result := NewGetLocationByZipCityStateOK() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 400: + result := NewGetLocationByZipCityStateBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 403: + result := NewGetLocationByZipCityStateForbidden() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 404: + result := NewGetLocationByZipCityStateNotFound() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 500: + result := NewGetLocationByZipCityStateInternalServerError() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("[GET /addresses/zip-city-lookup/{search}] getLocationByZipCityState", response, response.Code()) + } +} + +// NewGetLocationByZipCityStateOK creates a GetLocationByZipCityStateOK with default headers values +func NewGetLocationByZipCityStateOK() *GetLocationByZipCityStateOK { + return &GetLocationByZipCityStateOK{} +} + +/* +GetLocationByZipCityStateOK describes a response with status code 200, with default header values. + +the requested list of city, state, county, and postal code matches +*/ +type GetLocationByZipCityStateOK struct { + Payload primemessages.VLocations +} + +// IsSuccess returns true when this get location by zip city state o k response has a 2xx status code +func (o *GetLocationByZipCityStateOK) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this get location by zip city state o k response has a 3xx status code +func (o *GetLocationByZipCityStateOK) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get location by zip city state o k response has a 4xx status code +func (o *GetLocationByZipCityStateOK) IsClientError() bool { + return false +} + +// IsServerError returns true when this get location by zip city state o k response has a 5xx status code +func (o *GetLocationByZipCityStateOK) IsServerError() bool { + return false +} + +// IsCode returns true when this get location by zip city state o k response a status code equal to that given +func (o *GetLocationByZipCityStateOK) IsCode(code int) bool { + return code == 200 +} + +// Code gets the status code for the get location by zip city state o k response +func (o *GetLocationByZipCityStateOK) Code() int { + return 200 +} + +func (o *GetLocationByZipCityStateOK) Error() string { + return fmt.Sprintf("[GET /addresses/zip-city-lookup/{search}][%d] getLocationByZipCityStateOK %+v", 200, o.Payload) +} + +func (o *GetLocationByZipCityStateOK) String() string { + return fmt.Sprintf("[GET /addresses/zip-city-lookup/{search}][%d] getLocationByZipCityStateOK %+v", 200, o.Payload) +} + +func (o *GetLocationByZipCityStateOK) GetPayload() primemessages.VLocations { + return o.Payload +} + +func (o *GetLocationByZipCityStateOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + // response payload + if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetLocationByZipCityStateBadRequest creates a GetLocationByZipCityStateBadRequest with default headers values +func NewGetLocationByZipCityStateBadRequest() *GetLocationByZipCityStateBadRequest { + return &GetLocationByZipCityStateBadRequest{} +} + +/* +GetLocationByZipCityStateBadRequest describes a response with status code 400, with default header values. + +The request payload is invalid. +*/ +type GetLocationByZipCityStateBadRequest struct { + Payload *primemessages.ClientError +} + +// IsSuccess returns true when this get location by zip city state bad request response has a 2xx status code +func (o *GetLocationByZipCityStateBadRequest) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get location by zip city state bad request response has a 3xx status code +func (o *GetLocationByZipCityStateBadRequest) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get location by zip city state bad request response has a 4xx status code +func (o *GetLocationByZipCityStateBadRequest) IsClientError() bool { + return true +} + +// IsServerError returns true when this get location by zip city state bad request response has a 5xx status code +func (o *GetLocationByZipCityStateBadRequest) IsServerError() bool { + return false +} + +// IsCode returns true when this get location by zip city state bad request response a status code equal to that given +func (o *GetLocationByZipCityStateBadRequest) IsCode(code int) bool { + return code == 400 +} + +// Code gets the status code for the get location by zip city state bad request response +func (o *GetLocationByZipCityStateBadRequest) Code() int { + return 400 +} + +func (o *GetLocationByZipCityStateBadRequest) Error() string { + return fmt.Sprintf("[GET /addresses/zip-city-lookup/{search}][%d] getLocationByZipCityStateBadRequest %+v", 400, o.Payload) +} + +func (o *GetLocationByZipCityStateBadRequest) String() string { + return fmt.Sprintf("[GET /addresses/zip-city-lookup/{search}][%d] getLocationByZipCityStateBadRequest %+v", 400, o.Payload) +} + +func (o *GetLocationByZipCityStateBadRequest) GetPayload() *primemessages.ClientError { + return o.Payload +} + +func (o *GetLocationByZipCityStateBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(primemessages.ClientError) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetLocationByZipCityStateForbidden creates a GetLocationByZipCityStateForbidden with default headers values +func NewGetLocationByZipCityStateForbidden() *GetLocationByZipCityStateForbidden { + return &GetLocationByZipCityStateForbidden{} +} + +/* +GetLocationByZipCityStateForbidden describes a response with status code 403, with default header values. + +The request was denied. +*/ +type GetLocationByZipCityStateForbidden struct { + Payload *primemessages.ClientError +} + +// IsSuccess returns true when this get location by zip city state forbidden response has a 2xx status code +func (o *GetLocationByZipCityStateForbidden) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get location by zip city state forbidden response has a 3xx status code +func (o *GetLocationByZipCityStateForbidden) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get location by zip city state forbidden response has a 4xx status code +func (o *GetLocationByZipCityStateForbidden) IsClientError() bool { + return true +} + +// IsServerError returns true when this get location by zip city state forbidden response has a 5xx status code +func (o *GetLocationByZipCityStateForbidden) IsServerError() bool { + return false +} + +// IsCode returns true when this get location by zip city state forbidden response a status code equal to that given +func (o *GetLocationByZipCityStateForbidden) IsCode(code int) bool { + return code == 403 +} + +// Code gets the status code for the get location by zip city state forbidden response +func (o *GetLocationByZipCityStateForbidden) Code() int { + return 403 +} + +func (o *GetLocationByZipCityStateForbidden) Error() string { + return fmt.Sprintf("[GET /addresses/zip-city-lookup/{search}][%d] getLocationByZipCityStateForbidden %+v", 403, o.Payload) +} + +func (o *GetLocationByZipCityStateForbidden) String() string { + return fmt.Sprintf("[GET /addresses/zip-city-lookup/{search}][%d] getLocationByZipCityStateForbidden %+v", 403, o.Payload) +} + +func (o *GetLocationByZipCityStateForbidden) GetPayload() *primemessages.ClientError { + return o.Payload +} + +func (o *GetLocationByZipCityStateForbidden) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(primemessages.ClientError) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetLocationByZipCityStateNotFound creates a GetLocationByZipCityStateNotFound with default headers values +func NewGetLocationByZipCityStateNotFound() *GetLocationByZipCityStateNotFound { + return &GetLocationByZipCityStateNotFound{} +} + +/* +GetLocationByZipCityStateNotFound describes a response with status code 404, with default header values. + +The requested resource wasn't found. +*/ +type GetLocationByZipCityStateNotFound struct { + Payload *primemessages.ClientError +} + +// IsSuccess returns true when this get location by zip city state not found response has a 2xx status code +func (o *GetLocationByZipCityStateNotFound) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get location by zip city state not found response has a 3xx status code +func (o *GetLocationByZipCityStateNotFound) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get location by zip city state not found response has a 4xx status code +func (o *GetLocationByZipCityStateNotFound) IsClientError() bool { + return true +} + +// IsServerError returns true when this get location by zip city state not found response has a 5xx status code +func (o *GetLocationByZipCityStateNotFound) IsServerError() bool { + return false +} + +// IsCode returns true when this get location by zip city state not found response a status code equal to that given +func (o *GetLocationByZipCityStateNotFound) IsCode(code int) bool { + return code == 404 +} + +// Code gets the status code for the get location by zip city state not found response +func (o *GetLocationByZipCityStateNotFound) Code() int { + return 404 +} + +func (o *GetLocationByZipCityStateNotFound) Error() string { + return fmt.Sprintf("[GET /addresses/zip-city-lookup/{search}][%d] getLocationByZipCityStateNotFound %+v", 404, o.Payload) +} + +func (o *GetLocationByZipCityStateNotFound) String() string { + return fmt.Sprintf("[GET /addresses/zip-city-lookup/{search}][%d] getLocationByZipCityStateNotFound %+v", 404, o.Payload) +} + +func (o *GetLocationByZipCityStateNotFound) GetPayload() *primemessages.ClientError { + return o.Payload +} + +func (o *GetLocationByZipCityStateNotFound) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(primemessages.ClientError) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} + +// NewGetLocationByZipCityStateInternalServerError creates a GetLocationByZipCityStateInternalServerError with default headers values +func NewGetLocationByZipCityStateInternalServerError() *GetLocationByZipCityStateInternalServerError { + return &GetLocationByZipCityStateInternalServerError{} +} + +/* +GetLocationByZipCityStateInternalServerError describes a response with status code 500, with default header values. + +A server error occurred. +*/ +type GetLocationByZipCityStateInternalServerError struct { + Payload *primemessages.Error +} + +// IsSuccess returns true when this get location by zip city state internal server error response has a 2xx status code +func (o *GetLocationByZipCityStateInternalServerError) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this get location by zip city state internal server error response has a 3xx status code +func (o *GetLocationByZipCityStateInternalServerError) IsRedirect() bool { + return false +} + +// IsClientError returns true when this get location by zip city state internal server error response has a 4xx status code +func (o *GetLocationByZipCityStateInternalServerError) IsClientError() bool { + return false +} + +// IsServerError returns true when this get location by zip city state internal server error response has a 5xx status code +func (o *GetLocationByZipCityStateInternalServerError) IsServerError() bool { + return true +} + +// IsCode returns true when this get location by zip city state internal server error response a status code equal to that given +func (o *GetLocationByZipCityStateInternalServerError) IsCode(code int) bool { + return code == 500 +} + +// Code gets the status code for the get location by zip city state internal server error response +func (o *GetLocationByZipCityStateInternalServerError) Code() int { + return 500 +} + +func (o *GetLocationByZipCityStateInternalServerError) Error() string { + return fmt.Sprintf("[GET /addresses/zip-city-lookup/{search}][%d] getLocationByZipCityStateInternalServerError %+v", 500, o.Payload) +} + +func (o *GetLocationByZipCityStateInternalServerError) String() string { + return fmt.Sprintf("[GET /addresses/zip-city-lookup/{search}][%d] getLocationByZipCityStateInternalServerError %+v", 500, o.Payload) +} + +func (o *GetLocationByZipCityStateInternalServerError) GetPayload() *primemessages.Error { + return o.Payload +} + +func (o *GetLocationByZipCityStateInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + o.Payload = new(primemessages.Error) + + // response payload + if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { + return err + } + + return nil +} diff --git a/pkg/gen/primeclient/mymove_client.go b/pkg/gen/primeclient/mymove_client.go index 5a6cf119393..5f38f83617d 100644 --- a/pkg/gen/primeclient/mymove_client.go +++ b/pkg/gen/primeclient/mymove_client.go @@ -10,6 +10,7 @@ import ( httptransport "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" + "github.com/transcom/mymove/pkg/gen/primeclient/addresses" "github.com/transcom/mymove/pkg/gen/primeclient/move_task_order" "github.com/transcom/mymove/pkg/gen/primeclient/mto_service_item" "github.com/transcom/mymove/pkg/gen/primeclient/mto_shipment" @@ -58,6 +59,7 @@ func New(transport runtime.ClientTransport, formats strfmt.Registry) *Mymove { cli := new(Mymove) cli.Transport = transport + cli.Addresses = addresses.New(transport, formats) cli.MoveTaskOrder = move_task_order.New(transport, formats) cli.MtoServiceItem = mto_service_item.New(transport, formats) cli.MtoShipment = mto_shipment.New(transport, formats) @@ -106,6 +108,8 @@ func (cfg *TransportConfig) WithSchemes(schemes []string) *TransportConfig { // Mymove is a client for mymove type Mymove struct { + Addresses addresses.ClientService + MoveTaskOrder move_task_order.ClientService MtoServiceItem mto_service_item.ClientService @@ -120,6 +124,7 @@ type Mymove struct { // SetTransport changes the transport on the client and all its subresources func (c *Mymove) SetTransport(transport runtime.ClientTransport) { c.Transport = transport + c.Addresses.SetTransport(transport) c.MoveTaskOrder.SetTransport(transport) c.MtoServiceItem.SetTransport(transport) c.MtoShipment.SetTransport(transport) diff --git a/pkg/gen/primemessages/v_location.go b/pkg/gen/primemessages/v_location.go new file mode 100644 index 00000000000..77cd75ee6e3 --- /dev/null +++ b/pkg/gen/primemessages/v_location.go @@ -0,0 +1,302 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package primemessages + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "encoding/json" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// VLocation A postal code, city, and state lookup +// +// swagger:model VLocation +type VLocation struct { + + // City + // Example: Anytown + City string `json:"city,omitempty"` + + // County + // Example: LOS ANGELES + County *string `json:"county,omitempty"` + + // ZIP + // Example: 90210 + // Pattern: ^(\d{5}?)$ + PostalCode string `json:"postalCode,omitempty"` + + // State + // Enum: [AL AK AR AZ CA CO CT DC DE FL GA HI IA ID IL IN KS KY LA MA MD ME MI MN MO MS MT NC ND NE NH NJ NM NV NY OH OK OR PA RI SC SD TN TX UT VA VT WA WI WV WY] + State string `json:"state,omitempty"` + + // us post region cities ID + // Example: c56a4180-65aa-42ec-a945-5fd21dec0538 + // Format: uuid + UsPostRegionCitiesID strfmt.UUID `json:"usPostRegionCitiesID,omitempty"` +} + +// Validate validates this v location +func (m *VLocation) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validatePostalCode(formats); err != nil { + res = append(res, err) + } + + if err := m.validateState(formats); err != nil { + res = append(res, err) + } + + if err := m.validateUsPostRegionCitiesID(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *VLocation) validatePostalCode(formats strfmt.Registry) error { + if swag.IsZero(m.PostalCode) { // not required + return nil + } + + if err := validate.Pattern("postalCode", "body", m.PostalCode, `^(\d{5}?)$`); err != nil { + return err + } + + return nil +} + +var vLocationTypeStatePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["AL","AK","AR","AZ","CA","CO","CT","DC","DE","FL","GA","HI","IA","ID","IL","IN","KS","KY","LA","MA","MD","ME","MI","MN","MO","MS","MT","NC","ND","NE","NH","NJ","NM","NV","NY","OH","OK","OR","PA","RI","SC","SD","TN","TX","UT","VA","VT","WA","WI","WV","WY"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + vLocationTypeStatePropEnum = append(vLocationTypeStatePropEnum, v) + } +} + +const ( + + // VLocationStateAL captures enum value "AL" + VLocationStateAL string = "AL" + + // VLocationStateAK captures enum value "AK" + VLocationStateAK string = "AK" + + // VLocationStateAR captures enum value "AR" + VLocationStateAR string = "AR" + + // VLocationStateAZ captures enum value "AZ" + VLocationStateAZ string = "AZ" + + // VLocationStateCA captures enum value "CA" + VLocationStateCA string = "CA" + + // VLocationStateCO captures enum value "CO" + VLocationStateCO string = "CO" + + // VLocationStateCT captures enum value "CT" + VLocationStateCT string = "CT" + + // VLocationStateDC captures enum value "DC" + VLocationStateDC string = "DC" + + // VLocationStateDE captures enum value "DE" + VLocationStateDE string = "DE" + + // VLocationStateFL captures enum value "FL" + VLocationStateFL string = "FL" + + // VLocationStateGA captures enum value "GA" + VLocationStateGA string = "GA" + + // VLocationStateHI captures enum value "HI" + VLocationStateHI string = "HI" + + // VLocationStateIA captures enum value "IA" + VLocationStateIA string = "IA" + + // VLocationStateID captures enum value "ID" + VLocationStateID string = "ID" + + // VLocationStateIL captures enum value "IL" + VLocationStateIL string = "IL" + + // VLocationStateIN captures enum value "IN" + VLocationStateIN string = "IN" + + // VLocationStateKS captures enum value "KS" + VLocationStateKS string = "KS" + + // VLocationStateKY captures enum value "KY" + VLocationStateKY string = "KY" + + // VLocationStateLA captures enum value "LA" + VLocationStateLA string = "LA" + + // VLocationStateMA captures enum value "MA" + VLocationStateMA string = "MA" + + // VLocationStateMD captures enum value "MD" + VLocationStateMD string = "MD" + + // VLocationStateME captures enum value "ME" + VLocationStateME string = "ME" + + // VLocationStateMI captures enum value "MI" + VLocationStateMI string = "MI" + + // VLocationStateMN captures enum value "MN" + VLocationStateMN string = "MN" + + // VLocationStateMO captures enum value "MO" + VLocationStateMO string = "MO" + + // VLocationStateMS captures enum value "MS" + VLocationStateMS string = "MS" + + // VLocationStateMT captures enum value "MT" + VLocationStateMT string = "MT" + + // VLocationStateNC captures enum value "NC" + VLocationStateNC string = "NC" + + // VLocationStateND captures enum value "ND" + VLocationStateND string = "ND" + + // VLocationStateNE captures enum value "NE" + VLocationStateNE string = "NE" + + // VLocationStateNH captures enum value "NH" + VLocationStateNH string = "NH" + + // VLocationStateNJ captures enum value "NJ" + VLocationStateNJ string = "NJ" + + // VLocationStateNM captures enum value "NM" + VLocationStateNM string = "NM" + + // VLocationStateNV captures enum value "NV" + VLocationStateNV string = "NV" + + // VLocationStateNY captures enum value "NY" + VLocationStateNY string = "NY" + + // VLocationStateOH captures enum value "OH" + VLocationStateOH string = "OH" + + // VLocationStateOK captures enum value "OK" + VLocationStateOK string = "OK" + + // VLocationStateOR captures enum value "OR" + VLocationStateOR string = "OR" + + // VLocationStatePA captures enum value "PA" + VLocationStatePA string = "PA" + + // VLocationStateRI captures enum value "RI" + VLocationStateRI string = "RI" + + // VLocationStateSC captures enum value "SC" + VLocationStateSC string = "SC" + + // VLocationStateSD captures enum value "SD" + VLocationStateSD string = "SD" + + // VLocationStateTN captures enum value "TN" + VLocationStateTN string = "TN" + + // VLocationStateTX captures enum value "TX" + VLocationStateTX string = "TX" + + // VLocationStateUT captures enum value "UT" + VLocationStateUT string = "UT" + + // VLocationStateVA captures enum value "VA" + VLocationStateVA string = "VA" + + // VLocationStateVT captures enum value "VT" + VLocationStateVT string = "VT" + + // VLocationStateWA captures enum value "WA" + VLocationStateWA string = "WA" + + // VLocationStateWI captures enum value "WI" + VLocationStateWI string = "WI" + + // VLocationStateWV captures enum value "WV" + VLocationStateWV string = "WV" + + // VLocationStateWY captures enum value "WY" + VLocationStateWY string = "WY" +) + +// prop value enum +func (m *VLocation) validateStateEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, vLocationTypeStatePropEnum, true); err != nil { + return err + } + return nil +} + +func (m *VLocation) validateState(formats strfmt.Registry) error { + if swag.IsZero(m.State) { // not required + return nil + } + + // value enum + if err := m.validateStateEnum("state", "body", m.State); err != nil { + return err + } + + return nil +} + +func (m *VLocation) validateUsPostRegionCitiesID(formats strfmt.Registry) error { + if swag.IsZero(m.UsPostRegionCitiesID) { // not required + return nil + } + + if err := validate.FormatOf("usPostRegionCitiesID", "body", "uuid", m.UsPostRegionCitiesID.String(), formats); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this v location based on context it is used +func (m *VLocation) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *VLocation) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *VLocation) UnmarshalBinary(b []byte) error { + var res VLocation + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/pkg/gen/primemessages/v_locations.go b/pkg/gen/primemessages/v_locations.go new file mode 100644 index 00000000000..caa019fc057 --- /dev/null +++ b/pkg/gen/primemessages/v_locations.go @@ -0,0 +1,78 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package primemessages + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// VLocations v locations +// +// swagger:model VLocations +type VLocations []*VLocation + +// Validate validates this v locations +func (m VLocations) Validate(formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + if swag.IsZero(m[i]) { // not required + continue + } + + if m[i] != nil { + if err := m[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this v locations based on the context it is used +func (m VLocations) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + + if swag.IsZero(m[i]) { // not required + return nil + } + + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/pkg/handlers/primeapi/addresses.go b/pkg/handlers/primeapi/addresses.go new file mode 100644 index 00000000000..55263799d93 --- /dev/null +++ b/pkg/handlers/primeapi/addresses.go @@ -0,0 +1,62 @@ +package primeapi + +import ( + "context" + + "github.com/go-openapi/runtime/middleware" + "go.uber.org/zap" + + "github.com/transcom/mymove/pkg/appcontext" + addressop "github.com/transcom/mymove/pkg/gen/primeapi/primeoperations/addresses" + "github.com/transcom/mymove/pkg/handlers" + "github.com/transcom/mymove/pkg/handlers/primeapi/payloads" + "github.com/transcom/mymove/pkg/services" +) + +type GetLocationByZipCityStateHandler struct { + handlers.HandlerConfig + services.VLocation +} + +func (h GetLocationByZipCityStateHandler) Handle(params addressop.GetLocationByZipCityStateParams) middleware.Responder { + return h.AuditableAppContextFromRequestWithErrors(params.HTTPRequest, + func(appCtx appcontext.AppContext) (middleware.Responder, error) { + /** Feature Flag - Alaska - Determines if AK can be included/excluded **/ + isAlaskaEnabled := false + akFeatureFlagName := "enable_alaska" + flag, err := h.FeatureFlagFetcher().GetBooleanFlagForUser(context.TODO(), appCtx, akFeatureFlagName, map[string]string{}) + if err != nil { + appCtx.Logger().Error("Error fetching feature flag", zap.String("featureFlagKey", akFeatureFlagName), zap.Error(err)) + } else { + isAlaskaEnabled = flag.Match + } + + /** Feature Flag - Hawaii - Determines if HI can be included/excluded **/ + isHawaiiEnabled := false + hiFeatureFlagName := "enable_hawaii" + flag, err = h.FeatureFlagFetcher().GetBooleanFlagForUser(context.TODO(), appCtx, hiFeatureFlagName, map[string]string{}) + if err != nil { + appCtx.Logger().Error("Error fetching feature flag", zap.String("featureFlagKey", hiFeatureFlagName), zap.Error(err)) + } else { + isHawaiiEnabled = flag.Match + } + + // build states to exlude filter list + statesToExclude := make([]string, 0) + if !isAlaskaEnabled { + statesToExclude = append(statesToExclude, "AK") + } + if !isHawaiiEnabled { + statesToExclude = append(statesToExclude, "HI") + } + + locationList, err := h.GetLocationsByZipCityState(appCtx, params.Search, statesToExclude) + if err != nil { + appCtx.Logger().Error("Error searching for Zip/City/State: ", zap.Error(err)) + return addressop.NewGetLocationByZipCityStateInternalServerError(), err + } + + returnPayload := payloads.VLocations(*locationList) + return addressop.NewGetLocationByZipCityStateOK().WithPayload(returnPayload), nil + }) +} diff --git a/pkg/handlers/primeapi/api.go b/pkg/handlers/primeapi/api.go index 6394ed6c30c..b58d070ef14 100644 --- a/pkg/handlers/primeapi/api.go +++ b/pkg/handlers/primeapi/api.go @@ -109,6 +109,11 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primeoperations.MymoveAP mtoserviceitem.NewServiceRequestDocumentUploadCreator(handlerConfig.FileStorer()), } + primeAPI.AddressesGetLocationByZipCityStateHandler = GetLocationByZipCityStateHandler{ + handlerConfig, + vLocation, + } + primeAPI.MtoShipmentUpdateShipmentDestinationAddressHandler = UpdateShipmentDestinationAddressHandler{ handlerConfig, shipmentaddressupdate.NewShipmentAddressUpdateRequester(handlerConfig.HHGPlanner(), addressCreator, moveRouter), diff --git a/pkg/handlers/primeapi/payloads/model_to_payload.go b/pkg/handlers/primeapi/payloads/model_to_payload.go index 7fb7aaf2447..9f925a22c43 100644 --- a/pkg/handlers/primeapi/payloads/model_to_payload.go +++ b/pkg/handlers/primeapi/payloads/model_to_payload.go @@ -1094,3 +1094,31 @@ func GetCustomerContact(customerContacts models.MTOServiceItemCustomerContacts, return models.MTOServiceItemCustomerContact{} } + +// VLocation payload +func VLocation(vLocation *models.VLocation) *primemessages.VLocation { + if vLocation == nil { + return nil + } + if *vLocation == (models.VLocation{}) { + return nil + } + + return &primemessages.VLocation{ + City: vLocation.CityName, + State: vLocation.StateName, + PostalCode: vLocation.UsprZipID, + County: &vLocation.UsprcCountyNm, + UsPostRegionCitiesID: *handlers.FmtUUID(*vLocation.UsPostRegionCitiesID), + } +} + +// VLocations payload +func VLocations(vLocations models.VLocations) primemessages.VLocations { + payload := make(primemessages.VLocations, len(vLocations)) + for i, vLocation := range vLocations { + copyOfVLocation := vLocation + payload[i] = VLocation(©OfVLocation) + } + return payload +} diff --git a/pkg/handlers/primeapi/payloads/model_to_payload_test.go b/pkg/handlers/primeapi/payloads/model_to_payload_test.go index dc0707e5b06..af3c4e867b5 100644 --- a/pkg/handlers/primeapi/payloads/model_to_payload_test.go +++ b/pkg/handlers/primeapi/payloads/model_to_payload_test.go @@ -1142,3 +1142,30 @@ func (suite *PayloadsSuite) TestMTOServiceItemsPODFSC() { suite.Equal(portLocation.Port.PortCode, internationalFuelSurchargeItem.PortCode) suite.Equal(podfscServiceItem.ReService.Code.String(), internationalFuelSurchargeItem.ReServiceCode) } + +func (suite *PayloadsSuite) TestVLocation() { + suite.Run("correctly maps VLocation with all fields populated", func() { + city := "LOS ANGELES" + state := "CA" + postalCode := "90210" + county := "LOS ANGELES" + usPostRegionCityID := uuid.Must(uuid.NewV4()) + + vLocation := &models.VLocation{ + CityName: city, + StateName: state, + UsprZipID: postalCode, + UsprcCountyNm: county, + UsPostRegionCitiesID: &usPostRegionCityID, + } + + payload := VLocation(vLocation) + + suite.IsType(payload, &primemessages.VLocation{}) + suite.Equal(handlers.FmtUUID(usPostRegionCityID), &payload.UsPostRegionCitiesID, "Expected UsPostRegionCitiesID to match") + suite.Equal(city, payload.City, "Expected City to match") + suite.Equal(state, payload.State, "Expected State to match") + suite.Equal(postalCode, payload.PostalCode, "Expected PostalCode to match") + suite.Equal(county, *(payload.County), "Expected County to match") + }) +} diff --git a/pkg/handlers/primeapi/payloads/payload_to_model.go b/pkg/handlers/primeapi/payloads/payload_to_model.go index 08a64b02b82..e44c5b37510 100644 --- a/pkg/handlers/primeapi/payloads/payload_to_model.go +++ b/pkg/handlers/primeapi/payloads/payload_to_model.go @@ -874,3 +874,19 @@ func validateReasonOriginSIT(m primemessages.MTOServiceItemOriginSIT) *validate. } return verrs } + +func VLocationModel(vLocation *primemessages.VLocation) *models.VLocation { + if vLocation == nil { + return nil + } + + usPostRegionCitiesID := uuid.FromStringOrNil(vLocation.UsPostRegionCitiesID.String()) + + return &models.VLocation{ + CityName: vLocation.City, + StateName: vLocation.State, + UsprZipID: vLocation.PostalCode, + UsprcCountyNm: *vLocation.County, + UsPostRegionCitiesID: &usPostRegionCitiesID, + } +} diff --git a/pkg/handlers/primeapi/payloads/payload_to_model_test.go b/pkg/handlers/primeapi/payloads/payload_to_model_test.go index 4ceff5aeb1f..8c91a1aa1de 100644 --- a/pkg/handlers/primeapi/payloads/payload_to_model_test.go +++ b/pkg/handlers/primeapi/payloads/payload_to_model_test.go @@ -795,3 +795,28 @@ func (suite *PayloadsSuite) TestMTOShipmentModelFromCreate_WithOptionalFields() suite.NotNil(result.DestinationAddress) suite.Equal("456 Main St", result.DestinationAddress.StreetAddress1) } + +func (suite *PayloadsSuite) TestVLocationModel() { + city := "LOS ANGELES" + state := "CA" + postalCode := "90210" + county := "LOS ANGELES" + usPostRegionCityId := uuid.Must(uuid.NewV4()) + + vLocation := &primemessages.VLocation{ + City: city, + State: state, + PostalCode: postalCode, + County: &county, + UsPostRegionCitiesID: strfmt.UUID(usPostRegionCityId.String()), + } + + payload := VLocationModel(vLocation) + + suite.IsType(payload, &models.VLocation{}) + suite.Equal(usPostRegionCityId.String(), payload.UsPostRegionCitiesID.String(), "Expected UsPostRegionCitiesID to match") + suite.Equal(city, payload.CityName, "Expected City to match") + suite.Equal(state, payload.StateName, "Expected State to match") + suite.Equal(postalCode, payload.UsprZipID, "Expected PostalCode to match") + suite.Equal(county, payload.UsprcCountyNm, "Expected County to match") +} diff --git a/swagger-def/prime.yaml b/swagger-def/prime.yaml index f34788446eb..5cf79f0db2c 100644 --- a/swagger-def/prime.yaml +++ b/swagger-def/prime.yaml @@ -1401,6 +1401,31 @@ paths: $ref: '#/responses/UnprocessableEntity' '500': $ref: '#/responses/ServerError' + /addresses/zip-city-lookup/{search}: + get: + summary: Returns city, state, postal code, and county associated with the specified full/partial postal code or city state string + description: Find by API using full/partial postal code or city name that returns an us_post_region_cities json object containing city, state, county and postal code. + operationId: getLocationByZipCityState + tags: + - addresses + parameters: + - in: path + name: search + type: string + required: true + responses: + '200': + description: the requested list of city, state, county, and postal code matches + schema: + $ref: "#/definitions/VLocations" + '400': + $ref: '#/responses/InvalidRequest' + '403': + $ref: '#/responses/PermissionDenied' + '404': + $ref: '#/responses/NotFound' + '500': + $ref: '#/responses/ServerError' definitions: Amendments: description: > @@ -2114,6 +2139,10 @@ definitions: type: string x-nullable: true x-omitempty: false + VLocations: + type: array + items: + $ref: "definitions/VLocation.yaml" responses: InvalidRequest: description: The request payload is invalid. diff --git a/swagger/prime.yaml b/swagger/prime.yaml index b27503aba19..dfe76981d15 100644 --- a/swagger/prime.yaml +++ b/swagger/prime.yaml @@ -1777,6 +1777,36 @@ paths: $ref: '#/responses/UnprocessableEntity' '500': $ref: '#/responses/ServerError' + /addresses/zip-city-lookup/{search}: + get: + summary: >- + Returns city, state, postal code, and county associated with the + specified full/partial postal code or city state string + description: >- + Find by API using full/partial postal code or city name that returns an + us_post_region_cities json object containing city, state, county and + postal code. + operationId: getLocationByZipCityState + tags: + - addresses + parameters: + - in: path + name: search + type: string + required: true + responses: + '200': + description: the requested list of city, state, county, and postal code matches + schema: + $ref: '#/definitions/VLocations' + '400': + $ref: '#/responses/InvalidRequest' + '403': + $ref: '#/responses/PermissionDenied' + '404': + $ref: '#/responses/NotFound' + '500': + $ref: '#/responses/ServerError' definitions: Amendments: description: | @@ -2996,6 +3026,10 @@ definitions: type: string x-nullable: true x-omitempty: false + VLocations: + type: array + items: + $ref: '#/definitions/VLocation' ClientError: type: object properties: @@ -4675,6 +4709,136 @@ definitions: type: string required: - invalidFields + VLocation: + description: A postal code, city, and state lookup + type: object + properties: + city: + type: string + example: Anytown + title: City + state: + title: State + type: string + x-display-value: + AL: AL + AK: AK + AR: AR + AZ: AZ + CA: CA + CO: CO + CT: CT + DC: DC + DE: DE + FL: FL + GA: GA + HI: HI + IA: IA + ID: ID + IL: IL + IN: IN + KS: KS + KY: KY + LA: LA + MA: MA + MD: MD + ME: ME + MI: MI + MN: MN + MO: MO + MS: MS + MT: MT + NC: NC + ND: ND + NE: NE + NH: NH + NJ: NJ + NM: NM + NV: NV + NY: NY + OH: OH + OK: OK + OR: OR + PA: PA + RI: RI + SC: SC + SD: SD + TN: TN + TX: TX + UT: UT + VA: VA + VT: VT + WA: WA + WI: WI + WV: WV + WY: WY + enum: + - AL + - AK + - AR + - AZ + - CA + - CO + - CT + - DC + - DE + - FL + - GA + - HI + - IA + - ID + - IL + - IN + - KS + - KY + - LA + - MA + - MD + - ME + - MI + - MN + - MO + - MS + - MT + - NC + - ND + - NE + - NH + - NJ + - NM + - NV + - NY + - OH + - OK + - OR + - PA + - RI + - SC + - SD + - TN + - TX + - UT + - VA + - VT + - WA + - WI + - WV + - WY + postalCode: + type: string + format: zip + title: ZIP + example: '90210' + pattern: ^(\d{5}?)$ + county: + type: string + title: County + x-nullable: true + example: LOS ANGELES + usPostRegionCitiesID: + type: string + format: uuid + example: c56a4180-65aa-42ec-a945-5fd21dec0538 ReServiceCode: type: string description: > From f45238a54b8ac127d7de6be997b8a4e1128b4474 Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Tue, 28 Jan 2025 00:20:55 +0000 Subject: [PATCH 04/19] add address check to createMTOShipment v3 --- pkg/handlers/primeapi/api.go | 1 + pkg/handlers/primeapi/mto_shipment_address.go | 63 ++++++++++++ .../primeapi/mto_shipment_address_test.go | 2 + pkg/handlers/primeapiv3/api.go | 2 + pkg/handlers/primeapiv3/mto_shipment.go | 95 +++++++++++++++++++ pkg/handlers/primeapiv3/mto_shipment_test.go | 3 +- 6 files changed, 165 insertions(+), 1 deletion(-) diff --git a/pkg/handlers/primeapi/api.go b/pkg/handlers/primeapi/api.go index b58d070ef14..bf1388dcb70 100644 --- a/pkg/handlers/primeapi/api.go +++ b/pkg/handlers/primeapi/api.go @@ -162,6 +162,7 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primeoperations.MymoveAP primeAPI.MtoShipmentUpdateMTOShipmentAddressHandler = UpdateMTOShipmentAddressHandler{ handlerConfig, mtoshipment.NewMTOShipmentAddressUpdater(handlerConfig.HHGPlanner(), addressCreator, addressUpdater), + vLocation, } primeAPI.MtoShipmentCreateMTOAgentHandler = CreateMTOAgentHandler{ diff --git a/pkg/handlers/primeapi/mto_shipment_address.go b/pkg/handlers/primeapi/mto_shipment_address.go index 5f699f384c1..61d849cc56a 100644 --- a/pkg/handlers/primeapi/mto_shipment_address.go +++ b/pkg/handlers/primeapi/mto_shipment_address.go @@ -1,6 +1,10 @@ package primeapi import ( + "context" + "fmt" + "strings" + "github.com/go-openapi/runtime/middleware" "github.com/gofrs/uuid" "go.uber.org/zap" @@ -19,6 +23,7 @@ import ( type UpdateMTOShipmentAddressHandler struct { handlers.HandlerConfig MTOShipmentAddressUpdater services.MTOShipmentAddressUpdater + services.VLocation } // Handle updates an address on a shipment @@ -60,6 +65,64 @@ func (h UpdateMTOShipmentAddressHandler) Handle(params mtoshipmentops.UpdateMTOS newAddress := payloads.AddressModel(payload) newAddress.ID = addressID + /** Feature Flag - Alaska - Determines if AK can be included/excluded **/ + isAlaskaEnabled := false + akFeatureFlagName := "enable_alaska" + flag, err := h.FeatureFlagFetcher().GetBooleanFlagForUser(context.TODO(), appCtx, akFeatureFlagName, map[string]string{}) + if err != nil { + appCtx.Logger().Error("Error fetching feature flag", zap.String("featureFlagKey", akFeatureFlagName), zap.Error(err)) + } else { + isAlaskaEnabled = flag.Match + } + + /** Feature Flag - Hawaii - Determines if HI can be included/excluded **/ + isHawaiiEnabled := false + hiFeatureFlagName := "enable_hawaii" + flag, err = h.FeatureFlagFetcher().GetBooleanFlagForUser(context.TODO(), appCtx, hiFeatureFlagName, map[string]string{}) + if err != nil { + appCtx.Logger().Error("Error fetching feature flag", zap.String("featureFlagKey", hiFeatureFlagName), zap.Error(err)) + } else { + isHawaiiEnabled = flag.Match + } + + // build states to exlude filter list + statesToExclude := make([]string, 0) + if !isAlaskaEnabled { + statesToExclude = append(statesToExclude, "AK") + } + if !isHawaiiEnabled { + statesToExclude = append(statesToExclude, "HI") + } + + addressSearch := newAddress.City + ", " + newAddress.State + " " + newAddress.PostalCode + + locationList, err := h.GetLocationsByZipCityState(appCtx, addressSearch, statesToExclude, true) + if err != nil { + serverError := apperror.NewInternalServerError("Error searching for address") + errStr := serverError.Error() // we do this because InternalServerError wants a *string + appCtx.Logger().Warn(serverError.Error()) + payload := payloads.InternalServerError(&errStr, h.GetTraceIDFromRequest(params.HTTPRequest)) + return mtoshipmentops.NewUpdateShipmentDestinationAddressInternalServerError().WithPayload(payload), serverError + } else if len(*locationList) == 0 { + unprocessableErr := apperror.NewUnprocessableEntityError( + fmt.Sprintf("primeapi.UpdateMTOShipmentAddress: could not find the provided location: %s", addressSearch)) + appCtx.Logger().Warn(unprocessableErr.Error()) + payload := payloads.ValidationError(unprocessableErr.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) + return mtoshipmentops.NewUpdateShipmentDestinationAddressUnprocessableEntity().WithPayload(payload), unprocessableErr + } else if len(*locationList) > 1 { + var results []string + + for _, address := range *locationList { + results = append(results, address.CityName+" "+address.StateName+" "+address.UsprZipID) + } + joinedResult := strings.Join(results[:], ", ") + unprocessableErr := apperror.NewUnprocessableEntityError( + fmt.Sprintf("primeapi.UpdateMTOShipmentAddress: multiple locations found choose one of the following: %s", joinedResult)) + appCtx.Logger().Warn(unprocessableErr.Error()) + payload := payloads.ValidationError(unprocessableErr.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) + return mtoshipmentops.NewUpdateShipmentDestinationAddressUnprocessableEntity().WithPayload(payload), unprocessableErr + } + // Call the service object updatedAddress, err := h.MTOShipmentAddressUpdater.UpdateMTOShipmentAddress(appCtx, newAddress, mtoShipmentID, eTag, true) diff --git a/pkg/handlers/primeapi/mto_shipment_address_test.go b/pkg/handlers/primeapi/mto_shipment_address_test.go index cb662b28dfe..645ce862086 100644 --- a/pkg/handlers/primeapi/mto_shipment_address_test.go +++ b/pkg/handlers/primeapi/mto_shipment_address_test.go @@ -43,6 +43,7 @@ func (suite *HandlerSuite) TestUpdateMTOShipmentAddressHandler() { planner := &mocks.Planner{} addressCreator := address.NewAddressCreator() addressUpdater := address.NewAddressUpdater() + vLocationServices := address.NewVLocation() planner.On("ZipTransitDistance", mock.AnythingOfType("*appcontext.appContext"), mock.Anything, @@ -54,6 +55,7 @@ func (suite *HandlerSuite) TestUpdateMTOShipmentAddressHandler() { handler := UpdateMTOShipmentAddressHandler{ suite.HandlerConfig(), mtoshipment.NewMTOShipmentAddressUpdater(planner, addressCreator, addressUpdater), + vLocationServices, } return handler, availableMove } diff --git a/pkg/handlers/primeapiv3/api.go b/pkg/handlers/primeapiv3/api.go index 8365d280068..bb3d2897219 100644 --- a/pkg/handlers/primeapiv3/api.go +++ b/pkg/handlers/primeapiv3/api.go @@ -31,6 +31,7 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primev3operations.Mymove fetcher := fetch.NewFetcher(builder) queryBuilder := query.NewQueryBuilder() moveRouter := move.NewMoveRouter() + vLocation := address.NewVLocation() primeSpec, err := loads.Analyzed(primev3api.SwaggerJSON, "") if err != nil { @@ -71,6 +72,7 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primev3operations.Mymove handlerConfig, shipmentCreator, movetaskorder.NewMoveTaskOrderChecker(), + vLocation, } paymentRequestRecalculator := paymentrequest.NewPaymentRequestRecalculator( paymentrequest.NewPaymentRequestCreator( diff --git a/pkg/handlers/primeapiv3/mto_shipment.go b/pkg/handlers/primeapiv3/mto_shipment.go index cddeeaab45b..a25b529f1a2 100644 --- a/pkg/handlers/primeapiv3/mto_shipment.go +++ b/pkg/handlers/primeapiv3/mto_shipment.go @@ -1,7 +1,9 @@ package primeapiv3 import ( + "context" "fmt" + "strings" "github.com/go-openapi/runtime/middleware" "github.com/gobuffalo/validate/v3" @@ -26,6 +28,7 @@ type CreateMTOShipmentHandler struct { handlers.HandlerConfig services.ShipmentCreator mtoAvailabilityChecker services.MoveTaskOrderChecker + services.VLocation } // Handle creates the mto shipment @@ -89,6 +92,35 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment "Unaccompanied baggage shipments can't be created unless the unaccompanied_baggage feature flag is enabled.", h.GetTraceIDFromRequest(params.HTTPRequest), nil)), nil } + /** Feature Flag - Alaska - Determines if AK can be included/excluded **/ + isAlaskaEnabled := false + akFeatureFlagName := "enable_alaska" + flag, err = h.FeatureFlagFetcher().GetBooleanFlagForUser(context.TODO(), appCtx, akFeatureFlagName, map[string]string{}) + if err != nil { + appCtx.Logger().Error("Error fetching feature flag", zap.String("featureFlagKey", akFeatureFlagName), zap.Error(err)) + } else { + isAlaskaEnabled = flag.Match + } + + /** Feature Flag - Hawaii - Determines if HI can be included/excluded **/ + isHawaiiEnabled := false + hiFeatureFlagName := "enable_hawaii" + flag, err = h.FeatureFlagFetcher().GetBooleanFlagForUser(context.TODO(), appCtx, hiFeatureFlagName, map[string]string{}) + if err != nil { + appCtx.Logger().Error("Error fetching feature flag", zap.String("featureFlagKey", hiFeatureFlagName), zap.Error(err)) + } else { + isHawaiiEnabled = flag.Match + } + + // build states to exlude filter list + statesToExclude := make([]string, 0) + if !isAlaskaEnabled { + statesToExclude = append(statesToExclude, "AK") + } + if !isHawaiiEnabled { + statesToExclude = append(statesToExclude, "HI") + } + for _, mtoServiceItem := range params.Body.MtoServiceItems() { // restrict creation to a list if _, ok := CreateableServiceItemMap[mtoServiceItem.ModelType()]; !ok { @@ -128,6 +160,36 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment mtoAvailableToPrime, err := h.mtoAvailabilityChecker.MTOAvailableToPrime(appCtx, moveTaskOrderID) if mtoAvailableToPrime { + // check each address prior to creating the shipment to ensure only valid addresses are being used to create the shipment + var addresses []models.Address + addresses = append(addresses, *mtoShipment.PickupAddress) + addresses = append(addresses, *mtoShipment.DestinationAddress) + + if *mtoShipment.HasSecondaryPickupAddress { + addresses = append(addresses, *mtoShipment.SecondaryPickupAddress) + } + + if *mtoShipment.HasTertiaryPickupAddress { + addresses = append(addresses, *mtoShipment.TertiaryPickupAddress) + } + + if *mtoShipment.HasSecondaryDeliveryAddress { + addresses = append(addresses, *mtoShipment.SecondaryDeliveryAddress) + } + + if *mtoShipment.HasTertiaryDeliveryAddress { + addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) + } + + for _, address := range addresses { + addressSearch := address.City + ", " + address.State + " " + address.PostalCode + responder, err := checkValidAddress(h, appCtx, params, statesToExclude, addressSearch) + + if err != nil { + return responder, err + } + } + mtoShipment, err = h.ShipmentCreator.CreateShipment(appCtx, mtoShipment) } else if err == nil { appCtx.Logger().Error("primeapiv3.CreateMTOShipmentHandler error - MTO is not available to Prime") @@ -165,6 +227,39 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment }) } +func checkValidAddress(h CreateMTOShipmentHandler, appCtx appcontext.AppContext, params mtoshipmentops.CreateMTOShipmentParams, + statesToExclude []string, addressSearch string) (middleware.Responder, error) { + locationList, err := h.GetLocationsByZipCityState(appCtx, addressSearch, statesToExclude, true) + + if err != nil { + serverError := apperror.NewInternalServerError("Error searching for address") + errStr := serverError.Error() // we do this because InternalServerError wants a *string + appCtx.Logger().Warn(serverError.Error()) + payload := payloads.InternalServerError(&errStr, h.GetTraceIDFromRequest(params.HTTPRequest)) + return mtoshipmentops.NewCreateMTOShipmentInternalServerError().WithPayload(payload), serverError + } else if len(*locationList) == 0 { + unprocessableErr := apperror.NewUnprocessableEntityError( + fmt.Sprintf("primeapi.UpdateShipmentDestinationAddress: could not find the provided location: %s", addressSearch)) + appCtx.Logger().Warn(unprocessableErr.Error()) + payload := payloads.ValidationError(unprocessableErr.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) + return mtoshipmentops.NewCreateMTOShipmentUnprocessableEntity().WithPayload(payload), unprocessableErr + } else if len(*locationList) > 1 { + var results []string + + for _, address := range *locationList { + results = append(results, address.CityName+" "+address.StateName+" "+address.UsprZipID) + } + joinedResult := strings.Join(results[:], ", ") + unprocessableErr := apperror.NewUnprocessableEntityError( + fmt.Sprintf("primeapi.UpdateShipmentDestinationAddress: multiple locations found choose one of the following: %s", joinedResult)) + appCtx.Logger().Warn(unprocessableErr.Error()) + payload := payloads.ValidationError(unprocessableErr.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) + return mtoshipmentops.NewCreateMTOShipmentUnprocessableEntity().WithPayload(payload), unprocessableErr + } + + return mtoshipmentops.NewCreateMTOShipmentOK(), nil +} + // UpdateMTOShipmentHandler is the handler to update MTO shipments type UpdateMTOShipmentHandler struct { handlers.HandlerConfig diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index 308bdf4d462..ae4078ec0e8 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -114,7 +114,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { shipmentUpdater := shipmentorchestrator.NewShipmentUpdater(mtoShipmentUpdater, ppmShipmentUpdater, boatShipmentUpdater, mobileHomeShipmentUpdater) setupTestData := func(boatFeatureFlag bool, ubFeatureFlag bool) (CreateMTOShipmentHandler, models.Move) { - + vLocationServices := address.NewVLocation() move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) handlerConfig := suite.HandlerConfig() expectedFeatureFlag := services.FeatureFlag{ @@ -196,6 +196,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { handlerConfig, shipmentCreator, mtoChecker, + vLocationServices, } // Make stubbed addresses just to collect address data for payload From a6b9f968d5e11df96fd0c8a1a5383523b0e5e586 Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Wed, 29 Jan 2025 03:07:14 +0000 Subject: [PATCH 05/19] updating for tests --- .../primeapi/payloads/payload_to_model.go | 2 +- pkg/handlers/primeapiv2/mto_shipment.go | 130 ++++++++++++------ .../primeapiv2/payloads/payload_to_model.go | 2 +- pkg/handlers/primeapiv3/mto_shipment.go | 130 ++++++++++++------ pkg/handlers/primeapiv3/mto_shipment_test.go | 24 ++-- 5 files changed, 190 insertions(+), 98 deletions(-) diff --git a/pkg/handlers/primeapi/payloads/payload_to_model.go b/pkg/handlers/primeapi/payloads/payload_to_model.go index e44c5b37510..57b88781e86 100644 --- a/pkg/handlers/primeapi/payloads/payload_to_model.go +++ b/pkg/handlers/primeapi/payloads/payload_to_model.go @@ -233,7 +233,7 @@ func PPMShipmentModelFromCreate(ppmShipment *primemessages.CreatePPMShipment) *m StreetAddress1: "Deprecated Endpoint Prime V2", StreetAddress2: models.StringPointer("Endpoint no longer supported"), StreetAddress3: models.StringPointer("Update address field to appropriate values"), - City: "DEPV2", + City: "Beverly Hills", State: "CA", PostalCode: "90210", } diff --git a/pkg/handlers/primeapiv2/mto_shipment.go b/pkg/handlers/primeapiv2/mto_shipment.go index 204af4887ab..fab81516e59 100644 --- a/pkg/handlers/primeapiv2/mto_shipment.go +++ b/pkg/handlers/primeapiv2/mto_shipment.go @@ -164,23 +164,55 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment if mtoAvailableToPrime { // check each address prior to creating the shipment to ensure only valid addresses are being used to create the shipment var addresses []models.Address - addresses = append(addresses, *mtoShipment.PickupAddress) - addresses = append(addresses, *mtoShipment.DestinationAddress) - if *mtoShipment.HasSecondaryPickupAddress { - addresses = append(addresses, *mtoShipment.SecondaryPickupAddress) - } + if mtoShipment.ShipmentType == models.MTOShipmentTypeHHG { + if mtoShipment.PickupAddress != nil { + addresses = append(addresses, *mtoShipment.PickupAddress) + } - if *mtoShipment.HasTertiaryPickupAddress { - addresses = append(addresses, *mtoShipment.TertiaryPickupAddress) - } + if mtoShipment.DestinationAddress != nil { + addresses = append(addresses, *mtoShipment.DestinationAddress) + } - if *mtoShipment.HasSecondaryDeliveryAddress { - addresses = append(addresses, *mtoShipment.SecondaryDeliveryAddress) - } + if mtoShipment.SecondaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.SecondaryPickupAddress) + } - if *mtoShipment.HasTertiaryDeliveryAddress { - addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) + if mtoShipment.TertiaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.TertiaryPickupAddress) + } + + if mtoShipment.SecondaryDeliveryAddress != nil { + addresses = append(addresses, *mtoShipment.SecondaryDeliveryAddress) + } + + if mtoShipment.TertiaryDeliveryAddress != nil { + addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) + } + } else if mtoShipment.ShipmentType == models.MTOShipmentTypePPM { + if mtoShipment.PPMShipment.PickupAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.PickupAddress) + } + + if mtoShipment.PPMShipment.DestinationAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.DestinationAddress) + } + + if mtoShipment.PPMShipment.SecondaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.SecondaryPickupAddress) + } + + if mtoShipment.PPMShipment.TertiaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.TertiaryPickupAddress) + } + + if mtoShipment.PPMShipment.SecondaryDestinationAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.SecondaryDestinationAddress) + } + + if mtoShipment.PPMShipment.TertiaryDestinationAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.TertiaryDestinationAddress) + } } for _, address := range addresses { @@ -313,40 +345,54 @@ func (h UpdateMTOShipmentHandler) Handle(params mtoshipmentops.UpdateMTOShipment // we only care if the city, state or postal code have changed as those are the ones we need to validate var addresses []models.Address - if mtoShipment.PickupAddress.City != dbShipment.PickupAddress.City || - mtoShipment.PickupAddress.State != dbShipment.PickupAddress.State || - mtoShipment.PickupAddress.PostalCode != dbShipment.PickupAddress.PostalCode { - addresses = append(addresses, *mtoShipment.PickupAddress) - } + if mtoShipment.ShipmentType == models.MTOShipmentTypeHHG { + if mtoShipment.PickupAddress != nil { + addresses = append(addresses, *mtoShipment.PickupAddress) + } - if mtoShipment.SecondaryPickupAddress.City != dbShipment.SecondaryPickupAddress.City || - mtoShipment.SecondaryPickupAddress.State != dbShipment.SecondaryPickupAddress.State || - mtoShipment.SecondaryPickupAddress.PostalCode != dbShipment.SecondaryPickupAddress.PostalCode { - addresses = append(addresses, *mtoShipment.SecondaryPickupAddress) - } + if mtoShipment.SecondaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.SecondaryPickupAddress) + } - if mtoShipment.TertiaryPickupAddress.City != dbShipment.TertiaryPickupAddress.City || - mtoShipment.TertiaryPickupAddress.State != dbShipment.TertiaryPickupAddress.State || - mtoShipment.TertiaryPickupAddress.PostalCode != dbShipment.TertiaryPickupAddress.PostalCode { - addresses = append(addresses, *mtoShipment.TertiaryPickupAddress) - } + if mtoShipment.TertiaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.TertiaryPickupAddress) + } - if mtoShipment.DestinationAddress.City != dbShipment.DestinationAddress.City || - mtoShipment.DestinationAddress.State != dbShipment.DestinationAddress.State || - mtoShipment.DestinationAddress.PostalCode != dbShipment.DestinationAddress.PostalCode { - addresses = append(addresses, *mtoShipment.DestinationAddress) - } + if mtoShipment.DestinationAddress != nil { + addresses = append(addresses, *mtoShipment.DestinationAddress) + } - if mtoShipment.SecondaryDeliveryAddress.City != dbShipment.SecondaryDeliveryAddress.City || - mtoShipment.SecondaryDeliveryAddress.State != dbShipment.SecondaryDeliveryAddress.State || - mtoShipment.SecondaryDeliveryAddress.PostalCode != dbShipment.SecondaryDeliveryAddress.PostalCode { - addresses = append(addresses, *mtoShipment.SecondaryDeliveryAddress) - } + if mtoShipment.SecondaryDeliveryAddress != nil { + addresses = append(addresses, *mtoShipment.SecondaryDeliveryAddress) + } - if mtoShipment.TertiaryDeliveryAddress.City != dbShipment.TertiaryDeliveryAddress.City || - mtoShipment.TertiaryDeliveryAddress.State != dbShipment.TertiaryDeliveryAddress.State || - mtoShipment.TertiaryDeliveryAddress.PostalCode != dbShipment.TertiaryDeliveryAddress.PostalCode { - addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) + if mtoShipment.TertiaryDeliveryAddress != nil { + addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) + } + } else if mtoShipment.ShipmentType == models.MTOShipmentTypePPM { + if mtoShipment.PPMShipment.PickupAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.PickupAddress) + } + + if mtoShipment.PPMShipment.SecondaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.SecondaryPickupAddress) + } + + if mtoShipment.PPMShipment.TertiaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.TertiaryPickupAddress) + } + + if mtoShipment.PPMShipment.DestinationAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.DestinationAddress) + } + + if mtoShipment.PPMShipment.SecondaryDestinationAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.SecondaryDestinationAddress) + } + + if mtoShipment.PPMShipment.TertiaryDestinationAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.TertiaryDestinationAddress) + } } for _, address := range addresses { diff --git a/pkg/handlers/primeapiv2/payloads/payload_to_model.go b/pkg/handlers/primeapiv2/payloads/payload_to_model.go index 40c697b3672..c0ce0db8ba4 100644 --- a/pkg/handlers/primeapiv2/payloads/payload_to_model.go +++ b/pkg/handlers/primeapiv2/payloads/payload_to_model.go @@ -276,7 +276,7 @@ func PPMShipmentModelFromCreate(ppmShipment *primev2messages.CreatePPMShipment) StreetAddress1: "Deprecated Endpoint Prime V1", StreetAddress2: models.StringPointer("Endpoint no longer supported"), StreetAddress3: models.StringPointer("Update address field to appropriate values"), - City: "DEPV1", + City: "Beverly Hills", State: "CA", PostalCode: "90210", } diff --git a/pkg/handlers/primeapiv3/mto_shipment.go b/pkg/handlers/primeapiv3/mto_shipment.go index 6e2a922a17d..ddeed4891f1 100644 --- a/pkg/handlers/primeapiv3/mto_shipment.go +++ b/pkg/handlers/primeapiv3/mto_shipment.go @@ -162,23 +162,55 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment if mtoAvailableToPrime { // check each address prior to creating the shipment to ensure only valid addresses are being used to create the shipment var addresses []models.Address - addresses = append(addresses, *mtoShipment.PickupAddress) - addresses = append(addresses, *mtoShipment.DestinationAddress) - if *mtoShipment.HasSecondaryPickupAddress { - addresses = append(addresses, *mtoShipment.SecondaryPickupAddress) - } + if mtoShipment.ShipmentType == models.MTOShipmentTypeHHG { + if mtoShipment.PickupAddress != nil { + addresses = append(addresses, *mtoShipment.PickupAddress) + } - if *mtoShipment.HasTertiaryPickupAddress { - addresses = append(addresses, *mtoShipment.TertiaryPickupAddress) - } + if mtoShipment.DestinationAddress != nil { + addresses = append(addresses, *mtoShipment.DestinationAddress) + } - if *mtoShipment.HasSecondaryDeliveryAddress { - addresses = append(addresses, *mtoShipment.SecondaryDeliveryAddress) - } + if mtoShipment.SecondaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.SecondaryPickupAddress) + } - if *mtoShipment.HasTertiaryDeliveryAddress { - addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) + if mtoShipment.TertiaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.TertiaryPickupAddress) + } + + if mtoShipment.SecondaryDeliveryAddress != nil { + addresses = append(addresses, *mtoShipment.SecondaryDeliveryAddress) + } + + if mtoShipment.TertiaryDeliveryAddress != nil { + addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) + } + } else if mtoShipment.ShipmentType == models.MTOShipmentTypePPM { + if mtoShipment.PPMShipment.PickupAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.PickupAddress) + } + + if mtoShipment.PPMShipment.DestinationAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.DestinationAddress) + } + + if mtoShipment.PPMShipment.SecondaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.SecondaryPickupAddress) + } + + if mtoShipment.PPMShipment.TertiaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.TertiaryPickupAddress) + } + + if mtoShipment.PPMShipment.SecondaryDestinationAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.SecondaryDestinationAddress) + } + + if mtoShipment.PPMShipment.TertiaryDestinationAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.TertiaryDestinationAddress) + } } for _, address := range addresses { @@ -340,40 +372,54 @@ func (h UpdateMTOShipmentHandler) Handle(params mtoshipmentops.UpdateMTOShipment // we only care if the city, state or postal code have changed as those are the ones we need to validate var addresses []models.Address - if mtoShipment.PickupAddress.City != dbShipment.PickupAddress.City || - mtoShipment.PickupAddress.State != dbShipment.PickupAddress.State || - mtoShipment.PickupAddress.PostalCode != dbShipment.PickupAddress.PostalCode { - addresses = append(addresses, *mtoShipment.PickupAddress) - } + if mtoShipment.ShipmentType == models.MTOShipmentTypeHHG { + if mtoShipment.PickupAddress != nil { + addresses = append(addresses, *mtoShipment.PickupAddress) + } - if mtoShipment.SecondaryPickupAddress.City != dbShipment.SecondaryPickupAddress.City || - mtoShipment.SecondaryPickupAddress.State != dbShipment.SecondaryPickupAddress.State || - mtoShipment.SecondaryPickupAddress.PostalCode != dbShipment.SecondaryPickupAddress.PostalCode { - addresses = append(addresses, *mtoShipment.SecondaryPickupAddress) - } + if mtoShipment.SecondaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.SecondaryPickupAddress) + } - if mtoShipment.TertiaryPickupAddress.City != dbShipment.TertiaryPickupAddress.City || - mtoShipment.TertiaryPickupAddress.State != dbShipment.TertiaryPickupAddress.State || - mtoShipment.TertiaryPickupAddress.PostalCode != dbShipment.TertiaryPickupAddress.PostalCode { - addresses = append(addresses, *mtoShipment.TertiaryPickupAddress) - } + if mtoShipment.TertiaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.TertiaryPickupAddress) + } - if mtoShipment.DestinationAddress.City != dbShipment.DestinationAddress.City || - mtoShipment.DestinationAddress.State != dbShipment.DestinationAddress.State || - mtoShipment.DestinationAddress.PostalCode != dbShipment.DestinationAddress.PostalCode { - addresses = append(addresses, *mtoShipment.DestinationAddress) - } + if mtoShipment.DestinationAddress != nil { + addresses = append(addresses, *mtoShipment.DestinationAddress) + } - if mtoShipment.SecondaryDeliveryAddress.City != dbShipment.SecondaryDeliveryAddress.City || - mtoShipment.SecondaryDeliveryAddress.State != dbShipment.SecondaryDeliveryAddress.State || - mtoShipment.SecondaryDeliveryAddress.PostalCode != dbShipment.SecondaryDeliveryAddress.PostalCode { - addresses = append(addresses, *mtoShipment.SecondaryDeliveryAddress) - } + if mtoShipment.SecondaryDeliveryAddress != nil { + addresses = append(addresses, *mtoShipment.SecondaryDeliveryAddress) + } - if mtoShipment.TertiaryDeliveryAddress.City != dbShipment.TertiaryDeliveryAddress.City || - mtoShipment.TertiaryDeliveryAddress.State != dbShipment.TertiaryDeliveryAddress.State || - mtoShipment.TertiaryDeliveryAddress.PostalCode != dbShipment.TertiaryDeliveryAddress.PostalCode { - addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) + if mtoShipment.TertiaryDeliveryAddress != nil { + addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) + } + } else if mtoShipment.ShipmentType == models.MTOShipmentTypePPM { + if mtoShipment.PPMShipment.PickupAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.PickupAddress) + } + + if mtoShipment.PPMShipment.SecondaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.SecondaryPickupAddress) + } + + if mtoShipment.PPMShipment.TertiaryPickupAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.TertiaryPickupAddress) + } + + if mtoShipment.PPMShipment.DestinationAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.DestinationAddress) + } + + if mtoShipment.PPMShipment.SecondaryDestinationAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.SecondaryDestinationAddress) + } + + if mtoShipment.PPMShipment.TertiaryDestinationAddress != nil { + addresses = append(addresses, *mtoShipment.PPMShipment.TertiaryDestinationAddress) + } } for _, address := range addresses { diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index 347ccbc77bf..77a37406bfa 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -366,20 +366,20 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { address1 := models.Address{ StreetAddress1: "some address", - City: "city", + City: "Beverly Hills", State: "CA", PostalCode: "90210", } address2 := models.Address{ StreetAddress1: "some address", - City: "city", + City: "Scott Afb", State: "IL", PostalCode: "62225", } address3 := models.Address{ StreetAddress1: "some address", - City: "city", + City: "Suffolk", State: "VA", PostalCode: "23435", } @@ -715,13 +715,13 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { address1 := models.Address{ StreetAddress1: "some address", - City: "city", + City: "Beverly Hills", State: "CA", PostalCode: "90210", } addressWithEmptyStreet1 := models.Address{ StreetAddress1: "", - City: "city", + City: "Beverly Hills", State: "CA", PostalCode: "90210", } @@ -859,7 +859,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { // as empty on the server side. // ************************************************************************************* ppmDestinationAddressOptionalStreet1ContainingWhitespaces := primev3messages.PPMDestinationAddress{ - City: models.StringPointer("SomeCity"), + City: models.StringPointer("Beverly Hills"), Country: models.StringPointer("US"), PostalCode: models.StringPointer("90210"), State: models.StringPointer("CA"), @@ -1564,7 +1564,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { { Model: models.Address{ StreetAddress1: "some address", - City: "city", + City: "Beverly Hills", State: "CA", PostalCode: "90210", }, @@ -1573,7 +1573,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { { Model: models.Address{ StreetAddress1: "some address", - City: "city", + City: "Beverly Hills", State: "CA", PostalCode: "90210", }, @@ -1689,7 +1689,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { { Model: models.Address{ StreetAddress1: "some address", - City: "city", + City: "Beverly Hills", State: "CA", PostalCode: "90210", }, @@ -1698,7 +1698,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { { Model: models.Address{ StreetAddress1: "some address", - City: "city", + City: "Beverly Hills", State: "CA", PostalCode: "90210", }, @@ -1707,7 +1707,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { { Model: models.Address{ StreetAddress1: "some address", - City: "city", + City: "Beverly Hills", State: "CA", PostalCode: "90210", }, @@ -1716,7 +1716,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { { Model: models.Address{ StreetAddress1: "some address", - City: "city", + City: "Beverly Hills", State: "CA", PostalCode: "90210", }, From 1593a6cd64f479f8da00dda2b075a6535eefefb8 Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Wed, 29 Jan 2025 16:29:37 +0000 Subject: [PATCH 06/19] adding tests for create shipment v3 --- pkg/handlers/primeapiv2/mto_shipment.go | 9 +- pkg/handlers/primeapiv3/mto_shipment.go | 9 +- pkg/handlers/primeapiv3/mto_shipment_test.go | 134 +++++++++++++++++++ 3 files changed, 142 insertions(+), 10 deletions(-) diff --git a/pkg/handlers/primeapiv2/mto_shipment.go b/pkg/handlers/primeapiv2/mto_shipment.go index fab81516e59..f4d26a4176d 100644 --- a/pkg/handlers/primeapiv2/mto_shipment.go +++ b/pkg/handlers/primeapiv2/mto_shipment.go @@ -165,7 +165,7 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment // check each address prior to creating the shipment to ensure only valid addresses are being used to create the shipment var addresses []models.Address - if mtoShipment.ShipmentType == models.MTOShipmentTypeHHG { + if mtoShipment.ShipmentType != models.MTOShipmentTypePPM { if mtoShipment.PickupAddress != nil { addresses = append(addresses, *mtoShipment.PickupAddress) } @@ -189,7 +189,7 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment if mtoShipment.TertiaryDeliveryAddress != nil { addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) } - } else if mtoShipment.ShipmentType == models.MTOShipmentTypePPM { + } else { if mtoShipment.PPMShipment.PickupAddress != nil { addresses = append(addresses, *mtoShipment.PPMShipment.PickupAddress) } @@ -342,10 +342,9 @@ func (h UpdateMTOShipmentHandler) Handle(params mtoshipmentops.UpdateMTOShipment } // check each address prior to updating the shipment to ensure only valid addresses are being used - // we only care if the city, state or postal code have changed as those are the ones we need to validate var addresses []models.Address - if mtoShipment.ShipmentType == models.MTOShipmentTypeHHG { + if mtoShipment.ShipmentType != models.MTOShipmentTypePPM { if mtoShipment.PickupAddress != nil { addresses = append(addresses, *mtoShipment.PickupAddress) } @@ -369,7 +368,7 @@ func (h UpdateMTOShipmentHandler) Handle(params mtoshipmentops.UpdateMTOShipment if mtoShipment.TertiaryDeliveryAddress != nil { addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) } - } else if mtoShipment.ShipmentType == models.MTOShipmentTypePPM { + } else { if mtoShipment.PPMShipment.PickupAddress != nil { addresses = append(addresses, *mtoShipment.PPMShipment.PickupAddress) } diff --git a/pkg/handlers/primeapiv3/mto_shipment.go b/pkg/handlers/primeapiv3/mto_shipment.go index ddeed4891f1..e8a211d20b6 100644 --- a/pkg/handlers/primeapiv3/mto_shipment.go +++ b/pkg/handlers/primeapiv3/mto_shipment.go @@ -163,7 +163,7 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment // check each address prior to creating the shipment to ensure only valid addresses are being used to create the shipment var addresses []models.Address - if mtoShipment.ShipmentType == models.MTOShipmentTypeHHG { + if mtoShipment.ShipmentType != models.MTOShipmentTypePPM { if mtoShipment.PickupAddress != nil { addresses = append(addresses, *mtoShipment.PickupAddress) } @@ -187,7 +187,7 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment if mtoShipment.TertiaryDeliveryAddress != nil { addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) } - } else if mtoShipment.ShipmentType == models.MTOShipmentTypePPM { + } else { if mtoShipment.PPMShipment.PickupAddress != nil { addresses = append(addresses, *mtoShipment.PPMShipment.PickupAddress) } @@ -369,10 +369,9 @@ func (h UpdateMTOShipmentHandler) Handle(params mtoshipmentops.UpdateMTOShipment } // check each address prior to updating the shipment to ensure only valid addresses are being used - // we only care if the city, state or postal code have changed as those are the ones we need to validate var addresses []models.Address - if mtoShipment.ShipmentType == models.MTOShipmentTypeHHG { + if mtoShipment.ShipmentType != models.MTOShipmentTypePPM { if mtoShipment.PickupAddress != nil { addresses = append(addresses, *mtoShipment.PickupAddress) } @@ -396,7 +395,7 @@ func (h UpdateMTOShipmentHandler) Handle(params mtoshipmentops.UpdateMTOShipment if mtoShipment.TertiaryDeliveryAddress != nil { addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) } - } else if mtoShipment.ShipmentType == models.MTOShipmentTypePPM { + } else { if mtoShipment.PPMShipment.PickupAddress != nil { addresses = append(addresses, *mtoShipment.PPMShipment.PickupAddress) } diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index 77a37406bfa..592df5d1dec 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -1073,6 +1073,140 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { suite.Contains(*unprocessableEntity.Payload.Detail, "PickupAddress is required") }) + suite.Run("POST failure - 422 - Invalid address", func() { + // Under Test: CreateMTOShipment handler code + // Setup: Create an mto shipment on an available move + // Expected: Successful submission, status should be SUBMITTED + handler, move := setupTestData(false, true) + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + }, + } + + // set bad data for address so the validation fails + params.Body.PickupAddress.City = handlers.FmtString("Bad City") + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + }) + + suite.Run("Failure POST - 422 - Invalid address (PPM)", func() { + // Under Test: CreateMTOShipment handler code + // Setup: Create a PPM shipment on an available move + // Expected: Failure, returns an invalid address error + handler, move := setupTestData(true, false) + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + counselorRemarks := "Some counselor remarks" + expectedDepartureDate := time.Now().AddDate(0, 0, 10) + sitExpected := true + sitLocation := primev3messages.SITLocationTypeDESTINATION + sitEstimatedWeight := unit.Pound(1500) + sitEstimatedEntryDate := expectedDepartureDate.AddDate(0, 0, 5) + sitEstimatedDepartureDate := sitEstimatedEntryDate.AddDate(0, 0, 20) + estimatedWeight := unit.Pound(3200) + hasProGear := true + proGearWeight := unit.Pound(400) + spouseProGearWeight := unit.Pound(250) + estimatedIncentive := 123456 + sitEstimatedCost := 67500 + + address1 := models.Address{ + StreetAddress1: "some address", + City: "Bad City", + State: "CA", + PostalCode: "90210", + } + + expectedPickupAddress := address1 + pickupAddress = primev3messages.Address{ + City: &expectedPickupAddress.City, + PostalCode: &expectedPickupAddress.PostalCode, + State: &expectedPickupAddress.State, + StreetAddress1: &expectedPickupAddress.StreetAddress1, + StreetAddress2: expectedPickupAddress.StreetAddress2, + StreetAddress3: expectedPickupAddress.StreetAddress3, + } + + expectedDestinationAddress := address1 + destinationAddress = primev3messages.Address{ + City: &expectedDestinationAddress.City, + PostalCode: &expectedDestinationAddress.PostalCode, + State: &expectedDestinationAddress.State, + StreetAddress1: &expectedDestinationAddress.StreetAddress1, + StreetAddress2: expectedDestinationAddress.StreetAddress2, + StreetAddress3: expectedDestinationAddress.StreetAddress3, + } + ppmDestinationAddress = primev3messages.PPMDestinationAddress{ + City: &expectedDestinationAddress.City, + PostalCode: &expectedDestinationAddress.PostalCode, + State: &expectedDestinationAddress.State, + StreetAddress1: &expectedDestinationAddress.StreetAddress1, + StreetAddress2: expectedDestinationAddress.StreetAddress2, + StreetAddress3: expectedDestinationAddress.StreetAddress3, + } + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypePPM), + CounselorRemarks: &counselorRemarks, + PpmShipment: &primev3messages.CreatePPMShipment{ + ExpectedDepartureDate: handlers.FmtDate(expectedDepartureDate), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, + DestinationAddress: struct { + primev3messages.PPMDestinationAddress + }{ppmDestinationAddress}, + SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, + SitExpected: &sitExpected, + SitLocation: &sitLocation, + SitEstimatedWeight: handlers.FmtPoundPtr(&sitEstimatedWeight), + SitEstimatedEntryDate: handlers.FmtDate(sitEstimatedEntryDate), + SitEstimatedDepartureDate: handlers.FmtDate(sitEstimatedDepartureDate), + EstimatedWeight: handlers.FmtPoundPtr(&estimatedWeight), + HasProGear: &hasProGear, + ProGearWeight: handlers.FmtPoundPtr(&proGearWeight), + SpouseProGearWeight: handlers.FmtPoundPtr(&spouseProGearWeight), + }, + }, + } + + ppmEstimator.On("EstimateIncentiveWithDefaultChecks", + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("models.PPMShipment"), + mock.AnythingOfType("*models.PPMShipment")). + Return(models.CentPointer(unit.Cents(estimatedIncentive)), models.CentPointer(unit.Cents(sitEstimatedCost)), nil).Once() + + ppmEstimator.On("MaxIncentive", + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("models.PPMShipment"), + mock.AnythingOfType("*models.PPMShipment")). + Return(nil, nil) + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + }) + suite.Run("POST failure - 404 -- not found", func() { // Under Test: CreateMTOShipmentHandler // Setup: Create a shipment on a non-existent move From d6d3e52ab2c0f3b8126ef34e836ccb91c33f9f0d Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Wed, 29 Jan 2025 19:18:22 +0000 Subject: [PATCH 07/19] adding update shipment tests --- pkg/handlers/primeapiv3/mto_shipment_test.go | 74 ++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index 592df5d1dec..3fb3c3e01bf 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -1207,6 +1207,80 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) }) + suite.Run("PATCH failure - Invalid pickup address.", func() { + // Under Test: UpdateMTOShipmentHandler + // Setup: Set an invalid zip + // Expected: 422 Response returned + + shipmentUpdater := shipmentorchestrator.NewShipmentUpdater(mtoShipmentUpdater, ppmShipmentUpdater, boatShipmentUpdater, mobileHomeShipmentUpdater) + patchHandler := UpdateMTOShipmentHandler{ + suite.HandlerConfig(), + shipmentUpdater, + vLocationServices, + } + + now := time.Now() + mto_shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "some address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.PickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.DeliveryAddress, + }, + }, nil) + move := factory.BuildMoveWithPPMShipment(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + AvailableToPrimeAt: &now, + ApprovedAt: &now, + Status: models.MoveStatusAPPROVED, + }, + }, + }, nil) + + var testMove models.Move + err := suite.DB().EagerPreload("MTOShipments.PPMShipment").Find(&testMove, move.ID) + suite.NoError(err) + var testMtoShipment models.MTOShipment + err = suite.DB().Find(&testMtoShipment, mto_shipment.ID) + suite.NoError(err) + testMtoShipment.MoveTaskOrderID = testMove.ID + testMtoShipment.MoveTaskOrder = testMove + err = suite.DB().Save(&testMtoShipment) + suite.NoError(err) + testMove.MTOShipments = append(testMove.MTOShipments, mto_shipment) + err = suite.DB().Save(&testMove) + suite.NoError(err) + + patchReq := httptest.NewRequest("PATCH", fmt.Sprintf("/mto-shipments/%s", testMove.MTOShipments[0].ID), nil) + + eTag := etag.GenerateEtag(testMove.MTOShipments[0].UpdatedAt) + patchParams := mtoshipmentops.UpdateMTOShipmentParams{ + HTTPRequest: patchReq, + MtoShipmentID: strfmt.UUID(testMove.MTOShipments[0].ID.String()), + IfMatch: eTag, + } + tertiaryAddress := GetTestAddress() + patchParams.Body = &primev3messages.UpdateMTOShipment{ + TertiaryDeliveryAddress: struct{ primev3messages.Address }{tertiaryAddress}, + } + patchResponse := patchHandler.Handle(patchParams) + errResponse := patchResponse.(*mtoshipmentops.UpdateMTOShipmentUnprocessableEntity) + suite.IsType(&mtoshipmentops.UpdateMTOShipmentUnprocessableEntity{}, errResponse) + }) + suite.Run("POST failure - 404 -- not found", func() { // Under Test: CreateMTOShipmentHandler // Setup: Create a shipment on a non-existent move From 2ada1756276e17f70a955797ca6ac23eaa20af8c Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Wed, 29 Jan 2025 22:15:27 +0000 Subject: [PATCH 08/19] adding update dest address test --- pkg/handlers/primeapi/mto_shipment_address.go | 6 +-- .../primeapi/mto_shipment_address_test.go | 45 +++++++++++++++++-- pkg/handlers/primeapi/mto_shipment_test.go | 22 +++++++-- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/pkg/handlers/primeapi/mto_shipment_address.go b/pkg/handlers/primeapi/mto_shipment_address.go index 61d849cc56a..ea0dae7ad5d 100644 --- a/pkg/handlers/primeapi/mto_shipment_address.go +++ b/pkg/handlers/primeapi/mto_shipment_address.go @@ -102,13 +102,13 @@ func (h UpdateMTOShipmentAddressHandler) Handle(params mtoshipmentops.UpdateMTOS errStr := serverError.Error() // we do this because InternalServerError wants a *string appCtx.Logger().Warn(serverError.Error()) payload := payloads.InternalServerError(&errStr, h.GetTraceIDFromRequest(params.HTTPRequest)) - return mtoshipmentops.NewUpdateShipmentDestinationAddressInternalServerError().WithPayload(payload), serverError + return mtoshipmentops.NewUpdateMTOShipmentAddressInternalServerError().WithPayload(payload), serverError } else if len(*locationList) == 0 { unprocessableErr := apperror.NewUnprocessableEntityError( fmt.Sprintf("primeapi.UpdateMTOShipmentAddress: could not find the provided location: %s", addressSearch)) appCtx.Logger().Warn(unprocessableErr.Error()) payload := payloads.ValidationError(unprocessableErr.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) - return mtoshipmentops.NewUpdateShipmentDestinationAddressUnprocessableEntity().WithPayload(payload), unprocessableErr + return mtoshipmentops.NewUpdateMTOShipmentAddressUnprocessableEntity().WithPayload(payload), unprocessableErr } else if len(*locationList) > 1 { var results []string @@ -120,7 +120,7 @@ func (h UpdateMTOShipmentAddressHandler) Handle(params mtoshipmentops.UpdateMTOS fmt.Sprintf("primeapi.UpdateMTOShipmentAddress: multiple locations found choose one of the following: %s", joinedResult)) appCtx.Logger().Warn(unprocessableErr.Error()) payload := payloads.ValidationError(unprocessableErr.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) - return mtoshipmentops.NewUpdateShipmentDestinationAddressUnprocessableEntity().WithPayload(payload), unprocessableErr + return mtoshipmentops.NewUpdateMTOShipmentAddressUnprocessableEntity().WithPayload(payload), unprocessableErr } // Call the service object diff --git a/pkg/handlers/primeapi/mto_shipment_address_test.go b/pkg/handlers/primeapi/mto_shipment_address_test.go index 645ce862086..be9b58e1c87 100644 --- a/pkg/handlers/primeapi/mto_shipment_address_test.go +++ b/pkg/handlers/primeapi/mto_shipment_address_test.go @@ -62,8 +62,8 @@ func (suite *HandlerSuite) TestUpdateMTOShipmentAddressHandler() { newAddress := models.Address{ StreetAddress1: "7 Q St", - City: "Framington", - State: "MA", + City: "Acmar", + State: "AL", PostalCode: "35004", } @@ -123,7 +123,7 @@ func (suite *HandlerSuite) TestUpdateMTOShipmentAddressHandler() { StreetAddress3: models.StringPointer("441 SW Río de la Plata Drive"), City: "Alameda", State: "CA", - PostalCode: "35004", + PostalCode: "94502", } // Update with new address @@ -356,4 +356,43 @@ func (suite *HandlerSuite) TestUpdateMTOShipmentAddressHandler() { response := handler.Handle(params) suite.IsType(&mtoshipmentops.UpdateMTOShipmentAddressUnprocessableEntity{}, response) }) + + suite.Run("Failure - Unprocessable when updating address with invalid data", func() { + // Testcase: address is updated on a shipment that's available to MTO with invalid address + // Expected: Failure response 422 + // Under Test: UpdateMTOShipmentAddress handler code and mtoShipmentAddressUpdater service object + handler, availableMove := setupTestData() + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: availableMove, + LinkOnly: true, + }, + }, nil) + newAddress2 := models.Address{ + StreetAddress1: "7 Q St", + StreetAddress2: models.StringPointer("6622 Airport Way S #1430"), + StreetAddress3: models.StringPointer("441 SW Río de la Plata Drive"), + City: "Bad City", + State: "CA", + PostalCode: "99999", + } + + // Update with new address + payload := payloads.Address(&newAddress2) + req := httptest.NewRequest("PUT", fmt.Sprintf("/mto-shipments/%s/addresses/%s", shipment.ID.String(), shipment.ID.String()), nil) + params := mtoshipmentops.UpdateMTOShipmentAddressParams{ + HTTPRequest: req, + AddressID: *handlers.FmtUUID(shipment.PickupAddress.ID), + MtoShipmentID: *handlers.FmtUUID(shipment.ID), + Body: payload, + IfMatch: etag.GenerateEtag(shipment.PickupAddress.UpdatedAt), + } + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + // Run handler and check response + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.UpdateMTOShipmentAddressUnprocessableEntity{}, response) + }) } diff --git a/pkg/handlers/primeapi/mto_shipment_test.go b/pkg/handlers/primeapi/mto_shipment_test.go index 0cf04b2a8d5..0fb46ff996b 100644 --- a/pkg/handlers/primeapi/mto_shipment_test.go +++ b/pkg/handlers/primeapi/mto_shipment_test.go @@ -36,6 +36,7 @@ import ( func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { req := httptest.NewRequest("POST", "/mto-shipments/{mtoShipmentID}/shipment-address-updates", nil) + vLocationServices := address.NewVLocation() makeSubtestData := func() mtoshipmentops.UpdateShipmentDestinationAddressParams { contractorRemark := "This is a contractor remark" @@ -57,6 +58,7 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { return params } + suite.Run("POST failure - 422 Unprocessable Entity Error", func() { subtestData := makeSubtestData() mockCreator := mocks.ShipmentAddressUpdateRequester{} @@ -66,6 +68,23 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { &mockCreator, vLocationServices, } + + subtestData.Body.NewAddress.City = handlers.FmtString("Bad City") + // Validate incoming payload + suite.NoError(subtestData.Body.Validate(strfmt.Default)) + + response := handler.Handle(subtestData) + suite.IsType(&mtoshipmentops.UpdateShipmentDestinationAddressUnprocessableEntity{}, response) + }) + + suite.Run("POST failure - 422 Unprocessable Entity Error", func() { + subtestData := makeSubtestData() + mockCreator := mocks.ShipmentAddressUpdateRequester{} + handler := UpdateShipmentDestinationAddressHandler{ + suite.HandlerConfig(), + &mockCreator, + vLocationServices, + } // InvalidInputError should generate an UnprocessableEntity response error // Need verrs incorporated to satisfy swagger validation verrs := validate.NewErrors() @@ -94,7 +113,6 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { suite.Run("POST failure - 409 Request conflict reponse Error", func() { subtestData := makeSubtestData() mockCreator := mocks.ShipmentAddressUpdateRequester{} - vLocationServices := address.NewVLocation() handler := UpdateShipmentDestinationAddressHandler{ suite.HandlerConfig(), &mockCreator, @@ -126,7 +144,6 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { subtestData := makeSubtestData() mockCreator := mocks.ShipmentAddressUpdateRequester{} - vLocationServices := address.NewVLocation() handler := UpdateShipmentDestinationAddressHandler{ suite.HandlerConfig(), &mockCreator, @@ -158,7 +175,6 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { subtestData := makeSubtestData() mockCreator := mocks.ShipmentAddressUpdateRequester{} - vLocationServices := address.NewVLocation() handler := UpdateShipmentDestinationAddressHandler{ suite.HandlerConfig(), &mockCreator, From 0e29e9fc823bf02fb5b85bd97aa6f99ca4609bb4 Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Wed, 29 Jan 2025 22:41:07 +0000 Subject: [PATCH 09/19] updated comment for test --- pkg/handlers/primeapiv3/mto_shipment_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index 3fb3c3e01bf..d68902af197 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -1076,7 +1076,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { suite.Run("POST failure - 422 - Invalid address", func() { // Under Test: CreateMTOShipment handler code // Setup: Create an mto shipment on an available move - // Expected: Successful submission, status should be SUBMITTED + // Expected: Failure, invalid address handler, move := setupTestData(false, true) req := httptest.NewRequest("POST", "/mto-shipments", nil) From df4ee556e2ec7bb55f0301d6a7bb470fed54917f Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Wed, 29 Jan 2025 23:20:23 +0000 Subject: [PATCH 10/19] fixing update test v3 --- pkg/handlers/primeapiv3/mto_shipment_test.go | 140 +++++++++---------- 1 file changed, 66 insertions(+), 74 deletions(-) diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index d68902af197..3ee760b5894 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -1207,80 +1207,6 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) }) - suite.Run("PATCH failure - Invalid pickup address.", func() { - // Under Test: UpdateMTOShipmentHandler - // Setup: Set an invalid zip - // Expected: 422 Response returned - - shipmentUpdater := shipmentorchestrator.NewShipmentUpdater(mtoShipmentUpdater, ppmShipmentUpdater, boatShipmentUpdater, mobileHomeShipmentUpdater) - patchHandler := UpdateMTOShipmentHandler{ - suite.HandlerConfig(), - shipmentUpdater, - vLocationServices, - } - - now := time.Now() - mto_shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ - { - Model: models.Address{ - StreetAddress1: "some address", - City: "Beverly Hills", - State: "CA", - PostalCode: "90210", - }, - Type: &factory.Addresses.PickupAddress, - }, - { - Model: models.Address{ - StreetAddress1: "some address", - City: "Beverly Hills", - State: "CA", - PostalCode: "90210", - }, - Type: &factory.Addresses.DeliveryAddress, - }, - }, nil) - move := factory.BuildMoveWithPPMShipment(suite.DB(), []factory.Customization{ - { - Model: models.Move{ - AvailableToPrimeAt: &now, - ApprovedAt: &now, - Status: models.MoveStatusAPPROVED, - }, - }, - }, nil) - - var testMove models.Move - err := suite.DB().EagerPreload("MTOShipments.PPMShipment").Find(&testMove, move.ID) - suite.NoError(err) - var testMtoShipment models.MTOShipment - err = suite.DB().Find(&testMtoShipment, mto_shipment.ID) - suite.NoError(err) - testMtoShipment.MoveTaskOrderID = testMove.ID - testMtoShipment.MoveTaskOrder = testMove - err = suite.DB().Save(&testMtoShipment) - suite.NoError(err) - testMove.MTOShipments = append(testMove.MTOShipments, mto_shipment) - err = suite.DB().Save(&testMove) - suite.NoError(err) - - patchReq := httptest.NewRequest("PATCH", fmt.Sprintf("/mto-shipments/%s", testMove.MTOShipments[0].ID), nil) - - eTag := etag.GenerateEtag(testMove.MTOShipments[0].UpdatedAt) - patchParams := mtoshipmentops.UpdateMTOShipmentParams{ - HTTPRequest: patchReq, - MtoShipmentID: strfmt.UUID(testMove.MTOShipments[0].ID.String()), - IfMatch: eTag, - } - tertiaryAddress := GetTestAddress() - patchParams.Body = &primev3messages.UpdateMTOShipment{ - TertiaryDeliveryAddress: struct{ primev3messages.Address }{tertiaryAddress}, - } - patchResponse := patchHandler.Handle(patchParams) - errResponse := patchResponse.(*mtoshipmentops.UpdateMTOShipmentUnprocessableEntity) - suite.IsType(&mtoshipmentops.UpdateMTOShipmentUnprocessableEntity{}, errResponse) - }) - suite.Run("POST failure - 404 -- not found", func() { // Under Test: CreateMTOShipmentHandler // Setup: Create a shipment on a non-existent move @@ -1971,6 +1897,72 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { response := patchResponse.(*mtoshipmentops.UpdateMTOShipmentOK) suite.IsType(&mtoshipmentops.UpdateMTOShipmentOK{}, response) }) + + suite.Run("PATCH failure - Invalid pickup address.", func() { + // Under Test: UpdateMTOShipmentHandler + // Setup: Set an invalid zip + // Expected: 422 Response returned + + shipmentUpdater := shipmentorchestrator.NewShipmentUpdater(mtoShipmentUpdater, ppmShipmentUpdater, boatShipmentUpdater, mobileHomeShipmentUpdater) + patchHandler := UpdateMTOShipmentHandler{ + suite.HandlerConfig(), + shipmentUpdater, + vLocationServices, + } + + now := time.Now() + mto_shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "some address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.PickupAddress, + }, + }, nil) + move := factory.BuildMoveWithPPMShipment(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + AvailableToPrimeAt: &now, + ApprovedAt: &now, + Status: models.MoveStatusAPPROVED, + }, + }, + }, nil) + + var testMove models.Move + err := suite.DB().EagerPreload("MTOShipments.PPMShipment").Find(&testMove, move.ID) + suite.NoError(err) + var testMtoShipment models.MTOShipment + err = suite.DB().Find(&testMtoShipment, mto_shipment.ID) + suite.NoError(err) + testMtoShipment.MoveTaskOrderID = testMove.ID + testMtoShipment.MoveTaskOrder = testMove + err = suite.DB().Save(&testMtoShipment) + suite.NoError(err) + testMove.MTOShipments = append(testMove.MTOShipments, mto_shipment) + err = suite.DB().Save(&testMove) + suite.NoError(err) + + patchReq := httptest.NewRequest("PATCH", fmt.Sprintf("/mto-shipments/%s", testMove.MTOShipments[0].ID), nil) + + eTag := etag.GenerateEtag(testMtoShipment.UpdatedAt) + patchParams := mtoshipmentops.UpdateMTOShipmentParams{ + HTTPRequest: patchReq, + MtoShipmentID: strfmt.UUID(testMtoShipment.ID.String()), + IfMatch: eTag, + } + tertiaryAddress := GetTestAddress() + tertiaryAddress.PostalCode = handlers.FmtString("99999") + patchParams.Body = &primev3messages.UpdateMTOShipment{ + TertiaryDeliveryAddress: struct{ primev3messages.Address }{tertiaryAddress}, + } + patchResponse := patchHandler.Handle(patchParams) + errResponse := patchResponse.(*mtoshipmentops.UpdateMTOShipmentUnprocessableEntity) + suite.IsType(&mtoshipmentops.UpdateMTOShipmentUnprocessableEntity{}, errResponse) + }) } func GetTestAddress() primev3messages.Address { newAddress := factory.BuildAddress(nil, []factory.Customization{ From 018071d49039b901c76b5ec9112bc1700afbcb3f Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Wed, 29 Jan 2025 23:53:13 +0000 Subject: [PATCH 11/19] remove check for multiple results since shouldn't happen --- pkg/handlers/primeapi/mto_shipment.go | 13 ------------- pkg/handlers/primeapi/mto_shipment_address.go | 13 ------------- pkg/handlers/primeapi/mto_shipment_test.go | 2 +- pkg/handlers/primeapiv2/mto_shipment.go | 13 ------------- pkg/handlers/primeapiv3/mto_shipment.go | 13 ------------- 5 files changed, 1 insertion(+), 53 deletions(-) diff --git a/pkg/handlers/primeapi/mto_shipment.go b/pkg/handlers/primeapi/mto_shipment.go index 4b91c343fac..0fad3b2ff99 100644 --- a/pkg/handlers/primeapi/mto_shipment.go +++ b/pkg/handlers/primeapi/mto_shipment.go @@ -3,7 +3,6 @@ package primeapi import ( "context" "fmt" - "strings" "github.com/go-openapi/runtime/middleware" "github.com/gofrs/uuid" @@ -81,18 +80,6 @@ func (h UpdateShipmentDestinationAddressHandler) Handle(params mtoshipmentops.Up appCtx.Logger().Warn(unprocessableErr.Error()) payload := payloads.ValidationError(unprocessableErr.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) return mtoshipmentops.NewUpdateShipmentDestinationAddressUnprocessableEntity().WithPayload(payload), unprocessableErr - } else if len(*locationList) > 1 { - var results []string - - for _, address := range *locationList { - results = append(results, address.CityName+" "+address.StateName+" "+address.UsprZipID) - } - joinedResult := strings.Join(results[:], ", ") - unprocessableErr := apperror.NewUnprocessableEntityError( - fmt.Sprintf("primeapi.UpdateShipmentDestinationAddress: multiple locations found choose one of the following: %s", joinedResult)) - appCtx.Logger().Warn(unprocessableErr.Error()) - payload := payloads.ValidationError(unprocessableErr.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) - return mtoshipmentops.NewUpdateShipmentDestinationAddressUnprocessableEntity().WithPayload(payload), unprocessableErr } response, err := h.ShipmentAddressUpdateRequester.RequestShipmentDeliveryAddressUpdate(appCtx, shipmentID, addressUpdate.NewAddress, addressUpdate.ContractorRemarks, eTag) diff --git a/pkg/handlers/primeapi/mto_shipment_address.go b/pkg/handlers/primeapi/mto_shipment_address.go index ea0dae7ad5d..395fc89f11a 100644 --- a/pkg/handlers/primeapi/mto_shipment_address.go +++ b/pkg/handlers/primeapi/mto_shipment_address.go @@ -3,7 +3,6 @@ package primeapi import ( "context" "fmt" - "strings" "github.com/go-openapi/runtime/middleware" "github.com/gofrs/uuid" @@ -109,18 +108,6 @@ func (h UpdateMTOShipmentAddressHandler) Handle(params mtoshipmentops.UpdateMTOS appCtx.Logger().Warn(unprocessableErr.Error()) payload := payloads.ValidationError(unprocessableErr.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) return mtoshipmentops.NewUpdateMTOShipmentAddressUnprocessableEntity().WithPayload(payload), unprocessableErr - } else if len(*locationList) > 1 { - var results []string - - for _, address := range *locationList { - results = append(results, address.CityName+" "+address.StateName+" "+address.UsprZipID) - } - joinedResult := strings.Join(results[:], ", ") - unprocessableErr := apperror.NewUnprocessableEntityError( - fmt.Sprintf("primeapi.UpdateMTOShipmentAddress: multiple locations found choose one of the following: %s", joinedResult)) - appCtx.Logger().Warn(unprocessableErr.Error()) - payload := payloads.ValidationError(unprocessableErr.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) - return mtoshipmentops.NewUpdateMTOShipmentAddressUnprocessableEntity().WithPayload(payload), unprocessableErr } // Call the service object diff --git a/pkg/handlers/primeapi/mto_shipment_test.go b/pkg/handlers/primeapi/mto_shipment_test.go index 0fb46ff996b..52deef9332b 100644 --- a/pkg/handlers/primeapi/mto_shipment_test.go +++ b/pkg/handlers/primeapi/mto_shipment_test.go @@ -59,7 +59,7 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { } - suite.Run("POST failure - 422 Unprocessable Entity Error", func() { + suite.Run("POST failure - 422 Unprocessable Entity Error Invalid Address", func() { subtestData := makeSubtestData() mockCreator := mocks.ShipmentAddressUpdateRequester{} vLocationServices := address.NewVLocation() diff --git a/pkg/handlers/primeapiv2/mto_shipment.go b/pkg/handlers/primeapiv2/mto_shipment.go index f4d26a4176d..4b4f58a2153 100644 --- a/pkg/handlers/primeapiv2/mto_shipment.go +++ b/pkg/handlers/primeapiv2/mto_shipment.go @@ -3,7 +3,6 @@ package primeapiv2 import ( "context" "fmt" - "strings" "github.com/go-openapi/runtime/middleware" "github.com/gobuffalo/validate/v3" @@ -448,18 +447,6 @@ func checkValidAddress(vLocation services.VLocation, appCtx appcontext.AppContex unprocessableErr := apperror.NewUnprocessableEntityError( fmt.Sprintf("primeapi.UpdateShipmentDestinationAddress: could not find the provided location: %s", addressSearch)) return unprocessableErr - } else if len(*locationList) > 1 { - var results []string - - for _, address := range *locationList { - results = append(results, address.CityName+" "+address.StateName+" "+address.UsprZipID) - } - - joinedResult := strings.Join(results[:], ", ") - unprocessableErr := apperror.NewUnprocessableEntityError( - fmt.Sprintf("primeapi.UpdateShipmentDestinationAddress: multiple locations found choose one of the following: %s", joinedResult)) - appCtx.Logger().Warn(unprocessableErr.Error()) - return unprocessableErr } return nil diff --git a/pkg/handlers/primeapiv3/mto_shipment.go b/pkg/handlers/primeapiv3/mto_shipment.go index e8a211d20b6..648c510e2fa 100644 --- a/pkg/handlers/primeapiv3/mto_shipment.go +++ b/pkg/handlers/primeapiv3/mto_shipment.go @@ -3,7 +3,6 @@ package primeapiv3 import ( "context" "fmt" - "strings" "github.com/go-openapi/runtime/middleware" "github.com/gobuffalo/validate/v3" @@ -281,18 +280,6 @@ func checkValidAddress(vLocation services.VLocation, appCtx appcontext.AppContex unprocessableErr := apperror.NewUnprocessableEntityError( fmt.Sprintf("primeapi.UpdateShipmentDestinationAddress: could not find the provided location: %s", addressSearch)) return unprocessableErr - } else if len(*locationList) > 1 { - var results []string - - for _, address := range *locationList { - results = append(results, address.CityName+" "+address.StateName+" "+address.UsprZipID) - } - - joinedResult := strings.Join(results[:], ", ") - unprocessableErr := apperror.NewUnprocessableEntityError( - fmt.Sprintf("primeapi.UpdateShipmentDestinationAddress: multiple locations found choose one of the following: %s", joinedResult)) - appCtx.Logger().Warn(unprocessableErr.Error()) - return unprocessableErr } return nil From 8e8317c87c45f6a3452f735ae1e7b72ca4445a86 Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Thu, 30 Jan 2025 22:43:07 +0000 Subject: [PATCH 12/19] mock get location to throw error for test coverage --- .../primeapi/mto_shipment_address_test.go | 52 ++++++++ pkg/handlers/primeapi/mto_shipment_test.go | 24 ++++ pkg/handlers/primeapiv3/mto_shipment_test.go | 75 +++++++++++ pkg/services/mocks/VLocation.go | 25 ++-- pkg/services/mocks/WeightAllotmentFetcher.go | 117 ++++++++++++++++++ pkg/services/mocks/WeightRestrictor.go | 89 +++++++++++++ .../shipment_address_update_requester.go | 2 - 7 files changed, 373 insertions(+), 11 deletions(-) create mode 100644 pkg/services/mocks/WeightAllotmentFetcher.go create mode 100644 pkg/services/mocks/WeightRestrictor.go diff --git a/pkg/handlers/primeapi/mto_shipment_address_test.go b/pkg/handlers/primeapi/mto_shipment_address_test.go index be9b58e1c87..f7638d4dfbf 100644 --- a/pkg/handlers/primeapi/mto_shipment_address_test.go +++ b/pkg/handlers/primeapi/mto_shipment_address_test.go @@ -16,6 +16,7 @@ import ( "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/route/mocks" "github.com/transcom/mymove/pkg/services/address" + servicemocks "github.com/transcom/mymove/pkg/services/mocks" mtoshipment "github.com/transcom/mymove/pkg/services/mto_shipment" ) @@ -395,4 +396,55 @@ func (suite *HandlerSuite) TestUpdateMTOShipmentAddressHandler() { response := handler.Handle(params) suite.IsType(&mtoshipmentops.UpdateMTOShipmentAddressUnprocessableEntity{}, response) }) + + suite.Run("Failure - Internal Error mock GetLocationsByZipCityState return error", func() { + // Testcase: address is updated on a shipment that's available to MTO with invalid address + // Expected: Failure response 422 + // Under Test: UpdateMTOShipmentAddress handler code and mtoShipmentAddressUpdater service object + handler, availableMove := setupTestData() + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: availableMove, + LinkOnly: true, + }, + }, nil) + newAddress2 := models.Address{ + StreetAddress1: "7 Q St", + StreetAddress2: models.StringPointer("6622 Airport Way S #1430"), + StreetAddress3: models.StringPointer("441 SW Río de la Plata Drive"), + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + } + + // Update with new address + payload := payloads.Address(&newAddress2) + req := httptest.NewRequest("PUT", fmt.Sprintf("/mto-shipments/%s/addresses/%s", shipment.ID.String(), shipment.ID.String()), nil) + params := mtoshipmentops.UpdateMTOShipmentAddressParams{ + HTTPRequest: req, + AddressID: *handlers.FmtUUID(shipment.PickupAddress.ID), + MtoShipmentID: *handlers.FmtUUID(shipment.ID), + Body: payload, + IfMatch: etag.GenerateEtag(shipment.PickupAddress.UpdatedAt), + } + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + expectedError := models.ErrFetchNotFound + vLocationFetcher := &servicemocks.VLocation{} + vLocationFetcher.On("GetLocationsByZipCityState", + mock.AnythingOfType("*appcontext.appContext"), + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + ).Return(nil, expectedError).Once() + + handler.VLocation = vLocationFetcher + + // Run handler and check response + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.UpdateMTOShipmentAddressInternalServerError{}, response) + }) } diff --git a/pkg/handlers/primeapi/mto_shipment_test.go b/pkg/handlers/primeapi/mto_shipment_test.go index 52deef9332b..2f84f9651a6 100644 --- a/pkg/handlers/primeapi/mto_shipment_test.go +++ b/pkg/handlers/primeapi/mto_shipment_test.go @@ -59,6 +59,30 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { } + suite.Run("POST failure - 500 Internal Server GetLocationsByZipCityState returns error", func() { + subtestData := makeSubtestData() + mockCreator := mocks.ShipmentAddressUpdateRequester{} + + expectedError := models.ErrFetchNotFound + vLocationFetcher := &mocks.VLocation{} + vLocationFetcher.On("GetLocationsByZipCityState", + mock.AnythingOfType("*appcontext.appContext"), + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + ).Return(nil, expectedError).Once() + + handler := UpdateShipmentDestinationAddressHandler{ + HandlerConfig: suite.HandlerConfig(), + ShipmentAddressUpdateRequester: &mockCreator, + VLocation: vLocationFetcher, + } + + response := handler.Handle(subtestData) + suite.IsType(&mtoshipmentops.UpdateShipmentDestinationAddressInternalServerError{}, response) + }) + suite.Run("POST failure - 422 Unprocessable Entity Error Invalid Address", func() { subtestData := makeSubtestData() mockCreator := mocks.ShipmentAddressUpdateRequester{} diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index 3ee760b5894..ac9e89abaa6 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -1963,6 +1963,81 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { errResponse := patchResponse.(*mtoshipmentops.UpdateMTOShipmentUnprocessableEntity) suite.IsType(&mtoshipmentops.UpdateMTOShipmentUnprocessableEntity{}, errResponse) }) + + suite.Run("PATCH failure - Internal Server error GetLocationsByZipCityState", func() { + // Under Test: UpdateMTOShipmentHandler + // Setup: Mock location to return an error + // Expected: 500 Response returned + expectedError := models.ErrFetchNotFound + vLocationFetcher := &mocks.VLocation{} + vLocationFetcher.On("GetLocationsByZipCityState", + mock.AnythingOfType("*appcontext.appContext"), + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + ).Return(nil, expectedError).Once() + + shipmentUpdater := shipmentorchestrator.NewShipmentUpdater(mtoShipmentUpdater, ppmShipmentUpdater, boatShipmentUpdater, mobileHomeShipmentUpdater) + patchHandler := UpdateMTOShipmentHandler{ + HandlerConfig: suite.HandlerConfig(), + ShipmentUpdater: shipmentUpdater, + VLocation: vLocationFetcher, + } + + now := time.Now() + mto_shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "some address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.PickupAddress, + }, + }, nil) + move := factory.BuildMoveWithPPMShipment(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + AvailableToPrimeAt: &now, + ApprovedAt: &now, + Status: models.MoveStatusAPPROVED, + }, + }, + }, nil) + + var testMove models.Move + err := suite.DB().EagerPreload("MTOShipments.PPMShipment").Find(&testMove, move.ID) + suite.NoError(err) + var testMtoShipment models.MTOShipment + err = suite.DB().Find(&testMtoShipment, mto_shipment.ID) + suite.NoError(err) + testMtoShipment.MoveTaskOrderID = testMove.ID + testMtoShipment.MoveTaskOrder = testMove + err = suite.DB().Save(&testMtoShipment) + suite.NoError(err) + testMove.MTOShipments = append(testMove.MTOShipments, mto_shipment) + err = suite.DB().Save(&testMove) + suite.NoError(err) + + patchReq := httptest.NewRequest("PATCH", fmt.Sprintf("/mto-shipments/%s", testMove.MTOShipments[0].ID), nil) + + eTag := etag.GenerateEtag(testMtoShipment.UpdatedAt) + patchParams := mtoshipmentops.UpdateMTOShipmentParams{ + HTTPRequest: patchReq, + MtoShipmentID: strfmt.UUID(testMtoShipment.ID.String()), + IfMatch: eTag, + } + tertiaryAddress := GetTestAddress() + patchParams.Body = &primev3messages.UpdateMTOShipment{ + TertiaryDeliveryAddress: struct{ primev3messages.Address }{tertiaryAddress}, + } + + patchResponse := patchHandler.Handle(patchParams) + errResponse := patchResponse.(*mtoshipmentops.UpdateMTOShipmentInternalServerError) + suite.IsType(&mtoshipmentops.UpdateMTOShipmentInternalServerError{}, errResponse) + }) } func GetTestAddress() primev3messages.Address { newAddress := factory.BuildAddress(nil, []factory.Customization{ diff --git a/pkg/services/mocks/VLocation.go b/pkg/services/mocks/VLocation.go index 162924e8464..7c932ff7910 100644 --- a/pkg/services/mocks/VLocation.go +++ b/pkg/services/mocks/VLocation.go @@ -14,9 +14,16 @@ type VLocation struct { mock.Mock } -// GetLocationsByZipCityState provides a mock function with given fields: appCtx, search, exclusionStateFilters -func (_m *VLocation) GetLocationsByZipCityState(appCtx appcontext.AppContext, search string, exclusionStateFilters []string) (*models.VLocations, error) { - ret := _m.Called(appCtx, search, exclusionStateFilters) +// GetLocationsByZipCityState provides a mock function with given fields: appCtx, search, exclusionStateFilters, exactMatch +func (_m *VLocation) GetLocationsByZipCityState(appCtx appcontext.AppContext, search string, exclusionStateFilters []string, exactMatch ...bool) (*models.VLocations, error) { + _va := make([]interface{}, len(exactMatch)) + for _i := range exactMatch { + _va[_i] = exactMatch[_i] + } + var _ca []interface{} + _ca = append(_ca, appCtx, search, exclusionStateFilters) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) if len(ret) == 0 { panic("no return value specified for GetLocationsByZipCityState") @@ -24,19 +31,19 @@ func (_m *VLocation) GetLocationsByZipCityState(appCtx appcontext.AppContext, se var r0 *models.VLocations var r1 error - if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, []string) (*models.VLocations, error)); ok { - return rf(appCtx, search, exclusionStateFilters) + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, []string, ...bool) (*models.VLocations, error)); ok { + return rf(appCtx, search, exclusionStateFilters, exactMatch...) } - if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, []string) *models.VLocations); ok { - r0 = rf(appCtx, search, exclusionStateFilters) + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, []string, ...bool) *models.VLocations); ok { + r0 = rf(appCtx, search, exclusionStateFilters, exactMatch...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*models.VLocations) } } - if rf, ok := ret.Get(1).(func(appcontext.AppContext, string, []string) error); ok { - r1 = rf(appCtx, search, exclusionStateFilters) + if rf, ok := ret.Get(1).(func(appcontext.AppContext, string, []string, ...bool) error); ok { + r1 = rf(appCtx, search, exclusionStateFilters, exactMatch...) } else { r1 = ret.Error(1) } diff --git a/pkg/services/mocks/WeightAllotmentFetcher.go b/pkg/services/mocks/WeightAllotmentFetcher.go new file mode 100644 index 00000000000..fa36bfbee2e --- /dev/null +++ b/pkg/services/mocks/WeightAllotmentFetcher.go @@ -0,0 +1,117 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + appcontext "github.com/transcom/mymove/pkg/appcontext" + internalmessages "github.com/transcom/mymove/pkg/gen/internalmessages" + + mock "github.com/stretchr/testify/mock" + + models "github.com/transcom/mymove/pkg/models" +) + +// WeightAllotmentFetcher is an autogenerated mock type for the WeightAllotmentFetcher type +type WeightAllotmentFetcher struct { + mock.Mock +} + +// GetAllWeightAllotments provides a mock function with given fields: appCtx +func (_m *WeightAllotmentFetcher) GetAllWeightAllotments(appCtx appcontext.AppContext) (map[internalmessages.OrderPayGrade]models.WeightAllotment, error) { + ret := _m.Called(appCtx) + + if len(ret) == 0 { + panic("no return value specified for GetAllWeightAllotments") + } + + var r0 map[internalmessages.OrderPayGrade]models.WeightAllotment + var r1 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext) (map[internalmessages.OrderPayGrade]models.WeightAllotment, error)); ok { + return rf(appCtx) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext) map[internalmessages.OrderPayGrade]models.WeightAllotment); ok { + r0 = rf(appCtx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[internalmessages.OrderPayGrade]models.WeightAllotment) + } + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext) error); ok { + r1 = rf(appCtx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetWeightAllotment provides a mock function with given fields: appCtx, grade, ordersType +func (_m *WeightAllotmentFetcher) GetWeightAllotment(appCtx appcontext.AppContext, grade string, ordersType internalmessages.OrdersType) (models.WeightAllotment, error) { + ret := _m.Called(appCtx, grade, ordersType) + + if len(ret) == 0 { + panic("no return value specified for GetWeightAllotment") + } + + var r0 models.WeightAllotment + var r1 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, internalmessages.OrdersType) (models.WeightAllotment, error)); ok { + return rf(appCtx, grade, ordersType) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, string, internalmessages.OrdersType) models.WeightAllotment); ok { + r0 = rf(appCtx, grade, ordersType) + } else { + r0 = ret.Get(0).(models.WeightAllotment) + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, string, internalmessages.OrdersType) error); ok { + r1 = rf(appCtx, grade, ordersType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetWeightAllotmentByOrdersType provides a mock function with given fields: appCtx, ordersType +func (_m *WeightAllotmentFetcher) GetWeightAllotmentByOrdersType(appCtx appcontext.AppContext, ordersType internalmessages.OrdersType) (models.WeightAllotment, error) { + ret := _m.Called(appCtx, ordersType) + + if len(ret) == 0 { + panic("no return value specified for GetWeightAllotmentByOrdersType") + } + + var r0 models.WeightAllotment + var r1 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, internalmessages.OrdersType) (models.WeightAllotment, error)); ok { + return rf(appCtx, ordersType) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, internalmessages.OrdersType) models.WeightAllotment); ok { + r0 = rf(appCtx, ordersType) + } else { + r0 = ret.Get(0).(models.WeightAllotment) + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, internalmessages.OrdersType) error); ok { + r1 = rf(appCtx, ordersType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewWeightAllotmentFetcher creates a new instance of WeightAllotmentFetcher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewWeightAllotmentFetcher(t interface { + mock.TestingT + Cleanup(func()) +}) *WeightAllotmentFetcher { + mock := &WeightAllotmentFetcher{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/services/mocks/WeightRestrictor.go b/pkg/services/mocks/WeightRestrictor.go new file mode 100644 index 00000000000..6f7ad72bae4 --- /dev/null +++ b/pkg/services/mocks/WeightRestrictor.go @@ -0,0 +1,89 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + appcontext "github.com/transcom/mymove/pkg/appcontext" + + models "github.com/transcom/mymove/pkg/models" +) + +// WeightRestrictor is an autogenerated mock type for the WeightRestrictor type +type WeightRestrictor struct { + mock.Mock +} + +// ApplyWeightRestrictionToEntitlement provides a mock function with given fields: appCtx, entitlement, weightRestriction, eTag +func (_m *WeightRestrictor) ApplyWeightRestrictionToEntitlement(appCtx appcontext.AppContext, entitlement models.Entitlement, weightRestriction int, eTag string) (*models.Entitlement, error) { + ret := _m.Called(appCtx, entitlement, weightRestriction, eTag) + + if len(ret) == 0 { + panic("no return value specified for ApplyWeightRestrictionToEntitlement") + } + + var r0 *models.Entitlement + var r1 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.Entitlement, int, string) (*models.Entitlement, error)); ok { + return rf(appCtx, entitlement, weightRestriction, eTag) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.Entitlement, int, string) *models.Entitlement); ok { + r0 = rf(appCtx, entitlement, weightRestriction, eTag) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*models.Entitlement) + } + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, models.Entitlement, int, string) error); ok { + r1 = rf(appCtx, entitlement, weightRestriction, eTag) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RemoveWeightRestrictionFromEntitlement provides a mock function with given fields: appCtx, entitlement, eTag +func (_m *WeightRestrictor) RemoveWeightRestrictionFromEntitlement(appCtx appcontext.AppContext, entitlement models.Entitlement, eTag string) (*models.Entitlement, error) { + ret := _m.Called(appCtx, entitlement, eTag) + + if len(ret) == 0 { + panic("no return value specified for RemoveWeightRestrictionFromEntitlement") + } + + var r0 *models.Entitlement + var r1 error + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.Entitlement, string) (*models.Entitlement, error)); ok { + return rf(appCtx, entitlement, eTag) + } + if rf, ok := ret.Get(0).(func(appcontext.AppContext, models.Entitlement, string) *models.Entitlement); ok { + r0 = rf(appCtx, entitlement, eTag) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*models.Entitlement) + } + } + + if rf, ok := ret.Get(1).(func(appcontext.AppContext, models.Entitlement, string) error); ok { + r1 = rf(appCtx, entitlement, eTag) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewWeightRestrictor creates a new instance of WeightRestrictor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewWeightRestrictor(t interface { + mock.TestingT + Cleanup(func()) +}) *WeightRestrictor { + mock := &WeightRestrictor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/services/shipment_address_update/shipment_address_update_requester.go b/pkg/services/shipment_address_update/shipment_address_update_requester.go index 6cd87c837e0..0cba4af4be3 100644 --- a/pkg/services/shipment_address_update/shipment_address_update_requester.go +++ b/pkg/services/shipment_address_update/shipment_address_update_requester.go @@ -282,8 +282,6 @@ func (f *shipmentAddressUpdateRequester) RequestShipmentDeliveryAddressUpdate(ap return nil, apperror.NewPreconditionFailedError(shipmentID, nil) } - // check if the provided address is valid - isInternationalShipment := shipment.MarketCode == models.MarketCodeInternational shipmentHasApprovedDestSIT := f.doesShipmentContainApprovedDestinationSIT(shipment) From 89087863e077c467025506a23c0c28a59ee80d6a Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Fri, 31 Jan 2025 17:53:05 +0000 Subject: [PATCH 13/19] adding more tests --- .../primeapi/mto_shipment_address_test.go | 118 ++++- pkg/handlers/primeapi/mto_shipment_test.go | 74 +++ pkg/handlers/primeapiv2/mto_shipment.go | 53 +- pkg/handlers/primeapiv3/mto_shipment.go | 5 +- pkg/handlers/primeapiv3/mto_shipment_test.go | 455 +++++++++++++++--- 5 files changed, 578 insertions(+), 127 deletions(-) diff --git a/pkg/handlers/primeapi/mto_shipment_address_test.go b/pkg/handlers/primeapi/mto_shipment_address_test.go index f7638d4dfbf..52cdfe6c2f7 100644 --- a/pkg/handlers/primeapi/mto_shipment_address_test.go +++ b/pkg/handlers/primeapi/mto_shipment_address_test.go @@ -15,6 +15,7 @@ import ( "github.com/transcom/mymove/pkg/handlers/primeapi/payloads" "github.com/transcom/mymove/pkg/models" "github.com/transcom/mymove/pkg/route/mocks" + "github.com/transcom/mymove/pkg/services" "github.com/transcom/mymove/pkg/services/address" servicemocks "github.com/transcom/mymove/pkg/services/mocks" mtoshipment "github.com/transcom/mymove/pkg/services/mto_shipment" @@ -52,6 +53,7 @@ func (suite *HandlerSuite) TestUpdateMTOShipmentAddressHandler() { false, false, ).Return(400, nil) + // Create handler handler := UpdateMTOShipmentAddressHandler{ suite.HandlerConfig(), @@ -375,9 +377,123 @@ func (suite *HandlerSuite) TestUpdateMTOShipmentAddressHandler() { StreetAddress3: models.StringPointer("441 SW Río de la Plata Drive"), City: "Bad City", State: "CA", - PostalCode: "99999", + PostalCode: "99999", // invalid postal code + } + + // Update with new address + payload := payloads.Address(&newAddress2) + req := httptest.NewRequest("PUT", fmt.Sprintf("/mto-shipments/%s/addresses/%s", shipment.ID.String(), shipment.ID.String()), nil) + params := mtoshipmentops.UpdateMTOShipmentAddressParams{ + HTTPRequest: req, + AddressID: *handlers.FmtUUID(shipment.PickupAddress.ID), + MtoShipmentID: *handlers.FmtUUID(shipment.ID), + Body: payload, + IfMatch: etag.GenerateEtag(shipment.PickupAddress.UpdatedAt), + } + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + // Run handler and check response + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.UpdateMTOShipmentAddressUnprocessableEntity{}, response) + }) + + suite.Run("Failure - Unprocessable with AK FF off and valid AK address", func() { + // Testcase: address is updated on a shipment that's available to MTO with AK address but FF off + // Expected: Failure response 422 + // Under Test: UpdateMTOShipmentAddress handler code and mtoShipmentAddressUpdater service object + handler, availableMove := setupTestData() + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: availableMove, + LinkOnly: true, + }, + }, nil) + newAddress2 := models.Address{ + StreetAddress1: "7 Q St", + StreetAddress2: models.StringPointer("6622 Airport Way S #1430"), + StreetAddress3: models.StringPointer("441 SW Río de la Plata Drive"), + City: "JUNEAU", + State: "AK", + PostalCode: "99801", + } + + // setting the AK flag to false and use a valid address + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_alaska", + Match: false, } + mockFeatureFlagFetcher := &servicemocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + handler.HandlerConfig = handlerConfig + + // Update with new address + payload := payloads.Address(&newAddress2) + req := httptest.NewRequest("PUT", fmt.Sprintf("/mto-shipments/%s/addresses/%s", shipment.ID.String(), shipment.ID.String()), nil) + params := mtoshipmentops.UpdateMTOShipmentAddressParams{ + HTTPRequest: req, + AddressID: *handlers.FmtUUID(shipment.PickupAddress.ID), + MtoShipmentID: *handlers.FmtUUID(shipment.ID), + Body: payload, + IfMatch: etag.GenerateEtag(shipment.PickupAddress.UpdatedAt), + } + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + // Run handler and check response + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.UpdateMTOShipmentAddressUnprocessableEntity{}, response) + }) + + suite.Run("Failure - Unprocessable with HI FF off and valid HI address", func() { + // Testcase: address is updated on a shipment that's available to MTO with HI address but FF off + // Expected: Failure response 422 + // Under Test: UpdateMTOShipmentAddress handler code and mtoShipmentAddressUpdater service object + handler, availableMove := setupTestData() + shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: availableMove, + LinkOnly: true, + }, + }, nil) + newAddress2 := models.Address{ + StreetAddress1: "7 Q St", + StreetAddress2: models.StringPointer("6622 Airport Way S #1430"), + StreetAddress3: models.StringPointer("441 SW Río de la Plata Drive"), + City: "HONOLULU", + State: "HI", + PostalCode: "96835", + } + + // setting the HI flag to false and use a valid address + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_alaska", + Match: false, + } + + mockFeatureFlagFetcher := &servicemocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + handler.HandlerConfig = handlerConfig + // Update with new address payload := payloads.Address(&newAddress2) req := httptest.NewRequest("PUT", fmt.Sprintf("/mto-shipments/%s/addresses/%s", shipment.ID.String(), shipment.ID.String()), nil) diff --git a/pkg/handlers/primeapi/mto_shipment_test.go b/pkg/handlers/primeapi/mto_shipment_test.go index 2f84f9651a6..514791fb881 100644 --- a/pkg/handlers/primeapi/mto_shipment_test.go +++ b/pkg/handlers/primeapi/mto_shipment_test.go @@ -101,6 +101,80 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { suite.IsType(&mtoshipmentops.UpdateShipmentDestinationAddressUnprocessableEntity{}, response) }) + suite.Run("POST failure - 422 Unprocessable Entity Error Valid AK Address FF off", func() { + subtestData := makeSubtestData() + mockCreator := mocks.ShipmentAddressUpdateRequester{} + vLocationServices := address.NewVLocation() + + // setting the AK flag to false and use a valid address + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_alaska", + Match: false, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + handler := UpdateShipmentDestinationAddressHandler{ + handlerConfig, + &mockCreator, + vLocationServices, + } + + subtestData.Body.NewAddress.City = handlers.FmtString("JUNEAU") + subtestData.Body.NewAddress.State = handlers.FmtString("AK") + subtestData.Body.NewAddress.PostalCode = handlers.FmtString("99801") + // Validate incoming payload + suite.NoError(subtestData.Body.Validate(strfmt.Default)) + + response := handler.Handle(subtestData) + suite.IsType(&mtoshipmentops.UpdateShipmentDestinationAddressUnprocessableEntity{}, response) + }) + + suite.Run("POST failure - 422 Unprocessable Entity Error Valid AK Address FF off", func() { + subtestData := makeSubtestData() + mockCreator := mocks.ShipmentAddressUpdateRequester{} + vLocationServices := address.NewVLocation() + + // setting the AK flag to false and use a valid address + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_alaska", + Match: false, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + handler := UpdateShipmentDestinationAddressHandler{ + handlerConfig, + &mockCreator, + vLocationServices, + } + + subtestData.Body.NewAddress.City = handlers.FmtString("HONOLULU") + subtestData.Body.NewAddress.State = handlers.FmtString("HI") + subtestData.Body.NewAddress.PostalCode = handlers.FmtString("96835") + // Validate incoming payload + suite.NoError(subtestData.Body.Validate(strfmt.Default)) + + response := handler.Handle(subtestData) + suite.IsType(&mtoshipmentops.UpdateShipmentDestinationAddressUnprocessableEntity{}, response) + }) + suite.Run("POST failure - 422 Unprocessable Entity Error", func() { subtestData := makeSubtestData() mockCreator := mocks.ShipmentAddressUpdateRequester{} diff --git a/pkg/handlers/primeapiv2/mto_shipment.go b/pkg/handlers/primeapiv2/mto_shipment.go index 4b4f58a2153..241d3d0485a 100644 --- a/pkg/handlers/primeapiv2/mto_shipment.go +++ b/pkg/handlers/primeapiv2/mto_shipment.go @@ -172,22 +172,6 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment if mtoShipment.DestinationAddress != nil { addresses = append(addresses, *mtoShipment.DestinationAddress) } - - if mtoShipment.SecondaryPickupAddress != nil { - addresses = append(addresses, *mtoShipment.SecondaryPickupAddress) - } - - if mtoShipment.TertiaryPickupAddress != nil { - addresses = append(addresses, *mtoShipment.TertiaryPickupAddress) - } - - if mtoShipment.SecondaryDeliveryAddress != nil { - addresses = append(addresses, *mtoShipment.SecondaryDeliveryAddress) - } - - if mtoShipment.TertiaryDeliveryAddress != nil { - addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) - } } else { if mtoShipment.PPMShipment.PickupAddress != nil { addresses = append(addresses, *mtoShipment.PPMShipment.PickupAddress) @@ -196,22 +180,6 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment if mtoShipment.PPMShipment.DestinationAddress != nil { addresses = append(addresses, *mtoShipment.PPMShipment.DestinationAddress) } - - if mtoShipment.PPMShipment.SecondaryPickupAddress != nil { - addresses = append(addresses, *mtoShipment.PPMShipment.SecondaryPickupAddress) - } - - if mtoShipment.PPMShipment.TertiaryPickupAddress != nil { - addresses = append(addresses, *mtoShipment.PPMShipment.TertiaryPickupAddress) - } - - if mtoShipment.PPMShipment.SecondaryDestinationAddress != nil { - addresses = append(addresses, *mtoShipment.PPMShipment.SecondaryDestinationAddress) - } - - if mtoShipment.PPMShipment.TertiaryDestinationAddress != nil { - addresses = append(addresses, *mtoShipment.PPMShipment.TertiaryDestinationAddress) - } } for _, address := range addresses { @@ -224,13 +192,10 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment case apperror.UnprocessableEntityError: payload := payloads.ValidationError(err.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) return mtoshipmentops.NewCreateMTOShipmentUnprocessableEntity().WithPayload(payload), err - case apperror.InternalServerError: + default: errStr := e.Error() // we do this because InternalServerError wants a *string payload := payloads.InternalServerError(&errStr, h.GetTraceIDFromRequest(params.HTTPRequest)) return mtoshipmentops.NewCreateMTOShipmentInternalServerError().WithPayload(payload), e - default: - return mtoshipmentops.NewUpdateMTOShipmentInternalServerError().WithPayload( - payloads.InternalServerError(nil, h.GetTraceIDFromRequest(params.HTTPRequest))), err } } } @@ -352,10 +317,6 @@ func (h UpdateMTOShipmentHandler) Handle(params mtoshipmentops.UpdateMTOShipment addresses = append(addresses, *mtoShipment.SecondaryPickupAddress) } - if mtoShipment.TertiaryPickupAddress != nil { - addresses = append(addresses, *mtoShipment.TertiaryPickupAddress) - } - if mtoShipment.DestinationAddress != nil { addresses = append(addresses, *mtoShipment.DestinationAddress) } @@ -363,10 +324,6 @@ func (h UpdateMTOShipmentHandler) Handle(params mtoshipmentops.UpdateMTOShipment if mtoShipment.SecondaryDeliveryAddress != nil { addresses = append(addresses, *mtoShipment.SecondaryDeliveryAddress) } - - if mtoShipment.TertiaryDeliveryAddress != nil { - addresses = append(addresses, *mtoShipment.TertiaryDeliveryAddress) - } } else { if mtoShipment.PPMShipment.PickupAddress != nil { addresses = append(addresses, *mtoShipment.PPMShipment.PickupAddress) @@ -376,10 +333,6 @@ func (h UpdateMTOShipmentHandler) Handle(params mtoshipmentops.UpdateMTOShipment addresses = append(addresses, *mtoShipment.PPMShipment.SecondaryPickupAddress) } - if mtoShipment.PPMShipment.TertiaryPickupAddress != nil { - addresses = append(addresses, *mtoShipment.PPMShipment.TertiaryPickupAddress) - } - if mtoShipment.PPMShipment.DestinationAddress != nil { addresses = append(addresses, *mtoShipment.PPMShipment.DestinationAddress) } @@ -387,10 +340,6 @@ func (h UpdateMTOShipmentHandler) Handle(params mtoshipmentops.UpdateMTOShipment if mtoShipment.PPMShipment.SecondaryDestinationAddress != nil { addresses = append(addresses, *mtoShipment.PPMShipment.SecondaryDestinationAddress) } - - if mtoShipment.PPMShipment.TertiaryDestinationAddress != nil { - addresses = append(addresses, *mtoShipment.PPMShipment.TertiaryDestinationAddress) - } } for _, address := range addresses { diff --git a/pkg/handlers/primeapiv3/mto_shipment.go b/pkg/handlers/primeapiv3/mto_shipment.go index 648c510e2fa..adf9bc3a565 100644 --- a/pkg/handlers/primeapiv3/mto_shipment.go +++ b/pkg/handlers/primeapiv3/mto_shipment.go @@ -222,13 +222,10 @@ func (h CreateMTOShipmentHandler) Handle(params mtoshipmentops.CreateMTOShipment case apperror.UnprocessableEntityError: payload := payloads.ValidationError(err.Error(), h.GetTraceIDFromRequest(params.HTTPRequest), nil) return mtoshipmentops.NewCreateMTOShipmentUnprocessableEntity().WithPayload(payload), err - case apperror.InternalServerError: + default: errStr := e.Error() // we do this because InternalServerError wants a *string payload := payloads.InternalServerError(&errStr, h.GetTraceIDFromRequest(params.HTTPRequest)) return mtoshipmentops.NewCreateMTOShipmentInternalServerError().WithPayload(payload), e - default: - return mtoshipmentops.NewUpdateMTOShipmentInternalServerError().WithPayload( - payloads.InternalServerError(nil, h.GetTraceIDFromRequest(params.HTTPRequest))), err } } } diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index ac9e89abaa6..442b3a4b712 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -116,6 +116,66 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { mtoShipmentUpdater := mtoshipment.NewPrimeMTOShipmentUpdater(builder, fetcher, planner, moveRouter, moveWeights, suite.TestNotificationSender(), paymentRequestShipmentRecalculator, addressUpdater, addressCreator) shipmentUpdater := shipmentorchestrator.NewShipmentUpdater(mtoShipmentUpdater, ppmShipmentUpdater, boatShipmentUpdater, mobileHomeShipmentUpdater) + setupAddresses := func() { + // Make stubbed addresses just to collect address data for payload + newAddress := factory.BuildAddress(nil, []factory.Customization{ + { + Model: models.Address{ + ID: uuid.Must(uuid.NewV4()), + }, + }, + }, nil) + pickupAddress = primev3messages.Address{ + City: &newAddress.City, + PostalCode: &newAddress.PostalCode, + State: &newAddress.State, + StreetAddress1: &newAddress.StreetAddress1, + StreetAddress2: newAddress.StreetAddress2, + StreetAddress3: newAddress.StreetAddress3, + } + secondaryPickupAddress = primev3messages.Address{ + City: &newAddress.City, + PostalCode: &newAddress.PostalCode, + State: &newAddress.State, + StreetAddress1: &newAddress.StreetAddress1, + StreetAddress2: newAddress.StreetAddress2, + StreetAddress3: newAddress.StreetAddress3, + } + tertiaryPickupAddress = primev3messages.Address{ + City: &newAddress.City, + PostalCode: &newAddress.PostalCode, + State: &newAddress.State, + StreetAddress1: &newAddress.StreetAddress1, + StreetAddress2: newAddress.StreetAddress2, + StreetAddress3: newAddress.StreetAddress3, + } + newAddress = factory.BuildAddress(nil, nil, []factory.Trait{factory.GetTraitAddress2}) + destinationAddress = primev3messages.Address{ + City: &newAddress.City, + PostalCode: &newAddress.PostalCode, + State: &newAddress.State, + StreetAddress1: &newAddress.StreetAddress1, + StreetAddress2: newAddress.StreetAddress2, + StreetAddress3: newAddress.StreetAddress3, + } + secondaryDestinationAddress = primev3messages.Address{ + City: &newAddress.City, + PostalCode: &newAddress.PostalCode, + State: &newAddress.State, + StreetAddress1: &newAddress.StreetAddress1, + StreetAddress2: newAddress.StreetAddress2, + StreetAddress3: newAddress.StreetAddress3, + } + tertiaryDestinationAddress = primev3messages.Address{ + City: &newAddress.City, + PostalCode: &newAddress.PostalCode, + State: &newAddress.State, + StreetAddress1: &newAddress.StreetAddress1, + StreetAddress2: newAddress.StreetAddress2, + StreetAddress3: newAddress.StreetAddress3, + } + } + setupTestData := func(boatFeatureFlag bool, ubFeatureFlag bool) (CreateMTOShipmentHandler, models.Move) { vLocationServices := address.NewVLocation() move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) @@ -202,65 +262,23 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { vLocationServices, } - // Make stubbed addresses just to collect address data for payload - newAddress := factory.BuildAddress(nil, []factory.Customization{ - { - Model: models.Address{ - ID: uuid.Must(uuid.NewV4()), - }, - }, - }, nil) - pickupAddress = primev3messages.Address{ - City: &newAddress.City, - PostalCode: &newAddress.PostalCode, - State: &newAddress.State, - StreetAddress1: &newAddress.StreetAddress1, - StreetAddress2: newAddress.StreetAddress2, - StreetAddress3: newAddress.StreetAddress3, - } - secondaryPickupAddress = primev3messages.Address{ - City: &newAddress.City, - PostalCode: &newAddress.PostalCode, - State: &newAddress.State, - StreetAddress1: &newAddress.StreetAddress1, - StreetAddress2: newAddress.StreetAddress2, - StreetAddress3: newAddress.StreetAddress3, - } - tertiaryPickupAddress = primev3messages.Address{ - City: &newAddress.City, - PostalCode: &newAddress.PostalCode, - State: &newAddress.State, - StreetAddress1: &newAddress.StreetAddress1, - StreetAddress2: newAddress.StreetAddress2, - StreetAddress3: newAddress.StreetAddress3, - } - newAddress = factory.BuildAddress(nil, nil, []factory.Trait{factory.GetTraitAddress2}) - destinationAddress = primev3messages.Address{ - City: &newAddress.City, - PostalCode: &newAddress.PostalCode, - State: &newAddress.State, - StreetAddress1: &newAddress.StreetAddress1, - StreetAddress2: newAddress.StreetAddress2, - StreetAddress3: newAddress.StreetAddress3, - } - secondaryDestinationAddress = primev3messages.Address{ - City: &newAddress.City, - PostalCode: &newAddress.PostalCode, - State: &newAddress.State, - StreetAddress1: &newAddress.StreetAddress1, - StreetAddress2: newAddress.StreetAddress2, - StreetAddress3: newAddress.StreetAddress3, - } - tertiaryDestinationAddress = primev3messages.Address{ - City: &newAddress.City, - PostalCode: &newAddress.PostalCode, - State: &newAddress.State, - StreetAddress1: &newAddress.StreetAddress1, - StreetAddress2: newAddress.StreetAddress2, - StreetAddress3: newAddress.StreetAddress3, - } + setupAddresses() return handler, move + } + setupTestDataWithoutFF := func() (CreateMTOShipmentHandler, models.Move) { + vLocationServices := address.NewVLocation() + move := factory.BuildAvailableToPrimeMove(suite.DB(), nil, nil) + + handler := CreateMTOShipmentHandler{ + suite.HandlerConfig(), + shipmentCreator, + mtoChecker, + vLocationServices, + } + + setupAddresses() + return handler, move } suite.Run("Successful POST - Integration Test", func() { @@ -1077,21 +1095,25 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { // Under Test: CreateMTOShipment handler code // Setup: Create an mto shipment on an available move // Expected: Failure, invalid address - handler, move := setupTestData(false, true) + handler, move := setupTestDataWithoutFF() req := httptest.NewRequest("POST", "/mto-shipments", nil) params := mtoshipmentops.CreateMTOShipmentParams{ HTTPRequest: req, Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: handlers.FmtUUID(move.ID), - Agents: nil, - CustomerRemarks: nil, - PointOfContact: "John Doe", - PrimeEstimatedWeight: handlers.FmtInt64(1200), - RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), - PickupAddress: struct{ primev3messages.Address }{pickupAddress}, - DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, + TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, + TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, }, } @@ -1105,11 +1127,259 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) }) + suite.Run("POST failure - 422 - Doesn't return results for valid AK address if FF returns false", func() { + // Under Test: CreateMTOShipment handler code + // Setup: Create an mto shipment on an available move + // Expected: Failure, valid AK address but AK FF off, no results + handler, move := setupTestDataWithoutFF() + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, + TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, + TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + }, + } + + // setting the AK flag to false and use a valid address + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_alaska", + Match: false, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlag", + mock.Anything, // context.Context + mock.Anything, // *zap.Logger + mock.AnythingOfType("string"), // entityID (userID) + mock.AnythingOfType("string"), // key + mock.Anything, // flagContext (map[string]string) + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + handler.HandlerConfig = handlerConfig + params.Body.PickupAddress.City = handlers.FmtString("JUNEAU") + params.Body.PickupAddress.State = handlers.FmtString("AK") + params.Body.PickupAddress.PostalCode = handlers.FmtString("99801") + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + }) + + suite.Run("POST failure - 422 - Doesn't return results for valid HI address if FF returns false", func() { + // Under Test: CreateMTOShipment handler code + // Setup: Create an mto shipment on an available move + // Expected: Failure, valid HI address but HI FF off, no results + handler, move := setupTestDataWithoutFF() + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, + TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, + TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + }, + } + + // setting the HI flag to false and use a valid address + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_hawaii", + Match: false, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlag", + mock.Anything, // context.Context + mock.Anything, // *zap.Logger + mock.AnythingOfType("string"), // entityID (userID) + mock.AnythingOfType("string"), // key + mock.Anything, // flagContext (map[string]string) + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + handler.HandlerConfig = handlerConfig + params.Body.PickupAddress.City = handlers.FmtString("HONOLULU") + params.Body.PickupAddress.State = handlers.FmtString("HI") + params.Body.PickupAddress.PostalCode = handlers.FmtString("96835") + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + }) + + suite.Run("POST success - 200 - valid AK address if FF ON", func() { + // Under Test: CreateMTOShipment handler code + // Setup: Create an mto shipment on an available move + // Expected: Success, valid AK address AK FF ON + handler, move := setupTestData(false, true) + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, + TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, + TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + }, + } + + // setting the AK flag to false and use a valid address + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_alaska", + Match: true, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlag", + mock.Anything, // context.Context + mock.Anything, // *zap.Logger + mock.AnythingOfType("string"), // entityID (userID) + mock.AnythingOfType("string"), // key + mock.Anything, // flagContext (map[string]string) + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + handler.HandlerConfig = handlerConfig + params.Body.PickupAddress.City = handlers.FmtString("JUNEAU") + params.Body.PickupAddress.State = handlers.FmtString("AK") + params.Body.PickupAddress.PostalCode = handlers.FmtString("99801") + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentOK{}, response) + }) + + suite.Run("POST success - 200 - valid HI address if FF ON", func() { + // Under Test: CreateMTOShipment handler code + // Setup: Create an mto shipment on an available move + // Expected: Success, valid HI address HI FF ON + handler, move := setupTestData(false, true) + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, + TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, + TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + }, + } + + // setting the HI flag to false and use a valid address + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_hawaii", + Match: true, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlag", + mock.Anything, // context.Context + mock.Anything, // *zap.Logger + mock.AnythingOfType("string"), // entityID (userID) + mock.AnythingOfType("string"), // key + mock.Anything, // flagContext (map[string]string) + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + handler.HandlerConfig = handlerConfig + params.Body.PickupAddress.City = handlers.FmtString("HONOLULU") + params.Body.PickupAddress.State = handlers.FmtString("HI") + params.Body.PickupAddress.PostalCode = handlers.FmtString("96835") + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentOK{}, response) + }) + suite.Run("Failure POST - 422 - Invalid address (PPM)", func() { // Under Test: CreateMTOShipment handler code // Setup: Create a PPM shipment on an available move // Expected: Failure, returns an invalid address error - handler, move := setupTestData(true, false) + handler, move := setupTestDataWithoutFF() req := httptest.NewRequest("POST", "/mto-shipments", nil) counselorRemarks := "Some counselor remarks" @@ -1898,7 +2168,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { suite.IsType(&mtoshipmentops.UpdateMTOShipmentOK{}, response) }) - suite.Run("PATCH failure - Invalid pickup address.", func() { + suite.Run("PATCH failure - Invalid address.", func() { // Under Test: UpdateMTOShipmentHandler // Setup: Set an invalid zip // Expected: 422 Response returned @@ -1914,13 +2184,58 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { mto_shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ { Model: models.Address{ - StreetAddress1: "some address", + StreetAddress1: "some pickup address", City: "Beverly Hills", State: "CA", PostalCode: "90210", }, Type: &factory.Addresses.PickupAddress, }, + { + Model: models.Address{ + StreetAddress1: "some second pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.SecondaryPickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some third pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.TertiaryPickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some delivery address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.DeliveryAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some second delivery address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.SecondaryDeliveryAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some third delivery address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.TertiaryDeliveryAddress, + }, }, nil) move := factory.BuildMoveWithPPMShipment(suite.DB(), []factory.Customization{ { From a52e1932155aff238f8e9a1f5fa5b0b00cb54644 Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Fri, 31 Jan 2025 18:42:37 +0000 Subject: [PATCH 14/19] increase test coverage some more --- pkg/handlers/primeapiv2/mto_shipment_test.go | 41 ++++++++++ pkg/handlers/primeapiv3/mto_shipment_test.go | 86 ++++++-------------- 2 files changed, 67 insertions(+), 60 deletions(-) diff --git a/pkg/handlers/primeapiv2/mto_shipment_test.go b/pkg/handlers/primeapiv2/mto_shipment_test.go index c17819fc668..19a11cc5a05 100644 --- a/pkg/handlers/primeapiv2/mto_shipment_test.go +++ b/pkg/handlers/primeapiv2/mto_shipment_test.go @@ -445,6 +445,47 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { suite.Equal(handlers.InternalServerErrMessage, *errResponse.Payload.Title, "Payload title is wrong") }) + suite.Run("POST failure - 500 GetLocationsByZipCityState", func() { + // Under Test: CreateMTOShipment handler code + // Setup: Create an mto shipment on an available move + // Expected: Failure GetLocationsByZipCityState returns internal server error + handler, move := setupTestData(false) + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev2messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev2messages.NewMTOShipmentType(primev2messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev2messages.Address }{pickupAddress}, + DestinationAddress: struct{ primev2messages.Address }{destinationAddress}, + }, + } + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + expectedError := models.ErrFetchNotFound + vLocationFetcher := &mocks.VLocation{} + vLocationFetcher.On("GetLocationsByZipCityState", + mock.AnythingOfType("*appcontext.appContext"), + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + ).Return(nil, expectedError).Once() + + handler.VLocation = vLocationFetcher + + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentInternalServerError{}, response) + }) + suite.Run("POST failure - 422 -- Bad agent IDs set on shipment", func() { // Under Test: CreateMTOShipmentHandler // Setup: Create a shipment with an agent that doesn't really exist, handler should return unprocessable entity diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index 442b3a4b712..de514210a81 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -1441,10 +1441,12 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { ExpectedDepartureDate: handlers.FmtDate(expectedDepartureDate), PickupAddress: struct{ primev3messages.Address }{pickupAddress}, SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, + TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, DestinationAddress: struct { primev3messages.PPMDestinationAddress }{ppmDestinationAddress}, SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, + TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, SitExpected: &sitExpected, SitLocation: &sitLocation, SitEstimatedWeight: handlers.FmtPoundPtr(&sitEstimatedWeight), @@ -2283,6 +2285,27 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { // Under Test: UpdateMTOShipmentHandler // Setup: Mock location to return an error // Expected: 500 Response returned + handler, move := setupTestData(false, true) + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + }, + } + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + expectedError := models.ErrFetchNotFound vLocationFetcher := &mocks.VLocation{} vLocationFetcher.On("GetLocationsByZipCityState", @@ -2292,66 +2315,9 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { mock.Anything, mock.Anything, ).Return(nil, expectedError).Once() - - shipmentUpdater := shipmentorchestrator.NewShipmentUpdater(mtoShipmentUpdater, ppmShipmentUpdater, boatShipmentUpdater, mobileHomeShipmentUpdater) - patchHandler := UpdateMTOShipmentHandler{ - HandlerConfig: suite.HandlerConfig(), - ShipmentUpdater: shipmentUpdater, - VLocation: vLocationFetcher, - } - - now := time.Now() - mto_shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ - { - Model: models.Address{ - StreetAddress1: "some address", - City: "Beverly Hills", - State: "CA", - PostalCode: "90210", - }, - Type: &factory.Addresses.PickupAddress, - }, - }, nil) - move := factory.BuildMoveWithPPMShipment(suite.DB(), []factory.Customization{ - { - Model: models.Move{ - AvailableToPrimeAt: &now, - ApprovedAt: &now, - Status: models.MoveStatusAPPROVED, - }, - }, - }, nil) - - var testMove models.Move - err := suite.DB().EagerPreload("MTOShipments.PPMShipment").Find(&testMove, move.ID) - suite.NoError(err) - var testMtoShipment models.MTOShipment - err = suite.DB().Find(&testMtoShipment, mto_shipment.ID) - suite.NoError(err) - testMtoShipment.MoveTaskOrderID = testMove.ID - testMtoShipment.MoveTaskOrder = testMove - err = suite.DB().Save(&testMtoShipment) - suite.NoError(err) - testMove.MTOShipments = append(testMove.MTOShipments, mto_shipment) - err = suite.DB().Save(&testMove) - suite.NoError(err) - - patchReq := httptest.NewRequest("PATCH", fmt.Sprintf("/mto-shipments/%s", testMove.MTOShipments[0].ID), nil) - - eTag := etag.GenerateEtag(testMtoShipment.UpdatedAt) - patchParams := mtoshipmentops.UpdateMTOShipmentParams{ - HTTPRequest: patchReq, - MtoShipmentID: strfmt.UUID(testMtoShipment.ID.String()), - IfMatch: eTag, - } - tertiaryAddress := GetTestAddress() - patchParams.Body = &primev3messages.UpdateMTOShipment{ - TertiaryDeliveryAddress: struct{ primev3messages.Address }{tertiaryAddress}, - } - - patchResponse := patchHandler.Handle(patchParams) - errResponse := patchResponse.(*mtoshipmentops.UpdateMTOShipmentInternalServerError) - suite.IsType(&mtoshipmentops.UpdateMTOShipmentInternalServerError{}, errResponse) + handler.VLocation = vLocationFetcher + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentInternalServerError{}, response) }) } func GetTestAddress() primev3messages.Address { From 20315bafa1d9b320578742fb465583a16c2b5440 Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Fri, 31 Jan 2025 19:07:54 +0000 Subject: [PATCH 15/19] updating tests --- pkg/handlers/primeapi/mto_shipment_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/handlers/primeapi/mto_shipment_test.go b/pkg/handlers/primeapi/mto_shipment_test.go index 514791fb881..9bcd08fb728 100644 --- a/pkg/handlers/primeapi/mto_shipment_test.go +++ b/pkg/handlers/primeapi/mto_shipment_test.go @@ -138,7 +138,7 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { suite.IsType(&mtoshipmentops.UpdateShipmentDestinationAddressUnprocessableEntity{}, response) }) - suite.Run("POST failure - 422 Unprocessable Entity Error Valid AK Address FF off", func() { + suite.Run("POST failure - 422 Unprocessable Entity Error Valid HI Address FF off", func() { subtestData := makeSubtestData() mockCreator := mocks.ShipmentAddressUpdateRequester{} vLocationServices := address.NewVLocation() @@ -147,7 +147,7 @@ func (suite *HandlerSuite) TestUpdateShipmentDestinationAddressHandler() { handlerConfig := suite.HandlerConfig() expectedFeatureFlag := services.FeatureFlag{ - Key: "enable_alaska", + Key: "enable_hawaii", Match: false, } From 22c76cacc0d582ecc5a698333864214c3d9ecfce Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Fri, 31 Jan 2025 19:40:43 +0000 Subject: [PATCH 16/19] more tests, coverage better be happy --- pkg/handlers/primeapiv3/mto_shipment_test.go | 514 +++++++++++++++++++ 1 file changed, 514 insertions(+) diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index de514210a81..762f8bd1654 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -2319,6 +2319,520 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { response := handler.Handle(params) suite.IsType(&mtoshipmentops.CreateMTOShipmentInternalServerError{}, response) }) + + suite.Run("PATCH failure - valid AK address FF is on", func() { + // Under Test: UpdateMTOShipmentHandler + // Setup: Set an valid AK address but turn FF on + // Expected: 200 Response returned + + shipmentUpdater := shipmentorchestrator.NewShipmentUpdater(mtoShipmentUpdater, ppmShipmentUpdater, boatShipmentUpdater, mobileHomeShipmentUpdater) + patchHandler := UpdateMTOShipmentHandler{ + suite.HandlerConfig(), + shipmentUpdater, + vLocationServices, + } + + now := time.Now() + mto_shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "some pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.PickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some second pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.SecondaryPickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some third pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.TertiaryPickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some delivery address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.DeliveryAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some second delivery address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.SecondaryDeliveryAddress, + }, + }, nil) + move := factory.BuildMoveWithPPMShipment(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + AvailableToPrimeAt: &now, + ApprovedAt: &now, + Status: models.MoveStatusAPPROVED, + }, + }, + }, nil) + + var testMove models.Move + err := suite.DB().EagerPreload("MTOShipments.PPMShipment").Find(&testMove, move.ID) + suite.NoError(err) + var testMtoShipment models.MTOShipment + err = suite.DB().Find(&testMtoShipment, mto_shipment.ID) + suite.NoError(err) + testMtoShipment.MoveTaskOrderID = testMove.ID + testMtoShipment.MoveTaskOrder = testMove + err = suite.DB().Save(&testMtoShipment) + suite.NoError(err) + testMove.MTOShipments = append(testMove.MTOShipments, mto_shipment) + err = suite.DB().Save(&testMove) + suite.NoError(err) + + patchReq := httptest.NewRequest("PATCH", fmt.Sprintf("/mto-shipments/%s", testMove.MTOShipments[0].ID), nil) + + eTag := etag.GenerateEtag(testMtoShipment.UpdatedAt) + patchParams := mtoshipmentops.UpdateMTOShipmentParams{ + HTTPRequest: patchReq, + MtoShipmentID: strfmt.UUID(testMtoShipment.ID.String()), + IfMatch: eTag, + } + alaskaAddress := primev3messages.Address{ + City: handlers.FmtString("Juneau"), + PostalCode: handlers.FmtString("99801"), + State: handlers.FmtString("AK"), + StreetAddress1: handlers.FmtString("Some AK street"), + } + patchParams.Body = &primev3messages.UpdateMTOShipment{ + TertiaryDeliveryAddress: struct{ primev3messages.Address }{alaskaAddress}, + } + + // setting the AK flag to true + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_alaska", + Match: true, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + patchHandler.HandlerConfig = handlerConfig + patchResponse := patchHandler.Handle(patchParams) + errResponse := patchResponse.(*mtoshipmentops.UpdateMTOShipmentOK) + suite.IsType(&mtoshipmentops.UpdateMTOShipmentOK{}, errResponse) + }) + + suite.Run("PATCH success - valid HI address FF is on", func() { + // Under Test: UpdateMTOShipmentHandler + // Setup: Set an valid HI address but turn FF on + // Expected: 200 Response returned + + shipmentUpdater := shipmentorchestrator.NewShipmentUpdater(mtoShipmentUpdater, ppmShipmentUpdater, boatShipmentUpdater, mobileHomeShipmentUpdater) + patchHandler := UpdateMTOShipmentHandler{ + suite.HandlerConfig(), + shipmentUpdater, + vLocationServices, + } + + now := time.Now() + mto_shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "some pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.PickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some second pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.SecondaryPickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some third pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.TertiaryPickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some delivery address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.DeliveryAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some second delivery address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.SecondaryDeliveryAddress, + }, + }, nil) + move := factory.BuildMoveWithPPMShipment(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + AvailableToPrimeAt: &now, + ApprovedAt: &now, + Status: models.MoveStatusAPPROVED, + }, + }, + }, nil) + + var testMove models.Move + err := suite.DB().EagerPreload("MTOShipments.PPMShipment").Find(&testMove, move.ID) + suite.NoError(err) + var testMtoShipment models.MTOShipment + err = suite.DB().Find(&testMtoShipment, mto_shipment.ID) + suite.NoError(err) + testMtoShipment.MoveTaskOrderID = testMove.ID + testMtoShipment.MoveTaskOrder = testMove + err = suite.DB().Save(&testMtoShipment) + suite.NoError(err) + testMove.MTOShipments = append(testMove.MTOShipments, mto_shipment) + err = suite.DB().Save(&testMove) + suite.NoError(err) + + patchReq := httptest.NewRequest("PATCH", fmt.Sprintf("/mto-shipments/%s", testMove.MTOShipments[0].ID), nil) + + eTag := etag.GenerateEtag(testMtoShipment.UpdatedAt) + patchParams := mtoshipmentops.UpdateMTOShipmentParams{ + HTTPRequest: patchReq, + MtoShipmentID: strfmt.UUID(testMtoShipment.ID.String()), + IfMatch: eTag, + } + hawaiiAddress := primev3messages.Address{ + City: handlers.FmtString("HONOLULU"), + PostalCode: handlers.FmtString("96835"), + State: handlers.FmtString("HI"), + StreetAddress1: handlers.FmtString("Some HI street"), + } + patchParams.Body = &primev3messages.UpdateMTOShipment{ + TertiaryDeliveryAddress: struct{ primev3messages.Address }{hawaiiAddress}, + } + + // setting the HI flag to true + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_hawaii", + Match: true, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + patchHandler.HandlerConfig = handlerConfig + patchResponse := patchHandler.Handle(patchParams) + errResponse := patchResponse.(*mtoshipmentops.UpdateMTOShipmentOK) + suite.IsType(&mtoshipmentops.UpdateMTOShipmentOK{}, errResponse) + }) + + suite.Run("PATCH failure - valid AK address FF is off", func() { + // Under Test: UpdateMTOShipmentHandler + // Setup: Set an valid AK address but turn FF off + // Expected: 422 Response returned + + shipmentUpdater := shipmentorchestrator.NewShipmentUpdater(mtoShipmentUpdater, ppmShipmentUpdater, boatShipmentUpdater, mobileHomeShipmentUpdater) + patchHandler := UpdateMTOShipmentHandler{ + suite.HandlerConfig(), + shipmentUpdater, + vLocationServices, + } + + now := time.Now() + mto_shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "some pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.PickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some second pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.SecondaryPickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some third pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.TertiaryPickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some delivery address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.DeliveryAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some second delivery address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.SecondaryDeliveryAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some third delivery address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.TertiaryDeliveryAddress, + }, + }, nil) + move := factory.BuildMoveWithPPMShipment(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + AvailableToPrimeAt: &now, + ApprovedAt: &now, + Status: models.MoveStatusAPPROVED, + }, + }, + }, nil) + + var testMove models.Move + err := suite.DB().EagerPreload("MTOShipments.PPMShipment").Find(&testMove, move.ID) + suite.NoError(err) + var testMtoShipment models.MTOShipment + err = suite.DB().Find(&testMtoShipment, mto_shipment.ID) + suite.NoError(err) + testMtoShipment.MoveTaskOrderID = testMove.ID + testMtoShipment.MoveTaskOrder = testMove + err = suite.DB().Save(&testMtoShipment) + suite.NoError(err) + testMove.MTOShipments = append(testMove.MTOShipments, mto_shipment) + err = suite.DB().Save(&testMove) + suite.NoError(err) + + patchReq := httptest.NewRequest("PATCH", fmt.Sprintf("/mto-shipments/%s", testMove.MTOShipments[0].ID), nil) + + eTag := etag.GenerateEtag(testMtoShipment.UpdatedAt) + patchParams := mtoshipmentops.UpdateMTOShipmentParams{ + HTTPRequest: patchReq, + MtoShipmentID: strfmt.UUID(testMtoShipment.ID.String()), + IfMatch: eTag, + } + alaskaAddress := primev3messages.Address{ + City: handlers.FmtString("Juneau"), + PostalCode: handlers.FmtString("99801"), + State: handlers.FmtString("AK"), + StreetAddress1: handlers.FmtString("Some AK street"), + } + patchParams.Body = &primev3messages.UpdateMTOShipment{ + TertiaryDeliveryAddress: struct{ primev3messages.Address }{alaskaAddress}, + } + + // setting the AK flag to false + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_alaska", + Match: false, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + patchHandler.HandlerConfig = handlerConfig + patchResponse := patchHandler.Handle(patchParams) + errResponse := patchResponse.(*mtoshipmentops.UpdateMTOShipmentUnprocessableEntity) + suite.IsType(&mtoshipmentops.UpdateMTOShipmentUnprocessableEntity{}, errResponse) + }) + + suite.Run("PATCH failure - valid HI address FF is off", func() { + // Under Test: UpdateMTOShipmentHandler + // Setup: Set an valid HI address but turn FF off + // Expected: 422 Response returned + + shipmentUpdater := shipmentorchestrator.NewShipmentUpdater(mtoShipmentUpdater, ppmShipmentUpdater, boatShipmentUpdater, mobileHomeShipmentUpdater) + patchHandler := UpdateMTOShipmentHandler{ + suite.HandlerConfig(), + shipmentUpdater, + vLocationServices, + } + + now := time.Now() + mto_shipment := factory.BuildMTOShipment(suite.DB(), []factory.Customization{ + { + Model: models.Address{ + StreetAddress1: "some pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.PickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some second pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.SecondaryPickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some third pickup address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.TertiaryPickupAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some delivery address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.DeliveryAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some second delivery address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.SecondaryDeliveryAddress, + }, + { + Model: models.Address{ + StreetAddress1: "some third delivery address", + City: "Beverly Hills", + State: "CA", + PostalCode: "90210", + }, + Type: &factory.Addresses.TertiaryDeliveryAddress, + }, + }, nil) + move := factory.BuildMoveWithPPMShipment(suite.DB(), []factory.Customization{ + { + Model: models.Move{ + AvailableToPrimeAt: &now, + ApprovedAt: &now, + Status: models.MoveStatusAPPROVED, + }, + }, + }, nil) + + var testMove models.Move + err := suite.DB().EagerPreload("MTOShipments.PPMShipment").Find(&testMove, move.ID) + suite.NoError(err) + var testMtoShipment models.MTOShipment + err = suite.DB().Find(&testMtoShipment, mto_shipment.ID) + suite.NoError(err) + testMtoShipment.MoveTaskOrderID = testMove.ID + testMtoShipment.MoveTaskOrder = testMove + err = suite.DB().Save(&testMtoShipment) + suite.NoError(err) + testMove.MTOShipments = append(testMove.MTOShipments, mto_shipment) + err = suite.DB().Save(&testMove) + suite.NoError(err) + + patchReq := httptest.NewRequest("PATCH", fmt.Sprintf("/mto-shipments/%s", testMove.MTOShipments[0].ID), nil) + + eTag := etag.GenerateEtag(testMtoShipment.UpdatedAt) + patchParams := mtoshipmentops.UpdateMTOShipmentParams{ + HTTPRequest: patchReq, + MtoShipmentID: strfmt.UUID(testMtoShipment.ID.String()), + IfMatch: eTag, + } + hawaiiAddress := primev3messages.Address{ + City: handlers.FmtString("HONOLULU"), + PostalCode: handlers.FmtString("HI"), + State: handlers.FmtString("96835"), + StreetAddress1: handlers.FmtString("Some HI street"), + } + patchParams.Body = &primev3messages.UpdateMTOShipment{ + TertiaryDeliveryAddress: struct{ primev3messages.Address }{hawaiiAddress}, + } + + // setting the HI flag to false + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_hawaii", + Match: false, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + patchHandler.HandlerConfig = handlerConfig + patchResponse := patchHandler.Handle(patchParams) + errResponse := patchResponse.(*mtoshipmentops.UpdateMTOShipmentUnprocessableEntity) + suite.IsType(&mtoshipmentops.UpdateMTOShipmentUnprocessableEntity{}, errResponse) + }) } func GetTestAddress() primev3messages.Address { newAddress := factory.BuildAddress(nil, []factory.Customization{ From 0f456b5360938ad5d617b744f0589d4643581288 Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Mon, 10 Feb 2025 18:16:24 +0000 Subject: [PATCH 17/19] change test description to success --- pkg/handlers/primeapiv3/mto_shipment_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index 211f1e140e4..60d7ed6f023 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -2319,7 +2319,7 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { suite.IsType(&mtoshipmentops.CreateMTOShipmentInternalServerError{}, response) }) - suite.Run("PATCH failure - valid AK address FF is on", func() { + suite.Run("PATCH success - valid AK address FF is on", func() { // Under Test: UpdateMTOShipmentHandler // Setup: Set an valid AK address but turn FF on // Expected: 200 Response returned From cdc27ba84eff122ba84ca0678d4e4ef355c98839 Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Wed, 12 Feb 2025 17:41:40 +0000 Subject: [PATCH 18/19] moving new tests to end of file for cleaner diff view --- pkg/handlers/primeapiv3/mto_shipment_test.go | 1154 +++++++++--------- 1 file changed, 577 insertions(+), 577 deletions(-) diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index 60d7ed6f023..7c4a22d8f37 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -1090,411 +1090,282 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { suite.Contains(*unprocessableEntity.Payload.Detail, "PickupAddress is required") }) - suite.Run("POST failure - 422 - Invalid address", func() { - // Under Test: CreateMTOShipment handler code - // Setup: Create an mto shipment on an available move - // Expected: Failure, invalid address - handler, move := setupTestDataWithoutFF() + suite.Run("POST failure - 404 -- not found", func() { + // Under Test: CreateMTOShipmentHandler + // Setup: Create a shipment on a non-existent move + // Expected: 404 Not Found returned + handler, _ := setupTestData(true, false) req := httptest.NewRequest("POST", "/mto-shipments", nil) + // Generate a unique id + badID := strfmt.UUID(uuid.Must(uuid.NewV4()).String()) params := mtoshipmentops.CreateMTOShipmentParams{ HTTPRequest: req, Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: handlers.FmtUUID(move.ID), - Agents: nil, - CustomerRemarks: nil, - PointOfContact: "John Doe", - PrimeEstimatedWeight: handlers.FmtInt64(1200), - RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), - PickupAddress: struct{ primev3messages.Address }{pickupAddress}, - SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, - TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, - DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, - SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, - TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + MoveTaskOrderID: &badID, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, }, } - // set bad data for address so the validation fails - params.Body.PickupAddress.City = handlers.FmtString("Bad City") - // Validate incoming payload suite.NoError(params.Body.Validate(strfmt.Default)) response := handler.Handle(params) - suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + suite.IsType(&mtoshipmentops.CreateMTOShipmentNotFound{}, response) + responsePayload := response.(*mtoshipmentops.CreateMTOShipmentNotFound).Payload + + // Validate outgoing payload + suite.NoError(responsePayload.Validate(strfmt.Default)) }) - suite.Run("POST failure - 422 - Doesn't return results for valid AK address if FF returns false", func() { - // Under Test: CreateMTOShipment handler code - // Setup: Create an mto shipment on an available move - // Expected: Failure, valid AK address but AK FF off, no results - handler, move := setupTestDataWithoutFF() + suite.Run("POST failure - 400 -- nil body", func() { + // Under Test: CreateMTOShipmentHandler + // Setup: Create a request with no data in the body + // Expected: 422 Unprocessable Entity Response returned + + handler, _ := setupTestData(true, false) req := httptest.NewRequest("POST", "/mto-shipments", nil) - params := mtoshipmentops.CreateMTOShipmentParams{ + paramsNilBody := mtoshipmentops.CreateMTOShipmentParams{ HTTPRequest: req, - Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: handlers.FmtUUID(move.ID), - Agents: nil, - CustomerRemarks: nil, - PointOfContact: "John Doe", - PrimeEstimatedWeight: handlers.FmtInt64(1200), - RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), - PickupAddress: struct{ primev3messages.Address }{pickupAddress}, - SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, - TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, - DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, - SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, - TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, - }, - } - - // setting the AK flag to false and use a valid address - handlerConfig := suite.HandlerConfig() - - expectedFeatureFlag := services.FeatureFlag{ - Key: "enable_alaska", - Match: false, } - mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} - mockFeatureFlagFetcher.On("GetBooleanFlag", - mock.Anything, // context.Context - mock.Anything, // *zap.Logger - mock.AnythingOfType("string"), // entityID (userID) - mock.AnythingOfType("string"), // key - mock.Anything, // flagContext (map[string]string) - ).Return(expectedFeatureFlag, nil) - handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) - mockFeatureFlagFetcher.On("GetBooleanFlagForUser", - mock.Anything, - mock.AnythingOfType("*appcontext.appContext"), - mock.AnythingOfType("string"), - mock.Anything, - ).Return(expectedFeatureFlag, nil) - handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) - handler.HandlerConfig = handlerConfig - params.Body.PickupAddress.City = handlers.FmtString("JUNEAU") - params.Body.PickupAddress.State = handlers.FmtString("AK") - params.Body.PickupAddress.PostalCode = handlers.FmtString("99801") + // Validate incoming payload: nil body (the point of this test) - // Validate incoming payload - suite.NoError(params.Body.Validate(strfmt.Default)) + response := handler.Handle(paramsNilBody) + suite.IsType(&mtoshipmentops.CreateMTOShipmentBadRequest{}, response) + responsePayload := response.(*mtoshipmentops.CreateMTOShipmentBadRequest).Payload - response := handler.Handle(params) - suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + // Validate outgoing payload + suite.NoError(responsePayload.Validate(strfmt.Default)) }) - suite.Run("POST failure - 422 - Doesn't return results for valid HI address if FF returns false", func() { - // Under Test: CreateMTOShipment handler code - // Setup: Create an mto shipment on an available move - // Expected: Failure, valid HI address but HI FF off, no results - handler, move := setupTestDataWithoutFF() + suite.Run("POST failure - 404 -- MTO is not available to Prime", func() { + // Under Test: CreateMTOShipmentHandler + // Setup: Create a shipment on an unavailable move, prime cannot update these + // Expected: 404 Not found returned + + handler, _ := setupTestData(true, false) req := httptest.NewRequest("POST", "/mto-shipments", nil) + unavailableMove := factory.BuildMove(suite.DB(), nil, nil) params := mtoshipmentops.CreateMTOShipmentParams{ HTTPRequest: req, Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: handlers.FmtUUID(move.ID), - Agents: nil, - CustomerRemarks: nil, - PointOfContact: "John Doe", - PrimeEstimatedWeight: handlers.FmtInt64(1200), - RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), - PickupAddress: struct{ primev3messages.Address }{pickupAddress}, - SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, - TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, - DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, - SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, - TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + MoveTaskOrderID: handlers.FmtUUID(unavailableMove.ID), + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, }, } - // setting the HI flag to false and use a valid address - handlerConfig := suite.HandlerConfig() - - expectedFeatureFlag := services.FeatureFlag{ - Key: "enable_hawaii", - Match: false, - } - - mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} - mockFeatureFlagFetcher.On("GetBooleanFlag", - mock.Anything, // context.Context - mock.Anything, // *zap.Logger - mock.AnythingOfType("string"), // entityID (userID) - mock.AnythingOfType("string"), // key - mock.Anything, // flagContext (map[string]string) - ).Return(expectedFeatureFlag, nil) - handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) - mockFeatureFlagFetcher.On("GetBooleanFlagForUser", - mock.Anything, - mock.AnythingOfType("*appcontext.appContext"), - mock.AnythingOfType("string"), - mock.Anything, - ).Return(expectedFeatureFlag, nil) - handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) - handler.HandlerConfig = handlerConfig - params.Body.PickupAddress.City = handlers.FmtString("HONOLULU") - params.Body.PickupAddress.State = handlers.FmtString("HI") - params.Body.PickupAddress.PostalCode = handlers.FmtString("96835") - // Validate incoming payload suite.NoError(params.Body.Validate(strfmt.Default)) response := handler.Handle(params) - suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + suite.IsType(&mtoshipmentops.CreateMTOShipmentNotFound{}, response) + typedResponse := response.(*mtoshipmentops.CreateMTOShipmentNotFound) + + // Validate outgoing payload + suite.NoError(typedResponse.Payload.Validate(strfmt.Default)) + + suite.Contains(*typedResponse.Payload.Detail, unavailableMove.ID.String()) }) - suite.Run("POST success - 200 - valid AK address if FF ON", func() { - // Under Test: CreateMTOShipment handler code - // Setup: Create an mto shipment on an available move - // Expected: Success, valid AK address AK FF ON - handler, move := setupTestData(false, true) + suite.Run("POST failure - 500 - App Event Internal DTOD Server Error", func() { + // Under Test: CreateMTOShipmentHandler + // Setup: Create a shipment with DTOD outage simulated or bad zip + // Expected: 500 Internal Server Error returned + + handler, move := setupTestData(true, false) req := httptest.NewRequest("POST", "/mto-shipments", nil) + handler.ShipmentCreator = &mockCreator + + err := apperror.EventError{} + + mockCreator.On("CreateShipment", + mock.AnythingOfType("*appcontext.appContext"), + mock.Anything, + ).Return(nil, nil, err) params := mtoshipmentops.CreateMTOShipmentParams{ HTTPRequest: req, Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: handlers.FmtUUID(move.ID), - Agents: nil, - CustomerRemarks: nil, - PointOfContact: "John Doe", - PrimeEstimatedWeight: handlers.FmtInt64(1200), - RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), - PickupAddress: struct{ primev3messages.Address }{pickupAddress}, - SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, - TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, - DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, - SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, - TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, }, } - // setting the AK flag to false and use a valid address - handlerConfig := suite.HandlerConfig() + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentInternalServerError{}, response) + typedResponse := response.(*mtoshipmentops.CreateMTOShipmentInternalServerError) + suite.Contains(*typedResponse.Payload.Detail, "An internal server error has occurred") + }) - expectedFeatureFlag := services.FeatureFlag{ - Key: "enable_alaska", - Match: true, - } + suite.Run("POST failure - 422 - MTO Shipment object not formatted correctly", func() { + // Under Test: CreateMTOShipmentHandler + // Setup: Create a shipment with service items that don't match the modeltype + // Expected: 422 Unprocessable Entity returned - mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} - mockFeatureFlagFetcher.On("GetBooleanFlag", - mock.Anything, // context.Context - mock.Anything, // *zap.Logger - mock.AnythingOfType("string"), // entityID (userID) - mock.AnythingOfType("string"), // key - mock.Anything, // flagContext (map[string]string) - ).Return(expectedFeatureFlag, nil) - handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) - mockFeatureFlagFetcher.On("GetBooleanFlagForUser", - mock.Anything, + handler, move := setupTestData(true, false) + req := httptest.NewRequest("POST", "/mto-shipments", nil) + handler.ShipmentCreator = &mockCreator + + err := apperror.NotFoundError{} + + mockCreator.On("CreateShipment", mock.AnythingOfType("*appcontext.appContext"), - mock.AnythingOfType("string"), mock.Anything, - ).Return(expectedFeatureFlag, nil) - handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) - handler.HandlerConfig = handlerConfig - params.Body.PickupAddress.City = handlers.FmtString("JUNEAU") - params.Body.PickupAddress.State = handlers.FmtString("AK") - params.Body.PickupAddress.PostalCode = handlers.FmtString("99801") + ).Return(nil, nil, err) - // Validate incoming payload - suite.NoError(params.Body.Validate(strfmt.Default)) + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + BoatShipment: &primev3messages.CreateBoatShipment{}, // Empty boat shipment will trigger validation error on MTO Shipment creation + }, + } response := handler.Handle(params) - suite.IsType(&mtoshipmentops.CreateMTOShipmentOK{}, response) + suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + typedResponse := response.(*mtoshipmentops.CreateMTOShipmentUnprocessableEntity) + + suite.Contains(*typedResponse.Payload.Detail, "The MTO shipment object is invalid.") }) - suite.Run("POST success - 200 - valid HI address if FF ON", func() { - // Under Test: CreateMTOShipment handler code - // Setup: Create an mto shipment on an available move - // Expected: Success, valid HI address HI FF ON - handler, move := setupTestData(false, true) + suite.Run("POST failure - 422 - modelType() not supported", func() { + // Under Test: CreateMTOShipmentHandler + // Setup: Create a shipment with service items that don't match the modeltype + // Expected: 422 Unprocessable Entity returned + + handler, move := setupTestData(true, false) req := httptest.NewRequest("POST", "/mto-shipments", nil) + handler.ShipmentCreator = &mockCreator + + err := apperror.NotFoundError{} + + mockCreator.On("CreateShipment", + mock.AnythingOfType("*appcontext.appContext"), + mock.Anything, + ).Return(nil, nil, err) + // Create a service item that doesn't match the modeltype + mtoServiceItems := models.MTOServiceItems{ + models.MTOServiceItem{ + MoveTaskOrderID: move.ID, + MTOShipmentID: &uuid.Nil, + ReService: models.ReService{Code: models.ReServiceCodeMS}, + Reason: nil, + PickupPostalCode: nil, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + }, + } params := mtoshipmentops.CreateMTOShipmentParams{ HTTPRequest: req, Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: handlers.FmtUUID(move.ID), - Agents: nil, - CustomerRemarks: nil, - PointOfContact: "John Doe", - PrimeEstimatedWeight: handlers.FmtInt64(1200), - RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), - PickupAddress: struct{ primev3messages.Address }{pickupAddress}, - SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, - TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, - DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, - SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, - TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + MoveTaskOrderID: handlers.FmtUUID(move.ID), + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), }, } - // setting the HI flag to false and use a valid address - handlerConfig := suite.HandlerConfig() - - expectedFeatureFlag := services.FeatureFlag{ - Key: "enable_hawaii", - Match: true, - } - - mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} - mockFeatureFlagFetcher.On("GetBooleanFlag", - mock.Anything, // context.Context - mock.Anything, // *zap.Logger - mock.AnythingOfType("string"), // entityID (userID) - mock.AnythingOfType("string"), // key - mock.Anything, // flagContext (map[string]string) - ).Return(expectedFeatureFlag, nil) - handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) - mockFeatureFlagFetcher.On("GetBooleanFlagForUser", - mock.Anything, - mock.AnythingOfType("*appcontext.appContext"), - mock.AnythingOfType("string"), - mock.Anything, - ).Return(expectedFeatureFlag, nil) - handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) - handler.HandlerConfig = handlerConfig - params.Body.PickupAddress.City = handlers.FmtString("HONOLULU") - params.Body.PickupAddress.State = handlers.FmtString("HI") - params.Body.PickupAddress.PostalCode = handlers.FmtString("96835") + params.Body.SetMtoServiceItems(*payloads.MTOServiceItems(&mtoServiceItems)) // Validate incoming payload suite.NoError(params.Body.Validate(strfmt.Default)) response := handler.Handle(params) - suite.IsType(&mtoshipmentops.CreateMTOShipmentOK{}, response) - }) + suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + typedResponse := response.(*mtoshipmentops.CreateMTOShipmentUnprocessableEntity) - suite.Run("Failure POST - 422 - Invalid address (PPM)", func() { - // Under Test: CreateMTOShipment handler code - // Setup: Create a PPM shipment on an available move - // Expected: Failure, returns an invalid address error - handler, move := setupTestDataWithoutFF() - req := httptest.NewRequest("POST", "/mto-shipments", nil) + // Validate outgoing payload + suite.NoError(typedResponse.Payload.Validate(strfmt.Default)) - counselorRemarks := "Some counselor remarks" - expectedDepartureDate := time.Now().AddDate(0, 0, 10) - sitExpected := true - sitLocation := primev3messages.SITLocationTypeDESTINATION - sitEstimatedWeight := unit.Pound(1500) - sitEstimatedEntryDate := expectedDepartureDate.AddDate(0, 0, 5) - sitEstimatedDepartureDate := sitEstimatedEntryDate.AddDate(0, 0, 20) - estimatedWeight := unit.Pound(3200) - hasProGear := true - proGearWeight := unit.Pound(400) - spouseProGearWeight := unit.Pound(250) - estimatedIncentive := 123456 - sitEstimatedCost := 67500 + suite.Contains(*typedResponse.Payload.Detail, "MTOServiceItem modelType() not allowed") + }) - address1 := models.Address{ - StreetAddress1: "some address", - City: "Bad City", - State: "CA", - PostalCode: "90210", - } + suite.Run("POST failure - Error when feature flag fetcher fails and a boat shipment is passed in.", func() { + // Under Test: CreateMTOShipmentHandler + // Mocked: CreateMTOShipment creator + // Setup: If underlying CreateMTOShipment returns error, handler should return 500 response + // Expected: 500 Response returned + suite.T().Setenv("FEATURE_FLAG_BOAT", "true") // Set to true in order to test that it will default to "false" if flag fetcher errors out. - expectedPickupAddress := address1 - pickupAddress = primev3messages.Address{ - City: &expectedPickupAddress.City, - PostalCode: &expectedPickupAddress.PostalCode, - State: &expectedPickupAddress.State, - StreetAddress1: &expectedPickupAddress.StreetAddress1, - StreetAddress2: expectedPickupAddress.StreetAddress2, - StreetAddress3: expectedPickupAddress.StreetAddress3, - } + handler, move := setupTestData(false, false) - expectedDestinationAddress := address1 - destinationAddress = primev3messages.Address{ - City: &expectedDestinationAddress.City, - PostalCode: &expectedDestinationAddress.PostalCode, - State: &expectedDestinationAddress.State, - StreetAddress1: &expectedDestinationAddress.StreetAddress1, - StreetAddress2: expectedDestinationAddress.StreetAddress2, - StreetAddress3: expectedDestinationAddress.StreetAddress3, - } - ppmDestinationAddress = primev3messages.PPMDestinationAddress{ - City: &expectedDestinationAddress.City, - PostalCode: &expectedDestinationAddress.PostalCode, - State: &expectedDestinationAddress.State, - StreetAddress1: &expectedDestinationAddress.StreetAddress1, - StreetAddress2: expectedDestinationAddress.StreetAddress2, - StreetAddress3: expectedDestinationAddress.StreetAddress3, - } + req := httptest.NewRequest("POST", "/mto-shipments", nil) params := mtoshipmentops.CreateMTOShipmentParams{ HTTPRequest: req, Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: handlers.FmtUUID(move.ID), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypePPM), - CounselorRemarks: &counselorRemarks, - PpmShipment: &primev3messages.CreatePPMShipment{ - ExpectedDepartureDate: handlers.FmtDate(expectedDepartureDate), - PickupAddress: struct{ primev3messages.Address }{pickupAddress}, - SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, - TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, - DestinationAddress: struct { - primev3messages.PPMDestinationAddress - }{ppmDestinationAddress}, - SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, - TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, - SitExpected: &sitExpected, - SitLocation: &sitLocation, - SitEstimatedWeight: handlers.FmtPoundPtr(&sitEstimatedWeight), - SitEstimatedEntryDate: handlers.FmtDate(sitEstimatedEntryDate), - SitEstimatedDepartureDate: handlers.FmtDate(sitEstimatedDepartureDate), - EstimatedWeight: handlers.FmtPoundPtr(&estimatedWeight), - HasProGear: &hasProGear, - ProGearWeight: handlers.FmtPoundPtr(&proGearWeight), - SpouseProGearWeight: handlers.FmtPoundPtr(&spouseProGearWeight), - }, + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeBOATHAULAWAY), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, }, } - ppmEstimator.On("EstimateIncentiveWithDefaultChecks", - mock.AnythingOfType("*appcontext.appContext"), - mock.AnythingOfType("models.PPMShipment"), - mock.AnythingOfType("*models.PPMShipment")). - Return(models.CentPointer(unit.Cents(estimatedIncentive)), models.CentPointer(unit.Cents(sitEstimatedCost)), nil).Once() - - ppmEstimator.On("MaxIncentive", - mock.AnythingOfType("*appcontext.appContext"), - mock.AnythingOfType("models.PPMShipment"), - mock.AnythingOfType("*models.PPMShipment")). - Return(nil, nil) - // Validate incoming payload suite.NoError(params.Body.Validate(strfmt.Default)) response := handler.Handle(params) suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + errResponse := response.(*mtoshipmentops.CreateMTOShipmentUnprocessableEntity) + + suite.Contains(*errResponse.Payload.Detail, "Boat shipment type was used but the feature flag is not enabled.") }) - suite.Run("POST failure - 404 -- not found", func() { + suite.Run("POST failure - Error when UB FF is off and UB shipment is passed in.", func() { // Under Test: CreateMTOShipmentHandler - // Setup: Create a shipment on a non-existent move - // Expected: 404 Not Found returned - handler, _ := setupTestData(true, false) + // Mocked: CreateMTOShipment creator + // Setup: If underlying CreateMTOShipment returns error, handler should return 500 response + // Expected: 500 Response returned + suite.T().Setenv("FEATURE_FLAG_UNACCOMPANIED_BAGGAGE", "false") // Set to true in order to test that it will default to "false" if flag fetcher errors out. + + handler, move := setupTestData(false, false) + req := httptest.NewRequest("POST", "/mto-shipments", nil) - // Generate a unique id - badID := strfmt.UUID(uuid.Must(uuid.NewV4()).String()) params := mtoshipmentops.CreateMTOShipmentParams{ HTTPRequest: req, Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: &badID, + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, PointOfContact: "John Doe", PrimeEstimatedWeight: handlers.FmtInt64(1200), RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeUNACCOMPANIEDBAGGAGE), PickupAddress: struct{ primev3messages.Address }{pickupAddress}, DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, }, @@ -1504,279 +1375,20 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { suite.NoError(params.Body.Validate(strfmt.Default)) response := handler.Handle(params) - suite.IsType(&mtoshipmentops.CreateMTOShipmentNotFound{}, response) - responsePayload := response.(*mtoshipmentops.CreateMTOShipmentNotFound).Payload + suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + errResponse := response.(*mtoshipmentops.CreateMTOShipmentUnprocessableEntity) - // Validate outgoing payload - suite.NoError(responsePayload.Validate(strfmt.Default)) + suite.Contains(*errResponse.Payload.Detail, "Unaccompanied baggage shipments can't be created unless the unaccompanied_baggage feature flag is enabled.") }) - suite.Run("POST failure - 400 -- nil body", func() { + suite.Run("POST failure - Error creating a mto shipment contains tertiary destination address no secondary destination address.", func() { // Under Test: CreateMTOShipmentHandler - // Setup: Create a request with no data in the body - // Expected: 422 Unprocessable Entity Response returned + // Setup: If underlying CreateMTOShipment returns error, handler should return 422 response + // Expected: 422 Response returned - handler, _ := setupTestData(true, false) - req := httptest.NewRequest("POST", "/mto-shipments", nil) + handler, move := setupTestData(false, false) - paramsNilBody := mtoshipmentops.CreateMTOShipmentParams{ - HTTPRequest: req, - } - - // Validate incoming payload: nil body (the point of this test) - - response := handler.Handle(paramsNilBody) - suite.IsType(&mtoshipmentops.CreateMTOShipmentBadRequest{}, response) - responsePayload := response.(*mtoshipmentops.CreateMTOShipmentBadRequest).Payload - - // Validate outgoing payload - suite.NoError(responsePayload.Validate(strfmt.Default)) - }) - - suite.Run("POST failure - 404 -- MTO is not available to Prime", func() { - // Under Test: CreateMTOShipmentHandler - // Setup: Create a shipment on an unavailable move, prime cannot update these - // Expected: 404 Not found returned - - handler, _ := setupTestData(true, false) - req := httptest.NewRequest("POST", "/mto-shipments", nil) - - unavailableMove := factory.BuildMove(suite.DB(), nil, nil) - params := mtoshipmentops.CreateMTOShipmentParams{ - HTTPRequest: req, - Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: handlers.FmtUUID(unavailableMove.ID), - PointOfContact: "John Doe", - PrimeEstimatedWeight: handlers.FmtInt64(1200), - RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), - PickupAddress: struct{ primev3messages.Address }{pickupAddress}, - DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, - }, - } - - // Validate incoming payload - suite.NoError(params.Body.Validate(strfmt.Default)) - - response := handler.Handle(params) - suite.IsType(&mtoshipmentops.CreateMTOShipmentNotFound{}, response) - typedResponse := response.(*mtoshipmentops.CreateMTOShipmentNotFound) - - // Validate outgoing payload - suite.NoError(typedResponse.Payload.Validate(strfmt.Default)) - - suite.Contains(*typedResponse.Payload.Detail, unavailableMove.ID.String()) - }) - - suite.Run("POST failure - 500 - App Event Internal DTOD Server Error", func() { - // Under Test: CreateMTOShipmentHandler - // Setup: Create a shipment with DTOD outage simulated or bad zip - // Expected: 500 Internal Server Error returned - - handler, move := setupTestData(true, false) - req := httptest.NewRequest("POST", "/mto-shipments", nil) - handler.ShipmentCreator = &mockCreator - - err := apperror.EventError{} - - mockCreator.On("CreateShipment", - mock.AnythingOfType("*appcontext.appContext"), - mock.Anything, - ).Return(nil, nil, err) - - params := mtoshipmentops.CreateMTOShipmentParams{ - HTTPRequest: req, - Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: handlers.FmtUUID(move.ID), - Agents: nil, - CustomerRemarks: nil, - PointOfContact: "John Doe", - RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), - PickupAddress: struct{ primev3messages.Address }{pickupAddress}, - DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, - }, - } - - response := handler.Handle(params) - suite.IsType(&mtoshipmentops.CreateMTOShipmentInternalServerError{}, response) - typedResponse := response.(*mtoshipmentops.CreateMTOShipmentInternalServerError) - suite.Contains(*typedResponse.Payload.Detail, "An internal server error has occurred") - }) - - suite.Run("POST failure - 422 - MTO Shipment object not formatted correctly", func() { - // Under Test: CreateMTOShipmentHandler - // Setup: Create a shipment with service items that don't match the modeltype - // Expected: 422 Unprocessable Entity returned - - handler, move := setupTestData(true, false) - req := httptest.NewRequest("POST", "/mto-shipments", nil) - handler.ShipmentCreator = &mockCreator - - err := apperror.NotFoundError{} - - mockCreator.On("CreateShipment", - mock.AnythingOfType("*appcontext.appContext"), - mock.Anything, - ).Return(nil, nil, err) - - params := mtoshipmentops.CreateMTOShipmentParams{ - HTTPRequest: req, - Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: handlers.FmtUUID(move.ID), - Agents: nil, - CustomerRemarks: nil, - PointOfContact: "John Doe", - PrimeEstimatedWeight: handlers.FmtInt64(1200), - RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), - PickupAddress: struct{ primev3messages.Address }{pickupAddress}, - DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, - BoatShipment: &primev3messages.CreateBoatShipment{}, // Empty boat shipment will trigger validation error on MTO Shipment creation - }, - } - - response := handler.Handle(params) - suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) - typedResponse := response.(*mtoshipmentops.CreateMTOShipmentUnprocessableEntity) - - suite.Contains(*typedResponse.Payload.Detail, "The MTO shipment object is invalid.") - }) - - suite.Run("POST failure - 422 - modelType() not supported", func() { - // Under Test: CreateMTOShipmentHandler - // Setup: Create a shipment with service items that don't match the modeltype - // Expected: 422 Unprocessable Entity returned - - handler, move := setupTestData(true, false) - req := httptest.NewRequest("POST", "/mto-shipments", nil) - handler.ShipmentCreator = &mockCreator - - err := apperror.NotFoundError{} - - mockCreator.On("CreateShipment", - mock.AnythingOfType("*appcontext.appContext"), - mock.Anything, - ).Return(nil, nil, err) - - // Create a service item that doesn't match the modeltype - mtoServiceItems := models.MTOServiceItems{ - models.MTOServiceItem{ - MoveTaskOrderID: move.ID, - MTOShipmentID: &uuid.Nil, - ReService: models.ReService{Code: models.ReServiceCodeMS}, - Reason: nil, - PickupPostalCode: nil, - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - }, - } - params := mtoshipmentops.CreateMTOShipmentParams{ - HTTPRequest: req, - Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: handlers.FmtUUID(move.ID), - PointOfContact: "John Doe", - PrimeEstimatedWeight: handlers.FmtInt64(1200), - RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), - }, - } - - params.Body.SetMtoServiceItems(*payloads.MTOServiceItems(&mtoServiceItems)) - - // Validate incoming payload - suite.NoError(params.Body.Validate(strfmt.Default)) - - response := handler.Handle(params) - suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) - typedResponse := response.(*mtoshipmentops.CreateMTOShipmentUnprocessableEntity) - - // Validate outgoing payload - suite.NoError(typedResponse.Payload.Validate(strfmt.Default)) - - suite.Contains(*typedResponse.Payload.Detail, "MTOServiceItem modelType() not allowed") - }) - - suite.Run("POST failure - Error when feature flag fetcher fails and a boat shipment is passed in.", func() { - // Under Test: CreateMTOShipmentHandler - // Mocked: CreateMTOShipment creator - // Setup: If underlying CreateMTOShipment returns error, handler should return 500 response - // Expected: 500 Response returned - suite.T().Setenv("FEATURE_FLAG_BOAT", "true") // Set to true in order to test that it will default to "false" if flag fetcher errors out. - - handler, move := setupTestData(false, false) - - req := httptest.NewRequest("POST", "/mto-shipments", nil) - - params := mtoshipmentops.CreateMTOShipmentParams{ - HTTPRequest: req, - Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: handlers.FmtUUID(move.ID), - Agents: nil, - CustomerRemarks: nil, - PointOfContact: "John Doe", - PrimeEstimatedWeight: handlers.FmtInt64(1200), - RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeBOATHAULAWAY), - PickupAddress: struct{ primev3messages.Address }{pickupAddress}, - DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, - }, - } - - // Validate incoming payload - suite.NoError(params.Body.Validate(strfmt.Default)) - - response := handler.Handle(params) - suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) - errResponse := response.(*mtoshipmentops.CreateMTOShipmentUnprocessableEntity) - - suite.Contains(*errResponse.Payload.Detail, "Boat shipment type was used but the feature flag is not enabled.") - }) - - suite.Run("POST failure - Error when UB FF is off and UB shipment is passed in.", func() { - // Under Test: CreateMTOShipmentHandler - // Mocked: CreateMTOShipment creator - // Setup: If underlying CreateMTOShipment returns error, handler should return 500 response - // Expected: 500 Response returned - suite.T().Setenv("FEATURE_FLAG_UNACCOMPANIED_BAGGAGE", "false") // Set to true in order to test that it will default to "false" if flag fetcher errors out. - - handler, move := setupTestData(false, false) - - req := httptest.NewRequest("POST", "/mto-shipments", nil) - - params := mtoshipmentops.CreateMTOShipmentParams{ - HTTPRequest: req, - Body: &primev3messages.CreateMTOShipment{ - MoveTaskOrderID: handlers.FmtUUID(move.ID), - Agents: nil, - CustomerRemarks: nil, - PointOfContact: "John Doe", - PrimeEstimatedWeight: handlers.FmtInt64(1200), - RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), - ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeUNACCOMPANIEDBAGGAGE), - PickupAddress: struct{ primev3messages.Address }{pickupAddress}, - DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, - }, - } - - // Validate incoming payload - suite.NoError(params.Body.Validate(strfmt.Default)) - - response := handler.Handle(params) - suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) - errResponse := response.(*mtoshipmentops.CreateMTOShipmentUnprocessableEntity) - - suite.Contains(*errResponse.Payload.Detail, "Unaccompanied baggage shipments can't be created unless the unaccompanied_baggage feature flag is enabled.") - }) - - suite.Run("POST failure - Error creating a mto shipment contains tertiary destination address no secondary destination address.", func() { - // Under Test: CreateMTOShipmentHandler - // Setup: If underlying CreateMTOShipment returns error, handler should return 422 response - // Expected: 422 Response returned - - handler, move := setupTestData(false, false) - - req := httptest.NewRequest("POST", "/mto-shipments", nil) + req := httptest.NewRequest("POST", "/mto-shipments", nil) newAddress := factory.BuildAddress(nil, []factory.Customization{ { @@ -2832,6 +2444,394 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { errResponse := patchResponse.(*mtoshipmentops.UpdateMTOShipmentUnprocessableEntity) suite.IsType(&mtoshipmentops.UpdateMTOShipmentUnprocessableEntity{}, errResponse) }) + + suite.Run("POST failure - 422 - Invalid address", func() { + // Under Test: CreateMTOShipment handler code + // Setup: Create an mto shipment on an available move + // Expected: Failure, invalid address + handler, move := setupTestDataWithoutFF() + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, + TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, + TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + }, + } + + // set bad data for address so the validation fails + params.Body.PickupAddress.City = handlers.FmtString("Bad City") + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + }) + + suite.Run("POST failure - 422 - Doesn't return results for valid AK address if FF returns false", func() { + // Under Test: CreateMTOShipment handler code + // Setup: Create an mto shipment on an available move + // Expected: Failure, valid AK address but AK FF off, no results + handler, move := setupTestDataWithoutFF() + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, + TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, + TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + }, + } + + // setting the AK flag to false and use a valid address + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_alaska", + Match: false, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlag", + mock.Anything, // context.Context + mock.Anything, // *zap.Logger + mock.AnythingOfType("string"), // entityID (userID) + mock.AnythingOfType("string"), // key + mock.Anything, // flagContext (map[string]string) + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + handler.HandlerConfig = handlerConfig + params.Body.PickupAddress.City = handlers.FmtString("JUNEAU") + params.Body.PickupAddress.State = handlers.FmtString("AK") + params.Body.PickupAddress.PostalCode = handlers.FmtString("99801") + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + }) + + suite.Run("POST failure - 422 - Doesn't return results for valid HI address if FF returns false", func() { + // Under Test: CreateMTOShipment handler code + // Setup: Create an mto shipment on an available move + // Expected: Failure, valid HI address but HI FF off, no results + handler, move := setupTestDataWithoutFF() + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, + TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, + TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + }, + } + + // setting the HI flag to false and use a valid address + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_hawaii", + Match: false, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlag", + mock.Anything, // context.Context + mock.Anything, // *zap.Logger + mock.AnythingOfType("string"), // entityID (userID) + mock.AnythingOfType("string"), // key + mock.Anything, // flagContext (map[string]string) + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + handler.HandlerConfig = handlerConfig + params.Body.PickupAddress.City = handlers.FmtString("HONOLULU") + params.Body.PickupAddress.State = handlers.FmtString("HI") + params.Body.PickupAddress.PostalCode = handlers.FmtString("96835") + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + }) + + suite.Run("POST success - 200 - valid AK address if FF ON", func() { + // Under Test: CreateMTOShipment handler code + // Setup: Create an mto shipment on an available move + // Expected: Success, valid AK address AK FF ON + handler, move := setupTestData(false, true) + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, + TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, + TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + }, + } + + // setting the AK flag to false and use a valid address + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_alaska", + Match: true, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlag", + mock.Anything, // context.Context + mock.Anything, // *zap.Logger + mock.AnythingOfType("string"), // entityID (userID) + mock.AnythingOfType("string"), // key + mock.Anything, // flagContext (map[string]string) + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + handler.HandlerConfig = handlerConfig + params.Body.PickupAddress.City = handlers.FmtString("JUNEAU") + params.Body.PickupAddress.State = handlers.FmtString("AK") + params.Body.PickupAddress.PostalCode = handlers.FmtString("99801") + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentOK{}, response) + }) + + suite.Run("POST success - 200 - valid HI address if FF ON", func() { + // Under Test: CreateMTOShipment handler code + // Setup: Create an mto shipment on an available move + // Expected: Success, valid HI address HI FF ON + handler, move := setupTestData(false, true) + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + Agents: nil, + CustomerRemarks: nil, + PointOfContact: "John Doe", + PrimeEstimatedWeight: handlers.FmtInt64(1200), + RequestedPickupDate: handlers.FmtDatePtr(models.TimePointer(time.Now())), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypeHHG), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, + TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, + DestinationAddress: struct{ primev3messages.Address }{destinationAddress}, + SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, + TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + }, + } + + // setting the HI flag to false and use a valid address + handlerConfig := suite.HandlerConfig() + + expectedFeatureFlag := services.FeatureFlag{ + Key: "enable_hawaii", + Match: true, + } + + mockFeatureFlagFetcher := &mocks.FeatureFlagFetcher{} + mockFeatureFlagFetcher.On("GetBooleanFlag", + mock.Anything, // context.Context + mock.Anything, // *zap.Logger + mock.AnythingOfType("string"), // entityID (userID) + mock.AnythingOfType("string"), // key + mock.Anything, // flagContext (map[string]string) + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + mockFeatureFlagFetcher.On("GetBooleanFlagForUser", + mock.Anything, + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("string"), + mock.Anything, + ).Return(expectedFeatureFlag, nil) + handlerConfig.SetFeatureFlagFetcher(mockFeatureFlagFetcher) + handler.HandlerConfig = handlerConfig + params.Body.PickupAddress.City = handlers.FmtString("HONOLULU") + params.Body.PickupAddress.State = handlers.FmtString("HI") + params.Body.PickupAddress.PostalCode = handlers.FmtString("96835") + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentOK{}, response) + }) + + suite.Run("Failure POST - 422 - Invalid address (PPM)", func() { + // Under Test: CreateMTOShipment handler code + // Setup: Create a PPM shipment on an available move + // Expected: Failure, returns an invalid address error + handler, move := setupTestDataWithoutFF() + req := httptest.NewRequest("POST", "/mto-shipments", nil) + + counselorRemarks := "Some counselor remarks" + expectedDepartureDate := time.Now().AddDate(0, 0, 10) + sitExpected := true + sitLocation := primev3messages.SITLocationTypeDESTINATION + sitEstimatedWeight := unit.Pound(1500) + sitEstimatedEntryDate := expectedDepartureDate.AddDate(0, 0, 5) + sitEstimatedDepartureDate := sitEstimatedEntryDate.AddDate(0, 0, 20) + estimatedWeight := unit.Pound(3200) + hasProGear := true + proGearWeight := unit.Pound(400) + spouseProGearWeight := unit.Pound(250) + estimatedIncentive := 123456 + sitEstimatedCost := 67500 + + address1 := models.Address{ + StreetAddress1: "some address", + City: "Bad City", + State: "CA", + PostalCode: "90210", + } + + expectedPickupAddress := address1 + pickupAddress = primev3messages.Address{ + City: &expectedPickupAddress.City, + PostalCode: &expectedPickupAddress.PostalCode, + State: &expectedPickupAddress.State, + StreetAddress1: &expectedPickupAddress.StreetAddress1, + StreetAddress2: expectedPickupAddress.StreetAddress2, + StreetAddress3: expectedPickupAddress.StreetAddress3, + } + + expectedDestinationAddress := address1 + destinationAddress = primev3messages.Address{ + City: &expectedDestinationAddress.City, + PostalCode: &expectedDestinationAddress.PostalCode, + State: &expectedDestinationAddress.State, + StreetAddress1: &expectedDestinationAddress.StreetAddress1, + StreetAddress2: expectedDestinationAddress.StreetAddress2, + StreetAddress3: expectedDestinationAddress.StreetAddress3, + } + ppmDestinationAddress = primev3messages.PPMDestinationAddress{ + City: &expectedDestinationAddress.City, + PostalCode: &expectedDestinationAddress.PostalCode, + State: &expectedDestinationAddress.State, + StreetAddress1: &expectedDestinationAddress.StreetAddress1, + StreetAddress2: expectedDestinationAddress.StreetAddress2, + StreetAddress3: expectedDestinationAddress.StreetAddress3, + } + + params := mtoshipmentops.CreateMTOShipmentParams{ + HTTPRequest: req, + Body: &primev3messages.CreateMTOShipment{ + MoveTaskOrderID: handlers.FmtUUID(move.ID), + ShipmentType: primev3messages.NewMTOShipmentType(primev3messages.MTOShipmentTypePPM), + CounselorRemarks: &counselorRemarks, + PpmShipment: &primev3messages.CreatePPMShipment{ + ExpectedDepartureDate: handlers.FmtDate(expectedDepartureDate), + PickupAddress: struct{ primev3messages.Address }{pickupAddress}, + SecondaryPickupAddress: struct{ primev3messages.Address }{secondaryPickupAddress}, + TertiaryPickupAddress: struct{ primev3messages.Address }{tertiaryPickupAddress}, + DestinationAddress: struct { + primev3messages.PPMDestinationAddress + }{ppmDestinationAddress}, + SecondaryDestinationAddress: struct{ primev3messages.Address }{secondaryDestinationAddress}, + TertiaryDestinationAddress: struct{ primev3messages.Address }{tertiaryDestinationAddress}, + SitExpected: &sitExpected, + SitLocation: &sitLocation, + SitEstimatedWeight: handlers.FmtPoundPtr(&sitEstimatedWeight), + SitEstimatedEntryDate: handlers.FmtDate(sitEstimatedEntryDate), + SitEstimatedDepartureDate: handlers.FmtDate(sitEstimatedDepartureDate), + EstimatedWeight: handlers.FmtPoundPtr(&estimatedWeight), + HasProGear: &hasProGear, + ProGearWeight: handlers.FmtPoundPtr(&proGearWeight), + SpouseProGearWeight: handlers.FmtPoundPtr(&spouseProGearWeight), + }, + }, + } + + ppmEstimator.On("EstimateIncentiveWithDefaultChecks", + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("models.PPMShipment"), + mock.AnythingOfType("*models.PPMShipment")). + Return(models.CentPointer(unit.Cents(estimatedIncentive)), models.CentPointer(unit.Cents(sitEstimatedCost)), nil).Once() + + ppmEstimator.On("MaxIncentive", + mock.AnythingOfType("*appcontext.appContext"), + mock.AnythingOfType("models.PPMShipment"), + mock.AnythingOfType("*models.PPMShipment")). + Return(nil, nil) + + // Validate incoming payload + suite.NoError(params.Body.Validate(strfmt.Default)) + + response := handler.Handle(params) + suite.IsType(&mtoshipmentops.CreateMTOShipmentUnprocessableEntity{}, response) + }) } func GetTestAddress() primev3messages.Address { newAddress := factory.BuildAddress(nil, []factory.Customization{ From 65d5f1df25e1045e20a8593a2d321795550e728a Mon Sep 17 00:00:00 2001 From: Ricky Mettler Date: Fri, 21 Feb 2025 23:12:10 +0000 Subject: [PATCH 19/19] fixing order of vLocationServices in update, thou hast faile me git merge --- pkg/handlers/primeapiv3/api.go | 2 +- pkg/handlers/primeapiv3/mto_shipment.go | 2 +- pkg/handlers/primeapiv3/mto_shipment_test.go | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/handlers/primeapiv3/api.go b/pkg/handlers/primeapiv3/api.go index cc09cdd5902..a8bdf1c6e0a 100644 --- a/pkg/handlers/primeapiv3/api.go +++ b/pkg/handlers/primeapiv3/api.go @@ -116,8 +116,8 @@ func NewPrimeAPI(handlerConfig handlers.HandlerConfig) *primev3operations.Mymove primeAPIV3.MtoShipmentUpdateMTOShipmentHandler = UpdateMTOShipmentHandler{ handlerConfig, shipmentUpdater, - vLocation, handlerConfig.DTODPlanner(), + vLocation, } return primeAPIV3 diff --git a/pkg/handlers/primeapiv3/mto_shipment.go b/pkg/handlers/primeapiv3/mto_shipment.go index c9414e40721..8c893412669 100644 --- a/pkg/handlers/primeapiv3/mto_shipment.go +++ b/pkg/handlers/primeapiv3/mto_shipment.go @@ -287,8 +287,8 @@ func checkValidAddress(vLocation services.VLocation, appCtx appcontext.AppContex type UpdateMTOShipmentHandler struct { handlers.HandlerConfig services.ShipmentUpdater - services.VLocation planner route.Planner + services.VLocation } // Handle handler that updates a mto shipment diff --git a/pkg/handlers/primeapiv3/mto_shipment_test.go b/pkg/handlers/primeapiv3/mto_shipment_test.go index 61322862bc7..0082f97688d 100644 --- a/pkg/handlers/primeapiv3/mto_shipment_test.go +++ b/pkg/handlers/primeapiv3/mto_shipment_test.go @@ -571,8 +571,8 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { patchHandler := UpdateMTOShipmentHandler{ suite.HandlerConfig(), shipmentUpdater, - vLocationServices, planner, + vLocationServices, } patchReq := httptest.NewRequest("PATCH", fmt.Sprintf("/mto-shipments/%s", createdPPM.ShipmentID.String()), nil) @@ -854,8 +854,8 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { patchHandler := UpdateMTOShipmentHandler{ suite.HandlerConfig(), shipmentUpdater, - vLocationServices, planner, + vLocationServices, } patchReq := httptest.NewRequest("PATCH", fmt.Sprintf("/mto-shipments/%s", createdPPM.ShipmentID.String()), nil) @@ -1575,8 +1575,8 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { patchHandler := UpdateMTOShipmentHandler{ suite.HandlerConfig(), shipmentUpdater, - vLocationServices, planner, + vLocationServices, } now := time.Now() @@ -1652,8 +1652,8 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { patchHandler := UpdateMTOShipmentHandler{ suite.HandlerConfig(), shipmentUpdater, - vLocationServices, planner, + vLocationServices, } move := factory.BuildAvailableToPrimeMove(suite.DB(), []factory.Customization{}, nil) @@ -1702,8 +1702,8 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { patchHandler := UpdateMTOShipmentHandler{ suite.HandlerConfig(), shipmentUpdater, - vLocationServices, planner, + vLocationServices, } now := time.Now() @@ -1795,8 +1795,8 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { patchHandler := UpdateMTOShipmentHandler{ suite.HandlerConfig(), shipmentUpdater, - vLocationServices, planner, + vLocationServices, } now := time.Now() @@ -1946,8 +1946,8 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { patchHandler := UpdateMTOShipmentHandler{ suite.HandlerConfig(), shipmentUpdater, - vLocationServices, planner, + vLocationServices, } now := time.Now() @@ -2071,8 +2071,8 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { patchHandler := UpdateMTOShipmentHandler{ suite.HandlerConfig(), shipmentUpdater, - vLocationServices, planner, + vLocationServices, } now := time.Now() @@ -2196,8 +2196,8 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { patchHandler := UpdateMTOShipmentHandler{ suite.HandlerConfig(), shipmentUpdater, - vLocationServices, planner, + vLocationServices, } now := time.Now() @@ -2330,8 +2330,8 @@ func (suite *HandlerSuite) TestCreateMTOShipmentHandler() { patchHandler := UpdateMTOShipmentHandler{ suite.HandlerConfig(), shipmentUpdater, - vLocationServices, planner, + vLocationServices, } now := time.Now()