Compare commits

4 Commits

Author SHA1 Message Date
Senad Uka
f22af46a26 Upstream sync 2018-06-01 16:50:57 +02:00
Senad Uka
79f39b32c9 Upstream sync 2018-06-01 16:46:46 +02:00
Senad Uka
dfa43d09e4 Upstream sync 2018-06-01 16:14:56 +02:00
Senad Uka
69853b026b Upstream sync 2018-06-01 10:39:46 +02:00
39 changed files with 1100 additions and 661 deletions

View File

@@ -5,6 +5,7 @@ import (
"bitbucket.org/nemt/nemt-portal-api/application/entitymapping" "bitbucket.org/nemt/nemt-portal-api/application/entitymapping"
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice" "bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
"bitbucket.org/nemt/nemt-portal-api/application/third/eligibility/bcbsi"
"bitbucket.org/nemt/nemt-portal-api/domain/service" "bitbucket.org/nemt/nemt-portal-api/domain/service"
"bitbucket.org/nemt/nemt-portal-api/infra/config" "bitbucket.org/nemt/nemt-portal-api/infra/config"
) )
@@ -16,30 +17,34 @@ var (
// Service holds the domain service repositories // Service holds the domain service repositories
type Service struct { type Service struct {
Users *userService Users *userService
Rides *rideService Rides *rideService
Visits *visitService Visits *visitService
Provider *providerService Provider *providerService
Notification *notificationService Notification *notificationService
Profile *profileService Profile *profileService
Organization *organizationService Organization *organizationService
Zipcodes *zipcodeService Zipcodes *zipcodeService
Plan *planService Plan *planService
PasswordReset *passwordResetService
} }
// New returns a new domain Service instance // New returns a new domain Service instance
func New(svc *service.Service, mapper *entitymapping.Mapper, notification *notificationservice.Service, cfg *config.Config) *Service { func New(svc *service.Service, mapper *entitymapping.Mapper, notification *notificationservice.Service, cfg *config.Config) *Service {
once.Do(func() { once.Do(func() {
bcbsi := bcbsi.New(cfg)
instance = &Service{ instance = &Service{
Users: newUserService(svc, mapper), Users: newUserService(svc, mapper, bcbsi, cfg),
Rides: newRideService(svc, mapper), Rides: newRideService(svc, mapper),
Visits: newVisitService(svc, mapper), Visits: newVisitService(svc, mapper),
Provider: newProviderService(svc, mapper), Provider: newProviderService(svc, mapper),
Notification: newNotificationService(svc, mapper, notification, cfg), Notification: newNotificationService(svc, mapper, notification, cfg),
Profile: newProfileService(svc, mapper), Profile: newProfileService(svc, mapper),
Organization: newOrganizationService(svc, mapper), Organization: newOrganizationService(svc, mapper),
Zipcodes: newZipcodeService(svc, mapper), Zipcodes: newZipcodeService(svc, mapper),
Plan: newPlanService(svc, mapper), Plan: newPlanService(svc, mapper),
PasswordReset: newPasswordResetService(svc, mapper),
} }
}) })

View File

@@ -0,0 +1,62 @@
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"
)
// zipcodeService holds methods to participating zipcode application service
type passwordResetService struct {
svc *service.Service
mapEntity *entitymapping.Mapper
}
// newZipcodeService returns a zipcodeService instance
func newPasswordResetService(svc *service.Service, mapper *entitymapping.Mapper) *passwordResetService {
return &passwordResetService{
svc: svc,
mapEntity: mapper,
}
}
func (s *passwordResetService) GetAll() ([]viewmodel.PasswordReset, error) {
result, err := s.svc.PasswordReset.GetAll()
if err != nil {
return nil, err
}
return s.mapEntity.PasswordReset.ToPasswordResetModelSlice(result), nil
}
func (s *passwordResetService) CreatePasswordResetEntry(passwordResetEntry viewmodel.PasswordReset) (viewmodel.PasswordReset, error) {
passwordResetEntity := s.mapEntity.PasswordReset.ToPasswordResetEntity(passwordResetEntry)
result, err := s.svc.PasswordReset.CreatePasswordResetEntry(passwordResetEntity)
if err != nil {
return viewmodel.PasswordReset{}, err
}
return s.mapEntity.PasswordReset.ToPasswordResetModel(result), nil
}
func (s *passwordResetService) GetByID(ID int64) (viewmodel.PasswordReset, error) {
result, err := s.svc.PasswordReset.GetByID(ID)
if err != nil {
return viewmodel.PasswordReset{}, err
}
return s.mapEntity.PasswordReset.ToPasswordResetModel(result), nil
}
func (s *passwordResetService) GetByToken(token string) (viewmodel.PasswordReset, error) {
result, err := s.svc.PasswordReset.GetByToken(token)
if err != nil {
return viewmodel.PasswordReset{}, err
}
return s.mapEntity.PasswordReset.ToPasswordResetModel(result), nil
}
func (s *passwordResetService) SetTokenOpened(token string) error {
return s.svc.PasswordReset.SetTokenOpened(token)
}
func (s *passwordResetService) SetTokenUsed(token string) error {
return s.svc.PasswordReset.SetTokenUsed(token)
}

View File

@@ -1,24 +1,40 @@
package applicationservice package applicationservice
import ( import (
"context"
"encoding/xml"
"fmt"
"html"
"math/rand"
"strings"
"time"
"bitbucket.org/nemt/nemt-portal-api/application/entitymapping" "bitbucket.org/nemt/nemt-portal-api/application/entitymapping"
"bitbucket.org/nemt/nemt-portal-api/application/third/eligibility/bcbsi"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel" "bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/domain/entity" "bitbucket.org/nemt/nemt-portal-api/domain/entity"
"bitbucket.org/nemt/nemt-portal-api/domain/service" "bitbucket.org/nemt/nemt-portal-api/domain/service"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/infra/errors" "bitbucket.org/nemt/nemt-portal-api/infra/errors"
"bitbucket.org/nemt/nemt-portal-api/server/validation"
"googlemaps.github.io/maps"
) )
// userService holds methods to user application service // userService holds methods to user application service
type userService struct { type userService struct {
svc *service.Service svc *service.Service
mapEntity *entitymapping.Mapper mapEntity *entitymapping.Mapper
bcbsi *bcbsi.Service
cfg *config.Config
} }
// newUserService returns a userService instance // newUserService returns a userService instance
func newUserService(svc *service.Service, mapper *entitymapping.Mapper) *userService { func newUserService(svc *service.Service, mapper *entitymapping.Mapper, bcbsi *bcbsi.Service, cfg *config.Config) *userService {
return &userService{ return &userService{
svc: svc, svc: svc,
mapEntity: mapper, mapEntity: mapper,
bcbsi: bcbsi,
cfg: cfg,
} }
} }
@@ -61,6 +77,16 @@ func (s *userService) GetByMemberID(memberID string) (retVal viewmodel.User, err
return s.mapEntity.User.ToUserModel(user), nil return s.mapEntity.User.ToUserModel(user), nil
} }
// GetByEmail returns a specific user by its email
func (s *userService) GetByEmail(email string) (retVal viewmodel.User, err error) {
user, err := s.svc.Users.GetByEmail(email)
if err != nil {
return retVal, errors.Wrap(err)
}
return s.mapEntity.User.ToUserModel(user), nil
}
// Login returns a specific user by email and pass // 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) { 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) user, err := s.svc.Users.FullLogin(loginType, key, pass, profile)
@@ -184,3 +210,220 @@ func (s *userService) RemoveContact(contact viewmodel.Contact) (retVal viewmodel
return s.mapEntity.User.ToContactModel(entity), err return s.mapEntity.User.ToContactModel(entity), err
} }
func (s *userService) rangeIn(low, hi int) string {
result := low + rand.Intn(hi-low)
return fmt.Sprintf("%v", result)
}
func (s *userService) generatePassword(n int) string {
const (
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
)
return s.stringWithCharset(n, charset)
}
func (s *userService) 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 (s *userService) CheckAndCreateMember(user viewmodel.User, provider viewmodel.ProviderResp, authorUser viewmodel.User) (viewmodel.User, error) {
if validationErrors := validation.ValidateEligibility(&user); len(validationErrors) > 0 {
return viewmodel.User{}, &viewmodel.ValidationError{Message: "Eligibility validation failed", Errors: validationErrors}
}
entityUser := s.mapEntity.User.ToUserEntity(user)
entityAuthorUser := s.mapEntity.User.ToUserEntity(authorUser)
entityProvider := s.mapEntity.Provider.ToProviderRespEntity(provider)
newEmail := entityUser.Email
newPhoneNumber := entityUser.PhoneNumber
var err error
if entityUser.UUID != "" {
entityUser, err = s.svc.Users.GetByUUID(entityUser.UUID, "US")
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error finding user by UUID: %s", err.Error())}
}
} else {
entityUser, err = s.svc.Users.GetByMemberID(entityUser.Member)
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error finding user by Subscriber ID: %s", err.Error())}
}
}
if entityUser.UUID == "" {
entityUser = s.mapEntity.User.ToUserEntity(user)
}
var eligibility viewmodel.Eligibility
loc, _ := time.LoadLocation("America/Chicago")
eligibility.TrackingID = s.rangeIn(1000000, 9999999)
eligibility.ServiceInfo.DateOfService = time.Now().In(loc)
eligibility.ServiceInfo.ServiceTypeCodes = []string{"30"}
if entityProvider.InternalID == "" && entityProvider.ProviderUUID != "" {
entityProvider, err = s.svc.Provider.GetByUUID(entityProvider.ProviderUUID, entityAuthorUser)
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error finding provider by UUID: %s", err.Error())}
}
} else if entityProvider.InternalID != "" {
entityProvider, err = s.svc.Provider.GetByNPI(entityProvider.InternalID, entityAuthorUser)
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error finding provider by NPI: %s", err.Error())}
}
}
if entityProvider.InternalID == "" {
eligibility.Provider.ProviderNPI = "1699849786"
eligibility.Provider.ProviderName = "LITHOLINK CORPORATION"
} else {
eligibility.Provider.ProviderNPI = entityProvider.InternalID
eligibility.Provider.ProviderName = entityProvider.OrganizatioName
}
plan, err := s.svc.Plans.GetByAlphaPrefix(entityUser.Member[:3])
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error finding Plan: %s", err.Error())}
}
eligibility.Payer.PayerID = fmt.Sprintf("%v", plan.PayerID)
eligibility.Payer.PayerName = plan.PayerName
eligibility.Subscriber.SubscriberID = entityUser.Member
eligibility.Subscriber.PatientType = entityUser.Type
eligibility.Subscriber.Name.First = strings.Split(entityUser.Name, " ")[0]
eligibility.Subscriber.Name.Last = strings.Split(entityUser.Name, " ")[1]
eligibility.Subscriber.DemographicInfo.DateOfBirth = entityUser.BirthDate
eligibility.Subscriber.DemographicInfo.Gender = entityUser.Gender
ret, err := s.bcbsi.BXE.CheckEligibility(eligibility)
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error checking eligibility: %s", err.Error())}
}
if ret.QueryResult.QueryResultSummary.BenefitsFound {
xmlString := html.UnescapeString(ret.QueryResult.HIPPA271.T271)
xmlReader := strings.NewReader(xmlString)
var f viewmodel.Interchange271
err = xml.NewDecoder(xmlReader).Decode(&f)
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error unmarshaling 271 response: %s", err.Error())}
}
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)
if len(strings.TrimSpace(zipCode)) > 5 {
zipCode = strings.TrimSpace(zipCode)[:5]
}
if !s.cfg.App.DisableZipValidation {
_, err = s.svc.Zipcodes.GetByParticipatingZipcode(zipCode)
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Member's Home zipcode, %s, is not currently eligible for participation in this program", zipCode)}
}
}
if entityUser.ID == 0 {
profile, err := s.svc.Profile.GetByKey("US")
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error getting user profile: %s", err.Error())}
}
entityUser.Profiles = append(entityUser.Profiles, profile)
if len(entityUser.PhoneNumber) == 10 && !strings.Contains(entityUser.PhoneNumber, "+1") {
phoneNumber := fmt.Sprintf("+1%s", entityUser.PhoneNumber)
entityUser.PhoneNumber = phoneNumber
}
entityUser.Test = false
entityUser, err = s.svc.Users.Create(entityUser)
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error creating user: %s", err.Error())}
}
} else {
if len(newEmail) > 0 && newEmail != entityUser.Email {
entityUser.Email = newEmail
}
if len(newPhoneNumber) > 0 && len(newPhoneNumber) == 10 && !strings.Contains(newPhoneNumber, "+1") {
newPhoneNumber = fmt.Sprintf("+1%s", newPhoneNumber)
}
if len(newPhoneNumber) > 0 && len(newPhoneNumber) == 12 && strings.Contains(newPhoneNumber, "+1") {
entityUser.PhoneNumber = newPhoneNumber
}
err = s.svc.Users.UpdateLogin(entityUser)
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error updating login data: %s", err.Error())}
}
}
address := entity.Address{}
address.AddressType = entity.Params{
Key: "home",
Name: "Home",
}
address.Name = fmt.Sprintf("%s, %s", header.N301, body.N401)
address.Address = fmt.Sprintf("%s, %s (%s)", header.N301, body.N401, zipCode)
address.CreatedUser = entityAuthorUser
address.User = entityUser
address.Origin = entity.Params{
Key: "provider",
Name: "Provider",
}
googleMapsAPI, err := maps.NewClient(maps.WithClientIDAndSignature("gme-bluecrossandblue1", "msqgD-jdqCyR0M_1u5C1HION5iI="))
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error instanciating google maps: %s", err.Error())}
}
r := &maps.GeocodingRequest{
Address: address.Address + " " + body.N402 + ", " + zipCode,
}
result, err := googleMapsAPI.Geocode(context.Background(), r)
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error getting geocode info: %s", err.Error())}
}
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 := s.svc.Users.RemoveAddress(a.UUID)
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error removing old address: %s", err.Error())}
}
}
}
}
address, err = s.svc.Users.SaveAddress(address)
if err != nil {
return viewmodel.User{}, &viewmodel.ValidationError{Message: fmt.Sprintf("Error saving new address: %s", err.Error())}
}
entityUser.Addresses = append(entityUser.Addresses, address)
return s.mapEntity.User.ToUserModel(entityUser), nil
} else {
return viewmodel.User{}, &viewmodel.ValidationError{Message: "No benefits found for the member"}
}
return viewmodel.User{}, nil
}

View File

@@ -13,16 +13,17 @@ var (
// Mapper has mapping methods to map entities to view models // Mapper has mapping methods to map entities to view models
type Mapper struct { type Mapper struct {
User *userMapping User *userMapping
Ride *rideMapping Ride *rideMapping
Visit *visitMapping Visit *visitMapping
Address *addressMapping Address *addressMapping
Provider *providerMapping Provider *providerMapping
Notification *notificationMapping Notification *notificationMapping
Profile *profileMapping Profile *profileMapping
Organization *organizationMapping Organization *organizationMapping
Zipcode *zipcodeMapping Zipcode *zipcodeMapping
Plan *planMapping Plan *planMapping
PasswordReset *passwordResetMapping
} }
// New returns an EntityMapper for fluent mapping // New returns an EntityMapper for fluent mapping
@@ -40,6 +41,7 @@ func New() *Mapper {
instance.Organization = &organizationMapping{instance} instance.Organization = &organizationMapping{instance}
instance.Zipcode = &zipcodeMapping{instance} instance.Zipcode = &zipcodeMapping{instance}
instance.Plan = &planMapping{instance} instance.Plan = &planMapping{instance}
instance.PasswordReset = &passwordResetMapping{instance}
}) })
return instance return instance

View File

@@ -0,0 +1,59 @@
package entitymapping
import (
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/domain/entity"
)
// zipcodeMapping has method to map zipcode entities to view models
type passwordResetMapping struct {
mapper *Mapper
}
// ToUserEntitySlice maps a User entity slice to User view model slice
func (mapping *passwordResetMapping) ToPasswordResetEntitySlice(list []viewmodel.PasswordReset) (retVal []entity.PasswordReset) {
retVal = make([]entity.PasswordReset, 0)
for _, item := range list {
retVal = append(retVal, mapping.ToPasswordResetEntity(item))
}
return retVal
}
func (mapping *passwordResetMapping) ToPasswordResetEntity(model viewmodel.PasswordReset) entity.PasswordReset {
return entity.PasswordReset{
ID: model.ID,
UUID: model.UUID,
User: mapping.mapper.User.ToUserEntity(model.User),
Token: model.Token,
Created: model.Created,
Expires: model.Expires,
Used: model.Used,
Opened: model.Opened,
}
}
// ToUserEntitySlice maps a User entity slice to User view model slice
func (mapping *passwordResetMapping) ToPasswordResetModelSlice(list []entity.PasswordReset) (retVal []viewmodel.PasswordReset) {
retVal = make([]viewmodel.PasswordReset, 0)
for _, item := range list {
retVal = append(retVal, mapping.ToPasswordResetModel(item))
}
return retVal
}
func (mapping *passwordResetMapping) ToPasswordResetModel(model entity.PasswordReset) viewmodel.PasswordReset {
return viewmodel.PasswordReset{
ID: model.ID,
UUID: model.UUID,
User: mapping.mapper.User.ToUserModel(model.User),
Token: model.Token,
Created: model.Created,
Expires: model.Expires,
Used: model.Used,
Opened: model.Opened,
}
}

View File

@@ -51,6 +51,7 @@ func (mapping *userMapping) ToUserModel(item entity.User) viewmodel.User {
Organizations: mapping.mapper.Organization.ToOrganizationModelSlice(item.Organizations), Organizations: mapping.mapper.Organization.ToOrganizationModelSlice(item.Organizations),
Types: mapping.mapper.Organization.ToOrganizationTypeModelSlice(item.Types), Types: mapping.mapper.Organization.ToOrganizationTypeModelSlice(item.Types),
Type: &item.Type, Type: &item.Type,
Test: item.Test,
} }
} }
@@ -78,6 +79,7 @@ func (mapping *userMapping) ToUserEntity(item viewmodel.User) entity.User {
Profiles: mapping.mapper.Profile.ToProfileEntitySlice(item.Profiles), Profiles: mapping.mapper.Profile.ToProfileEntitySlice(item.Profiles),
Organizations: mapping.mapper.Organization.ToOrganizationEntitySlice(item.Organizations), Organizations: mapping.mapper.Organization.ToOrganizationEntitySlice(item.Organizations),
Types: mapping.mapper.Organization.ToOrganizationTypeEntitySlice(item.Types), Types: mapping.mapper.Organization.ToOrganizationTypeEntitySlice(item.Types),
Test: item.Test,
} }
if item.Type == nil { if item.Type == nil {

View File

@@ -6,12 +6,13 @@ import (
) )
type Eligibility struct { type Eligibility struct {
TrackingID string `json:"tracking_id,omitempty"` TrackingID string `json:"tracking_id,omitempty"`
Payer Payer `json:"payer,omitempty"` Payer Payer `json:"payer,omitempty"`
Provider Provider `json:"provider,omitempty"` Provider Provider `json:"provider,omitempty"`
Subscriber Subscriber `json:"subscriber,omitempty"` Subscriber Subscriber `json:"subscriber,omitempty"`
ServiceInfo ServiceInfo `json:"service_info,omitempty"` ServiceInfo ServiceInfo `json:"service_info,omitempty"`
User User `json:"user,omitempty"` User User `json:"user,omitempty"`
RawProvider ProviderResponse `json:"raw_provider,omitempty"`
} }
type Payer struct { type Payer struct {

View File

@@ -0,0 +1,16 @@
package viewmodel
import (
"fmt"
"bitbucket.org/nemt/nemt-portal-api/infra/errors"
)
type ValidationError struct {
Errors []errors.ValidationError
Message string
}
func (ve *ValidationError) Error() string {
return fmt.Sprintf("Error: %s", ve.Message)
}

View File

@@ -0,0 +1,14 @@
package viewmodel
import "time"
type PasswordReset struct {
ID int64 `json:"-"`
UUID string `json:"uuid"`
User User `json:"user"`
Token string `json:"token"`
Created time.Time `json:"create_date"`
Expires time.Time `json:"expire_date"`
Used bool `json:"used"`
Opened bool `json:"opened"`
}

View File

@@ -26,6 +26,7 @@ type User struct {
Organizations []Organization `json:"organizations,omitempty"` Organizations []Organization `json:"organizations,omitempty"`
Provider *ProviderResp `json:"provider,omitempty"` Provider *ProviderResp `json:"provider,omitempty"`
Consent bool `json:"consent,omitempty"` Consent bool `json:"consent,omitempty"`
Test bool `json:"test,omitempty"`
} }
type Contact struct { type Contact struct {

View File

@@ -1,6 +1,7 @@
[app] [app]
name = "nemt" name = "nemt"
debug = true debug = true
disable-zip-validation = true
[app.docs] [app.docs]
swagger-path = "./static/swagger-ui" swagger-path = "./static/swagger-ui"

View File

@@ -1,6 +1,7 @@
[app] [app]
name = "nemt" name = "nemt"
debug = true debug = true
disable-zip-validation = true
[app.docs] [app.docs]
swagger-path = "/opt/app/nemt-portal-api/static/swagger-ui" swagger-path = "/opt/app/nemt-portal-api/static/swagger-ui"
@@ -9,7 +10,7 @@ debug = true
[db] [db]
host = "db01.cj5318jcaupw.us-east-2.rds.amazonaws.com" host = "db01.cj5318jcaupw.us-east-2.rds.amazonaws.com"
port = 3306 port = 3306
name = "nemt_clean" name = "nemt"
user = "nemt-user" user = "nemt-user"
pass = "OL&!n#p6J8Lu" pass = "OL&!n#p6J8Lu"
max-life-minutes = 5 max-life-minutes = 5
@@ -74,8 +75,8 @@ user-uuid = "11a49fa4-fbc7-4fe9-87fe-52a5bc3b71f8"
[bxe] [bxe]
url = "https://api-pve.bcbs.com/BXDirectConnect/V30" url = "https://api-pve.bcbs.com/BXDirectConnect/V30"
key = "dacg7jtsmb6ajr3z553jbczg" key = "c63z7gv9a5qz2zgx7zcm3253"
secret = "vFhNeWE8JdJpbDZQtcm6AHjX" secret = "hK4Yc9jtBrxaZTr4UU2VsfX9"
[blue365] [blue365]
url = "https://api-cloud.bcbs.com/blue365deals-stg/v1/validatePrefix/" url = "https://api-cloud.bcbs.com/blue365deals-stg/v1/validatePrefix/"

View File

@@ -1,6 +1,7 @@
[app] [app]
name = "nemt" name = "nemt"
debug = true debug = true
disable-zip-validation = true
[app.docs] [app.docs]
swagger-path = "/opt/app/nemt-portal-api/static/swagger-ui" swagger-path = "/opt/app/nemt-portal-api/static/swagger-ui"

View File

@@ -20,16 +20,17 @@ var (
// Conn is the MySQL connection manager // Conn is the MySQL connection manager
type Conn struct { type Conn struct {
db *sql.DB db *sql.DB
users *userRepo users *userRepo
rides *rideRepo rides *rideRepo
visit *visitRepo visit *visitRepo
provider *providerRepo provider *providerRepo
notification *notificationRepo notification *notificationRepo
profile *profileRepo profile *profileRepo
organization *organizationRepo organization *organizationRepo
zipcodes *zipcodeRepo zipcodes *zipcodeRepo
plan *planRepo plan *planRepo
passwordReset *passwordResetRepo
} }
// Begin starts a transaction // Begin starts a transaction
@@ -90,6 +91,10 @@ func (c *Conn) Plans() contract.PlanRepo {
return c.plan return c.plan
} }
func (c *Conn) PasswordReset() contract.PasswordResetRepo {
return c.passwordReset
}
// Instance returns an instance of a DataManager // Instance returns an instance of a DataManager
func Instance(cfg *config.Config) (contract.DataManager, error) { func Instance(cfg *config.Config) (contract.DataManager, error) {
once.Do(func() { once.Do(func() {
@@ -123,6 +128,7 @@ func Instance(cfg *config.Config) (contract.DataManager, error) {
instance.organization = newOrganizationRepo(db) instance.organization = newOrganizationRepo(db)
instance.zipcodes = newZipcodeRepo(db) instance.zipcodes = newZipcodeRepo(db)
instance.plan = newPlanRepo(db) instance.plan = newPlanRepo(db)
instance.passwordReset = newPasswordResetRepo(db)
}) })
return instance, connErr return instance, connErr

View File

@@ -0,0 +1,144 @@
package datamysql
import (
"database/sql"
"fmt"
"bitbucket.org/nemt/nemt-portal-api/domain/entity"
"bitbucket.org/nemt/nemt-portal-api/infra/errors"
uuid "github.com/satori/go.uuid"
)
// rideRepo maps methods to database
type passwordResetRepo struct {
conn executor
}
func newPasswordResetRepo(conn executor) *passwordResetRepo {
return &passwordResetRepo{
conn: conn,
}
}
func (c *passwordResetRepo) getQuery() string {
const (
query = `SELECT
a.password_reset_id,
a.password_reset_uuid,
a.user_id,
b.user_uuid,
a.token,
a.create_date,
a.expire_date,
(IFNULL(a.used, b'0') = b'1') used,
(IFNULL(a.opened, b'0') = b'1') opened
FROM
tab_password_reset a
INNER JOIN tab_user b
ON a.user_id = b.user_id`
)
return query
}
// parseSet parses a result set result to an entity array
func (c *passwordResetRepo) parseSet(rows *sql.Rows, err error) ([]entity.PasswordReset, error) {
if err != nil {
return nil, errors.Wrap(err)
}
result := make([]entity.PasswordReset, 0)
for rows.Next() {
entity, err := c.parseEntity(rows)
if err != nil {
return nil, errors.Wrap(err)
}
result = append(result, entity)
}
return result, nil
}
// parseEntity parses a result to an entity
func (c *passwordResetRepo) parseEntity(row scanner) (retVal entity.PasswordReset, err error) {
err = row.Scan(
&retVal.ID, &retVal.UUID, &retVal.User.ID, &retVal.User.UUID, &retVal.Token, &retVal.Created, &retVal.Expires, &retVal.Used, &retVal.Opened)
return retVal, errors.Wrap(err)
}
func (c *passwordResetRepo) GetAll() ([]entity.PasswordReset, error) {
return c.parseSet(c.conn.Query(c.getQuery()))
}
func (c *passwordResetRepo) CreatePasswordResetEntry(passwordResetEntry entity.PasswordReset) (entity.PasswordReset, error) {
const (
insertQuery = `INSERT INTO tab_password_reset(password_reset_uuid, user_id, token, expire_date, used, opened) VALUES(?, ?, ?, ?, 0, 0);`
)
retVal := passwordResetEntry
guid, _ := uuid.NewV4()
userRepo := newUserRepo(c.conn)
fullUser, err := userRepo.GetByEmail(passwordResetEntry.User.Email)
if err != nil {
return retVal, err
}
if fullUser.Email != passwordResetEntry.User.Email {
return retVal, fmt.Errorf("User not found")
}
results, err := c.conn.Exec(insertQuery, guid, fullUser.ID, passwordResetEntry.Token, passwordResetEntry.Expires)
if err != nil {
return retVal, err
}
retVal.ID, err = results.LastInsertId()
if err != nil {
return retVal, err
}
return c.GetByID(retVal.ID)
}
func (c *passwordResetRepo) GetByID(ID int64) (entity.PasswordReset, error) {
return c.parseEntity(c.conn.QueryRow(c.getQuery()+" WHERE a.password_reset_id = ?; ", ID))
}
func (c *passwordResetRepo) GetByToken(token string) (entity.PasswordReset, error) {
return c.parseEntity(c.conn.QueryRow(c.getQuery()+" WHERE a.token = ? AND a.used = 0; ", token))
}
func (c *passwordResetRepo) SetTokenOpened(token string) error {
const (
query = `UPDATE tab_password_reset a
SET a.opened = 1
WHERE a.token = ? AND a.used = 0 AND a.expire_date > CURRENT_TIMESTAMP`
)
result, err := c.conn.Exec(query, token)
if err != nil {
return err
}
if updateCount, err := result.RowsAffected(); err != nil || updateCount == 0 {
return fmt.Errorf("Invalid token")
}
return nil
}
func (c *passwordResetRepo) SetTokenUsed(token string) error {
const (
query = `UPDATE tab_password_reset a
SET a.opened = 1,
a.used = 1
WHERE a.token = ? AND a.used = 0`
)
if _, err := c.conn.Exec(query, token); err != nil {
return err
}
return nil
}

View File

@@ -8,16 +8,17 @@ import (
) )
type transaction struct { type transaction struct {
tx *sql.Tx tx *sql.Tx
users *userRepo users *userRepo
rides *rideRepo rides *rideRepo
visits *visitRepo visits *visitRepo
provider *providerRepo provider *providerRepo
notification *notificationRepo notification *notificationRepo
profile *profileRepo profile *profileRepo
organization *organizationRepo organization *organizationRepo
zipcodes *zipcodeRepo zipcodes *zipcodeRepo
plan *planRepo plan *planRepo
passwordReset *passwordResetRepo
} }
func newTransaction(tx *sql.Tx) *transaction { func newTransaction(tx *sql.Tx) *transaction {
@@ -34,6 +35,7 @@ func newTransaction(tx *sql.Tx) *transaction {
t.organization = newOrganizationRepo(tx) t.organization = newOrganizationRepo(tx)
t.zipcodes = newZipcodeRepo(tx) t.zipcodes = newZipcodeRepo(tx)
t.plan = newPlanRepo(tx) t.plan = newPlanRepo(tx)
t.passwordReset = newPasswordResetRepo(tx)
return t return t
} }
@@ -81,6 +83,10 @@ func (t transaction) Plans() contract.PlanRepo {
return t.plan return t.plan
} }
func (t transaction) PasswordReset() contract.PasswordResetRepo {
return t.passwordReset
}
func (t *transaction) Commit() error { func (t *transaction) Commit() error {
err := t.tx.Commit() err := t.tx.Commit()

View File

@@ -48,6 +48,32 @@ func (c *userRepo) GetByMemberID(memberID string) (entity.User, error) {
} }
} }
func (c *userRepo) GetByEmail(email string) (entity.User, error) {
finalQuery := c.getQuery() + " AND b.email = ?"
user, err := c.parseSet(c.conn.Query(finalQuery, email))
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) { func (c *userRepo) GetByUUID(uuid string, profile string) (entity.User, error) {
params := make([]interface{}, 0) params := make([]interface{}, 0)
params = append(params, uuid) params = append(params, uuid)
@@ -192,7 +218,7 @@ func (c *userRepo) parseEntity(row scanner) (retVal entity.User, err error) {
err = row.Scan(&retVal.ID, &retVal.UUID, &retVal.Name, &retVal.Member, &birthDate, &retVal.LoginID, &retVal.LoginUUID, &retVal.Email, &retVal.PhoneNumber, &retVal.LoginKey, &retVal.Gender, &retVal.Active, &retVal.Created, &retVal.Updated, &profile.ID, &profile.Name, &profile.Key, &profile.Active, &profile.Blocked, &profile.Suspended, &profile.Created, &profile.Updated, &profile.Organization.ID, &profile.Organization.UUID, &profile.Organization.Type.ID, &profile.Organization.Type.Name, &profile.Organization.Type.Key, &profile.Organization.Type.Description, &profile.Organization.Name, &profile.Organization.Description, &profile.Organization.ReferenceID, &profile.Organization.ParentID, &profile.Organization.Main, err = row.Scan(&retVal.ID, &retVal.UUID, &retVal.Name, &retVal.Member, &birthDate, &retVal.LoginID, &retVal.LoginUUID, &retVal.Email, &retVal.PhoneNumber, &retVal.LoginKey, &retVal.Gender, &retVal.Active, &retVal.Created, &retVal.Updated, &profile.ID, &profile.Name, &profile.Key, &profile.Active, &profile.Blocked, &profile.Suspended, &profile.Created, &profile.Updated, &profile.Organization.ID, &profile.Organization.UUID, &profile.Organization.Type.ID, &profile.Organization.Type.Name, &profile.Organization.Type.Key, &profile.Organization.Type.Description, &profile.Organization.Name, &profile.Organization.Description, &profile.Organization.ReferenceID, &profile.Organization.ParentID, &profile.Organization.Main,
&homeAddress.ID, &homeAddress.UUID, &homeAddress.AddressType.ID, &homeAddress.AddressType.Key, &homeAddress.AddressType.Name, &homeAddress.Name, &homeAddress.Address, &homeAddress.Latitude, &homeAddress.Longitude, &homeAddress.ID, &homeAddress.UUID, &homeAddress.AddressType.ID, &homeAddress.AddressType.Key, &homeAddress.AddressType.Name, &homeAddress.Name, &homeAddress.Address, &homeAddress.Latitude, &homeAddress.Longitude,
&workAddress.ID, &workAddress.UUID, &workAddress.AddressType.ID, &workAddress.AddressType.Key, &workAddress.AddressType.Name, &workAddress.Name, &workAddress.Address, &workAddress.Latitude, &workAddress.Longitude, &retVal.Type) &workAddress.ID, &workAddress.UUID, &workAddress.AddressType.ID, &workAddress.AddressType.Key, &workAddress.AddressType.Name, &workAddress.Name, &workAddress.Address, &workAddress.Latitude, &workAddress.Longitude, &retVal.Type, &retVal.Test)
if err != nil { if err != nil {
if err != sql.ErrNoRows { if err != sql.ErrNoRows {
return retVal, errors.Wrap(err) return retVal, errors.Wrap(err)
@@ -278,7 +304,8 @@ func (c *userRepo) getQuery() string {
IFNULL(i.address, '') work_address, IFNULL(i.address, '') work_address,
IFNULL(i.lat, 0) work_lat, IFNULL(i.lat, 0) work_lat,
IFNULL(i.long, 0) work_long, IFNULL(i.long, 0) work_long,
IFNULL(a.member_type, 'S') member_type IFNULL(a.member_type, 'S') member_type,
user_test
FROM FROM
tab_user a tab_user a
INNER JOIN INNER JOIN
@@ -587,11 +614,11 @@ func (c *userRepo) createLogin(user entity.User) (int64, string, error) {
func (c *userRepo) createUser(user entity.User) (int64, string, error) { func (c *userRepo) createUser(user entity.User) (int64, string, error) {
const ( const (
query = "INSERT INTO tab_user(user_uuid, `name`, subscriber_id, birth_date, gender, member_type, user_hash) VALUES(?, ?, ?, ?, ?, ?, SHA2(CONCAT_WS('-',?,?,?,?,?), 512)) ON DUPLICATE KEY UPDATE user_uuid = ?, `name` = ?, subscriber_id = ?, birth_date = ?, gender = ?;" query = "INSERT INTO tab_user(user_uuid, `name`, subscriber_id, birth_date, gender, member_type, user_hash, user_test) VALUES(?, ?, ?, ?, ?, ?, SHA2(CONCAT_WS('-',?,?,?,?,?), 512), ?) ON DUPLICATE KEY UPDATE user_uuid = ?, `name` = ?, subscriber_id = ?, birth_date = ?, gender = ?, user_test = ?;"
) )
guid, _ := uuid.NewV4() guid, _ := uuid.NewV4()
result, err := c.conn.Exec(query, guid.String(), user.Name, toNullString(user.Member), toNullTime(user.BirthDate), toNullString(user.Gender), user.Type, user.Name, toNullString(user.Member), toNullTime(user.BirthDate), toNullString(user.Gender), user.Type, guid.String(), user.Name, toNullString(user.Member), toNullTime(user.BirthDate), toNullString(user.Gender)) result, err := c.conn.Exec(query, guid.String(), user.Name, toNullString(user.Member), toNullTime(user.BirthDate), toNullString(user.Gender), user.Type, user.Name, toNullString(user.Member), toNullTime(user.BirthDate), toNullString(user.Gender), user.Type, user.Test, guid.String(), user.Name, toNullString(user.Member), toNullTime(user.BirthDate), toNullString(user.Gender), user.Test)
if err != nil { if err != nil {
return 0, "", err return 0, "", err
} }

View File

@@ -14,6 +14,7 @@ type repoManager interface {
Organization() OrganizationRepo Organization() OrganizationRepo
Zipcodes() ZipcodeRepo Zipcodes() ZipcodeRepo
Plans() PlanRepo Plans() PlanRepo
PasswordReset() PasswordResetRepo
} }
// UserRepo defines the data set for users // UserRepo defines the data set for users
@@ -22,6 +23,7 @@ type UserRepo interface {
GetByID(userID int64) (retVal entity.User, err error) GetByID(userID int64) (retVal entity.User, err error)
GetByUUID(uuid string, profile string) (entity.User, error) GetByUUID(uuid string, profile string) (entity.User, error)
GetByMemberID(memberID string) (entity.User, error) GetByMemberID(memberID string) (entity.User, error)
GetByEmail(email string) (entity.User, error)
Login(email string, pass string) (entity.User, error) Login(email string, pass string) (entity.User, error)
FullLogin(loginType string, key string, pass string, profile string) (entity.User, error) FullLogin(loginType string, key string, pass string, profile string) (entity.User, error)
Create(user entity.User) (entity.User, error) Create(user entity.User) (entity.User, error)
@@ -124,3 +126,12 @@ type ZipcodeRepo interface {
GetAll() ([]entity.Zipcode, error) GetAll() ([]entity.Zipcode, error)
GetByParticipatingZipcode(zipcode string) (entity.Zipcode, error) GetByParticipatingZipcode(zipcode string) (entity.Zipcode, error)
} }
type PasswordResetRepo interface {
GetAll() ([]entity.PasswordReset, error)
CreatePasswordResetEntry(passwordResetEntry entity.PasswordReset) (entity.PasswordReset, error)
GetByID(ID int64) (entity.PasswordReset, error)
GetByToken(token string) (entity.PasswordReset, error)
SetTokenOpened(token string) error
SetTokenUsed(token string) error
}

View File

@@ -0,0 +1,14 @@
package entity
import "time"
type PasswordReset struct {
ID int64 `db:"password_reset_id" json:"-"`
UUID string `db:"password_reset_uuid" json:"uuid"`
User User `db:"-" json:"user"`
Token string `db:"token" json:"token"`
Created time.Time `db:"create_date" json:"create_date"`
Expires time.Time `db:"expire_date" json:"expire_date"`
Used bool `db:"used" json:"used"`
Opened bool `db:"opend" json:"opened"`
}

View File

@@ -31,6 +31,7 @@ type User struct {
Types []OrganizationType `json:"types,omitempty"` Types []OrganizationType `json:"types,omitempty"`
Organizations []Organization `json:"organizations,omitempty"` Organizations []Organization `json:"organizations,omitempty"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
Test bool `json:"test,omitempty"`
} }
type ContactInfo struct { type ContactInfo struct {

View File

@@ -0,0 +1,41 @@
package service
import (
"bitbucket.org/nemt/nemt-portal-api/domain/entity"
)
// userService is the domain service for user operations
type passwordResetService struct {
svc *Service
}
// newUserService returns an instance of userService
func newPasswordResetService(svc *Service) *passwordResetService {
return &passwordResetService{
svc: svc,
}
}
func (s *passwordResetService) GetAll() ([]entity.PasswordReset, error) {
return s.svc.db.PasswordReset().GetAll()
}
func (s *passwordResetService) GetByID(ID int64) (entity.PasswordReset, error) {
return s.svc.db.PasswordReset().GetByID(ID)
}
func (s *passwordResetService) GetByToken(token string) (entity.PasswordReset, error) {
return s.svc.db.PasswordReset().GetByToken(token)
}
func (s *passwordResetService) CreatePasswordResetEntry(passwordResetEntry entity.PasswordReset) (entity.PasswordReset, error) {
return s.svc.db.PasswordReset().CreatePasswordResetEntry(passwordResetEntry)
}
func (s *passwordResetService) SetTokenOpened(token string) error {
return s.svc.db.PasswordReset().SetTokenOpened(token)
}
func (s *passwordResetService) SetTokenUsed(token string) error {
return s.svc.db.PasswordReset().SetTokenUsed(token)
}

View File

@@ -15,18 +15,19 @@ var (
// Service holds the domain service repositories // Service holds the domain service repositories
type Service struct { type Service struct {
db contract.DataManager db contract.DataManager
cache contract.CacheManager cache contract.CacheManager
tnc contract.TNCManager tnc contract.TNCManager
Users *userService Users *userService
Rides *rideService Rides *rideService
Visits *visitService Visits *visitService
Provider *providerService Provider *providerService
Notification *notificationService Notification *notificationService
Profile *profileService Profile *profileService
Organization *organizationService Organization *organizationService
Zipcodes *zipcodeService Zipcodes *zipcodeService
Plans *planService Plans *planService
PasswordReset *passwordResetService
} }
// New returns a new domain Service instance // New returns a new domain Service instance
@@ -43,6 +44,7 @@ func New(db contract.DataManager, cache contract.CacheManager, cfg *config.Confi
instance.Organization = newOrganizationService(instance) instance.Organization = newOrganizationService(instance)
instance.Zipcodes = newZipcodeService(instance) instance.Zipcodes = newZipcodeService(instance)
instance.Plans = newPlanService(instance) instance.Plans = newPlanService(instance)
instance.PasswordReset = newPasswordResetService(instance)
}) })
return instance, nil return instance, nil

View File

@@ -37,6 +37,10 @@ func (s *userService) GetByMemberID(memberID string) (entity.User, error) {
return s.svc.db.Users().GetByMemberID(memberID) return s.svc.db.Users().GetByMemberID(memberID)
} }
func (s *userService) GetByEmail(email string) (entity.User, error) {
return s.svc.db.Users().GetByEmail(email)
}
// Login returns a specific user by email and pass // Login returns a specific user by email and pass
func (s *userService) Login(email string, pass string) (entity.User, error) { func (s *userService) Login(email string, pass string) (entity.User, error) {
return s.svc.db.Users().Login(email, pass) return s.svc.db.Users().Login(email, pass)
@@ -72,7 +76,7 @@ func (s *userService) CreateBulk(users []entity.User) ([]entity.User, error) {
return users, nil return users, nil
} }
func (s *userService) UpdateLogin(user entity.User) error { func (s *userService) UpdateLogin(user entity.User) error {
return s.svc.db.Users().UpdateLogin(user) return s.svc.db.Users().UpdateLogin(user)
} }

View File

@@ -25,14 +25,15 @@ type Config struct {
Blue365 Blue365Config Blue365 Blue365Config
Email EmailConfig Email EmailConfig
GoogleShortener GoogleShortenerConfig GoogleShortener GoogleShortenerConfig
Eligibility EligibilityConfig Eligibility EligibilityConfig
} }
// AppConfig represents the configuration values about the application. // AppConfig represents the configuration values about the application.
type AppConfig struct { type AppConfig struct {
Name string Name string
Debug bool Debug bool
Docs DocsConfig Docs DocsConfig
DisableZipValidation bool
} }
// TwilioConfig represents the configuration values about the twilio. // TwilioConfig represents the configuration values about the twilio.
@@ -139,7 +140,7 @@ type GoogleShortenerConfig struct {
} }
type EligibilityConfig struct { type EligibilityConfig struct {
Url string Url string
} }
// Read returns the configuration values, // Read returns the configuration values,
@@ -160,8 +161,9 @@ func Read() (*Config, error) {
return &Config{ return &Config{
App: AppConfig{ App: AppConfig{
Name: viper.GetString("app.name"), Name: viper.GetString("app.name"),
Debug: viper.GetBool("app.debug"), Debug: viper.GetBool("app.debug"),
DisableZipValidation: viper.GetBool("app.disable-zip-validation"),
Docs: DocsConfig{ Docs: DocsConfig{
YAMLPath: viper.GetString("app.docs.yaml-path"), YAMLPath: viper.GetString("app.docs.yaml-path"),
SwaggerPath: viper.GetString("app.docs.swagger-path"), SwaggerPath: viper.GetString("app.docs.swagger-path"),
@@ -243,8 +245,8 @@ func Read() (*Config, error) {
ClientID: viper.GetString("google-shortener.client-id"), ClientID: viper.GetString("google-shortener.client-id"),
SecretKey: viper.GetString("google-shortener.secret-key"), SecretKey: viper.GetString("google-shortener.secret-key"),
}, },
Eligibility : EligibilityConfig{ Eligibility: EligibilityConfig{
Url : viper.GetString("eligibility.url"), Url: viper.GetString("eligibility.url"),
}, },
}, nil }, nil
} }

View File

@@ -1,12 +1,8 @@
package eligibilityroute package eligibilityroute
import ( import (
"context"
"encoding/xml"
"fmt" "fmt"
"html"
"math/rand" "math/rand"
"strings"
"sync" "sync"
"time" "time"
@@ -16,9 +12,7 @@ import (
"bitbucket.org/nemt/nemt-portal-api/infra/auth" "bitbucket.org/nemt/nemt-portal-api/infra/auth"
"bitbucket.org/nemt/nemt-portal-api/infra/config" "bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils" "bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"bitbucket.org/nemt/nemt-portal-api/server/validation"
"github.com/labstack/echo" "github.com/labstack/echo"
"googlemaps.github.io/maps"
) )
var ( var (
@@ -78,185 +72,53 @@ func (c *controller) handleEligibility(ctx echo.Context) error {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
if validationErrors := validation.ValidateEligibility(&eligibility.User); len(validationErrors) > 0 { var provider viewmodel.ProviderResp
return routeutils.ResponseAPICustomValidationError(ctx, "eligibility validation failed", validationErrors) provider, err = c.svc.Provider.GetByNPI(eligibility.RawProvider.FivePartKeyGroups[0].ProviderNum, authUser)
}
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 { if err != nil {
fmt.Println("Error Here: ", err.Error())
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
if ret.QueryResult.QueryResultSummary.BenefitsFound { if provider.ProviderUUID == "" {
xmlString := html.UnescapeString(ret.QueryResult.HIPPA271.T271) org := viewmodel.Organization{
xmlReader := strings.NewReader(xmlString) Type: viewmodel.OrganizationType{
Key: "provider",
var f viewmodel.Interchange271 Name: "Provider",
err = xml.NewDecoder(xmlReader).Decode(&f) },
if err != nil { Name: eligibility.RawProvider.OrgName,
fmt.Println("Error to unmarshal: ", err.Error()) Description: eligibility.RawProvider.OrgName,
Main: false,
Author: authUser,
LastEditor: authUser,
Reference: eligibility.RawProvider,
} }
user, err := c.svc.Users.GetByMemberID(eligibility.Subscriber.SubscriberID) org, err = c.svc.Organization.AddOrganization(org, authUser)
if err != nil { if err != nil {
fmt.Println(err)
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
if user.ID == "" { provider, err = c.svc.Provider.GetByOrganization(org.UUID, authUser)
user.Pass = c.generatePassword(8) if err != nil {
user.BirthDate = &eligibility.Subscriber.DemographicInfo.DateOfBirth return routeutils.HandleAPIError(ctx, err)
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") user, err := c.svc.Users.CheckAndCreateMember(eligibility.User, provider, authUser)
if err != nil { if err != nil {
return routeutils.HandleAPIError(ctx, err) if validationError, ok := err.(*viewmodel.ValidationError); ok {
} if len(validationError.Errors) > 0 {
user.Profiles = append(user.Profiles, profile) return routeutils.ResponseAPICustomValidationError(ctx, validationError.Message, validationError.Errors)
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
} else { } else {
return routeutils.ResponseAPIFieldValidationError(ctx, "phonenumber", "Phone number is required") return routeutils.ResponseAPIServiceError(ctx, validationError.Message)
}
user, err = c.svc.Users.Create(user, authUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
} }
} else { } 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
}
if len(*eligibility.User.PhoneNumber) > 0 {
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) return routeutils.HandleAPIError(ctx, err)
} }
}
r := &maps.GeocodingRequest{ if authUser.Profiles[0].Key == "VIRPT" {
Address: address.Address + " " + body.N402 + ", " + zipCode, return ctx.JSON(204, nil)
}
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 { } else {
return routeutils.ResponseAPINotEligibleWithMessageError(ctx, "No benefits found for member") return ctx.JSON(200, user)
} }
} }

View File

@@ -63,12 +63,17 @@ func (c *controller) handleCancel(ctx echo.Context) error {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
authUser, err := c.svc.Users.GetByUUID(ride.CreatedUser.ID, "")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
lyftRide := viewmodel.RideRequest{RideID: ride.InternalID} lyftRide := viewmodel.RideRequest{RideID: ride.InternalID}
if ride.Status.Key == "scheduled" && !strings.Contains(ride.InternalID, "s_") { if ride.Status.Key == "scheduled" && !strings.Contains(ride.InternalID, "s_") {
lyftRide.RideID = "s_" + ride.InternalID lyftRide.RideID = "s_" + ride.InternalID
} }
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID { if authUser.Test {
if err = c.tnc.Lyft.CancelRide(lyftRide); err != nil { if err = c.tnc.Lyft.CancelRide(lyftRide); err != nil {
if err.Error() != "ride_not_found" { if err.Error() != "ride_not_found" {
fmt.Println("Error to cancel with Lyft: ", err.Error()) fmt.Println("Error to cancel with Lyft: ", err.Error())
@@ -144,13 +149,18 @@ func (c *controller) handle(ctx echo.Context) error {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
authUser, err := c.svc.Users.GetByUUID(ride.CreatedUser.ID, "")
if err != nil {
fmt.Println("Error: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
if ride.Status.Key == "accepted" || ride.Status.Key == "pickedUp" || ride.Status.Key == "arrived" { if ride.Status.Key == "accepted" || ride.Status.Key == "pickedUp" || ride.Status.Key == "arrived" {
var lyftRide viewmodel.RideRequest var lyftRide viewmodel.RideRequest
var err error var err error
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID { if authUser.Test {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID}) lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
} else { } else {
fmt.Println("In Production")
lyftRide, err = c.tnc.LyftProd.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID}) lyftRide, err = c.tnc.LyftProd.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
} }
if err != nil { if err != nil {
@@ -236,7 +246,7 @@ func (c *controller) handleReady(ctx echo.Context) error {
} }
var lyftRide viewmodel.RideRequest var lyftRide viewmodel.RideRequest
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID { if authUser.Test {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID}) lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
} else { } else {
fmt.Println("In Production") fmt.Println("In Production")
@@ -258,7 +268,7 @@ func (c *controller) handleReady(ctx echo.Context) error {
lyftRide.Passenger.PhoneNumber = *nextRide.User.PhoneNumber lyftRide.Passenger.PhoneNumber = *nextRide.User.PhoneNumber
lyftRide.RideType = "lyft" lyftRide.RideType = "lyft"
if c.cfg.LyftProd.UserUUID != nextRide.CreatedUser.ID { if authUser.Test {
lyftRide, err = c.tnc.Lyft.RequestRide(lyftRide) lyftRide, err = c.tnc.Lyft.RequestRide(lyftRide)
} else { } else {
fmt.Println("In Production") fmt.Println("In Production")

View File

@@ -57,8 +57,14 @@ func (c *controller) handleStateChange(ctx echo.Context) error {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
authUser, err := c.svc.Users.GetByUUID(ride.CreatedUser.ID, "")
if err != nil {
fmt.Println("Error: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
// var lyftRide viewmodel.RideRequest // var lyftRide viewmodel.RideRequest
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID { if authUser.Test {
_, err = c.tnc.Lyft.GetRideStatus(viewmodel.RideRequest{RideID: ride.InternalID}, status) _, err = c.tnc.Lyft.GetRideStatus(viewmodel.RideRequest{RideID: ride.InternalID}, status)
go func() { go func() {
secondCall := func() { secondCall := func() {

View File

@@ -0,0 +1,168 @@
package passwordresetroute
import (
"crypto/sha256"
"fmt"
"math/rand"
"strings"
"sync"
"time"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"github.com/labstack/echo"
)
const (
tokenExpirationTime = 90 // in minutes
randomStringLength = 15
baseURL = "http://localhost:5000"
passwordResetEmailSubject = "Reset Your Password"
passwordResetEmailMainBody = "To reset your password click here or copy the following link and paste it into your browser: \n\n " + baseURL + "/#/reset-password/"
passwordResetEmailFooter = "\nThis link expires in 90 minutes"
passwordResetCompleteEmailSubject = "Your Password Has been Reset"
passwordResetCompleteEmailBody = "Your password has been reset. To login click here or copy the following link and paste it into your browser: \n\n" + baseURL + "/#/login"
)
var (
instance *controller
once sync.Once
)
type controller struct {
svc *applicationservice.Service
cfg *config.Config
}
func controllerInstance(svc *applicationservice.Service, cfg *config.Config) *controller {
once.Do(func() {
instance = &controller{
svc: svc,
cfg: cfg,
}
rand.Seed(time.Now().UTC().UnixNano())
})
return instance
}
func (c *controller) handleResetRequest(ctx echo.Context) error {
userEmail, err := routeutils.GetAndValidateStringParam(ctx, "email", "mandatory field")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
//find if user with email exists
user, err := c.svc.Users.GetByEmail(userEmail)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if user.Email == nil || (*user.Email != userEmail) {
return routeutils.ResponseAPIOK(ctx, nil) //more secure, don't inform user (attacker) that email doesn't exists
}
//create and store reset token
timeNow := time.Now()
expirationTime := timeNow.Add(time.Minute * tokenExpirationTime)
randomArray := make([]byte, randomStringLength)
rand.Read(randomArray)
token := fmt.Sprintf("%x", sha256.Sum256(randomArray))
passwordResetEntry := viewmodel.PasswordReset{
User: user,
Token: token,
Expires: expirationTime,
Opened: false,
Used: false,
}
_, err = c.svc.PasswordReset.CreatePasswordResetEntry(passwordResetEntry)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
//Send email with reset link
notification := viewmodel.Notification{
Type: applicationservice.NotificationTypeEmail,
From: c.cfg.Email.Sender,
To: *user.Email,
Subject: passwordResetEmailSubject,
Message: passwordResetEmailMainBody + token + passwordResetEmailFooter,
}
notification, err = c.svc.Notification.SendNotificationWithoutWritingToDatabase(notification)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, nil)
}
func (c *controller) handleResetComplete(ctx echo.Context) error {
userToken, err := routeutils.GetAndValidateStringParam(ctx, "token", "mandatory field")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
var user viewmodel.User
if err = ctx.Bind(&user); err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if len(strings.TrimSpace(user.Pass)) < 1 {
routeutils.ResponseAPIPasswordResetFailed(ctx, "No password")
}
passwordResetEntry, err := c.svc.PasswordReset.GetByToken(userToken)
if err != nil || len(passwordResetEntry.Token) < 1 || passwordResetEntry.Expires.Before(time.Now()) || passwordResetEntry.Used == true {
routeutils.ResponseAPIPasswordResetFailed(ctx, "Token error")
}
fullUserData, err := c.svc.Users.GetByUUID(passwordResetEntry.User.ID, "")
if err != nil {
routeutils.ResponseAPIPasswordResetFailed(ctx, "User problem")
}
fmt.Println(fullUserData)
//write new password in database
//TODO
if err := c.svc.PasswordReset.SetTokenUsed(userToken); err != nil {
routeutils.ResponseAPIPasswordResetFailed(ctx, "Reset failed")
}
//Send email with reset link
notification := viewmodel.Notification{
Type: applicationservice.NotificationTypeEmail,
From: c.cfg.Email.Sender,
To: *user.Email,
Subject: passwordResetCompleteEmailSubject,
Message: passwordResetCompleteEmailBody,
}
notification, err = c.svc.Notification.SendNotificationWithoutWritingToDatabase(notification)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, nil)
}
func (c *controller) handleTokenOpen(ctx echo.Context) error {
token, err := routeutils.GetAndValidateStringParam(ctx, "token", "mandatory field")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if err := c.svc.PasswordReset.SetTokenOpened(token); err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, nil)
}

View File

@@ -0,0 +1,21 @@
package passwordresetroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
resetRequest = "/request/:email"
resetComplete = "/complete/:token"
tokenOpen = "/open/:token"
)
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service) {
ctrl := controllerInstance(svc, cfg)
r.POST(resetRequest, ctrl.handleResetRequest)
r.POST(resetComplete, ctrl.handleResetComplete)
r.POST(tokenOpen, ctrl.handleTokenOpen)
}

View File

@@ -13,6 +13,7 @@ import (
"bitbucket.org/nemt/nemt-portal-api/server/router/lyfthookroute" "bitbucket.org/nemt/nemt-portal-api/server/router/lyfthookroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/notificationroute" "bitbucket.org/nemt/nemt-portal-api/server/router/notificationroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/organizationroute" "bitbucket.org/nemt/nemt-portal-api/server/router/organizationroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/passwordresetroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/placesroute" "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/profileroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/providerroute" "bitbucket.org/nemt/nemt-portal-api/server/router/providerroute"
@@ -39,6 +40,7 @@ func Register(e *echo.Echo, cfg *config.Config, svc *applicationservice.Service,
externalroute.Register(prefixGroup.Group("/ext"), cfg, svc, tnc, notification) externalroute.Register(prefixGroup.Group("/ext"), cfg, svc, tnc, notification)
authenticateroute.Register(prefixGroup.Group("/authenticate"), cfg, svc) authenticateroute.Register(prefixGroup.Group("/authenticate"), cfg, svc)
selfregisterroute.Register(prefixGroup.Group("/selfregister"), cfg, svc) selfregisterroute.Register(prefixGroup.Group("/selfregister"), cfg, svc)
passwordresetroute.Register(prefixGroup.Group("/passwordreset"), cfg, svc)
appGroup := prefixGroup.Group("/" + cfg.App.Name) appGroup := prefixGroup.Group("/" + cfg.App.Name)
usersroute.Register(appGroup.Group("/users"), cfg, svc) usersroute.Register(appGroup.Group("/users"), cfg, svc)

View File

@@ -102,6 +102,11 @@ func ResponseAPINotEligibleWithMessageError(c echo.Context, message string) erro
return ResponseAPIError(c, http.StatusForbidden, message, false) return ResponseAPIError(c, http.StatusForbidden, message, false)
} }
//ResponseAPIPasswordResetFailed returns a standard API error when password reset fails
func ResponseAPIPasswordResetFailed(c echo.Context, message string) error {
return ResponseAPIError(c, http.StatusForbidden, message, false)
}
func ignoreDefaultWrappedErrors(c echo.Context, errorToHandle *errors.WrappedError, handler func(echo.Context, error) error) error { func ignoreDefaultWrappedErrors(c echo.Context, errorToHandle *errors.WrappedError, handler func(echo.Context, error) error) error {
err := errorToHandle.GetOriginalError() err := errorToHandle.GetOriginalError()

View File

@@ -2,6 +2,7 @@ package selfregisterroute
import ( import (
"fmt" "fmt"
"strings"
"sync" "sync"
b64 "encoding/base64" b64 "encoding/base64"
@@ -22,6 +23,10 @@ const (
notificationSmsBody = "You have registered as a Visit Reporter for CHM NEMT. Login: https://portal.bcbsinstitute.com Reset PW: https://portal.bcbsinstitute.com/forgot" notificationSmsBody = "You have registered as a Visit Reporter for CHM NEMT. Login: https://portal.bcbsinstitute.com Reset PW: https://portal.bcbsinstitute.com/forgot"
) )
const (
phoneNumberMaxLength = 12
)
var ( var (
instance *controller instance *controller
once sync.Once once sync.Once
@@ -44,12 +49,34 @@ func controllerInstance(svc *applicationservice.Service, cfg *config.Config) *co
return instance return instance
} }
func removeNonNumberChars(input string) string {
result := ""
for _, char := range input {
if char >= '0' && char <= '9' {
result += string(char)
}
}
return result
}
func (c *controller) handle(ctx echo.Context) error { func (c *controller) handle(ctx echo.Context) error {
var user viewmodel.User var user viewmodel.User
if err := ctx.Bind(&user); err != nil { if err := ctx.Bind(&user); err != nil {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
//format phone number - max length in database is 12 chars
formatedPhoneNumber := strings.TrimSpace(*user.PhoneNumber)
formatedPhoneNumber = strings.Replace(formatedPhoneNumber, "+1", "", -1)
formatedPhoneNumber = removeNonNumberChars(formatedPhoneNumber)
if len(formatedPhoneNumber) > phoneNumberMaxLength {
formatedPhoneNumber = formatedPhoneNumber[:phoneNumberMaxLength]
}
*user.PhoneNumber = formatedPhoneNumber
authUser, err := c.svc.Users.GetByUUID("573c70ff-733d-11e7-ba8f-0a6ad3fcdeaa", "") authUser, err := c.svc.Users.GetByUUID("573c70ff-733d-11e7-ba8f-0a6ad3fcdeaa", "")
if err != nil { if err != nil {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
@@ -132,10 +159,14 @@ func (c *controller) handle(ctx echo.Context) error {
notification, err = c.svc.Notification.SendNotificationWithoutWritingToDatabase(notification) notification, err = c.svc.Notification.SendNotificationWithoutWritingToDatabase(notification)
if err != nil { if err != nil {
return routeutils.HandleAPIError(ctx, err) logger := ctx.Logger()
logger.Warnf("Application Error: Could not send email notification to user email : %s", *user.Email)
} }
//Send sms notification to Authorized user //Send sms notification to Authorized user
formatedPhoneNumber = *user.PhoneNumber
notification = viewmodel.Notification{ notification = viewmodel.Notification{
Type: applicationservice.NOtificationTypeSMS, Type: applicationservice.NOtificationTypeSMS,
To: *user.PhoneNumber, To: *user.PhoneNumber,
@@ -144,7 +175,8 @@ func (c *controller) handle(ctx echo.Context) error {
notification, err = c.svc.Notification.SendNotificationWithoutWritingToDatabase(notification) notification, err = c.svc.Notification.SendNotificationWithoutWritingToDatabase(notification)
if err != nil { if err != nil {
return routeutils.HandleAPIError(ctx, err) logger := ctx.Logger()
logger.Warnf("Application Error: Could not send sms notification to user mobile : %s", *user.PhoneNumber)
} }
return routeutils.ResponseAPIOK(ctx, user) return routeutils.ResponseAPIOK(ctx, user)

View File

@@ -289,7 +289,7 @@ func (c *controller) handle(ctx echo.Context) error {
requestRide.Destination = newOrigin requestRide.Destination = newOrigin
} }
if c.cfg.LyftProd.UserUUID != createdUser.ID { if createdUser.Test {
resp, err = c.tnc.Lyft.RequestRide(requestRide) resp, err = c.tnc.Lyft.RequestRide(requestRide)
} else { } else {
fmt.Println("In Production") fmt.Println("In Production")
@@ -404,7 +404,7 @@ func (c *controller) handle(ctx echo.Context) error {
newRide.Passenger.LastName = " " newRide.Passenger.LastName = " "
newRide.Passenger.PhoneNumber = *user.PhoneNumber newRide.Passenger.PhoneNumber = *user.PhoneNumber
if c.cfg.LyftProd.UserUUID != createdUser.ID { if createdUser.Test {
newRide, err = c.tnc.Lyft.RequestRide(newRide) newRide, err = c.tnc.Lyft.RequestRide(newRide)
} else { } else {
fmt.Println("In Production") fmt.Println("In Production")
@@ -514,7 +514,7 @@ func (c *controller) handleRawLyft(ctx echo.Context) error {
} }
var lyftRide viewmodel.RideRequest var lyftRide viewmodel.RideRequest
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID { if user.Test {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID}) lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
} else { } else {
fmt.Println("In Production") fmt.Println("In Production")
@@ -546,7 +546,7 @@ func (c *controller) handleRideETA(ctx echo.Context) error {
if ride.Status.Key == "accepted" || ride.Status.Key == "pickedUp" || ride.Status.Key == "arrived" { if ride.Status.Key == "accepted" || ride.Status.Key == "pickedUp" || ride.Status.Key == "arrived" {
var lyftRide viewmodel.RideRequest var lyftRide viewmodel.RideRequest
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID { if user.Test {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID}) lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
} else { } else {
fmt.Println("In Production") fmt.Println("In Production")
@@ -614,7 +614,7 @@ func (c *controller) handleCancel(ctx echo.Context) error {
requestRide.RideID = "s_" + ride.InternalID requestRide.RideID = "s_" + ride.InternalID
} }
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID { if user.Test {
err = c.tnc.Lyft.CancelRide(requestRide) err = c.tnc.Lyft.CancelRide(requestRide)
if err != nil && err.Error() == "ride_not_found" { if err != nil && err.Error() == "ride_not_found" {
err = nil err = nil
@@ -719,7 +719,7 @@ func (c *controller) handleRide(ctx echo.Context) error {
if ride.Status.Key == "accepted" || ride.Status.Key == "pickedUp" || ride.Status.Key == "arrived" { if ride.Status.Key == "accepted" || ride.Status.Key == "pickedUp" || ride.Status.Key == "arrived" {
var lyftRide viewmodel.RideRequest var lyftRide viewmodel.RideRequest
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID { if user.Test {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID}) lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
} else { } else {
fmt.Println("In Production") fmt.Println("In Production")
@@ -858,7 +858,7 @@ func (c *controller) handleReady(ctx echo.Context) error {
} }
var lyftRide viewmodel.RideRequest var lyftRide viewmodel.RideRequest
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID { if user.Test {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID}) lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
} else { } else {
fmt.Println("In Production") fmt.Println("In Production")
@@ -880,7 +880,7 @@ func (c *controller) handleReady(ctx echo.Context) error {
lyftRide.Passenger.PhoneNumber = *nextRide.User.PhoneNumber lyftRide.Passenger.PhoneNumber = *nextRide.User.PhoneNumber
lyftRide.RideType = "lyft" lyftRide.RideType = "lyft"
if c.cfg.LyftProd.UserUUID != nextRide.CreatedUser.ID { if user.Test {
lyftRide, err = c.tnc.Lyft.RequestRide(lyftRide) lyftRide, err = c.tnc.Lyft.RequestRide(lyftRide)
} else { } else {
fmt.Println("In Production") fmt.Println("In Production")

View File

@@ -128,6 +128,13 @@ func (c *controller) handleTwilio(ctx echo.Context) error {
message = fmt.Sprintf("We received a request to cancel a ride from you at %s, but cannot find a ride for this mobile number.", libphonenumber.Format(num, libphonenumber.NATIONAL)) message = fmt.Sprintf("We received a request to cancel a ride from you at %s, but cannot find a ride for this mobile number.", libphonenumber.Format(num, libphonenumber.NATIONAL))
} }
} }
authUser, err := c.svc.Users.GetByUUID(lastRide.CreatedUser.ID, "")
if err != nil {
fmt.Println("Error to author of the ride: ", err.Error())
message = "There was a problem to call your ride"
}
if !isDriver { if !isDriver {
if requestMessage == "I AM READY" && lastRide.UUID != "" { if requestMessage == "I AM READY" && lastRide.UUID != "" {
if (lastRide.Visit.TripType.Key == "roundtrip_call" && (lastRide.TripType.Key == "to_visit" || lastRide.TripType.Key == "from_visit_call")) || lastRide.Visit.TripType.Key == "from_visit_call" { if (lastRide.Visit.TripType.Key == "roundtrip_call" && (lastRide.TripType.Key == "to_visit" || lastRide.TripType.Key == "from_visit_call")) || lastRide.Visit.TripType.Key == "from_visit_call" {
@@ -161,7 +168,7 @@ func (c *controller) handleTwilio(ctx echo.Context) error {
lyftRide.Passenger.PhoneNumber = *readyRide.User.PhoneNumber lyftRide.Passenger.PhoneNumber = *readyRide.User.PhoneNumber
lyftRide.RideType = "lyft" lyftRide.RideType = "lyft"
if c.cfg.LyftProd.UserUUID != readyRide.CreatedUser.ID { if authUser.Test {
lyftRide, err = c.tnc.Lyft.RequestRide(lyftRide) lyftRide, err = c.tnc.Lyft.RequestRide(lyftRide)
} else { } else {
fmt.Println("In Production") fmt.Println("In Production")
@@ -209,7 +216,7 @@ func (c *controller) handleTwilio(ctx echo.Context) error {
if requestMessage == "YES" && lastRide.UUID != "" { if requestMessage == "YES" && lastRide.UUID != "" {
var lyftRide viewmodel.RideRequest var lyftRide viewmodel.RideRequest
if lastRide.CreatedUser.ID != c.cfg.LyftProd.UserUUID { if authUser.Test {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: lastRide.InternalID}) lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: lastRide.InternalID})
if err != nil { if err != nil {
fmt.Println("Error: ", err.Error()) fmt.Println("Error: ", err.Error())

View File

@@ -1,13 +1,9 @@
package usersroute package usersroute
import ( import (
"bytes"
b64 "encoding/base64" b64 "encoding/base64"
"encoding/json"
"fmt" "fmt"
"math/rand" "math/rand"
"net/http"
"strings"
"sync" "sync"
"time" "time"
@@ -22,8 +18,6 @@ import (
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils" "bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"bitbucket.org/nemt/nemt-portal-api/server/validation" "bitbucket.org/nemt/nemt-portal-api/server/validation"
"github.com/labstack/echo" "github.com/labstack/echo"
"golang.org/x/net/context"
"googlemaps.github.io/maps"
) )
const ( const (
@@ -345,11 +339,6 @@ func (c *controller) handleMember(ctx echo.Context) error {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
//Validate member
if validationErrors := validation.ValidateEligibility(&user); len(validationErrors) > 0 {
return routeutils.ResponseAPICustomValidationError(ctx, "eligibility validation failed", validationErrors)
}
authUser, err := auth.GetUserDetail(ctx, c.cfg) authUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil { if err != nil {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
@@ -359,153 +348,20 @@ func (c *controller) handleMember(ctx echo.Context) error {
return routeutils.ResponseAPIAuthError(ctx, "phonenumber or email is required", false) return routeutils.ResponseAPIAuthError(ctx, "phonenumber or email is required", false)
} }
if len(user.Pass) == 0 { provider := viewmodel.ProviderResp{}
user.Pass = c.generatePassword(8) user, err = c.svc.Users.CheckAndCreateMember(user, provider, authUser)
} 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)
}
profile, err := c.svc.Profile.GetByKey("US")
if err != nil { if err != nil {
return routeutils.HandleAPIError(ctx, err) if validationError, ok := err.(*viewmodel.ValidationError); ok {
} if len(validationError.Errors) > 0 {
user.Profiles = append(user.Profiles, profile) return routeutils.ResponseAPICustomValidationError(ctx, validationError.Message, validationError.Errors)
} else {
eligibility := viewmodel.Eligibility{} return routeutils.ResponseAPIServiceError(ctx, validationError.Message)
eligibility.Provider.ProviderNPI = "1699849786" }
eligibility.Provider.ProviderName = "LITHOLINK CORPORATION" } else {
eligibility.TrackingID = user.ID
eligibility.Subscriber.SubscriberID = *user.Member
eligibility.Subscriber.PatientType = "S"
eligibility.Subscriber.Name.First = user.First
eligibility.Subscriber.Name.Last = user.Last
eligibility.Subscriber.DemographicInfo.DateOfBirth = *user.BirthDate
eligibility.Subscriber.DemographicInfo.Gender = *user.Gender
eligibility.ServiceInfo.DateOfService = time.Now()
eligibility.ServiceInfo.ServiceTypeCodes = []string{"30"}
/*
resp, err := c.bcbsi.BXE.Get271(eligibility)
if err != nil {
fmt.Println("Eligibility Not Found or Error: ", err.Error())
return routeutils.ResponseAPINotEligibleError(ctx)
}
*/
//This part is emulating eligibility check for testing purposes
client := &http.Client{}
eligibilityJson, err := json.Marshal(eligibility)
if err != nil {
return routeutils.ResponseAPINotEligibleError(ctx)
}
req, _ := http.NewRequest("POST", c.cfg.Eligibility.Url, bytes.NewBuffer(eligibilityJson))
req.Header.Add("App", c.cfg.HTTP.Auth.AppKey)
req.Header.Add("Token", ctx.Request().Header.Get("Token"))
req.Header.Add("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return routeutils.ResponseAPINotEligibleError(ctx)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode > 300 {
return routeutils.ResponseAPINotEligibleError(ctx)
}
eligibilityResponse := viewmodel.Interchange271{}
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&eligibilityResponse)
if err != nil {
return routeutils.ResponseAPINotEligibleError(ctx)
}
//================================================================
if len(eligibilityResponse.Division.HealthCareEligibilityResponse.LoopHL0030) < 1 {
return routeutils.ResponseAPINotEligibleError(ctx)
}
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
header := eligibilityResponse.Division.HealthCareEligibilityResponse.LoopHL0030[0].HL_0460[0].HL_0890[0].NM1_0920[0].N3_0950
body := eligibilityResponse.Division.HealthCareEligibilityResponse.LoopHL0030[0].HL_0460[0].HL_0890[0].NM1_0920[0].N4_0960
address.AddressTypeName = "Home"
address.AddressType = "home"
address.Name = fmt.Sprintf("%s, %s", header.N301, body.N401)
address.Address = fmt.Sprintf("%s, %s", header.N301, body.N401)
address.CreatedUserUUID = authUser.ID
address.User = user
cleanZipcode := strings.TrimSpace(body.N403)
trimmedZipcode := cleanZipcode
if len(cleanZipcode) > zipcodeTrimLength {
trimmedZipcode = cleanZipcode[:zipcodeTrimLength]
}
_, err = c.svc.Zipcodes.GetByParticipatingZipcode(trimmedZipcode)
if err != nil {
return routeutils.ResponseAPINotEligibleWithMessageError(ctx, "Member's Home zipcode, "+trimmedZipcode+", is not currently eligible for participation in this program")
}
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 + ", " + body.N403,
}
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
_, err := c.svc.Users.SaveAddress(address)
if err != nil {
fmt.Println("Error to save address: ", err.Error())
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
} }
user, err = c.svc.Users.Create(user, authUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, user) return routeutils.ResponseAPIOK(ctx, user)
} }
@@ -547,6 +403,7 @@ func (c *controller) handleBulkPortal(ctx echo.Context) error {
return routeutils.ResponseAPIAuthError(ctx, "name is required", false) return routeutils.ResponseAPIAuthError(ctx, "name is required", false)
} }
users[i].Test = true
if len(users[i].First) != 0 && len(users[i].Last) != 0 { if len(users[i].First) != 0 && len(users[i].Last) != 0 {
users[i].Name = fmt.Sprintf("%s %s", users[i].First, users[i].Last) users[i].Name = fmt.Sprintf("%s %s", users[i].First, users[i].Last)
} }
@@ -608,6 +465,7 @@ func (c *controller) handlePortal(ctx echo.Context) error {
if len(user.First) != 0 && len(user.Last) != 0 { if len(user.First) != 0 && len(user.Last) != 0 {
user.Name = fmt.Sprintf("%s %s", user.First, user.Last) user.Name = fmt.Sprintf("%s %s", user.First, user.Last)
} }
user.Test = true
user, err = c.svc.Users.Create(user, authUser) user, err = c.svc.Users.Create(user, authUser)
if err != nil { if err != nil {

View File

@@ -1,15 +1,12 @@
package visitroute package visitroute
import ( import (
"context"
"fmt" "fmt"
"math/rand" "math/rand"
"strings" "strings"
"sync" "sync"
"time" "time"
b64 "encoding/base64"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice" "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/third/eligibility/bcbsi"
"bitbucket.org/nemt/nemt-portal-api/application/tncservice" "bitbucket.org/nemt/nemt-portal-api/application/tncservice"
@@ -20,7 +17,6 @@ import (
"bitbucket.org/nemt/nemt-portal-api/server/validation" "bitbucket.org/nemt/nemt-portal-api/server/validation"
"github.com/labstack/echo" "github.com/labstack/echo"
uuid "github.com/satori/go.uuid" uuid "github.com/satori/go.uuid"
"googlemaps.github.io/maps"
) )
var ( var (
@@ -81,103 +77,39 @@ func (c *controller) handleRide(ctx echo.Context) error {
if err != nil { if err != nil {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
visit.User.PhoneNumber = ride.User.PhoneNumber
visit.User.Email = ride.User.Email
visit.User.Consent = true
ride.Visit = visit ride.Visit = visit
user, err := c.svc.Users.GetByMemberID(*visit.User.Member)
if err != nil {
fmt.Println(err)
return routeutils.HandleAPIError(ctx, err)
}
user.PhoneNumber = ride.User.PhoneNumber
user.Email = ride.User.Email
ride.Visit.User = user
if ride.Visit.User.Type == nil {
subscriber := "S"
ride.Visit.User.Type = &subscriber
}
var provider viewmodel.ProviderResp var provider viewmodel.ProviderResp
provider, err = c.svc.Provider.GetByUUID(ride.Visit.Provider.ProviderUUID, authUser) provider, err = c.svc.Provider.GetByUUID(ride.Visit.Provider.ProviderUUID, authUser)
if err != nil { if err != nil {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
visit.Provider = provider ride.Visit.Provider = provider
eligibility := viewmodel.Eligibility{} // user, err := c.svc.Users.GetByUUID(ride.User.ID, "US")
eligibility.Provider.ProviderNPI = provider.InternalID // if err != nil {
eligibility.Provider.ProviderName = provider.OrganizatioName // return routeutils.HandleAPIError(ctx, err)
eligibility.TrackingID = c.rangeIn(1000000, 9999999) // }
eligibility.Subscriber.SubscriberID = *ride.Visit.User.Member user, err := c.svc.Users.CheckAndCreateMember(ride.Visit.User, provider, authUser)
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
loc, _ := time.LoadLocation("America/Chicago")
eligibility.ServiceInfo.DateOfService = time.Now().In(loc)
eligibility.ServiceInfo.ServiceTypeCodes = []string{"30"}
resp271, err := c.bcbsi.BXE.Get271(eligibility)
if err != nil { if err != nil {
fmt.Println(err) if validationError, ok := err.(*viewmodel.ValidationError); ok {
return routeutils.ResponseAPIValidationError(ctx, err.Error()) if len(validationError.Errors) > 0 {
} return routeutils.ResponseAPICustomValidationError(ctx, validationError.Message, validationError.Errors)
} else {
address := viewmodel.Address{} return routeutils.ResponseAPIServiceError(ctx, validationError.Message)
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 address.Latitude != 0 && address.Longitude != 0 {
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)
}
}
} }
} } else {
address, err = c.svc.Users.SaveAddress(address)
if err != nil {
fmt.Println("Error to save address: ", err.Error())
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
user.Addresses = append(user.Addresses, address)
} }
ride.Visit.User = user
ride.User = user
visit.User = user
homeAddress := viewmodel.Address{} homeAddress := viewmodel.Address{}
for _, a := range visit.User.Addresses { for _, a := range visit.User.Addresses {
@@ -206,9 +138,9 @@ func (c *controller) handleRide(ctx echo.Context) error {
} }
ride.Notes = ride.Notes ride.Notes = ride.Notes
ride.Passenger.FirstName = visit.User.First ride.Passenger.FirstName = ride.Visit.User.First
ride.Passenger.LastName = " " ride.Passenger.LastName = " "
ride.Passenger.PhoneNumber = *visit.User.PhoneNumber ride.Passenger.PhoneNumber = *ride.Visit.User.PhoneNumber
ride.RideType = "lyft" ride.RideType = "lyft"
ride.VisitDate = &visit.VisitDatetime ride.VisitDate = &visit.VisitDatetime
ride.VisitTime = &visit.VisitDatetime ride.VisitTime = &visit.VisitDatetime
@@ -226,7 +158,7 @@ func (c *controller) handleRide(ctx echo.Context) error {
ride.Destination = newOrigin ride.Destination = newOrigin
} }
if c.cfg.LyftProd.UserUUID != authUser.ID { if authUser.Test {
resp, err = c.tnc.Lyft.RequestRide(ride) resp, err = c.tnc.Lyft.RequestRide(ride)
} else { } else {
fmt.Println("In Production") fmt.Println("In Production")
@@ -340,7 +272,7 @@ func (c *controller) handleRide(ctx echo.Context) error {
newRide.Passenger.LastName = " " newRide.Passenger.LastName = " "
newRide.Passenger.PhoneNumber = *visit.User.PhoneNumber newRide.Passenger.PhoneNumber = *visit.User.PhoneNumber
if c.cfg.LyftProd.UserUUID != authUser.ID { if authUser.Test {
newRide, err = c.tnc.Lyft.RequestRide(newRide) newRide, err = c.tnc.Lyft.RequestRide(newRide)
} else { } else {
fmt.Println("In Production") fmt.Println("In Production")
@@ -471,154 +403,19 @@ func (c *controller) handle(ctx echo.Context) error {
return routeutils.ResponseAPICustomValidationError(ctx, "visit validation failed", validationErrors) return routeutils.ResponseAPICustomValidationError(ctx, "visit validation failed", validationErrors)
} }
eligibility := viewmodel.Eligibility{} user, err := c.svc.Users.CheckAndCreateMember(visit.User, provider, authUser)
eligibility.Provider.ProviderNPI = provider.InternalID
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
eligibility.Subscriber.Name.Last = visit.User.Last
eligibility.Subscriber.DemographicInfo.DateOfBirth = *visit.User.BirthDate
eligibility.Subscriber.DemographicInfo.Gender = *visit.User.Gender
loc, _ := time.LoadLocation("America/Chicago")
eligibility.ServiceInfo.DateOfService = time.Now().In(loc)
eligibility.ServiceInfo.ServiceTypeCodes = []string{"30"}
resp, err := c.bcbsi.BXE.Get271(eligibility)
if err != nil { if err != nil {
fmt.Println("Error to get eligibility: ", err.Error()) if validationError, ok := err.(*viewmodel.ValidationError); ok {
return routeutils.ResponseAPIValidationError(ctx, err.Error()) if len(validationError.Errors) > 0 {
} return routeutils.ResponseAPICustomValidationError(ctx, validationError.Message, validationError.Errors)
} else {
user, err := c.svc.Users.GetByMemberID(*visit.User.Member) return routeutils.ResponseAPIServiceError(ctx, validationError.Message)
if err != nil { }
fmt.Println("Error to get user by memberID: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
if user.ID == "" {
if len(user.Pass) == 0 {
user.Pass = c.generatePassword(8)
} else { } 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 {
fmt.Println("Error to get Profile: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
user.Profiles = append(user.Profiles, profile)
user, err = c.svc.Users.Create(user, authUser)
if err != nil {
fmt.Println("Error to create the user: ", err.Error())
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 {
fmt.Println("Error to update login: ", err.Error())
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
} }
visit.User = user
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 address.Latitude != 0 && address.Longitude != 0 {
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 saving address: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
user.Addresses = append(user.Addresses, address)
}
visit.TripType = viewmodel.TripType{ visit.TripType = viewmodel.TripType{
Key: "no_trip", Key: "no_trip",
@@ -634,7 +431,11 @@ func (c *controller) handle(ctx echo.Context) error {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
return routeutils.ResponseAPIOK(ctx, visit) if authUser.Profiles[0].Key == "VIRPT" {
return routeutils.ResponseNoContent(ctx, nil)
} else {
return routeutils.ResponseAPIOK(ctx, visit)
}
} }
func (c *controller) handleGetByID(ctx echo.Context) error { func (c *controller) handleGetByID(ctx echo.Context) error {

View File

@@ -19,7 +19,8 @@ func authSkipper(ctx echo.Context) bool {
strings.Contains(path, "/v1/notification/ws") || strings.Contains(path, "/v1/notification/ws") ||
strings.HasPrefix(path, "/v1/lyfthook") || strings.HasPrefix(path, "/v1/lyfthook") ||
strings.HasPrefix(path, "/v1/docs") || strings.HasPrefix(path, "/v1/docs") ||
strings.HasPrefix(path, "/v1/selfregister")) strings.HasPrefix(path, "/v1/selfregister") ||
strings.HasPrefix(path, "/v1/passwordreset"))
} }
// appSkipper is the default skipper for the application routes // appSkipper is the default skipper for the application routes

View File

@@ -53,7 +53,7 @@ func ValidateSelfregistration(user *viewmodel.User) []errors.ValidationError {
//Provider NPI validation //Provider NPI validation
if len(user.Provider.InternalID) != 10 || !isNumeric(user.Provider.InternalID) { if len(user.Provider.InternalID) != 10 || !isNumeric(user.Provider.InternalID) {
result = append(result, errors.ValidationError{Field: "provider.internal_id", Message: "Provider NPI must be 10 digit number"}) result = append(result, errors.ValidationError{Field: "provider.internal_id", Message: "Provider NPI must be a 10 digit number"})
} }
//First name validation //First name validation

BIN
svijetlastrana Executable file

Binary file not shown.