From a9f113b9e9c62f1347f6dc73f858fadb8f91ce77 Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Fri, 18 May 2018 18:55:00 +0200 Subject: [PATCH] Upstream sync --- application/applicationservice/provider.go | 10 ++++ application/third/eligibility/bcbsi/bxe.go | 1 + application/viewmodel/user.go | 1 + bitbucket-pipelines.yml | 4 +- data/datamysql/provider.go | 14 +++++ domain/contract/repo.go | 1 + domain/service/provider.go | 4 ++ server/authorization/organization.go | 2 +- server/router/router.go | 3 + server/router/selfregisterroute/controller.go | 38 ++++++++++++ server/router/selfregisterroute/router.go | 18 ++++++ server/router/tncroute/controller.go | 3 - server/router/visitroute/controller.go | 59 +++++++++++++++++-- server/router/visitroute/router.go | 2 + server/validation/tnc.go | 37 ++++++++---- server/validation/user.go | 37 ++++++------ server/validation/visit.go | 36 +++++++++++ 17 files changed, 227 insertions(+), 43 deletions(-) create mode 100644 server/router/selfregisterroute/controller.go create mode 100644 server/router/selfregisterroute/router.go create mode 100644 server/validation/visit.go diff --git a/application/applicationservice/provider.go b/application/applicationservice/provider.go index ce4ff31..5501226 100644 --- a/application/applicationservice/provider.go +++ b/application/applicationservice/provider.go @@ -48,3 +48,13 @@ func (s *providerService) Get(query string, lat float64, long float64, distance } return s.mapEntity.Provider.ToProviderRespModelSlice(providers), nil } + +func (s *providerService) GetByUUID(providerUUID string, user viewmodel.User) (viewmodel.ProviderResp, error) { + eUser := s.mapEntity.User.ToUserEntity(user) + provider, err := s.svc.Provider.GetByUUID(providerUUID, eUser) + if err != nil { + return viewmodel.ProviderResp{}, err + } + + return s.mapEntity.Provider.ToProviderRespModel(provider), nil +} diff --git a/application/third/eligibility/bcbsi/bxe.go b/application/third/eligibility/bcbsi/bxe.go index c1bd0a1..a79453e 100644 --- a/application/third/eligibility/bcbsi/bxe.go +++ b/application/third/eligibility/bcbsi/bxe.go @@ -146,6 +146,7 @@ func (s bxeService) CheckEligibility(eligibility viewmodel.Eligibility) (bcbsimo fmt.Println("Error WebService getting object: ", err) return bcbsimodel.MemberEligibilityResponse{}, err } + fmt.Println("Result: ", result.Body.MemberEligibilityResponse) return result.Body.MemberEligibilityResponse, nil } diff --git a/application/viewmodel/user.go b/application/viewmodel/user.go index abe337c..b03e1af 100644 --- a/application/viewmodel/user.go +++ b/application/viewmodel/user.go @@ -13,6 +13,7 @@ type User struct { BirthDate *time.Time `json:"birthdate,omitempty"` Email *string `json:"email,omitempty"` PhoneNumber *string `json:"phonenumber,omitempty"` + Type *string `json:"type,omitempty"` Pass string `json:"pass,omitempty"` Active bool `json:"active,omitempty"` Created time.Time `json:"created,omitempty"` diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 26efc53..434bf54 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -4,7 +4,7 @@ pipelines: master: - step: script: - - curl https://glide.sh/get | sh + - curl https://glide.sh/get -L -o - | sh - mkdir -p /go/src/bitbucket.org/nemt/nemt-portal-api - pwd - ls -al @@ -33,7 +33,7 @@ pipelines: development: - step: script: - - curl https://glide.sh/get | sh + - curl https://glide.sh/get -L -o - | sh - mkdir -p /go/src/bitbucket.org/nemt/nemt-portal-api - pwd - ls -al diff --git a/data/datamysql/provider.go b/data/datamysql/provider.go index 426c434..2aa2870 100644 --- a/data/datamysql/provider.go +++ b/data/datamysql/provider.go @@ -131,6 +131,20 @@ func (c *providerRepo) GetAll(user entity.User) ([]entity.Provider, error) { return c.getKeys("", "", providers) } +func (c *providerRepo) GetByUUID(providerUUID string, user entity.User) (entity.Provider, error) { + lat := 41.886406 + long := -87.624225 + + query, where, err := c.getProfileQuery(user) + if err != nil { + return entity.Provider{}, err + } + + query = c.getSelectQueryBase() + query + " WHERE a.provider_uuid = ? " + where + + return c.parseEntity(c.conn.QueryRow(query, lat, long, lat, providerUUID)) +} + func (c *providerRepo) GetByMukID(mukID string, user entity.User) (entity.Provider, error) { lat := 41.886406 long := -87.624225 diff --git a/domain/contract/repo.go b/domain/contract/repo.go index f8940d0..77d1581 100644 --- a/domain/contract/repo.go +++ b/domain/contract/repo.go @@ -56,6 +56,7 @@ type ProviderRepo interface { GetAll(user entity.User) ([]entity.Provider, error) Get(query string, lat float64, long float64, distance int64, planCode string, productID string, mukID string, internalID string, sort string, user entity.User) ([]entity.Provider, error) GetByMukID(mukID string, user entity.User) (entity.Provider, error) + GetByUUID(providerUUID string, user entity.User) (entity.Provider, error) } // NotificationRepo defines the data set for Notification diff --git a/domain/service/provider.go b/domain/service/provider.go index ee03dfe..c03e036 100644 --- a/domain/service/provider.go +++ b/domain/service/provider.go @@ -44,3 +44,7 @@ func (s *providerService) Get(query string, lat float64, long float64, distance func (s *providerService) GetByMukID(mukID string, user entity.User) (entity.Provider, error) { return s.svc.db.Provider().GetByMukID(mukID, user) } + +func (s *providerService) GetByUUID(providerUUID string, user entity.User) (entity.Provider, error) { + return s.svc.db.Provider().GetByUUID(providerUUID, user) +} diff --git a/server/authorization/organization.go b/server/authorization/organization.go index 2923b89..18a801e 100644 --- a/server/authorization/organization.go +++ b/server/authorization/organization.go @@ -73,4 +73,4 @@ func CanCreateOrganization(user viewmodel.User, organization viewmodel.Organizat func CanUpdateOrganization(user viewmodel.User, organization viewmodel.Organization) bool{ return CanCreateOrganization(user, organization) -} \ No newline at end of file +} diff --git a/server/router/router.go b/server/router/router.go index e09067e..26c390c 100644 --- a/server/router/router.go +++ b/server/router/router.go @@ -16,6 +16,7 @@ import ( "bitbucket.org/nemt/nemt-portal-api/server/router/placesroute" "bitbucket.org/nemt/nemt-portal-api/server/router/profileroute" "bitbucket.org/nemt/nemt-portal-api/server/router/providerroute" + "bitbucket.org/nemt/nemt-portal-api/server/router/selfregisterroute" "bitbucket.org/nemt/nemt-portal-api/server/router/tncroute" "bitbucket.org/nemt/nemt-portal-api/server/router/twilioroute" "bitbucket.org/nemt/nemt-portal-api/server/router/usersroute" @@ -37,6 +38,7 @@ func Register(e *echo.Echo, cfg *config.Config, svc *applicationservice.Service, lyfthookroute.Register(prefixGroup.Group("/lyfthook"), cfg, svc, tnc, notification) externalroute.Register(prefixGroup.Group("/ext"), cfg, svc, tnc, notification) authenticateroute.Register(prefixGroup.Group("/authenticate"), cfg, svc) + selfregisterroute.Register(prefixGroup.Group("/selfregister"), cfg, svc) appGroup := prefixGroup.Group("/" + cfg.App.Name) usersroute.Register(appGroup.Group("/users"), cfg, svc) @@ -47,4 +49,5 @@ func Register(e *echo.Echo, cfg *config.Config, svc *applicationservice.Service, placesroute.Register(appGroup.Group("/places"), cfg, svc) profileroute.Register(appGroup.Group("/profile"), cfg, svc) organizationroute.Register(appGroup.Group("/organization"), cfg, svc) + } diff --git a/server/router/selfregisterroute/controller.go b/server/router/selfregisterroute/controller.go new file mode 100644 index 0000000..04c0392 --- /dev/null +++ b/server/router/selfregisterroute/controller.go @@ -0,0 +1,38 @@ +package selfregisterroute + +import ( + "sync" + + "bitbucket.org/nemt/nemt-portal-api/application/applicationservice" + "bitbucket.org/nemt/nemt-portal-api/application/third/eligibility/bcbsi" + "bitbucket.org/nemt/nemt-portal-api/infra/config" + "bitbucket.org/nemt/nemt-portal-api/server/router/routeutils" + "github.com/labstack/echo" +) + +var ( + instance *controller + once sync.Once +) + +type controller struct { + svc *applicationservice.Service + cfg *config.Config + bcbsi *bcbsi.Service +} + +func controllerInstance(svc *applicationservice.Service, cfg *config.Config) *controller { + once.Do(func() { + instance = &controller{ + svc: svc, + cfg: cfg, + bcbsi: bcbsi.New(cfg), + } + }) + return instance +} + +func (c *controller) handle(ctx echo.Context) error { + + return routeutils.ResponseAPIOK(ctx, "OK") +} diff --git a/server/router/selfregisterroute/router.go b/server/router/selfregisterroute/router.go new file mode 100644 index 0000000..5656606 --- /dev/null +++ b/server/router/selfregisterroute/router.go @@ -0,0 +1,18 @@ +package selfregisterroute + +import ( + "bitbucket.org/nemt/nemt-portal-api/application/applicationservice" + "bitbucket.org/nemt/nemt-portal-api/infra/config" + "github.com/labstack/echo" +) + +const ( + rootRoute = "/" +) + +// Register registers the routes in the echo group +func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service) { + ctrl := controllerInstance(svc, cfg) + + r.POST(rootRoute, ctrl.handle) +} diff --git a/server/router/tncroute/controller.go b/server/router/tncroute/controller.go index 736b49d..2facbac 100644 --- a/server/router/tncroute/controller.go +++ b/server/router/tncroute/controller.go @@ -228,9 +228,6 @@ func (c *controller) handle(ctx echo.Context) error { if err != nil { return routeutils.HandleAPIError(ctx, err) } - if user.ID == "" { - return routeutils.ResponseAPIValidationError(ctx, "User not found") - } //Validate ride request if validationErrors := validation.ValidateRide(&requestRide, &user) ; len(validationErrors) > 0 { diff --git a/server/router/visitroute/controller.go b/server/router/visitroute/controller.go index dd93e70..ef98f58 100644 --- a/server/router/visitroute/controller.go +++ b/server/router/visitroute/controller.go @@ -1,12 +1,17 @@ package visitroute import ( + "fmt" "sync" + "time" "bitbucket.org/nemt/nemt-portal-api/application/applicationservice" + "bitbucket.org/nemt/nemt-portal-api/application/third/eligibility/bcbsi" + "bitbucket.org/nemt/nemt-portal-api/application/viewmodel" "bitbucket.org/nemt/nemt-portal-api/infra/auth" "bitbucket.org/nemt/nemt-portal-api/infra/config" "bitbucket.org/nemt/nemt-portal-api/server/router/routeutils" + "bitbucket.org/nemt/nemt-portal-api/server/validation" "github.com/labstack/echo" ) @@ -16,20 +21,66 @@ var ( ) type controller struct { - svc *applicationservice.Service - cfg *config.Config + svc *applicationservice.Service + cfg *config.Config + bcbsi *bcbsi.Service } func controllerInstance(svc *applicationservice.Service, cfg *config.Config) *controller { once.Do(func() { instance = &controller{ - svc: svc, - cfg: cfg, + svc: svc, + cfg: cfg, + bcbsi: bcbsi.New(cfg), } }) return instance } +func (c *controller) handle(ctx echo.Context) error { + var visit viewmodel.Visit + if err := ctx.Bind(&visit); err != nil { + fmt.Println(err) + return routeutils.ResponseAPIValidationError(ctx, "invalid parameters") + } + + user, err := auth.GetUserDetail(ctx, c.cfg) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + + provider, err := c.svc.Provider.GetByUUID(visit.Provider.ProviderUUID, user) + if err != nil { + fmt.Println(err) + return routeutils.ResponseAPIValidationError(ctx, "invalid provider") + } + + if validationErrors := validation.ValidateVisit(&visit, &user); len(validationErrors) > 0 { + return routeutils.ResponseAPICustomValidationError(ctx, "visit validation failed", validationErrors) + } + + eligibility := viewmodel.Eligibility{} + eligibility.Provider.ProviderNPI = provider.InternalID + eligibility.Provider.ProviderName = provider.Name + eligibility.TrackingID = *visit.User.Member + eligibility.Subscriber.SubscriberID = *visit.User.Member + eligibility.Subscriber.PatientType = *visit.User.Type + eligibility.Subscriber.Name.First = visit.User.First + eligibility.Subscriber.Name.Last = visit.User.Last + eligibility.Subscriber.DemographicInfo.DateOfBirth = *visit.User.BirthDate + eligibility.Subscriber.DemographicInfo.Gender = *visit.User.Gender + eligibility.ServiceInfo.DateOfService = time.Now() + eligibility.ServiceInfo.ServiceTypeCodes = []string{"30"} + + resp, err := c.bcbsi.BXE.Get271(eligibility) + if err != nil { + fmt.Println(err) + return routeutils.ResponseAPIValidationError(ctx, err.Error()) + } + + return routeutils.ResponseAPIOK(ctx, resp) +} + func (c *controller) handleGetByID(ctx echo.Context) error { visit_uuid := ctx.Param("visit_uuid") if visit_uuid == "" { diff --git a/server/router/visitroute/router.go b/server/router/visitroute/router.go index 62724ba..109bc16 100644 --- a/server/router/visitroute/router.go +++ b/server/router/visitroute/router.go @@ -15,6 +15,8 @@ const ( func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service) { ctrl := controllerInstance(svc, cfg) + r.POST(rootRoute, ctrl.handle) + r.GET(rootRoute, ctrl.handleGetAll) r.GET(idRoute, ctrl.handleGetByID) } diff --git a/server/validation/tnc.go b/server/validation/tnc.go index 8d3c98f..bddbc0a 100644 --- a/server/validation/tnc.go +++ b/server/validation/tnc.go @@ -5,6 +5,7 @@ import ( "time" "fmt" "strconv" + "regexp" "bitbucket.org/nemt/nemt-portal-api/application/viewmodel" "bitbucket.org/nemt/nemt-portal-api/infra/errors" @@ -12,11 +13,11 @@ import ( ) const ( - tripTypeFromVisit = "From Visit" - tripTypeToVisit = "To Visit" - tripTypeFromVisitWillCall = "From Visit / Will Call" - tripTypeRoundTrip = "Round Trip" - tripTypeRountTripWillCall = "Round Trip / Will Call" + tripTypeFromVisit = "from_visit" + tripTypeToVisit = "to_visit" + tripTypeFromVisitWillCall = "from_visit_call" + tripTypeRoundTrip = "roundtrip" + tripTypeRountTripWillCall = "roundtrip_call" ) const ( @@ -34,14 +35,15 @@ const ( func ValidateRide(requestRide *viewmodel.RideRequest, user *viewmodel.User) []errors.ValidationError { var result []errors.ValidationError + var validUUIDregex = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`) //Step #1 validation - if userID, err := strconv.Atoi(user.ID) ; err != nil || userID <= 0 { + if !validUUIDregex.MatchString(user.ID){ result = append(result, errors.ValidationError{Field : "user_uuid", Message : "Step #1 - Choose a Member" }) } - if originID, err := strconv.Atoi(requestRide.Origin.ID) ; err != nil || originID <= 0 { + if !validUUIDregex.MatchString(requestRide.Origin.ID) { result = append (result, errors.ValidationError{Field : "origin.id", Message : "Step #1 - Choose a Pickup Address"}) } @@ -50,7 +52,7 @@ func ValidateRide(requestRide *viewmodel.RideRequest, user *viewmodel.User) []er } //Step #2 validation - + if destinationID, err := strconv.Atoi(requestRide.Destination.ID) ; err!= nil || destinationID <= 0 { result = append (result, errors.ValidationError{Field : "destination.id", Message : "Step #2 - Choose a Provider"}) } @@ -84,14 +86,14 @@ func ValidateRide(requestRide *viewmodel.RideRequest, user *viewmodel.User) []er //Step #4 validation - if requestRide.TripType.Value == "" { - result = append (result, errors.ValidationError{Field : "trip_type.value", Message : "Step #4 - Choose a Trip Type"}) - } - timeWithDurationAndLoadingTime := requestRide.VisitTime.Add(-time.Duration(requestRide.Duration)*time.Second).Add(-loadingTime*time.Minute) after10Minutes := time.Now().Add(time.Minute*time10Minutes) - switch requestRide.TripType.Value { + isTripTypeValid := true + + fmt.Println("\n\n",requestRide.PickupTime,"\n\n") + + switch requestRide.TripType.Key { case tripTypeToVisit: if requestRide.PickupTime == nil { result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Choose a Pickup Time"}) @@ -178,6 +180,15 @@ func ValidateRide(requestRide *viewmodel.RideRequest, user *viewmodel.User) []er result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Visit cannot occur in the past "}) } } + + default: + isTripTypeValid = false } + + if !isTripTypeValid { + result = append (result, errors.ValidationError{Field : "trip_type.key", Message : "Step #4 - Choose a Trip Type"}) + } + + return result } \ No newline at end of file diff --git a/server/validation/user.go b/server/validation/user.go index 1576e23..c4b73d8 100644 --- a/server/validation/user.go +++ b/server/validation/user.go @@ -1,12 +1,10 @@ package validation import ( - "strings" "bitbucket.org/nemt/nemt-portal-api/application/viewmodel" "bitbucket.org/nemt/nemt-portal-api/infra/errors" - ) func characterIsUpperCase(character rune) bool { @@ -25,29 +23,29 @@ func ValidatePassword(user *viewmodel.User) []errors.ValidationError { var result []errors.ValidationError userOrganizationName := "" - if len(user.Organizations) > 0{ + if len(user.Organizations) > 0 { userOrganizationName = user.Organizations[0].Name } - if (len(user.Pass) < 8) { - result = append(result, errors.ValidationError{Field : "password", Message : "Password must be at least 8 characters."}) + if len(user.Pass) < 8 { + result = append(result, errors.ValidationError{Field: "password", Message: "Password must be at least 8 characters."}) } - if (strings.Contains(user.Pass, user.First)){ - result = append(result, errors.ValidationError{Field : "password", Message : "Password cannot include your First Name."}) + if strings.Contains(user.Pass, user.First) { + result = append(result, errors.ValidationError{Field: "password", Message: "Password cannot include your First Name."}) } - if (strings.Contains(user.Pass, user.Last)){ - result = append(result, errors.ValidationError{Field : "password", Message : "Password cannot include your Last Name."}) + if strings.Contains(user.Pass, user.Last) { + result = append(result, errors.ValidationError{Field: "password", Message: "Password cannot include your Last Name."}) } - if (strings.Contains(user.Pass, userOrganizationName)){ - result = append(result, errors.ValidationError{Field : "password", Message : "Password cannot include your Organization Name."}) + if strings.Contains(user.Pass, userOrganizationName) { + result = append(result, errors.ValidationError{Field: "password", Message: "Password cannot include your Organization Name."}) } - containsUpperCaseLetter := false; - containsLowerCaseLetter := false; - containsNumber := false; + containsUpperCaseLetter := false + containsLowerCaseLetter := false + containsNumber := false for _, character := range user.Pass { containsUpperCaseLetter = containsUpperCaseLetter || characterIsUpperCase(character) @@ -56,12 +54,11 @@ func ValidatePassword(user *viewmodel.User) []errors.ValidationError { } if !containsUpperCaseLetter || !containsLowerCaseLetter || !containsNumber { - result = append(result, errors.ValidationError{Field : "password", Message : "Password must contain one of EACH :"}) - result = append(result, errors.ValidationError{Field : "password-tab", Message : "an uppercase letter"}) - result = append(result, errors.ValidationError{Field : "password-tab", Message : "a lowercase letter"}) - result = append(result, errors.ValidationError{Field : "password-tab", Message : "a number"}) + result = append(result, errors.ValidationError{Field: "password", Message: "Password must contain one of EACH :"}) + result = append(result, errors.ValidationError{Field: "password-tab", Message: "an uppercase letter"}) + result = append(result, errors.ValidationError{Field: "password-tab", Message: "a lowercase letter"}) + result = append(result, errors.ValidationError{Field: "password-tab", Message: "a number"}) } - return result -} \ No newline at end of file +} diff --git a/server/validation/visit.go b/server/validation/visit.go new file mode 100644 index 0000000..cce8393 --- /dev/null +++ b/server/validation/visit.go @@ -0,0 +1,36 @@ +package validation + +import ( + "bitbucket.org/nemt/nemt-portal-api/application/viewmodel" + "bitbucket.org/nemt/nemt-portal-api/infra/errors" +) + +func ValidateVisit(visit *viewmodel.Visit, user *viewmodel.User) []errors.ValidationError { + var result []errors.ValidationError + + if len(visit.User.First) == 0 { + result = append(result, errors.ValidationError{Field: "first", Message: "Step #1 - First Name is mandatory"}) + } + + if len(visit.User.Last) == 0 { + result = append(result, errors.ValidationError{Field: "last", Message: "Step #1 - Last Name is mandatory"}) + } + + if visit.User.Gender == nil || (*visit.User.Gender != "M" && *visit.User.Gender != "F" && *visit.User.Gender != "U") { + result = append(result, errors.ValidationError{Field: "gender", Message: "Step #1 - Gender is mandatory"}) + } + + if visit.User.Type == nil || (*visit.User.Type != "S" && *visit.User.Gender != "D" && *visit.User.Gender != "U") { + result = append(result, errors.ValidationError{Field: "type", Message: "Step #1 - Member Type is mandatory"}) + } + + if visit.User.Member == nil || len(*visit.User.Member) == 0 { + result = append(result, errors.ValidationError{Field: "member", Message: "Step #1 - Member # is mandatory"}) + } + + if visit.User.BirthDate == nil || visit.User.BirthDate.IsZero() { + result = append(result, errors.ValidationError{Field: "birthdate", Message: "Step #1 - Birth Date is mandatory"}) + } + + return result +}