diff --git a/Dockerfile.run b/Dockerfile.run index 4c1fc6c..2c9ede5 100644 --- a/Dockerfile.run +++ b/Dockerfile.run @@ -1,6 +1,7 @@ FROM nginx:latest RUN apt-get update; apt-get install -y openssl +RUN apt-get install -y ca-certificates RUN rm -rf /etc/nginx/conf.d/*; COPY ./dist/default.conf /etc/nginx/conf.d/ diff --git a/application/applicationservice/applicationservice.go b/application/applicationservice/applicationservice.go index 11a729d..6bbbaea 100644 --- a/application/applicationservice/applicationservice.go +++ b/application/applicationservice/applicationservice.go @@ -23,7 +23,8 @@ type Service struct { Notification *notificationService Profile *profileService Organization *organizationService - Zipcodes *zipcodeService + Zipcodes *zipcodeService + Plan *planService } // New returns a new domain Service instance @@ -37,7 +38,8 @@ func New(svc *service.Service, mapper *entitymapping.Mapper, notification *notif Notification: newNotificationService(svc, mapper, notification, cfg), Profile: newProfileService(svc, mapper), Organization: newOrganizationService(svc, mapper), - Zipcodes: newZipcodeService(svc, mapper), + Zipcodes: newZipcodeService(svc, mapper), + Plan: newPlanService(svc, mapper), } }) diff --git a/application/applicationservice/plan.go b/application/applicationservice/plan.go new file mode 100644 index 0000000..3b6e90a --- /dev/null +++ b/application/applicationservice/plan.go @@ -0,0 +1,57 @@ +package applicationservice + +import ( + "bitbucket.org/nemt/nemt-portal-api/application/entitymapping" + "bitbucket.org/nemt/nemt-portal-api/application/viewmodel" + "bitbucket.org/nemt/nemt-portal-api/domain/service" +) + +// providerService holds methods to provider application service +type planService struct { + svc *service.Service + mapEntity *entitymapping.Mapper +} + +// newProviderService returns a providerService instance +func newPlanService(svc *service.Service, mapper *entitymapping.Mapper) *planService { + return &planService{ + svc: svc, + mapEntity: mapper, + } +} + +func (s *planService) GetByAlphaPrefix(alphaPrefix string) (viewmodel.Plan, error) { + plan, err := s.svc.Plans.GetByAlphaPrefix(alphaPrefix) + if err != nil { + return viewmodel.Plan{}, err + } + + return s.mapEntity.Plan.ToPlanModel(plan), nil +} + +func (s *planService) GetByUUID(planUUID string) ([]viewmodel.Plan, error) { + plans, err := s.svc.Plans.GetByUUID(planUUID) + if err != nil { + return nil, err + } + + return s.mapEntity.Plan.ToPlanModelSlice(plans), nil +} + +func (s *planService) GetByID(planID int64) ([]viewmodel.Plan, error) { + plans, err := s.svc.Plans.GetByID(planID) + if err != nil { + return nil, err + } + + return s.mapEntity.Plan.ToPlanModelSlice(plans), nil +} + +func (s *planService) GetByPrefixUUID(prefixUUID string) (viewmodel.Plan, error) { + plan, err := s.svc.Plans.GetByPrefixUUID(prefixUUID) + if err != nil { + return viewmodel.Plan{}, err + } + + return s.mapEntity.Plan.ToPlanModel(plan), nil +} diff --git a/application/applicationservice/provider.go b/application/applicationservice/provider.go index 9823d59..e82acc4 100644 --- a/application/applicationservice/provider.go +++ b/application/applicationservice/provider.go @@ -68,3 +68,13 @@ func (s *providerService) GetByNPI(NPI string, user viewmodel.User) (viewmodel.P return s.mapEntity.Provider.ToProviderRespModel(provider), nil } + +func (s *providerService) GetByOrganization(organizationUUID string, user viewmodel.User) (viewmodel.ProviderResp, error) { + eUser := s.mapEntity.User.ToUserEntity(user) + provider, err := s.svc.Provider.GetByOrganization(organizationUUID, eUser) + if err != nil { + return viewmodel.ProviderResp{}, err + } + + return s.mapEntity.Provider.ToProviderRespModel(provider), nil +} diff --git a/application/applicationservice/user.go b/application/applicationservice/user.go index b191e31..0b588c5 100644 --- a/application/applicationservice/user.go +++ b/application/applicationservice/user.go @@ -51,6 +51,16 @@ func (s *userService) GetByUUID(uuid string, profile string) (retVal viewmodel.U return s.mapEntity.User.ToUserModel(user), nil } +// GetByID returns a specific user by its ID +func (s *userService) GetByMemberID(memberID string) (retVal viewmodel.User, err error) { + user, err := s.svc.Users.GetByMemberID(memberID) + if err != nil { + return retVal, errors.Wrap(err) + } + + return s.mapEntity.User.ToUserModel(user), nil +} + // Login returns a specific user by email and pass func (s *userService) FullLogin(loginType string, key string, pass string, profile string) (retVal viewmodel.User, err error) { user, err := s.svc.Users.FullLogin(loginType, key, pass, profile) @@ -131,6 +141,11 @@ func (s *userService) RemoveAddress(addressUUID string) error { return s.svc.Users.RemoveAddress(addressUUID) } +func (s *userService) UpdateLogin(user viewmodel.User) error { + eUser := s.mapEntity.User.ToUserEntity(user) + return s.svc.Users.UpdateLogin(eUser) +} + func (s *userService) SaveAddress(address viewmodel.Address) (retVal viewmodel.Address, err error) { entity := s.mapEntity.Address.ToAddressEntity(address) entity, err = s.svc.Users.SaveAddress(entity) diff --git a/application/applicationservice/visit.go b/application/applicationservice/visit.go index e3e0235..15e344e 100644 --- a/application/applicationservice/visit.go +++ b/application/applicationservice/visit.go @@ -21,6 +21,38 @@ func newVisitService(svc *service.Service, mapper *entitymapping.Mapper) *visitS } } +func (s *visitService) Save(visit viewmodel.Visit) (viewmodel.Visit, error) { + entity := s.mapEntity.Visit.ToVisitEntity(visit) + for _, a := range visit.User.Addresses { + if a.AddressType == "home" { + address, err := s.svc.Users.GetAddressByUUID(a.UUID) + if err != nil { + return viewmodel.Visit{}, err + } + + entity.PickupAddressID = address.ID + entity.Pickup.Address = address.Address + entity.Pickup.ID = address.UUID + entity.Pickup.Latitude = address.Latitude + entity.Pickup.Longitude = address.Longitude + entity.Pickup.Name = address.Name + } + } + + provider, err := s.svc.Provider.GetByUUID(entity.Provider.ProviderUUID, entity.CreatedUser) + if err != nil { + return viewmodel.Visit{}, errors.Wrap(err) + } + entity.Provider = provider + + retVal, err := s.svc.Visits.Create(entity) + if err != nil { + return viewmodel.Visit{}, errors.Wrap(err) + } + + return s.mapEntity.Visit.ToVisitModel(retVal), err +} + // Save a new ride func (s *visitService) Create(visit viewmodel.Visit) (viewmodel.Visit, error) { entity := s.mapEntity.Visit.ToVisitEntity(visit) @@ -51,5 +83,17 @@ func (s *visitService) GetByUUID(visitUUID string, user viewmodel.User) (viewmod return viewmodel.Visit{}, errors.Wrap(err) } + visitUser, err := s.svc.Users.GetByUUID(retVal.User.UUID, "US") + if err != nil { + return viewmodel.Visit{}, errors.Wrap(err) + } + retVal.User = visitUser + + provider, err := s.svc.Provider.GetByUUID(retVal.Provider.ProviderUUID, eUser) + if err != nil { + return viewmodel.Visit{}, errors.Wrap(err) + } + retVal.Provider = provider + return s.mapEntity.Visit.ToVisitModel(retVal), err } diff --git a/application/entitymapping/entitymapping.go b/application/entitymapping/entitymapping.go index ca4c71a..1e39153 100644 --- a/application/entitymapping/entitymapping.go +++ b/application/entitymapping/entitymapping.go @@ -21,7 +21,8 @@ type Mapper struct { Notification *notificationMapping Profile *profileMapping Organization *organizationMapping - Zipcode *zipcodeMapping + Zipcode *zipcodeMapping + Plan *planMapping } // New returns an EntityMapper for fluent mapping @@ -38,6 +39,7 @@ func New() *Mapper { instance.Profile = &profileMapping{instance} instance.Organization = &organizationMapping{instance} instance.Zipcode = &zipcodeMapping{instance} + instance.Plan = &planMapping{instance} }) return instance diff --git a/application/entitymapping/plan.go b/application/entitymapping/plan.go new file mode 100644 index 0000000..0f1aa40 --- /dev/null +++ b/application/entitymapping/plan.go @@ -0,0 +1,65 @@ +package entitymapping + +import ( + "bitbucket.org/nemt/nemt-portal-api/application/viewmodel" + "bitbucket.org/nemt/nemt-portal-api/domain/entity" +) + +// providerMapping has method to map provider entities to view models +type planMapping struct { + mapper *Mapper +} + +// ToUserEntitySlice maps a User entity slice to User view model slice +func (mapping *planMapping) ToPlanEntitySlice(list []viewmodel.Plan) (retVal []entity.Plan) { + retVal = make([]entity.Plan, 0) + + for _, item := range list { + retVal = append(retVal, mapping.ToPlanEntity(item)) + } + + return retVal +} + +func (mapping *planMapping) ToPlanEntity(model viewmodel.Plan) entity.Plan { + return entity.Plan{ + UUID: model.ID, + Name: model.Name, + InternalID: model.InternalID, + Status: model.Status, + PlanEntityID: model.PlanEntityID, + EntityID: model.EntityID, + PayerID: model.PayerID, + PayerName: model.PayerName, + PrefixUUID: model.PrefixID, + AlphaPrefix: model.AlphaPrefix, + Active: model.Active, + } +} + +// ToUserEntitySlice maps a User entity slice to User view model slice +func (mapping *planMapping) ToPlanModelSlice(list []entity.Plan) (retVal []viewmodel.Plan) { + retVal = make([]viewmodel.Plan, 0) + + for _, item := range list { + retVal = append(retVal, mapping.ToPlanModel(item)) + } + + return retVal +} + +func (mapping *planMapping) ToPlanModel(model entity.Plan) viewmodel.Plan { + return viewmodel.Plan{ + ID: model.UUID, + Name: model.Name, + InternalID: model.InternalID, + Status: model.Status, + PlanEntityID: model.PlanEntityID, + EntityID: model.EntityID, + PayerID: model.PayerID, + PayerName: model.PayerName, + PrefixID: model.PrefixUUID, + AlphaPrefix: model.AlphaPrefix, + Active: model.Active, + } +} diff --git a/application/third/eligibility/bcbsi/bxe.go b/application/third/eligibility/bcbsi/bxe.go index a79453e..2587b19 100644 --- a/application/third/eligibility/bcbsi/bxe.go +++ b/application/third/eligibility/bcbsi/bxe.go @@ -80,6 +80,8 @@ func (s bxeService) Get271(eligibility viewmodel.Eligibility) (bcbsimodel.Interc return bcbsimodel.Interchange271{}, err } + fmt.Println(resp) + if resp.QueryResult.HIPPA271.T271 != "" { xmlString := html.UnescapeString(resp.QueryResult.HIPPA271.T271) xmlReader := strings.NewReader(xmlString) @@ -98,13 +100,10 @@ func (s bxeService) Get271(eligibility viewmodel.Eligibility) (bcbsimodel.Interc } func (s bxeService) CheckEligibility(eligibility viewmodel.Eligibility) (bcbsimodel.MemberEligibilityResponse, error) { - // payer, err := s.GetPayerDetails(eligibility.Subscriber.SubscriberID) - // if err != nil { - // return bcbsimodel.MemberEligibilityResponse{}, err - // } - - eligibility.Payer.PayerID = "621" - eligibility.Payer.PayerName = "blue_cross_blue_shield_il" + if eligibility.Payer.PayerID == "" || eligibility.Payer.PayerName == "" { + eligibility.Payer.PayerID = "621" + eligibility.Payer.PayerName = "blue_cross_blue_shield_il" + } envelope := bcbsimodel.GetEnvelope(eligibility) apiKey := s.cfg.BXE.APIKey @@ -146,7 +145,6 @@ 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/bxe.go b/application/viewmodel/bxe.go index 4893993..b68db8d 100644 --- a/application/viewmodel/bxe.go +++ b/application/viewmodel/bxe.go @@ -11,6 +11,7 @@ type Eligibility struct { Provider Provider `json:"provider,omitempty"` Subscriber Subscriber `json:"subscriber,omitempty"` ServiceInfo ServiceInfo `json:"service_info,omitempty"` + User User `json:"user,omitempty"` } type Payer struct { diff --git a/application/viewmodel/plan.go b/application/viewmodel/plan.go new file mode 100644 index 0000000..b968cf2 --- /dev/null +++ b/application/viewmodel/plan.go @@ -0,0 +1,15 @@ +package viewmodel + +type Plan struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + InternalID string `json:"key,omitempty"` + Status bool `json:"desc,omitempty"` + PlanEntityID int64 `json:"plan_entity_id,omitempty"` + EntityID int64 `json:"entity_id,omitempty"` + PayerID int64 `json:"payer_id,omitempty"` + PayerName string `json:"payer_name,omitempty"` + PrefixID string `json:"prefix_uuid,omitempty"` + AlphaPrefix string `json:"alpha_prefix,omitempty"` + Active bool `json:"active,omitempty"` +} diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 89fe208..254cd9e 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -18,6 +18,7 @@ pipelines: - cp nemt-portal-api ./dist/ - cp -R static/* ./dist/static/ - cp -R docs/swagger/ ./dist/docs/ + - cp -R certs/prd/* ./dist/certs/ - cp config.prd.toml ./dist/config.toml - cp default.prd.conf ./dist/default.conf - cp nginx.conf ./dist/nginx.conf @@ -49,6 +50,7 @@ pipelines: - cp nemt-portal-api ./dist/ - cp -R static/* ./dist/static/ - cp -R docs/swagger/ ./dist/docs/ + - cp -R certs/stg/* ./dist/certs/ - cp config.stg.toml ./dist/config.toml - cp default.stg.conf ./dist/default.conf - cp nginx.conf ./dist/nginx.conf diff --git a/certs/prd/ssl_certificate.cer b/certs/prd/ssl_certificate.cer index ce970c5..d8a1b2d 100755 --- a/certs/prd/ssl_certificate.cer +++ b/certs/prd/ssl_certificate.cer @@ -1,41 +1,41 @@ -----BEGIN CERTIFICATE----- -MIIGvjCCBaagAwIBAgIQC3nVkJVbuOa1WFfJQc+IMDANBgkqhkiG9w0BAQsFADBN +MIIGxzCCBa+gAwIBAgIQDARDXxHDhGyEB0wVou2tYTANBgkqhkiG9w0BAQsFADBN MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5E aWdpQ2VydCBTSEEyIFNlY3VyZSBTZXJ2ZXIgQ0EwHhcNMTgwNTIxMDAwMDAwWhcN -MjAwNTIwMTIwMDAwWjCBhTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCElsbGlub2lz +MjAwNTIwMTIwMDAwWjCBgTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCElsbGlub2lz MRAwDgYDVQQHEwdDaGljYWdvMS8wLQYDVQQKEyZCbHVlIENyb3NzIGFuZCBCbHVl -IFNoaWVsZCBBc3NvY2lhdGlvbjEgMB4GA1UEAwwXKi5kZXYuYmNic2luc3RpdHV0 -ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0VyKNSM/a/CzE -16vidZzip1p4B7+EhJVyENoofRDk7D7afDddWV33yHUvI5opsyKIEMuGFRNIROH/ -ZX47vV3H2ST/5UmkXhghEVjA/xQFUqx2hUxAjI+EkHyf+D3cofcoqFBGA7DVQ9MK -qgbRmwV09IgfQmdlFlF/D3/ZROYNYhY7NC5W+9d+JdmkV45Kp2zIDEa990roDF2p -oN6Nk0WsDMNn5nhyFtGyeuFmYcWwKeCI/XlMu5roko7tJtY0oC0boKSZ8S7xiEPK -uBUxw9cWcHVbkLyeJXIr7lfIzg5xejX3A5/KA7UuKtPAc3HQh09TEOD40Rof5Mts -l5njh/MhAgMBAAGjggNfMIIDWzAfBgNVHSMEGDAWgBQPgGEcgjFh1S8o541GOLQs -4cbZ4jAdBgNVHQ4EFgQUOlOHeILArFmCfRgy4zob9YD7ZdMwIgYDVR0RBBswGYIX -Ki5kZXYuYmNic2luc3RpdHV0ZS5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW -MBQGCCsGAQUFBwMBBggrBgEFBQcDAjBrBgNVHR8EZDBiMC+gLaArhilodHRwOi8v -Y3JsMy5kaWdpY2VydC5jb20vc3NjYS1zaGEyLWc2LmNybDAvoC2gK4YpaHR0cDov -L2NybDQuZGlnaWNlcnQuY29tL3NzY2Etc2hhMi1nNi5jcmwwTAYDVR0gBEUwQzA3 -BglghkgBhv1sAQEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQu -Y29tL0NQUzAIBgZngQwBAgIwfAYIKwYBBQUHAQEEcDBuMCQGCCsGAQUFBzABhhho -dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wRgYIKwYBBQUHMAKGOmh0dHA6Ly9jYWNl -cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJTZWN1cmVTZXJ2ZXJDQS5jcnQw -CQYDVR0TBAIwADCCAYAGCisGAQQB1nkCBAIEggFwBIIBbAFqAHYApLkJkLQYWBSH -uxOizGdwCjw1mAT5G9+443fNDsgN3BAAAAFjhC1vrQAABAMARzBFAiEAjDaMTkKz -JL1M7AloHKciZwIm9PhTpRJhtDedIoV2l5oCIEyT5kke4/OXoBE9guw1TPCKwuS5 -klDOeG+rlHiTHZvbAHcAb1N2rDHwMRnYmQCkURX/dxUcEdkCwQApBo2yCJo32RMA -AAFjhC1xQwAABAMASDBGAiEAzo+0zJUoqV0X0IqzaZ+9boXzWvIzZCR/OqT1qcAe -a3QCIQCdMYaIEYnhnsaOXPMsyShv+ry41lQ9UvUZ3EAvhjgCeQB3ALvZ37wfinG1 -k5Qjl6qSe0c4V5UKq1LoGpCWZDaOHtGFAAABY4QtcKQAAAQDAEgwRgIhAJ4XGViz -vfkGsR3/UWlVZhX9t5rLAlyhW/t1K17vkNjJAiEAhzQycmcTPIVb1Ul1Ug5UgWQ4 -eD2rp/4raewQVACs26QwDQYJKoZIhvcNAQELBQADggEBADjGzhdcyCYxemKuWoP9 -Umd0jnS6gHPivwgpy2Ra4SSsFCkucHX8dRMi63MRghiKi4FPj9LsFoLpdHacfi+6 -Q1lX3fZ6Mv2ZpNjMtBe/g6SATLsFxUQl+UP5bMShEXHsQduinAvSW3m8UpQZ2UxG -7L/XmNfeW8x213Mi4G2DNUJKDMjO28bO/GbCNB85HGfhnaDxj7OsQYllbE5oEDXN -Wkx1QZFrvqE3/2zoOa1ga36xymRXt0Sn3OSFr/ukZh3MCWAL5fuT4zjnSjybHDqT -2qdmV08ECgXwVNUMtbTFq9kenBN0xTrfPFR0Y0IPzqXQP5kTPktgrrYiAf+S875J -IVo= +IFNoaWVsZCBBc3NvY2lhdGlvbjEcMBoGA1UEAwwTKi5iY2JzaW5zdGl0dXRlLmNv +bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKzhKJlWTkQjsFBzjdIC +Z4CuIQqjEtvR3fZ8JeW0X+FsWi4wNUDeHlNpxNNUIf0msojKRXaFsld3wMxRQ47S +AAnoM6O+Yd4OtSq5Pf55epfvVAsZUpJWBwMHL4iN1KU2PVEN3FPWAEp1ZiM4cUew +VmAZlzU7KxeLDD2ZSHH4mEVKrkQ6juNUzr+458Dt1drOTqwLGJP5K4Q30eG49l2U +dSd/5L8E8MjU8r3QAbpaPxT6euhZlnxkwbKHXQUEueZ1nfSI7suXveV6pHjusX07 +HjDgMPBZhpqI1ZfcK+d5hR2ZMJQ8kLkbzYBiUxVn11WWgg5g8zPVfAf2CzW/PAIA +faUCAwEAAaOCA2wwggNoMB8GA1UdIwQYMBaAFA+AYRyCMWHVLyjnjUY4tCzhxtni +MB0GA1UdDgQWBBQLp7ndHlG3iCGf7P7Np16aWJn+fzAxBgNVHREEKjAoghMqLmJj +YnNpbnN0aXR1dGUuY29tghFiY2JzaW5zdGl0dXRlLmNvbTAOBgNVHQ8BAf8EBAMC +BaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMGsGA1UdHwRkMGIwL6At +oCuGKWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zc2NhLXNoYTItZzYuY3JsMC+g +LaArhilodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc3NjYS1zaGEyLWc2LmNybDBM +BgNVHSAERTBDMDcGCWCGSAGG/WwBATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3 +dy5kaWdpY2VydC5jb20vQ1BTMAgGBmeBDAECAjB8BggrBgEFBQcBAQRwMG4wJAYI +KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBGBggrBgEFBQcwAoY6 +aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMlNlY3VyZVNl +cnZlckNBLmNydDAJBgNVHRMEAjAAMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgA +dgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAWOELVu6AAAEAwBH +MEUCIAZMGHS0chj0XWT4ikeIjqg0dglEBDIavlnouMrAEiCnAiEAzFN2rUdA0N/l ++cJqOaEJHIop81RoAcrQ8ltrNkXdZD8AdgBvU3asMfAxGdiZAKRRFf93FRwR2QLB +ACkGjbIImjfZEwAAAWOELV3xAAAEAwBHMEUCIFojBHhsdHar7n0eJ35etIlqUtHR +WV9EQxu752OWrnmsAiEAod+vqZXLOUYR4DRzlA3h9BBkkm0hc4QAU2v2h9h46rQA +dgDuS723dc5guuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWOELVwOAAAEAwBH +MEUCIEBiLv2mAcIhhIHNmCKFFxQM6UtbO4XmlNNm9PTQLtxEAiEAichicElMgsp9 +wAXBuHQ5stQiDicsJ/04Ws2QzuxbpTQwDQYJKoZIhvcNAQELBQADggEBAB3kQbAX +/RaGmnyIduvFE66Nzd/goThIr4bpBnrW0YKvZj2oHq34KjaVkmWX4VXHmyHTd9TI +YJyHifiMrfZ/BCkS5FDg32J1Vocp89k5YDS+pHxlLLxqqMsrz80v5mtblhDhqenx +2dsdwYErlbrAvNp95BKdwqcVbqvg2IHBhJ8myB5kgGLgU6Sn96SfsF5wytW+qnQx +gIn3qfPkwXVou/mq8PbZSqsrNvggjOTKbkNj8k4u+/OAHd9nP4p/CBGN7hb5RxXs +e24Ql2MFqKXx2EIrsMs+ow2xnz6hNRAD39vsYp7HSFj7Ph32Wi8X1O+J68PiNbaW +ZblXGGMRXKXUfFA= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIElDCCA3ygAwIBAgIQAf2j627KdciIQ4tyS8+8kTANBgkqhkiG9w0BAQsFADBh diff --git a/data/datamysql/datamysql.go b/data/datamysql/datamysql.go index a9c8d98..d57838d 100644 --- a/data/datamysql/datamysql.go +++ b/data/datamysql/datamysql.go @@ -28,7 +28,8 @@ type Conn struct { notification *notificationRepo profile *profileRepo organization *organizationRepo - zipcodes *zipcodeRepo + zipcodes *zipcodeRepo + plan *planRepo } // Begin starts a transaction @@ -81,10 +82,14 @@ func (c *Conn) Organization() contract.OrganizationRepo { return c.organization } -func (c *Conn) Zipcodes() contract.ZipcodeRepo{ +func (c *Conn) Zipcodes() contract.ZipcodeRepo { return c.zipcodes } +func (c *Conn) Plans() contract.PlanRepo { + return c.plan +} + // Instance returns an instance of a DataManager func Instance(cfg *config.Config) (contract.DataManager, error) { once.Do(func() { @@ -117,6 +122,7 @@ func Instance(cfg *config.Config) (contract.DataManager, error) { instance.profile = newProfileRepo(db) instance.organization = newOrganizationRepo(db) instance.zipcodes = newZipcodeRepo(db) + instance.plan = newPlanRepo(db) }) return instance, connErr diff --git a/data/datamysql/organization.go b/data/datamysql/organization.go index a8345f9..9a48ab5 100644 --- a/data/datamysql/organization.go +++ b/data/datamysql/organization.go @@ -44,7 +44,7 @@ func (c *organizationRepo) getProfileQuery(user entity.User) (query string, wher where = fmt.Sprintf(` AND (a.organization_uuid = '%s' OR f.organization_uuid = '%s') `, p.Organization.UUID, p.Organization.UUID) return } - case "SP", "SPT": + case "SP", "SPT", "VIRPT": switch p.Organization.Type.Key { case "techsupport", "bcbsi", "bcbsa": return diff --git a/data/datamysql/plan.go b/data/datamysql/plan.go new file mode 100644 index 0000000..ab4c8de --- /dev/null +++ b/data/datamysql/plan.go @@ -0,0 +1,82 @@ +package datamysql + +import ( + "database/sql" + + "bitbucket.org/nemt/nemt-portal-api/domain/entity" + "bitbucket.org/nemt/nemt-portal-api/infra/errors" +) + +type planRepo struct { + conn executor +} + +func newPlanRepo(conn executor) *planRepo { + return &planRepo{ + conn: conn, + } +} + +func (c *planRepo) getQuery() string { + const query = `SELECT + a.plan_id, + a.plan_uuid, + a.plan_name, + a.plan_internal_id, + a.plan_internal_status, + a.plan_entity_id, + a.entity_id, + a.payer_id, + a.payer_name, + a.active, + a.created, + a.updated, + b.plan_prefix_id, + b.plan_prefix_uuid, + b.alpha_prefix + FROM + tab_plan a + INNER JOIN + tab_plan_alpha_prefix b ON a.plan_id = b.plan_id ` + + return query +} + +func (c *planRepo) parseSet(rows *sql.Rows, err error) ([]entity.Plan, error) { + if err != nil { + return nil, errors.Wrap(err) + } + result := make([]entity.Plan, 0) + for rows.Next() { + entity, err := c.parseEntity(rows) + if err != nil { + return nil, errors.Wrap(err) + } + result = append(result, entity) + } + return result, nil +} + +func (c *planRepo) parseEntity(row scanner) (retVal entity.Plan, err error) { + err = row.Scan( + &retVal.ID, &retVal.UUID, &retVal.Name, &retVal.InternalID, &retVal.Status, &retVal.PlanEntityID, &retVal.EntityID, &retVal.PayerID, &retVal.PayerName, + &retVal.Active, &retVal.Created, &retVal.Updated, &retVal.PrefixID, &retVal.PrefixUUID, &retVal.AlphaPrefix) + + return retVal, errors.Wrap(err) +} + +func (c *planRepo) GetByAlphaPrefix(alphaPrefix string) (entity.Plan, error) { + return c.parseEntity(c.conn.QueryRow(c.getQuery()+" WHERE b.alpha_prefix = ? ", alphaPrefix)) +} + +func (c *planRepo) GetByUUID(planUUID string) ([]entity.Plan, error) { + return c.parseSet(c.conn.Query(c.getQuery()+" WHERE a.plan_uuid = ? ", planUUID)) +} + +func (c *planRepo) GetByID(planID int64) ([]entity.Plan, error) { + return c.parseSet(c.conn.Query(c.getQuery()+" WHERE a.plan_id = ? ", planID)) +} + +func (c *planRepo) GetByPrefixUUID(prefixUUID string) (entity.Plan, error) { + return c.parseEntity(c.conn.QueryRow(c.getQuery()+" WHERE b.plan_prefix_uuid = ? ", prefixUUID)) +} diff --git a/data/datamysql/provider.go b/data/datamysql/provider.go index 316835e..bdaa08f 100644 --- a/data/datamysql/provider.go +++ b/data/datamysql/provider.go @@ -27,7 +27,7 @@ func (c *providerRepo) getProfileQuery(user entity.User) (query string, where st if len(user.Profiles) > 0 { for _, p := range user.Profiles { switch p.Key { - case "AD", "BCBSIAD", "BDCAD", "PLANAD": + case "AD", "BCBSIAD", "BDCAD", "PLANAD", "VIRPT": return case "SP", "SPT": switch p.Organization.Type.Key { @@ -145,6 +145,20 @@ func (c *providerRepo) GetByUUID(providerUUID string, user entity.User) (entity. return c.parseEntity(c.conn.QueryRow(query, lat, long, lat, providerUUID)) } +func (c *providerRepo) GetByID(providerID int64, 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_id = ? " + where + + return c.parseEntity(c.conn.QueryRow(query, lat, long, lat, providerID)) +} + func (c *providerRepo) GetByMukID(mukID string, user entity.User) (entity.Provider, error) { lat := 41.886406 long := -87.624225 diff --git a/data/datamysql/ride.go b/data/datamysql/ride.go index 74da273..591539a 100644 --- a/data/datamysql/ride.go +++ b/data/datamysql/ride.go @@ -144,13 +144,13 @@ func (c *rideRepo) getProfileQuery(user entity.User) (query string, where string switch p.Key { case "AD", "BCBSIAD", "BDCAD", "PLANAD": return - case "SP", "SPT": + case "SP", "SPT", "VIRPT": switch p.Organization.Type.Key { case "techsupport", "bcbsi", "bcbsa", "plan": return case "provider": - query = `INNER JOIN viw_visit_provider o ON a.ride_id = o.ride_id` - where = fmt.Sprintf(` AND (o.organization_uuid = '%s' OR o.parent_organization_uuid = '%s') `, p.Organization.UUID, p.Organization.UUID) + query = `INNER JOIN viw_visit_provider z ON a.ride_id = z.ride_id` + where = fmt.Sprintf(` AND (z.organization_uuid = '%s' OR z.parent_organization_uuid = '%s') `, p.Organization.UUID, p.Organization.UUID) return } } @@ -676,6 +676,7 @@ func (c *rideRepo) GetByUUID(uuid string, user entity.User) (entity.Ride, error) } query = c.getQuery() + query + " WHERE a.ride_uuid = ? " + where + return c.parseEntity(c.conn.QueryRow(query, uuid)) } diff --git a/data/datamysql/transaction.go b/data/datamysql/transaction.go index 696ff6e..6bc7fd0 100644 --- a/data/datamysql/transaction.go +++ b/data/datamysql/transaction.go @@ -16,7 +16,8 @@ type transaction struct { notification *notificationRepo profile *profileRepo organization *organizationRepo - zipcodes *zipcodeRepo + zipcodes *zipcodeRepo + plan *planRepo } func newTransaction(tx *sql.Tx) *transaction { @@ -28,6 +29,11 @@ func newTransaction(tx *sql.Tx) *transaction { t.rides = newRideRepo(tx) t.visits = newVisitRepo(tx) t.provider = newProviderRepo(tx) + t.notification = newNotificationRepo(tx) + t.profile = newProfileRepo(tx) + t.organization = newOrganizationRepo(tx) + t.zipcodes = newZipcodeRepo(tx) + t.plan = newPlanRepo(tx) return t } @@ -67,10 +73,14 @@ func (t transaction) Organization() contract.OrganizationRepo { return t.organization } -func (t transaction) Zipcodes() contract.ZipcodeRepo{ +func (t transaction) Zipcodes() contract.ZipcodeRepo { return t.zipcodes } +func (t transaction) Plans() contract.PlanRepo { + return t.plan +} + func (t *transaction) Commit() error { err := t.tx.Commit() diff --git a/data/datamysql/user.go b/data/datamysql/user.go index 1ece11f..ecab9d4 100644 --- a/data/datamysql/user.go +++ b/data/datamysql/user.go @@ -22,6 +22,32 @@ func newUserRepo(conn executor) *userRepo { } } +func (c *userRepo) GetByMemberID(memberID string) (entity.User, error) { + finalQuery := c.getQuery() + " AND a.member = ? AND e.key = 'US'" + + user, err := c.parseSet(c.conn.Query(finalQuery, memberID)) + if err != nil { + return entity.User{}, err + } + + if len(user) > 0 { + retVal := user[0] + retVal.Contacts, err = c.GetContacts(retVal.ID) + if err != nil { + return entity.User{}, err + } + + retVal.Addresses = nil + retVal.Addresses, err = c.getAddressByUserID(retVal.ID) + if err != nil { + return entity.User{}, err + } + return retVal, nil + } else { + return entity.User{}, nil + } +} + func (c *userRepo) GetByUUID(uuid string, profile string) (entity.User, error) { params := make([]interface{}, 0) params = append(params, uuid) @@ -390,6 +416,24 @@ func (c *userRepo) SaveContact(contact entity.ContactInfo) (entity.ContactInfo, return c.addContactInfo(contact) } +func (c *userRepo) UpdateLogin(user entity.User) error { + const ( + query = `UPDATE tab_login a + INNER JOIN tab_user b + ON a.user_id = b.user_id + SET a.email = ?, + a.phone_number = ? + WHERE + b.user_uuid = ?` + ) + + if _, err := c.conn.Exec(query, user.Email, user.PhoneNumber, user.UUID); err != nil { + return err + } + + return nil +} + func (c *userRepo) RemoveContact(contact entity.ContactInfo) (entity.ContactInfo, error) { const ( query = `DELETE FROM tab_contact WHERE contact_uuid = ?;` @@ -561,7 +605,7 @@ func (c *userRepo) createUser(user entity.User) (int64, string, error) { func (c *userRepo) RemoveAddress(addressUUID string) error { const ( - query = "UPDATE tab_address SET active = 0 WHERE address_uuid = ?" + query = "DELETE FROM tab_address WHERE address_uuid = ?;" ) _, err := c.conn.Exec(query, addressUUID) diff --git a/data/datamysql/visit.go b/data/datamysql/visit.go index e13e057..dc1b7ac 100644 --- a/data/datamysql/visit.go +++ b/data/datamysql/visit.go @@ -27,14 +27,13 @@ func (c *visitRepo) getProfileQuery(user entity.User) (query string, where strin switch p.Key { case "AD", "BCBSIAD", "BDCAD", "PLANAD": return - case "SP", "SPT": + case "SP", "SPT", "VIRPT": switch p.Organization.Type.Key { case "techsupport", "bcbsi", "bcbsa", "plan": return case "provider": - query = ` INNER JOIN viw_visit_provider f - ON f.visit_id = a.visit_id ` - where = fmt.Sprintf(` AND (f.organization_uuid = '%s' OR f.parent_organization_uuid = '%s') `, p.Organization.UUID, p.Organization.UUID) + query = ` INNER JOIN viw_visit_provider AS o ON o.visit_id = a.visit_id ` + where = fmt.Sprintf(` AND ( o.organization_uuid = '%s' OR o.parent_organization_uuid = '%s' ) `, p.Organization.UUID, p.Organization.UUID) return } } @@ -85,17 +84,17 @@ func (c *visitRepo) getQuery() string { IFNULL(f.provider_muk_id, '') provider_muk_id, IFNULL(f.provider_name, '') provider_name FROM - tab_visit a - INNER JOIN tab_visit_status b + tab_visit AS a + INNER JOIN tab_visit_status AS b ON a.visit_status_id = b.visit_status_id - INNER JOIN tab_user c + INNER JOIN tab_user AS c ON c.user_id = a.user_id - INNER JOIN tab_user d + INNER JOIN tab_user AS d ON d.user_id = a.created_user_id - INNER JOIN tab_trip_type e + INNER JOIN tab_trip_type AS e ON e.trip_type_id = a.trip_type_id - INNER JOIN tab_provider f - ON f.provider_id = a.provider_id ` + INNER JOIN tab_provider AS f + ON f.provider_id = a.provider_id ` } func (c *visitRepo) GetAll(user entity.User) ([]entity.Visit, error) { @@ -205,6 +204,5 @@ func (c *visitRepo) Create(visit entity.Visit) (entity.Visit, error) { return retVal, err } - fmt.Println("Visit ID: ", retVal.ID) return retVal, nil } diff --git a/default.dev.conf b/default.dev.conf index 3fc630f..ff7c5c6 100644 --- a/default.dev.conf +++ b/default.dev.conf @@ -3,20 +3,6 @@ map $http_upgrade $connection_upgrade { '' close; } -server { - listen 80; - server_name portal-api.dev.bcbinstitute.com; - - location / { - proxy_pass http://127.0.0.1:5100; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $http_host; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - } -} - server { listen 443; ssl on; diff --git a/default.prd.conf b/default.prd.conf index 8470fc3..5749776 100644 --- a/default.prd.conf +++ b/default.prd.conf @@ -3,20 +3,6 @@ map $http_upgrade $connection_upgrade { '' close; } -server { - listen 80; - server_name portal-api.bcbinstitute.com; - - location / { - proxy_pass http://127.0.0.1:5100; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $http_host; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - } -} - server { listen 443; ssl on; diff --git a/default.stg.conf b/default.stg.conf index 3fc630f..ff7c5c6 100644 --- a/default.stg.conf +++ b/default.stg.conf @@ -3,20 +3,6 @@ map $http_upgrade $connection_upgrade { '' close; } -server { - listen 80; - server_name portal-api.dev.bcbinstitute.com; - - location / { - proxy_pass http://127.0.0.1:5100; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $http_host; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - } -} - server { listen 443; ssl on; diff --git a/domain/contract/repo.go b/domain/contract/repo.go index 5c03b0f..f875f7d 100644 --- a/domain/contract/repo.go +++ b/domain/contract/repo.go @@ -13,6 +13,7 @@ type repoManager interface { Profile() ProfileRepo Organization() OrganizationRepo Zipcodes() ZipcodeRepo + Plans() PlanRepo } // UserRepo defines the data set for users @@ -20,6 +21,7 @@ type UserRepo interface { GetAll() (list []entity.User, err error) GetByID(userID int64) (retVal entity.User, err error) GetByUUID(uuid string, profile string) (entity.User, error) + GetByMemberID(memberID string) (entity.User, error) Login(email string, pass string) (entity.User, error) FullLogin(loginType string, key string, pass string, profile string) (entity.User, error) Create(user entity.User) (entity.User, error) @@ -30,6 +32,7 @@ type UserRepo interface { RemoveAddress(addressUUID string) error SaveContact(contact entity.ContactInfo) (entity.ContactInfo, error) RemoveContact(contact entity.ContactInfo) (entity.ContactInfo, error) + UpdateLogin(user entity.User) error } // RideRepo defines the data set for Rides @@ -59,6 +62,7 @@ type ProviderRepo interface { GetByMukID(mukID string, user entity.User) (entity.Provider, error) GetByUUID(providerUUID string, user entity.User) (entity.Provider, error) GetByNPI(NPI string, user entity.User) (entity.Provider, error) + GetByID(providerID int64, user entity.User) (entity.Provider, error) } // NotificationRepo defines the data set for Notification @@ -94,6 +98,13 @@ type OrganizationRepo interface { GetTypeByKey(key string) (entity.OrganizationType, error) } +type PlanRepo interface { + GetByAlphaPrefix(alphaPrefix string) (entity.Plan, error) + GetByUUID(planUUID string) ([]entity.Plan, error) + GetByID(planID int64) ([]entity.Plan, error) + GetByPrefixUUID(prefixUUID string) (entity.Plan, error) +} + // VisitRepo defines the data set for Rides type VisitRepo interface { Create(visit entity.Visit) (entity.Visit, error) diff --git a/domain/entity/plan.go b/domain/entity/plan.go new file mode 100644 index 0000000..4bfee11 --- /dev/null +++ b/domain/entity/plan.go @@ -0,0 +1,21 @@ +package entity + +import "time" + +type Plan struct { + ID int64 `json:"-"` + UUID string `json:"id"` + Name string `json:"name"` + InternalID string `json:"key"` + Status bool `json:"desc"` + PlanEntityID int64 `json:"plan_entity_id"` + EntityID int64 `json:"entity_id"` + PayerID int64 `json:"payer_id"` + PayerName string `json:"payer_name"` + PrefixID int64 `json:"prefix_id"` + PrefixUUID string `json:"prefix_uuid"` + AlphaPrefix string `json:"alpha_prefix"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` + Active bool `json:"active"` +} diff --git a/domain/service/plan.go b/domain/service/plan.go new file mode 100644 index 0000000..51f42f6 --- /dev/null +++ b/domain/service/plan.go @@ -0,0 +1,31 @@ +package service + +import ( + "bitbucket.org/nemt/nemt-portal-api/domain/entity" +) + +type planService struct { + svc *Service +} + +func newPlanService(svc *Service) *planService { + return &planService{ + svc: svc, + } +} + +func (s *planService) GetByAlphaPrefix(alphaPrefix string) (entity.Plan, error) { + return s.svc.db.Plans().GetByAlphaPrefix(alphaPrefix) +} + +func (s *planService) GetByUUID(planUUID string) ([]entity.Plan, error) { + return s.svc.db.Plans().GetByUUID(planUUID) +} + +func (s *planService) GetByID(planID int64) ([]entity.Plan, error) { + return s.svc.db.Plans().GetByID(planID) +} + +func (s *planService) GetByPrefixUUID(prefixUUID string) (entity.Plan, error) { + return s.svc.db.Plans().GetByPrefixUUID(prefixUUID) +} diff --git a/domain/service/provider.go b/domain/service/provider.go index 0351e99..3d8307e 100644 --- a/domain/service/provider.go +++ b/domain/service/provider.go @@ -1,6 +1,8 @@ package service import ( + "errors" + "bitbucket.org/nemt/nemt-portal-api/domain/entity" ) @@ -85,3 +87,21 @@ func (s *providerService) GetByNPI(NPI string, user entity.User) (entity.Provide provider.Organization = organization return provider, nil } + +func (s *providerService) GetByOrganization(organizationUUID string, user entity.User) (entity.Provider, error) { + organization, err := s.svc.db.Organization().GetByUUID(organizationUUID, user) + if err != nil { + return entity.Provider{}, err + } + + if organization.Type.Key != "provider" { + return entity.Provider{}, errors.New("invalid organization") + } + + provider, err := s.svc.db.Provider().GetByID(organization.ReferenceID, user) + if err != nil { + return entity.Provider{}, err + } + + return provider, nil +} diff --git a/domain/service/ride.go b/domain/service/ride.go index 6437d10..4be3463 100644 --- a/domain/service/ride.go +++ b/domain/service/ride.go @@ -56,7 +56,12 @@ func (s *rideService) GetByID(id int64, user entity.User) (entity.Ride, error) { // GetByUUID return a specific ride func (s *rideService) GetByUUID(uuid string, user entity.User) (entity.Ride, error) { - return s.svc.db.Rides().GetByUUID(uuid, user) + ride, err := s.svc.db.Rides().GetByUUID(uuid, user) + if err != nil { + return entity.Ride{}, err + } + + return ride, nil } // GetByUUID return a specific ride diff --git a/domain/service/service.go b/domain/service/service.go index d4c6a4b..1bfb2b6 100644 --- a/domain/service/service.go +++ b/domain/service/service.go @@ -25,7 +25,8 @@ type Service struct { Notification *notificationService Profile *profileService Organization *organizationService - Zipcodes *zipcodeService + Zipcodes *zipcodeService + Plans *planService } // New returns a new domain Service instance @@ -41,6 +42,7 @@ func New(db contract.DataManager, cache contract.CacheManager, cfg *config.Confi instance.Profile = newProfileService(instance) instance.Organization = newOrganizationService(instance) instance.Zipcodes = newZipcodeService(instance) + instance.Plans = newPlanService(instance) }) return instance, nil diff --git a/domain/service/user.go b/domain/service/user.go index d5e31a3..ea00135 100644 --- a/domain/service/user.go +++ b/domain/service/user.go @@ -33,6 +33,10 @@ func (s *userService) GetByUUID(uuid string, profile string) (entity.User, error return s.svc.db.Users().GetByUUID(uuid, profile) } +func (s *userService) GetByMemberID(memberID string) (entity.User, error) { + return s.svc.db.Users().GetByMemberID(memberID) +} + // Login returns a specific user by email and pass func (s *userService) Login(email string, pass string) (entity.User, error) { return s.svc.db.Users().Login(email, pass) @@ -68,6 +72,10 @@ func (s *userService) CreateBulk(users []entity.User) ([]entity.User, error) { return users, nil } +func (s *userService) UpdateLogin(user entity.User) error { + return s.svc.db.Users().UpdateLogin(user) +} + // GetUsersByProfile returns a list of users by profile func (s *userService) GetUsersByProfile(profile string) ([]entity.User, error) { return s.svc.db.Users().GetUsersByProfile(profile) diff --git a/domain/service/visit.go b/domain/service/visit.go index dd89224..3d1f345 100644 --- a/domain/service/visit.go +++ b/domain/service/visit.go @@ -29,10 +29,10 @@ func (s *visitService) GetAll(user entity.User) ([]entity.Visit, error) { return nil, err } - rides, err := s.svc.db.Rides().GetAll(user) - if err != nil { - return nil, err - } + rides, _ := s.svc.db.Rides().GetAll(user) + // if err != nil { + // return nil, err + // } ridesByVisit := make(map[int64][]entity.Ride) for _, r := range rides { @@ -55,10 +55,10 @@ func (s *visitService) GetByUUID(visitUUID string, user entity.User) (entity.Vis return entity.Visit{}, errors.Wrap(err) } - rides, err := s.svc.db.Rides().GetByVisitUUID(visitUUID, user) - if err != nil { - return entity.Visit{}, errors.Wrap(err) - } + rides, _ := s.svc.db.Rides().GetByVisitUUID(visitUUID, user) + // if err != nil { + // return entity.Visit{}, errors.Wrap(err) + // } visit.Rides = rides return visit, nil @@ -71,10 +71,10 @@ func (s *visitService) GetByID(visitID int64, user entity.User) (entity.Visit, e return entity.Visit{}, errors.Wrap(err) } - rides, err := s.svc.db.Rides().GetByVisitUUID(visit.UUID, user) - if err != nil { - return entity.Visit{}, errors.Wrap(err) - } + rides, _ := s.svc.db.Rides().GetByVisitUUID(visit.UUID, user) + // if err != nil { + // return entity.Visit{}, errors.Wrap(err) + // } visit.Rides = rides return visit, nil diff --git a/server/router/eligibilityroute/controller.go b/server/router/eligibilityroute/controller.go index 4c439d8..abfc714 100644 --- a/server/router/eligibilityroute/controller.go +++ b/server/router/eligibilityroute/controller.go @@ -1,18 +1,23 @@ package eligibilityroute import ( + "context" "encoding/xml" "fmt" "html" + "math/rand" "strings" "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" "github.com/labstack/echo" + "googlemaps.github.io/maps" ) var ( @@ -37,6 +42,29 @@ func controllerInstance(cfg *config.Config, svc *applicationservice.Service) *co return instance } +func (c *controller) rangeIn(low, hi int) string { + result := low + rand.Intn(hi-low) + return fmt.Sprintf("%v", result) +} + +func (c *controller) generatePassword(n int) string { + const ( + charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + ) + + return c.stringWithCharset(n, charset) +} + +func (c *controller) stringWithCharset(length int, charset string) string { + b := make([]byte, length) + var seededRand *rand.Rand = rand.New( + rand.NewSource(time.Now().UnixNano())) + for i := range b { + b[i] = charset[seededRand.Intn(len(charset))] + } + return string(b) +} + func (c *controller) handleEligibility(ctx echo.Context) error { var eligibility viewmodel.Eligibility @@ -44,13 +72,45 @@ func (c *controller) handleEligibility(ctx echo.Context) error { return routeutils.HandleAPIError(ctx, err) } + authUser, err := auth.GetUserDetail(ctx, c.cfg) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + + if eligibility.Provider.ProviderNPI == "" { + provider, err := c.svc.Provider.GetByOrganization(authUser.Profiles[0].Organization.UUID, authUser) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + eligibility.Provider.ProviderNPI = provider.InternalID + eligibility.Provider.ProviderName = provider.OrganizatioName + } + + loc, _ := time.LoadLocation("America/Chicago") + eligibility.TrackingID = c.rangeIn(1000000, 9999999) + eligibility.ServiceInfo.DateOfService = time.Now().In(loc) + eligibility.ServiceInfo.ServiceTypeCodes = []string{"30"} + + var plan viewmodel.Plan + if eligibility.Subscriber.SubscriberID != "" { + plan, err = c.svc.Plan.GetByAlphaPrefix(eligibility.Subscriber.SubscriberID[:3]) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + + eligibility.Payer.PayerID = fmt.Sprintf("%v", plan.PayerID) + eligibility.Payer.PayerName = plan.PayerName + } else { + return routeutils.ResponseAPIFieldValidationError(ctx, "subscriber_id", "member # is required.") + } + ret, err := c.bcbsi.BXE.CheckEligibility(eligibility) if err != nil { fmt.Println("Error Here: ", err.Error()) return routeutils.HandleAPIError(ctx, err) } - if ret.QueryResult.HIPPA271.T271 != "" { + if ret.QueryResult.QueryResultSummary.BenefitsFound { xmlString := html.UnescapeString(ret.QueryResult.HIPPA271.T271) xmlReader := strings.NewReader(xmlString) @@ -60,8 +120,133 @@ func (c *controller) handleEligibility(ctx echo.Context) error { fmt.Println("Error to unmarshal: ", err.Error()) } - return ctx.JSON(200, f) + user, err := c.svc.Users.GetByMemberID(eligibility.Subscriber.SubscriberID) + if err != nil { + fmt.Println(err) + return routeutils.HandleAPIError(ctx, err) + } + + if user.ID == "" { + user.Pass = c.generatePassword(8) + user.BirthDate = &eligibility.Subscriber.DemographicInfo.DateOfBirth + user.Member = &eligibility.Subscriber.SubscriberID + user.Gender = &eligibility.Subscriber.DemographicInfo.Gender + user.First = eligibility.Subscriber.Name.First + user.Last = eligibility.Subscriber.Name.Last + user.Active = true + user.Type = &eligibility.Subscriber.PatientType + user.PhoneNumber = eligibility.User.PhoneNumber + user.Email = eligibility.User.Email + + profile, err := c.svc.Profile.GetByKey("US") + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + user.Profiles = append(user.Profiles, profile) + + if user.BirthDate == nil || user.BirthDate.IsZero() { + return routeutils.ResponseAPIAuthError(ctx, "birthdate is required", false) + } + + if user.Member == nil || len(*user.Member) == 0 { + return routeutils.ResponseAPIAuthError(ctx, "member is required", false) + } + + if user.Gender == nil || len(*user.Gender) == 0 || (*user.Gender != "M" && *user.Gender != "F" && *user.Gender != "U") { + return routeutils.ResponseAPIAuthError(ctx, "gender is required", false) + } + + if len(user.Name) == 0 && len(user.First) == 0 && len(user.Last) == 0 { + return routeutils.ResponseAPIAuthError(ctx, "name is required", false) + } + + if len(user.First) != 0 && len(user.Last) != 0 { + user.Name = fmt.Sprintf("%s %s", user.First, user.Last) + } + + if user.PhoneNumber != nil && len(*user.PhoneNumber) == 10 && !strings.Contains(*user.PhoneNumber, "+1") { + phoneNumber := fmt.Sprintf("+1%s", *user.PhoneNumber) + user.PhoneNumber = &phoneNumber + } + + user, err = c.svc.Users.Create(user, authUser) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + } else { + if eligibility.User.Email != nil && len(*eligibility.User.Email) > 0 { + user.Email = eligibility.User.Email + } + + if eligibility.User.PhoneNumber != nil { + if len(*eligibility.User.PhoneNumber) == 10 && !strings.Contains(*eligibility.User.PhoneNumber, "+1") { + phoneNumber := fmt.Sprintf("+1%s", *eligibility.User.PhoneNumber) + eligibility.User.PhoneNumber = &phoneNumber + } + user.PhoneNumber = eligibility.User.PhoneNumber + } + + err = c.svc.Users.UpdateLogin(user) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + } + + address := viewmodel.Address{} + header := f.Division.HealthCareEligibilityResponse.LoopHL0030[0].HL_0460[0].HL_0890[0].NM1_0920[0].N3_0950 + body := f.Division.HealthCareEligibilityResponse.LoopHL0030[0].HL_0460[0].HL_0890[0].NM1_0920[0].N4_0960 + zipCode := strings.TrimSpace(body.N403) + + address.AddressTypeName = "Home" + address.AddressType = "home" + address.Name = fmt.Sprintf("%s, %s", header.N301, body.N401) + address.Address = fmt.Sprintf("%s, %s (%s)", header.N301, body.N401, zipCode) + address.CreatedUserUUID = authUser.ID + address.User = user + address.Type = "provider" + + googleMapsAPI, err := maps.NewClient(maps.WithClientIDAndSignature("gme-bluecrossandblue1", "msqgD-jdqCyR0M_1u5C1HION5iI=")) + if err != nil { + fmt.Println("Error to instantiate googles api: ", err.Error()) + return routeutils.HandleAPIError(ctx, err) + } + + r := &maps.GeocodingRequest{ + Address: address.Address + " " + body.N402 + ", " + zipCode, + } + + result, err := googleMapsAPI.Geocode(context.Background(), r) + if err != nil { + fmt.Println("Error to instantiate googles api: ", err.Error()) + return routeutils.HandleAPIError(ctx, err) + } + + if len(result) > 0 { + address.Latitude = result[0].Geometry.Location.Lat + address.Longitude = result[0].Geometry.Location.Lng + } + + if len(user.Addresses) > 0 { + for _, a := range user.Addresses { + if a.AddressType == "home" { + err := c.svc.Users.RemoveAddress(a.UUID) + if err != nil { + fmt.Println("Error to remove old address: ", err.Error()) + return routeutils.HandleAPIError(ctx, err) + } + } + } + } + + address, err = c.svc.Users.SaveAddress(address) + if err != nil { + fmt.Println("Error to save address: ", err.Error()) + return routeutils.HandleAPIError(ctx, err) + } + user.Addresses = append(user.Addresses, address) + + return ctx.JSON(200, user) } else { - return routeutils.ResponseAPIOK(ctx, ret) + return routeutils.ResponseAPINotEligibleWithMessageError(ctx, "No benefits found for member") } } diff --git a/server/router/providerroute/controller.go b/server/router/providerroute/controller.go index 0931520..8ebd096 100644 --- a/server/router/providerroute/controller.go +++ b/server/router/providerroute/controller.go @@ -124,27 +124,27 @@ func (c *controller) handleList(ctx echo.Context) error { name := ctx.QueryParam("name") searchBy := ctx.QueryParam("searchBy") - // lat, err := routeutils.GetAndValidateFloatQueryParam(ctx, "lat", "latitude is mandatory") - // if err != nil { - // return err - // } + lat, err := routeutils.GetAndValidateFloatQueryParam(ctx, "lat", "latitude is required") + if err != nil { + return err + } - // long, err := routeutils.GetAndValidateFloatQueryParam(ctx, "long", "longitude is mandatory") - // if err != nil { - // return err - // } + long, err := routeutils.GetAndValidateFloatQueryParam(ctx, "long", "longitude is required") + if err != nil { + return err + } - distance, err := routeutils.GetAndValidateIntQueryParam(ctx, "distance", "distance is mandatory") + distance, err := routeutils.GetAndValidateIntQueryParam(ctx, "distance", "distance is required") if err != nil { distance = 10 } - limit, err := routeutils.GetAndValidateIntQueryParam(ctx, "limit", "limit is mandatory") + limit, err := routeutils.GetAndValidateIntQueryParam(ctx, "limit", "limit is required") if err != nil { limit = 50 } - sortBy, err := routeutils.GetAndValidateStringQueryParam(ctx, "sortby", "limit is mandatory") + sortBy, err := routeutils.GetAndValidateStringQueryParam(ctx, "sortby", "limit is required") if err != nil || (sortBy != "distance" && sortBy != "name") { sortBy = "distance" } @@ -152,8 +152,8 @@ func (c *controller) handleList(ctx echo.Context) error { providerParams := npdmodel.ProviderSearchParams{ Name: name, SearchBy: searchBy, - Latitude: 41.819078, - Longitude: -87.623129, + Latitude: lat, + Longitude: long, Distance: distance, Limit: limit, Offset: 0, diff --git a/server/router/router.go b/server/router/router.go index 26c390c..de0f3b3 100644 --- a/server/router/router.go +++ b/server/router/router.go @@ -44,7 +44,7 @@ func Register(e *echo.Echo, cfg *config.Config, svc *applicationservice.Service, usersroute.Register(appGroup.Group("/users"), cfg, svc) eligibilityroute.Register(appGroup.Group("/eligibility"), cfg, svc) tncroute.Register(appGroup.Group("/rides"), cfg, svc, tnc, notification) - visitroute.Register(appGroup.Group("/visits"), cfg, svc) + visitroute.Register(appGroup.Group("/visits"), cfg, svc, tnc) providerroute.Register(appGroup.Group("/provider"), cfg, svc) placesroute.Register(appGroup.Group("/places"), cfg, svc) profileroute.Register(appGroup.Group("/profile"), cfg, svc) diff --git a/server/router/tncroute/controller.go b/server/router/tncroute/controller.go index 2facbac..336dfce 100644 --- a/server/router/tncroute/controller.go +++ b/server/router/tncroute/controller.go @@ -230,7 +230,7 @@ func (c *controller) handle(ctx echo.Context) error { } //Validate ride request - if validationErrors := validation.ValidateRide(&requestRide, &user) ; len(validationErrors) > 0 { + if validationErrors := validation.ValidateRide(&requestRide, &user); len(validationErrors) > 0 { return routeutils.ResponseAPICustomValidationError(ctx, "ride validation failed", validationErrors) } @@ -436,7 +436,7 @@ func (c *controller) handle(ctx echo.Context) error { go func() { err = c.svc.Notification.SendNotification(newRide.Status, roudtripRide, newRide) if err != nil { - fmt.Println("Error to notify user: ", err.Error()) + fmt.Println("Error notifying user: ", err.Error()) } }() } @@ -872,4 +872,4 @@ func (c *controller) handleReady(ctx echo.Context) error { }() return routeutils.ResponseAPIOK(ctx, nextRide) -} \ No newline at end of file +} diff --git a/server/router/visitroute/controller.go b/server/router/visitroute/controller.go index ef98f58..3e12d1e 100644 --- a/server/router/visitroute/controller.go +++ b/server/router/visitroute/controller.go @@ -1,18 +1,26 @@ package visitroute import ( + "context" "fmt" + "math/rand" + "strings" "sync" "time" + b64 "encoding/base64" + "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/tncservice" "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" + uuid "github.com/satori/go.uuid" + "googlemaps.github.io/maps" ) var ( @@ -24,19 +32,383 @@ type controller struct { svc *applicationservice.Service cfg *config.Config bcbsi *bcbsi.Service + tnc *tncservice.Service } -func controllerInstance(svc *applicationservice.Service, cfg *config.Config) *controller { +func controllerInstance(svc *applicationservice.Service, cfg *config.Config, tnc *tncservice.Service) *controller { once.Do(func() { instance = &controller{ svc: svc, cfg: cfg, bcbsi: bcbsi.New(cfg), + tnc: tnc, } }) return instance } +func (c *controller) generatePassword(n int) string { + const ( + charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + ) + + return c.stringWithCharset(n, charset) +} + +func (c *controller) stringWithCharset(length int, charset string) string { + b := make([]byte, length) + var seededRand *rand.Rand = rand.New( + rand.NewSource(time.Now().UnixNano())) + for i := range b { + b[i] = charset[seededRand.Intn(len(charset))] + } + return string(b) +} + +func (c *controller) handleRide(ctx echo.Context) error { + var ride viewmodel.RideRequest + if err := ctx.Bind(&ride); err != nil { + fmt.Println(err) + return routeutils.ResponseAPIValidationError(ctx, "invalid parameters") + } + + authUser, err := auth.GetUserDetail(ctx, c.cfg) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + + visit, err := c.svc.Visits.GetByUUID(ride.Visit.UUID, authUser) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + ride.Visit = visit + + user, err := c.svc.Users.GetByMemberID(*visit.User.Member) + if err != nil { + fmt.Println(err) + return routeutils.HandleAPIError(ctx, err) + } + ride.Visit.User = user + subscriber := "S" + ride.Visit.User.Type = &subscriber + + var provider viewmodel.ProviderResp + if authUser.Profiles[0].Key == "VIRPT" { + provider, err = c.svc.Provider.GetByOrganization(authUser.Profiles[0].Organization.UUID, authUser) + } else { + provider, err = c.svc.Provider.GetByUUID(ride.Visit.Provider.ProviderUUID, authUser) + } + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + + eligibility := viewmodel.Eligibility{} + eligibility.Provider.ProviderNPI = provider.InternalID + eligibility.Provider.ProviderName = provider.OrganizatioName + eligibility.TrackingID = "1234567" + eligibility.Subscriber.SubscriberID = *ride.Visit.User.Member + eligibility.Subscriber.PatientType = *ride.Visit.User.Type + eligibility.Subscriber.Name.First = ride.Visit.User.First + eligibility.Subscriber.Name.Last = ride.Visit.User.Last + eligibility.Subscriber.DemographicInfo.DateOfBirth = *ride.Visit.User.BirthDate + eligibility.Subscriber.DemographicInfo.Gender = *ride.Visit.User.Gender + eligibility.ServiceInfo.DateOfService = time.Now() + eligibility.ServiceInfo.ServiceTypeCodes = []string{"30"} + + resp271, err := c.bcbsi.BXE.Get271(eligibility) + if err != nil { + fmt.Println(err) + return routeutils.ResponseAPIValidationError(ctx, err.Error()) + } + + address := viewmodel.Address{} + header := resp271.Division.HealthCareEligibilityResponse.LoopHL0030[0].HL_0460[0].HL_0890[0].NM1_0920[0].N3_0950 + body := resp271.Division.HealthCareEligibilityResponse.LoopHL0030[0].HL_0460[0].HL_0890[0].NM1_0920[0].N4_0960 + zipCode := strings.TrimSpace(body.N403) + + address.AddressTypeName = "Home" + address.AddressType = "home" + address.Name = fmt.Sprintf("%s, %s", header.N301, body.N401) + address.Address = fmt.Sprintf("%s, %s (%s)", header.N301, body.N401, zipCode) + address.CreatedUserUUID = authUser.ID + address.User = user + address.Type = "provider" + + googleMapsAPI, err := maps.NewClient(maps.WithClientIDAndSignature("gme-bluecrossandblue1", "msqgD-jdqCyR0M_1u5C1HION5iI=")) + if err != nil { + fmt.Println("Error to instantiate googles api: ", err.Error()) + return routeutils.HandleAPIError(ctx, err) + } + + r := &maps.GeocodingRequest{ + Address: address.Address + " " + body.N402 + ", " + zipCode, + } + + result, err := googleMapsAPI.Geocode(context.Background(), r) + if err != nil { + fmt.Println("Error to instantiate googles api: ", err.Error()) + return routeutils.HandleAPIError(ctx, err) + } + + if len(result) > 0 { + address.Latitude = result[0].Geometry.Location.Lat + address.Longitude = result[0].Geometry.Location.Lng + } + + if len(user.Addresses) > 0 { + for _, a := range user.Addresses { + if a.AddressType == "home" { + err := c.svc.Users.RemoveAddress(a.UUID) + if err != nil { + fmt.Println("Error to remove old address: ", err.Error()) + return routeutils.HandleAPIError(ctx, err) + } + } + } + } + + address, err = c.svc.Users.SaveAddress(address) + if err != nil { + fmt.Println("Error to save address: ", err.Error()) + return routeutils.HandleAPIError(ctx, err) + } + user.Addresses = append(user.Addresses, address) + + homeAddress := viewmodel.Address{} + for _, a := range visit.User.Addresses { + if a.AddressType == "home" { + homeAddress = a + } + } + + ride.CreateUserUUID = authUser.ID + ride.Destination = viewmodel.Location{ + ID: visit.Provider.ProviderUUID, + Name: visit.Provider.OrganizatioName, + Latitude: visit.Provider.Address.Latitude, + Longitude: visit.Provider.Address.Longitude, + Address: visit.Provider.Address.StreetAddress1, + } + ride.Origin = viewmodel.Location{ + ID: homeAddress.UUID, + Name: homeAddress.AddressTypeName, + Latitude: homeAddress.Latitude, + Longitude: homeAddress.Longitude, + Address: homeAddress.Address, + } + ride.Notes = ride.Notes + ride.Passenger.FirstName = visit.User.First + ride.Passenger.LastName = " " + ride.Passenger.PhoneNumber = *visit.User.PhoneNumber + ride.RideType = "lyft" + ride.VisitDate = &visit.VisitDatetime + ride.VisitTime = &visit.VisitDatetime + + var resp viewmodel.RideRequest + if ride.TripType.Key != "from_visit_call" { + if ride.TripType.Key == "from_visit" { + newOrigin := ride.Origin + ride.Origin = ride.Destination + ride.Destination = newOrigin + } + + if c.cfg.LyftProd.UserUUID != authUser.ID { + resp, err = c.tnc.Lyft.RequestRide(ride) + } else { + fmt.Println("In Production") + resp, err = c.tnc.LyftProd.RequestRide(ride) + } + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + + if ride.TripType.Key == "from_visit" { + newOrigin := resp.Origin + resp.Origin = resp.Destination + resp.Destination = newOrigin + } + + resp.RideID = strings.Replace(resp.RideID, "s_", "", -1) + } else { + resp = ride + + UUID, _ := uuid.NewV4() + resp.RideID = UUID.String() + } + + if resp.Status == "scheduled" || ride.TripType.Key == "from_visit_call" { + if ride.PickupTime == nil { + currentDate := time.Now() + ride.PickupTime = ¤tDate + } + requestMS := (ride.PickupTime.UnixNano() / int64(time.Millisecond)) + generateDate := time.Now() + generateMS := (generateDate.UnixNano() / int64(time.Millisecond)) + resp.RequestAt = ride.PickupTime + resp.RequestAtMS = &requestMS + resp.GeneratedAt = &generateDate + resp.GeneratedAtMS = &generateMS + } + + resp.Passenger.FirstName = visit.User.First + resp.Passenger.LastName = visit.User.Last + resp.Passenger.PhoneNumber = *visit.User.PhoneNumber + if resp.Passenger.ImageURL == nil { + imageURL := " " + resp.Passenger.ImageURL = &imageURL + } + resp.UserUUID = visit.User.ID + + if ride.TripType.Key == "from_visit" { + resp.Origin.Name = ride.Destination.Name + resp.Origin.ID = ride.Destination.ID + resp.Destination.Name = ride.Origin.Name + resp.Destination.ID = ride.Origin.ID + } else { + resp.Origin.Name = ride.Origin.Name + resp.Origin.ID = ride.Origin.ID + resp.Destination.Name = ride.Destination.Name + resp.Destination.ID = ride.Destination.ID + } + + resp.Distance = ride.Distance + resp.Duration = ride.Duration + resp.ETA = ride.ETA + resp.PickupTime = ride.PickupTime + resp.VisitDate = &ride.Visit.VisitDatetime + resp.VisitTime = &ride.Visit.VisitDatetime + resp.VisitExternalID = ride.Visit.ExternalID + resp.CreateUserUUID = authUser.ID + resp.Visit.TripType = ride.Visit.TripType + resp.Visit = visit + + if resp.TripType.Key == "from_visit_call" { + resp.Status = "willCall" + resp.Passenger.UserID = &resp.UserUUID + } + + resp.TripType.Key = ride.TripType.Key + if ride.TripType.Key == "roundtrip" || ride.TripType.Key == "roundtrip_call" { + resp.TripType.Key = "to_visit" + } else if ride.TripType.Key == "from_visit_call" { + resp.TripType.Key = "from_visit_call" + } + + entity, err := c.svc.Rides.Save(resp) + if err != nil { + fmt.Println("Error to save first ride: ", err.Error()) + return routeutils.HandleAPIError(ctx, err) + } + + go func() { + err = c.svc.Notification.SendNotification(resp.Status, entity, resp) + if err != nil { + fmt.Println("Error to notify user: ", err.Error()) + } + }() + + if ride.TripType.Key == "roundtrip" || ride.TripType.Key == "roundtrip_call" { + newRide := ride + + if ride.TripType.Key == "roundtrip" { + destination := newRide.Origin + newRide.Origin = newRide.Destination + newRide.Destination = destination + if ride.ReturnTime != nil { + newRide.PickupTime = ride.ReturnTime + } + + scheduledRide := make(map[string]interface{}) + scheduledRide["timestamp_ms"] = ride.PickupTime.Unix() + newRide.ScheduledPickupRange = scheduledRide + + newRide.Passenger.FirstName = visit.User.First + newRide.Passenger.LastName = " " + newRide.Passenger.PhoneNumber = *visit.User.PhoneNumber + + if c.cfg.LyftProd.UserUUID != authUser.ID { + newRide, err = c.tnc.Lyft.RequestRide(newRide) + } else { + fmt.Println("In Production") + newRide, err = c.tnc.LyftProd.RequestRide(newRide) + } + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + + destination = newRide.Origin + newRide.Origin = newRide.Destination + newRide.Destination = destination + + if newRide.Error != "" { + fmt.Println("Error to schedule a ride on lyft: ", newRide.Error, newRide.ErrorDescription) + } else { + fmt.Println("Ride Scheduled: ", newRide.Status) + } + + newRide.TripType.Key = "from_visit" + } else { + newRide.TripType.Key = "from_visit_call" + newRide.Status = "willCall" + newRide.RideID = entity.UUID + } + + newRide.Visit = visit + newRide.PickupTime = ride.ReturnTime + + requestMS := (newRide.PickupTime.UnixNano() / int64(time.Millisecond)) + generateDate := time.Now() + generateMS := (generateDate.UnixNano() / int64(time.Millisecond)) + newRide.RequestAt = newRide.PickupTime + newRide.RequestAtMS = &requestMS + newRide.GeneratedAt = &generateDate + newRide.GeneratedAtMS = &generateMS + + newRide.Passenger.FirstName = visit.User.First + newRide.Passenger.LastName = visit.User.Last + newRide.Passenger.PhoneNumber = *visit.User.PhoneNumber + newRide.Passenger.UserID = &ride.UserUUID + + if newRide.Passenger.ImageURL == nil { + imageURL := " " + newRide.Passenger.ImageURL = &imageURL + } + + newRide.UserUUID = ride.UserUUID + newRide.Origin.Name = ride.Destination.Name + newRide.Origin.ID = ride.Destination.ID + newRide.Destination.Name = ride.Origin.Name + newRide.Destination.ID = ride.Origin.ID + newRide.Distance = ride.Distance + newRide.Duration = ride.Duration + newRide.ETA = ride.ETA + newRide.VisitDate = ride.VisitDate + newRide.VisitTime = ride.VisitTime + newRide.VisitExternalID = ride.VisitExternalID + newRide.CreateUserUUID = authUser.ID + + roudtripRide, err := c.svc.Rides.Save(newRide) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + + go func() { + err = c.svc.Notification.SendNotification(newRide.Status, roudtripRide, newRide) + if err != nil { + fmt.Println("Error to notify user: ", err.Error()) + } + }() + } + + ctx.Response().Header().Set("Content-Type", "application/json") + return routeutils.ResponseAPIOK(ctx, entity) +} + +func (c *controller) rangeIn(low, hi int) string { + result := low + rand.Intn(hi-low) + return fmt.Sprintf("%v", result) +} + func (c *controller) handle(ctx echo.Context) error { var visit viewmodel.Visit if err := ctx.Bind(&visit); err != nil { @@ -44,25 +416,29 @@ func (c *controller) handle(ctx echo.Context) error { return routeutils.ResponseAPIValidationError(ctx, "invalid parameters") } - user, err := auth.GetUserDetail(ctx, c.cfg) + authUser, err := auth.GetUserDetail(ctx, c.cfg) if err != nil { return routeutils.HandleAPIError(ctx, err) } - provider, err := c.svc.Provider.GetByUUID(visit.Provider.ProviderUUID, user) + var provider viewmodel.ProviderResp + if authUser.Profiles[0].Key == "VIRPT" { + provider, err = c.svc.Provider.GetByOrganization(authUser.Profiles[0].Organization.UUID, authUser) + } else { + provider, err = c.svc.Provider.GetByUUID(visit.Provider.ProviderUUID, authUser) + } if err != nil { - fmt.Println(err) - return routeutils.ResponseAPIValidationError(ctx, "invalid provider") + return routeutils.HandleAPIError(ctx, err) } - if validationErrors := validation.ValidateVisit(&visit, &user); len(validationErrors) > 0 { + if validationErrors := validation.ValidateVisit(&visit, &authUser); 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.Provider.ProviderName = provider.OrganizatioName + eligibility.TrackingID = c.rangeIn(1000000, 9999999) eligibility.Subscriber.SubscriberID = *visit.User.Member eligibility.Subscriber.PatientType = *visit.User.Type eligibility.Subscriber.Name.First = visit.User.First @@ -78,7 +454,144 @@ func (c *controller) handle(ctx echo.Context) error { return routeutils.ResponseAPIValidationError(ctx, err.Error()) } - return routeutils.ResponseAPIOK(ctx, resp) + user, err := c.svc.Users.GetByMemberID(*visit.User.Member) + if err != nil { + fmt.Println(err) + return routeutils.HandleAPIError(ctx, err) + } + + if user.ID == "" { + if len(user.Pass) == 0 { + user.Pass = c.generatePassword(8) + } else { + pass, err := b64.StdEncoding.DecodeString(user.Pass) + if err != nil { + return routeutils.ResponseAPIAuthError(ctx, "Invalid password", false) + } + user.Pass = string(pass) + } + + if user.BirthDate == nil || user.BirthDate.IsZero() { + return routeutils.ResponseAPIAuthError(ctx, "birthdate is required", false) + } + + if user.Member == nil || len(*user.Member) == 0 { + return routeutils.ResponseAPIAuthError(ctx, "member is required", false) + } + + if user.Gender == nil || len(*user.Gender) == 0 || (*user.Gender != "M" && *user.Gender != "F" && *user.Gender != "U") { + return routeutils.ResponseAPIAuthError(ctx, "gender is required", false) + } + + if len(user.Name) == 0 && len(user.First) == 0 && len(user.Last) == 0 { + return routeutils.ResponseAPIAuthError(ctx, "name is required", false) + } + + if len(user.First) != 0 && len(user.Last) != 0 { + user.Name = fmt.Sprintf("%s %s", user.First, user.Last) + } + + if user.PhoneNumber != nil && len(*user.PhoneNumber) == 10 && !strings.Contains(*user.PhoneNumber, "+1") { + phoneNumber := fmt.Sprintf("+1%s", *user.PhoneNumber) + user.PhoneNumber = &phoneNumber + } + + profile, err := c.svc.Profile.GetByKey("US") + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + user.Profiles = append(user.Profiles, profile) + + user, err = c.svc.Users.Create(user, authUser) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + } else { + if visit.User.Email != nil && len(*visit.User.Email) > 0 { + user.Email = visit.User.Email + } + + if visit.User.PhoneNumber != nil { + if len(*visit.User.PhoneNumber) == 10 && !strings.Contains(*visit.User.PhoneNumber, "+1") { + phoneNumber := fmt.Sprintf("+1%s", *visit.User.PhoneNumber) + visit.User.PhoneNumber = &phoneNumber + } + user.PhoneNumber = visit.User.PhoneNumber + } + + err = c.svc.Users.UpdateLogin(user) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + } + + address := viewmodel.Address{} + header := resp.Division.HealthCareEligibilityResponse.LoopHL0030[0].HL_0460[0].HL_0890[0].NM1_0920[0].N3_0950 + body := resp.Division.HealthCareEligibilityResponse.LoopHL0030[0].HL_0460[0].HL_0890[0].NM1_0920[0].N4_0960 + zipCode := strings.TrimSpace(body.N403) + + address.AddressTypeName = "Home" + address.AddressType = "home" + address.Name = fmt.Sprintf("%s, %s", header.N301, body.N401) + address.Address = fmt.Sprintf("%s, %s (%s)", header.N301, body.N401, zipCode) + address.CreatedUserUUID = authUser.ID + address.User = user + address.Type = "provider" + + googleMapsAPI, err := maps.NewClient(maps.WithClientIDAndSignature("gme-bluecrossandblue1", "msqgD-jdqCyR0M_1u5C1HION5iI=")) + if err != nil { + fmt.Println("Error to instantiate googles api: ", err.Error()) + return routeutils.HandleAPIError(ctx, err) + } + + r := &maps.GeocodingRequest{ + Address: address.Address + " " + body.N402 + ", " + zipCode, + } + + result, err := googleMapsAPI.Geocode(context.Background(), r) + if err != nil { + fmt.Println("Error to instantiate googles api: ", err.Error()) + return routeutils.HandleAPIError(ctx, err) + } + + if len(result) > 0 { + address.Latitude = result[0].Geometry.Location.Lat + address.Longitude = result[0].Geometry.Location.Lng + } + + if len(user.Addresses) > 0 { + for _, a := range user.Addresses { + if a.AddressType == "home" { + err := c.svc.Users.RemoveAddress(a.UUID) + if err != nil { + fmt.Println("Error to remove old address: ", err.Error()) + return routeutils.HandleAPIError(ctx, err) + } + } + } + } + + address, err = c.svc.Users.SaveAddress(address) + if err != nil { + fmt.Println("Error to save address: ", err.Error()) + return routeutils.HandleAPIError(ctx, err) + } + user.Addresses = append(user.Addresses, address) + + visit.TripType = viewmodel.TripType{ + Key: "no_trip", + Value: "No Trip", + } + + visit.User = user + visit.CreatedUser = authUser + + visit, err = c.svc.Visits.Save(visit) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + + return routeutils.ResponseAPIOK(ctx, visit) } func (c *controller) handleGetByID(ctx echo.Context) error { @@ -87,13 +600,14 @@ func (c *controller) handleGetByID(ctx echo.Context) error { return routeutils.ResponseAPIValidationError(ctx, "visit_uuid param is mandatory") } - user, err := auth.GetUserDetail(ctx, c.cfg) + authUser, err := auth.GetUserDetail(ctx, c.cfg) if err != nil { return routeutils.HandleAPIError(ctx, err) } - resp, err := c.svc.Visits.GetByUUID(visit_uuid, user) + resp, err := c.svc.Visits.GetByUUID(visit_uuid, authUser) if err != nil { + fmt.Println("Error to get Visit: ", err.Error()) return routeutils.HandleAPIError(ctx, err) } diff --git a/server/router/visitroute/router.go b/server/router/visitroute/router.go index 109bc16..c6b0b62 100644 --- a/server/router/visitroute/router.go +++ b/server/router/visitroute/router.go @@ -2,6 +2,7 @@ package visitroute import ( "bitbucket.org/nemt/nemt-portal-api/application/applicationservice" + "bitbucket.org/nemt/nemt-portal-api/application/tncservice" "bitbucket.org/nemt/nemt-portal-api/infra/config" "github.com/labstack/echo" ) @@ -9,13 +10,15 @@ import ( const ( rootRoute = "/" idRoute = "/:visit_uuid" + rideRoute = "/:visit_uuid/ride" ) // Register registers the routes in the echo group -func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service) { - ctrl := controllerInstance(svc, cfg) +func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service) { + ctrl := controllerInstance(svc, cfg, tnc) r.POST(rootRoute, ctrl.handle) + r.POST(rideRoute, ctrl.handleRide) r.GET(rootRoute, ctrl.handleGetAll) r.GET(idRoute, ctrl.handleGetByID)