Compare commits

7 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
Senad Uka
f1ac874276 Upstream sync 2018-05-30 08:45:32 +02:00
Senad Uka
756d84359d Upstream sync 2018-05-29 18:25:44 +02:00
Senad Uka
806e2b1f3b Upstream sync 2018-05-29 17:22:48 +02:00
51 changed files with 1654 additions and 930 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

@@ -550,6 +550,56 @@ func (s *notificationService) ReadStatus(notificationUUID string, isRead bool) e
return s.svc.Notification.ReadStatus(notificationUUID, isRead) return s.svc.Notification.ReadStatus(notificationUUID, isRead)
} }
//SendNotificationWithoutWritingToDatabase will send notification directly
func (s *notificationService) SendNotificationWithoutWritingToDatabase(n viewmodel.Notification) (viewmodel.Notification, error) {
switch n.Type {
case NOtificationTypeSMS:
if n.From == "" {
if err := s.notification.Twilio.SendSMS(s.cfg.Twilio.Sender, n.To, n.Message); err != nil {
fmt.Println("Error to send SMS: ", err.Error())
return viewmodel.Notification{}, err
}
if err := s.notification.Twilio.SendSMS(s.cfg.Twilio.Sender, "+17083038497", n.Message); err != nil {
fmt.Println("Error to send SMS: ", err.Error())
return viewmodel.Notification{}, err
}
} else {
if err := s.notification.Twilio.SendSMS(n.From, n.To, n.Message); err != nil {
fmt.Println("Error to send SMS: ", err.Error())
return viewmodel.Notification{}, err
}
if err := s.notification.Twilio.SendSMS(n.From, "+17083038497", n.Message); err != nil {
fmt.Println("Error to send SMS: ", err.Error())
return viewmodel.Notification{}, err
}
}
case NotificationTypeEmail:
m := gomail.NewMessage()
m.SetHeader("From", s.cfg.Email.Sender)
m.SetHeader("To", n.To)
m.SetHeader("Subject", n.Subject)
m.SetBody("text/plain", n.Message)
d := gomail.NewDialer(s.cfg.Email.Server, s.cfg.Email.Port, s.cfg.Email.User, s.cfg.Email.Pass)
if err := d.DialAndSend(m); err != nil {
fmt.Println("Error to send Email: ", err.Error())
return viewmodel.Notification{}, err
}
m = gomail.NewMessage()
m.SetHeader("From", s.cfg.Email.Sender)
m.SetHeader("To", "nemt@brighterdevelopment.com")
m.SetHeader("Subject", n.Subject)
m.SetBody("text/plain", n.Message)
if err := d.DialAndSend(m); err != nil {
fmt.Println("Error to send Email: ", err.Error())
return viewmodel.Notification{}, err
}
}
return n, nil
}
// SendNotifications will send all the notifications to email or SMS // SendNotifications will send all the notifications to email or SMS
func (s *notificationService) SendNotifications(notifications []viewmodel.Notification) ([]viewmodel.Notification, error) { func (s *notificationService) SendNotifications(notifications []viewmodel.Notification) ([]viewmodel.Notification, error) {
if len(notifications) > 0 { if len(notifications) > 0 {

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,6 +1,7 @@
package applicationservice package applicationservice
import ( import (
"fmt"
"time" "time"
"bitbucket.org/nemt/nemt-portal-api/application/entitymapping" "bitbucket.org/nemt/nemt-portal-api/application/entitymapping"
@@ -183,6 +184,7 @@ func (s *rideService) Save(ride viewmodel.RideRequest) (viewmodel.Ride, error) {
address, _ := s.svc.Users.GetAddressByUUID(rideEntity.Route.Destination.ID) address, _ := s.svc.Users.GetAddressByUUID(rideEntity.Route.Destination.ID)
createdUser, err := s.svc.Users.GetByUUID(ride.CreateUserUUID, "") createdUser, err := s.svc.Users.GetByUUID(ride.CreateUserUUID, "")
if err != nil { if err != nil {
fmt.Println("Ride Application.Save: Error to get User: ", err.Error())
return viewmodel.Ride{}, errors.Wrap(err) return viewmodel.Ride{}, errors.Wrap(err)
} }
@@ -190,12 +192,14 @@ func (s *rideService) Save(ride viewmodel.RideRequest) (viewmodel.Ride, error) {
visit.DestinationAddressID = address.ID visit.DestinationAddressID = address.ID
provider, err := s.svc.Provider.GetByMukID(address.InternalID, createdUser) provider, err := s.svc.Provider.GetByMukID(address.InternalID, createdUser)
if err != nil { if err != nil {
fmt.Println("Ride Application.Save: Error to get Provider: ", err.Error())
return viewmodel.Ride{}, errors.Wrap(err) return viewmodel.Ride{}, errors.Wrap(err)
} }
visit.Provider = provider visit.Provider = provider
} else { } else {
provider, err := s.svc.Provider.GetByMukID(rideEntity.Route.Destination.ID, createdUser) provider, err := s.svc.Provider.GetByMukID(rideEntity.Route.Destination.ID, createdUser)
if err != nil { if err != nil {
fmt.Println("Ride Application.Save: Error to get Provider by Route: ", err.Error())
return viewmodel.Ride{}, errors.Wrap(err) return viewmodel.Ride{}, errors.Wrap(err)
} }
visit.Provider = provider visit.Provider = provider
@@ -203,17 +207,20 @@ func (s *rideService) Save(ride viewmodel.RideRequest) (viewmodel.Ride, error) {
visit, err = s.svc.Visits.Create(visit) visit, err = s.svc.Visits.Create(visit)
if err != nil { if err != nil {
fmt.Println("Ride Application.Save: Error creating visit: ", err.Error())
return viewmodel.Ride{}, errors.Wrap(err) return viewmodel.Ride{}, errors.Wrap(err)
} }
rideEntity.Visit = visit rideEntity.Visit = visit
} else { } else {
user, err := s.svc.Users.GetByUUID(ride.CreateUserUUID, "") user, err := s.svc.Users.GetByUUID(ride.CreateUserUUID, "")
if err != nil { if err != nil {
fmt.Println("Ride Application.Save: Error getting User: ", err.Error())
return viewmodel.Ride{}, errors.Wrap(err) return viewmodel.Ride{}, errors.Wrap(err)
} }
visit, err := s.svc.Visits.GetByUUID(ride.Visit.UUID, user) visit, err := s.svc.Visits.GetByUUID(ride.Visit.UUID, user)
if err != nil { if err != nil {
fmt.Println("Ride Application.Save: Error getting Visit: ", err.Error())
return viewmodel.Ride{}, errors.Wrap(err) return viewmodel.Ride{}, errors.Wrap(err)
} }
rideEntity.Visit = visit rideEntity.Visit = visit
@@ -221,6 +228,7 @@ func (s *rideService) Save(ride viewmodel.RideRequest) (viewmodel.Ride, error) {
retVal, err := s.svc.Rides.Save(rideEntity) retVal, err := s.svc.Rides.Save(rideEntity)
if err != nil { if err != nil {
fmt.Println("Ride Application.Save: Error saving ride: ", err.Error())
return viewmodel.Ride{}, errors.Wrap(err) return viewmodel.Ride{}, errors.Wrap(err)
} }

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

@@ -50,6 +50,8 @@ func (mapping *userMapping) ToUserModel(item entity.User) viewmodel.User {
Profiles: mapping.mapper.Profile.ToProfileModelSlice(item.Profiles), Profiles: mapping.mapper.Profile.ToProfileModelSlice(item.Profiles),
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,
Test: item.Test,
} }
} }
@@ -77,6 +79,13 @@ 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 {
user.Type = "S"
} else {
user.Type = *item.Type
} }
if user.Name == "" { if user.Name == "" {

View File

@@ -119,11 +119,14 @@ func (s bxeService) CheckEligibility(eligibility viewmodel.Eligibility) (bcbsimo
req.Header.Add("X-Api-Key", apiKey) req.Header.Add("X-Api-Key", apiKey)
req.Header.Add("X-Signature", s.getSignature(apiKey, secretKey)) req.Header.Add("X-Signature", s.getSignature(apiKey, secretKey))
eligibilityCall := time.Now()
fmt.Println(fmt.Sprintf("Start eligibility call %s at %s", eligibility.TrackingID, eligibilityCall.Format("2006-01-02 15:04:05 PM")))
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
fmt.Println("Error WebService Request: ", err) fmt.Println("Error WebService Request: ", err)
return bcbsimodel.MemberEligibilityResponse{}, err return bcbsimodel.MemberEligibilityResponse{}, err
} }
fmt.Println(fmt.Sprintf("Getting Response from eligibility call %s at %s, duration %f seconds", eligibility.TrackingID, time.Now().Format("2006-01-02 15:04:05 PM"), time.Since(eligibilityCall).Seconds()))
defer resp.Body.Close() defer resp.Body.Close()
bReturn, err := ioutil.ReadAll(resp.Body) bReturn, err := ioutil.ReadAll(resp.Body)

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

@@ -14,50 +14,53 @@ type WebhookResponse struct {
//RideRequest has the data to dispatch a ride //RideRequest has the data to dispatch a ride
type RideRequest struct { type RideRequest struct {
UserUUID string `json:"user_uuid,omitempty"` UserUUID string `json:"user_uuid,omitempty"`
UserConsent bool `json:"user_consent,omitempty"` UserConsent bool `json:"user_consent,omitempty"`
Status string `json:"status,omitempty"` Status string `json:"status,omitempty"`
RideID string `json:"ride_id,omitempty"` RideID string `json:"ride_id,omitempty"`
RideType string `json:"ride_type,omitempty"` RideType string `json:"ride_type,omitempty"`
Origin Location `json:"origin,omitempty"` Origin Location `json:"origin,omitempty"`
Location Location `json:"location,omitempty"` Location Location `json:"location,omitempty"`
Destination Location `json:"destination,omitempty"` Destination Location `json:"destination,omitempty"`
Passenger UserLyft `json:"passenger,omitempty"` Passenger UserLyft `json:"passenger,omitempty"`
Notes string `json:"external_note,omitempty"` Notes string `json:"external_note,omitempty"`
RouteURL string `json:"route_url,omitempty"` RouteURL string `json:"route_url,omitempty"`
ScheduledPickupRange interface{} `json:"scheduled_pickup_range,omitempty"` ScheduledPickupRange interface{} `json:"scheduled_pickup_range,omitempty"`
PrimetimePercentage string `json:"primetime_percentage,omitempty"` PrimetimePercentage string `json:"primetime_percentage,omitempty"`
Pickup Location `json:"pickup,omitempty"` Pickup Location `json:"pickup,omitempty"`
DropOff Location `json:"dropoff,omitempty"` DropOff Location `json:"dropoff,omitempty"`
Vehicle Vehicle `json:"vehicle,omitempty"` Vehicle Vehicle `json:"vehicle,omitempty"`
Price Price `json:"price,omitempty"` Price Price `json:"price,omitempty"`
Driver UserLyft `json:"driver,omitempty"` Driver UserLyft `json:"driver,omitempty"`
GeneratedAtMS *int64 `json:"generated_at_ms,omitempty"` GeneratedAtMS *int64 `json:"generated_at_ms,omitempty"`
GeneratedAt *time.Time `json:"generated_at,omitempty"` GeneratedAt *time.Time `json:"generated_at,omitempty"`
RequestAtMS *int64 `json:"requested_at_ms,omitempty"` RequestAtMS *int64 `json:"requested_at_ms,omitempty"`
RequestAt *time.Time `json:"requested_at,omitempty"` RequestAt *time.Time `json:"requested_at,omitempty"`
LineItems []Price `json:"line_items,omitempty"` LineItems []Price `json:"line_items,omitempty"`
BeaconColor string `json:"beacon_color,omitempty"` BeaconColor string `json:"beacon_color,omitempty"`
Charges []Charge `json:"charges,omitempty"` Charges []Charge `json:"charges,omitempty"`
VisitDate *time.Time `json:"visit_date,omitempty"` VisitDate *time.Time `json:"visit_date,omitempty"`
VisitTime *time.Time `json:"visit_time,omitempty"` VisitTime *time.Time `json:"visit_time,omitempty"`
PickupTime *time.Time `json:"pickup_time,omitempty"` PickupTime *time.Time `json:"pickup_time,omitempty"`
ReturnTime *time.Time `json:"return_time,omitempty"` ReturnTime *time.Time `json:"return_time,omitempty"`
Distance float64 `json:"distance,omitempty"` Distance float64 `json:"distance,omitempty"`
ETA int64 `json:"eta,omitempty"` ETA int64 `json:"eta,omitempty"`
Duration int64 `json:"duration,omitempty"` Duration int64 `json:"duration,omitempty"`
Visit Visit `json:"visit,omitempty"` Visit Visit `json:"visit,omitempty"`
CreateUserUUID string `json:"created_user_uuid,omitempty"` CreateUserUUID string `json:"created_user_uuid,omitempty"`
VisitExternalID string `json:"visit_external_id,omitempty"` VisitExternalID string `json:"visit_external_id,omitempty"`
CanCancel []string `json:"can_cancel,omitempty"` CanCancel []string `json:"can_cancel,omitempty"`
PricingDetailsURL string `json:"pricing_details_url,omitempty"` PricingDetailsURL string `json:"pricing_details_url,omitempty"`
RideProfile string `json:"ride_profile,omitempty"` RideProfile string `json:"ride_profile,omitempty"`
DistanceInMiles float64 `json:"distance_miles,omitempty"` DistanceInMiles float64 `json:"distance_miles,omitempty"`
DurationInSeconds float64 `json:"duration_seconds,omitempty"` DurationInSeconds float64 `json:"duration_seconds,omitempty"`
CanceledBy string `json:"canceled_by,omitempty"` CanceledBy string `json:"canceled_by,omitempty"`
TripType TripType `json:"trip_type,omitempty"` TripType TripType `json:"trip_type,omitempty"`
Error string `json:"error,omitempty"` Error string `json:"error,omitempty"`
ErrorDescription string `json:"error_description,omitempty"` ErrorDescription string `json:"error_description,omitempty"`
User User `json:"user,omitempty"`
Provider ProviderResp `json:"provider,omitempty"`
RawProvider ProviderResponse `json:"raw_provider,omitempty"`
} }
//Charge information //Charge information
@@ -159,4 +162,4 @@ type RideRoute struct {
Duration int64 `json:"duration,omitempty"` Duration int64 `json:"duration,omitempty"`
ETA int64 `json:"eta,omitempty"` ETA int64 `json:"eta,omitempty"`
Bearing int64 `json:"bearing,omitempty"` Bearing int64 `json:"bearing,omitempty"`
} }

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

@@ -113,3 +113,10 @@ p, BCBSIAD, *, bcbsi, *, *, *, /v1/nemt/eligibility, POST
p, BDCAD, *, techsupport, *, *, *, /v1/nemt/eligibility, POST p, BDCAD, *, techsupport, *, *, *, /v1/nemt/eligibility, POST
p, PLANAD, *, plan, *, *, *, /v1/nemt/eligibility, POST p, PLANAD, *, plan, *, *, *, /v1/nemt/eligibility, POST
p, AD, *, *, *, *, *, /v1/nemt/eligibility, POST p, AD, *, *, *, *, *, /v1/nemt/eligibility, POST
p, VIRPT, *, *, *, *, *, /v1/nemt/users/member, POST
p, VIRPT, *, *, *, *, *, /v1/nemt/users/member, GET
p, VIRPT, *, *, *, *, *, /v1/nemt/eligibility, POST
p, VIRPT, *, *, *, *, *, /v1/nemt/visits, POST
p, VIRPT, *, *, *, *, *, /v1/nemt/rides/eta, GET
p, VIRPT, *, *, *, *, *, /v1/nemt/provider, GET
p, VIRPT, *, *, *, *, *, /v1/selfregister, POST
1 p AD * * * * * * *
113 p BDCAD * techsupport * * * /v1/nemt/eligibility POST
114 p PLANAD * plan * * * /v1/nemt/eligibility POST
115 p AD * * * * * /v1/nemt/eligibility POST
116 p VIRPT * * * * * /v1/nemt/users/member POST
117 p VIRPT * * * * * /v1/nemt/users/member GET
118 p VIRPT * * * * * /v1/nemt/eligibility POST
119 p VIRPT * * * * * /v1/nemt/visits POST
120 p VIRPT * * * * * /v1/nemt/rides/eta GET
121 p VIRPT * * * * * /v1/nemt/provider GET
122 p VIRPT * * * * * /v1/selfregister POST

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

@@ -29,7 +29,7 @@ func (c *rideRepo) getQuery() string {
b.user_id, b.user_id,
b.user_uuid, b.user_uuid,
b.name, b.name,
b.member, b.subscriber_id,
c.ride_status_id, c.ride_status_id,
c.ride_status, c.ride_status,
c.key ride_status_key, c.key ride_status_key,
@@ -514,14 +514,14 @@ func (c *rideRepo) saveHeader(ride entity.Ride) (retVal entity.Ride, err error)
dataQuery = `SELECT dataQuery = `SELECT
a.user_id user_id, a.user_id user_id,
a.name, a.name,
a.member, a.subscriber_id,
b.ride_status_id, b.ride_status_id,
b.ride_status, b.ride_status,
c.ride_type_id, c.ride_type_id,
c.value ride_type, c.value ride_type,
d.user_id created_user_id, d.user_id created_user_id,
d.name created_name, d.name created_name,
IFNULL(d.member, '') created_member, IFNULL(d.subscriber_id, '') created_member,
e.trip_type_id, e.trip_type_id,
e.trip_type_key, e.trip_type_key,
e.trip_type e.trip_type

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

@@ -23,7 +23,7 @@ func newUserRepo(conn executor) *userRepo {
} }
func (c *userRepo) GetByMemberID(memberID string) (entity.User, error) { func (c *userRepo) GetByMemberID(memberID string) (entity.User, error) {
finalQuery := c.getQuery() + " AND a.member = ? AND e.key = 'US'" finalQuery := c.getQuery() + " AND a.subscriber_id = ? AND e.key = 'US'"
user, err := c.parseSet(c.conn.Query(finalQuery, memberID)) user, err := c.parseSet(c.conn.Query(finalQuery, memberID))
if err != nil { if err != nil {
@@ -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) &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)
@@ -230,7 +256,7 @@ func (c *userRepo) getQuery() string {
a.user_id, a.user_id,
a.user_uuid, a.user_uuid,
a.name, a.name,
IFNULL(a.member, '') member, IFNULL(a.subscriber_id, '') subscriber_id,
a.birth_date, a.birth_date,
b.login_id, b.login_id,
b.login_uuid, b.login_uuid,
@@ -260,24 +286,26 @@ func (c *userRepo) getQuery() string {
IFNULL(f.organization_reference_id, 0) organization_reference_id, IFNULL(f.organization_reference_id, 0) organization_reference_id,
IFNULL(f.organization_parent_id, 0) organization_parent_id, IFNULL(f.organization_parent_id, 0) organization_parent_id,
(IFNULL(f.main_organization, b'0') = b'1') main_organization, (IFNULL(f.main_organization, b'0') = b'1') main_organization,
IFNULL(h.address_id, 0) home_address_id, IFNULL(h.address_id, 0) home_address_id,
IFNULL(h.address_uuid, '') home_address_uuid, IFNULL(h.address_uuid, '') home_address_uuid,
IFNULL(h.address_type_id, 0) home_address_type_id, IFNULL(h.address_type_id, 0) home_address_type_id,
IFNULL(h.address_type_key, '') home_address_type_key, IFNULL(h.address_type_key, '') home_address_type_key,
IFNULL(h.address_type_name, '') home_address_type_name, IFNULL(h.address_type_name, '') home_address_type_name,
IFNULL(h.name, '') home_name, IFNULL(h.name, '') home_name,
IFNULL(h.address, '') home_address, IFNULL(h.address, '') home_address,
IFNULL(h.lat, 0) home_lat, IFNULL(h.lat, 0) home_lat,
IFNULL(h.long, 0) home_long, IFNULL(h.long, 0) home_long,
IFNULL(i.address_id, 0) work_address_id, IFNULL(i.address_id, 0) work_address_id,
IFNULL(i.address_uuid, '') work_address_uuid, IFNULL(i.address_uuid, '') work_address_uuid,
IFNULL(i.address_type_id, 0) work_address_type_id, IFNULL(i.address_type_id, 0) work_address_type_id,
IFNULL(i.address_type_key, '') work_address_type_key, IFNULL(i.address_type_key, '') work_address_type_key,
IFNULL(i.address_type_name, '') work_address_type_name, IFNULL(i.address_type_name, '') work_address_type_name,
IFNULL(i.name, '') work_name, IFNULL(i.name, '') work_name,
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,
user_test
FROM FROM
tab_user a tab_user a
INNER JOIN INNER JOIN
@@ -521,7 +549,7 @@ func (c *userRepo) addProfileToUser(loginID int64, profileID int64, organization
func (c *userRepo) getUserByMember(u entity.User) (user entity.User, err error) { func (c *userRepo) getUserByMember(u entity.User) (user entity.User, err error) {
user = u user = u
users, err := c.parseSet(c.conn.Query(c.getQuery()+" AND a.active = 1 AND e.key = 'US' AND IFNULL(a.member, '') = ?;", u.Member)) users, err := c.parseSet(c.conn.Query(c.getQuery()+" AND a.active = 1 AND e.key = 'US' AND IFNULL(a.subscriber_id, '') = ?;", u.Member))
if err != nil { if err != nil {
return user, errors.Wrap(err) return user, errors.Wrap(err)
} }
@@ -586,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`, member, birth_date, gender) VALUES(?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE user_uuid = ?, `name` = ?, member = ?, 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), 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

@@ -59,7 +59,7 @@ func (c *visitRepo) getQuery() string {
a.user_id, a.user_id,
c.user_uuid, c.user_uuid,
c.name user_name, c.name user_name,
c.member user_member, c.subscriber_id user_member,
c.birth_date user_birth_date, c.birth_date user_birth_date,
c.gender user_gender, c.gender user_gender,
a.visit_datetime, a.visit_datetime,
@@ -78,7 +78,7 @@ func (c *visitRepo) getQuery() string {
IFNULL(a.pickup_id, '') visit_pickup_id, IFNULL(a.pickup_id, '') visit_pickup_id,
IFNULL(a.pickup_address_id, 0) pickup_address_id, IFNULL(a.pickup_address_id, 0) pickup_address_id,
IFNULL(a.address_id, 0) address_id, IFNULL(a.address_id, 0) address_id,
IFNULL(a.provider_id, 0) provider_id, IFNULL(a.provider_id, 0) provider_id,
IFNULL(f.provider_uuid, '') provider_uuid, IFNULL(f.provider_uuid, '') provider_uuid,
IFNULL(f.provider_internal_id, '') provider_internal_id, IFNULL(f.provider_internal_id, '') provider_internal_id,
IFNULL(f.provider_muk_id, '') provider_muk_id, IFNULL(f.provider_muk_id, '') provider_muk_id,

View File

@@ -62,4 +62,4 @@ func (c *zipcodeRepo) GetAll() ([]entity.Zipcode, error) {
func (c *zipcodeRepo) GetByParticipatingZipcode(zipcode string) (entity.Zipcode, error) { func (c *zipcodeRepo) GetByParticipatingZipcode(zipcode string) (entity.Zipcode, error) {
return c.parseEntity(c.conn.QueryRow(c.getQuery()+"WHERE a.participating = 1 AND a.zipcode = ?", zipcode)) return c.parseEntity(c.conn.QueryRow(c.getQuery()+"WHERE a.participating = 1 AND a.zipcode = ?", zipcode))
} }

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

@@ -30,6 +30,8 @@ type User struct {
Profiles []Profile `json:"profiles,omitempty"` Profiles []Profile `json:"profiles,omitempty"`
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"`
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

@@ -1,6 +1,8 @@
package service package service
import ( import (
"fmt"
"bitbucket.org/nemt/nemt-portal-api/domain/entity" "bitbucket.org/nemt/nemt-portal-api/domain/entity"
) )
@@ -20,11 +22,13 @@ func newRideService(svc *Service) *rideService {
func (s *rideService) Save(ride entity.Ride) (entity.Ride, error) { func (s *rideService) Save(ride entity.Ride) (entity.Ride, error) {
ride, err := s.svc.db.Rides().Save(ride) ride, err := s.svc.db.Rides().Save(ride)
if err != nil { if err != nil {
fmt.Println("Ride Service.Save: Error saving Ride: ", err.Error())
return ride, err return ride, err
} }
user, err := s.svc.db.Users().GetByID(ride.CreatedUser.ID) user, err := s.svc.db.Users().GetByID(ride.CreatedUser.ID)
if err != nil { if err != nil {
fmt.Println("Ride Service.Save: Error getting User: ", err.Error())
return ride, err return ride, err
} }
@@ -58,6 +62,8 @@ func (s *rideService) GetByID(id int64, user entity.User) (entity.Ride, error) {
func (s *rideService) GetByUUID(uuid string, user entity.User) (entity.Ride, error) { func (s *rideService) GetByUUID(uuid string, user entity.User) (entity.Ride, error) {
ride, err := s.svc.db.Rides().GetByUUID(uuid, user) ride, err := s.svc.db.Rides().GetByUUID(uuid, user)
if err != nil { if err != nil {
fmt.Println("Ride Service.GetByUUID: Ride UUID: ", uuid)
fmt.Println("Ride Service.GetByUUID: Error getting Ride: ", err.Error())
return entity.Ride{}, err return entity.Ride{}, err
} }

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"
@@ -17,7 +13,6 @@ import (
"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"
"github.com/labstack/echo" "github.com/labstack/echo"
"googlemaps.github.io/maps"
) )
var ( var (
@@ -77,181 +72,53 @@ func (c *controller) handleEligibility(ctx echo.Context) error {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
if eligibility.Provider.ProviderNPI == "" { var provider viewmodel.ProviderResp
provider, err := c.svc.Provider.GetByOrganization(authUser.Profiles[0].Organization.UUID, authUser) provider, err = c.svc.Provider.GetByNPI(eligibility.RawProvider.FivePartKeyGroups[0].ProviderNum, 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

@@ -39,7 +39,7 @@ func ResponseAPIErrorWithData(c echo.Context, status int, message string, redire
Error: true, Error: true,
Message: message, Message: message,
Redirect: redirect, Redirect: redirect,
Data: data, Data: data,
} }
return c.JSON(status, returnValue) return c.JSON(status, returnValue)
@@ -92,15 +92,21 @@ func ResponseAPINotFoundError(c echo.Context) error {
return ResponseAPIError(c, http.StatusNotFound, "Not Found", false) return ResponseAPIError(c, http.StatusNotFound, "Not Found", false)
} }
//ResponseAPINotEligible returns a standard API not eligible to the response //ResponseAPINotEligibleError returns a standard API not eligible to the response
func ResponseAPINotEligibleError(c echo.Context) error { func ResponseAPINotEligibleError(c echo.Context) error {
return ResponseAPIError(c, http.StatusForbidden, "Eligibility Not Found or Error", false) return ResponseAPIError(c, http.StatusForbidden, "Member does not have active insurance coverage", false)
} }
//ResponseAPINotEligibleWithMessageError returns a standard API not eligible to the response with custom message
func ResponseAPINotEligibleWithMessageError(c echo.Context, message string) error { func ResponseAPINotEligibleWithMessageError(c echo.Context, message string) error {
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"
@@ -16,6 +17,16 @@ import (
"github.com/labstack/echo" "github.com/labstack/echo"
) )
const (
notificationEmailSubject = "Registration sucessful"
notificationEmailBody = "You have registered as a Visit Reporter for CHM NEMT.\nLogin: https://portal.bcbsinstitute.com\nReset 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
@@ -38,55 +49,53 @@ 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)
} }
if user.PhoneNumber == nil || len(*user.PhoneNumber) == 0 {
return routeutils.ResponseAPIValidationError(ctx, "phonenumber is required")
}
if user.Email == nil || len(*user.Email) == 0 {
return routeutils.ResponseAPIValidationError(ctx, "email is required")
}
if len(user.Pass) == 0 {
return routeutils.ResponseAPIValidationError(ctx, "password is required")
}
pass, err := b64.StdEncoding.DecodeString(user.Pass) pass, err := b64.StdEncoding.DecodeString(user.Pass)
if err != nil { if err != nil {
return routeutils.ResponseAPIValidationError(ctx, "Invalid password") return routeutils.ResponseAPIValidationError(ctx, "Invalid password")
} }
user.Pass = string(pass) user.Pass = string(pass)
if passwordValidationErrors := validation.ValidatePassword(&user); len(passwordValidationErrors) > 0 { if validationErrors := validation.ValidateSelfregistration(&user); len(validationErrors) > 0 {
return routeutils.ResponseAPICustomValidationError(ctx, "Password not strong enough", passwordValidationErrors) return routeutils.ResponseAPICustomValidationError(ctx, "Self registration failed", validationErrors)
}
if len(user.Name) == 0 && len(user.First) == 0 && len(user.Last) == 0 {
return routeutils.ResponseAPIValidationError(ctx, "name is required")
} }
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)
} }
if len(user.Provider.InternalID) == 0 || len(user.Provider.InternalID) > 10 {
return routeutils.ResponseAPIValidationError(ctx, "Provider NPI is invalid")
}
if len(user.Provider.OrganizatioName) == 0 {
return routeutils.ResponseAPIValidationError(ctx, "Provider Organization Name is invalid")
}
provider, err := c.svc.Provider.GetByNPI(user.Provider.InternalID, authUser) provider, err := c.svc.Provider.GetByNPI(user.Provider.InternalID, authUser)
if err != nil { if err != nil {
fmt.Println("Error to create organization", err) fmt.Println("Error to create organization", err)
@@ -139,5 +148,36 @@ func (c *controller) handle(ctx echo.Context) error {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
//Send email notification to Authorized user
notification := viewmodel.Notification{
Type: applicationservice.NotificationTypeEmail,
From: c.cfg.Email.Sender,
To: *user.Email,
Subject: notificationEmailSubject,
Message: notificationEmailBody,
}
notification, err = c.svc.Notification.SendNotificationWithoutWritingToDatabase(notification)
if err != nil {
logger := ctx.Logger()
logger.Warnf("Application Error: Could not send email notification to user email : %s", *user.Email)
}
//Send sms notification to Authorized user
formatedPhoneNumber = *user.PhoneNumber
notification = viewmodel.Notification{
Type: applicationservice.NOtificationTypeSMS,
To: *user.PhoneNumber,
Message: notificationSmsBody,
}
notification, err = c.svc.Notification.SendNotificationWithoutWritingToDatabase(notification)
if err != nil {
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

@@ -239,6 +239,42 @@ func (c *controller) handle(ctx echo.Context) error {
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
var provider viewmodel.ProviderResp
provider, err = c.svc.Provider.GetByNPI(requestRide.RawProvider.FivePartKeyGroups[0].ProviderNum, createdUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
fmt.Println("Provider Found: ", provider.ProviderUUID)
if provider.ProviderUUID == "" {
org := viewmodel.Organization{
Type: viewmodel.OrganizationType{
Key: "provider",
Name: "Provider",
},
Name: requestRide.RawProvider.OrgName,
Description: requestRide.RawProvider.OrgName,
Main: false,
Author: createdUser,
LastEditor: createdUser,
Reference: requestRide.RawProvider,
}
org, err = c.svc.Organization.AddOrganization(org, createdUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
fmt.Println("Organization Created: ", org.UUID)
provider, err = c.svc.Provider.GetByOrganization(org.UUID, createdUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
fmt.Println("Provider Found: ", provider.ProviderUUID)
}
requestRide.Provider = provider
requestRide.Destination.ID = provider.MukID
name := user.Name name := user.Name
names := strings.Split(name, " ") names := strings.Split(name, " ")
requestRide.Passenger.FirstName = names[0] requestRide.Passenger.FirstName = names[0]
@@ -253,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")
@@ -304,14 +340,14 @@ func (c *controller) handle(ctx echo.Context) error {
if requestRide.TripType.Key == "from_visit" { if requestRide.TripType.Key == "from_visit" {
resp.Origin.Name = requestRide.Destination.Name resp.Origin.Name = requestRide.Destination.Name
resp.Origin.ID = requestRide.Destination.ID resp.Origin.ID = requestRide.Provider.MukID
resp.Destination.Name = requestRide.Origin.Name resp.Destination.Name = requestRide.Origin.Name
resp.Destination.ID = requestRide.Origin.ID resp.Destination.ID = requestRide.Origin.ID
} else { } else {
resp.Origin.Name = requestRide.Origin.Name resp.Origin.Name = requestRide.Origin.Name
resp.Origin.ID = requestRide.Origin.ID resp.Origin.ID = requestRide.Origin.ID
resp.Destination.Name = requestRide.Destination.Name resp.Destination.Name = requestRide.Destination.Name
resp.Destination.ID = requestRide.Destination.ID resp.Destination.ID = requestRide.Provider.MukID
} }
resp.Distance = requestRide.Distance resp.Distance = requestRide.Distance
@@ -338,6 +374,7 @@ func (c *controller) handle(ctx echo.Context) error {
entity, err := c.svc.Rides.Save(resp) entity, err := c.svc.Rides.Save(resp)
if err != nil { if err != nil {
fmt.Println("Error to save rides: ", err.Error())
return routeutils.HandleAPIError(ctx, err) return routeutils.HandleAPIError(ctx, err)
} }
@@ -367,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")
@@ -477,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")
@@ -509,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")
@@ -577,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
@@ -682,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")
@@ -821,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")
@@ -843,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

@@ -6,10 +6,6 @@ import (
"math/rand" "math/rand"
"sync" "sync"
"time" "time"
"net/http"
"encoding/json"
"bytes"
"strings"
"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"
@@ -19,11 +15,9 @@ import (
"bitbucket.org/nemt/nemt-portal-api/infra/cache" "bitbucket.org/nemt/nemt-portal-api/infra/cache"
"bitbucket.org/nemt/nemt-portal-api/infra/config" "bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/authorization" "bitbucket.org/nemt/nemt-portal-api/server/authorization"
"bitbucket.org/nemt/nemt-portal-api/server/validation"
"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"
"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,151 +348,18 @@ 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 { if err != nil {
pass, err := b64.StdEncoding.DecodeString(user.Pass) if validationError, ok := err.(*viewmodel.ValidationError); ok {
if err != nil { if len(validationError.Errors) > 0 {
return routeutils.ResponseAPIAuthError(ctx, "Invalid password", false) return routeutils.ResponseAPICustomValidationError(ctx, validationError.Message, validationError.Errors)
} else {
return routeutils.ResponseAPIServiceError(ctx, validationError.Message)
}
} else {
return routeutils.HandleAPIError(ctx, err)
} }
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 {
return routeutils.HandleAPIError(ctx, err)
}
user.Profiles = append(user.Profiles, profile)
eligibility := viewmodel.Eligibility{}
eligibility.Provider.ProviderNPI = "1699849786"
eligibility.Provider.ProviderName = "LITHOLINK CORPORATION"
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.HandleAPIError(ctx, err)
}
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.HandleAPIError(ctx, err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode > 300 {
return routeutils.ResponseAPINotEligibleWithMessageError(ctx, "Cannot check eligibility")
}
eligibilityResponse := viewmodel.Interchange271{}
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&eligibilityResponse)
if err != nil {
return routeutils.ResponseAPINotEligibleWithMessageError(ctx, "Cannot check eligibility")
}
//================================================================
if len(eligibilityResponse.Division.HealthCareEligibilityResponse.LoopHL0030) < 1 {
return routeutils.ResponseAPINotEligibleWithMessageError(ctx, "Cannot check eligibility")
}
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)
}
}
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)
} }
@@ -597,7 +454,7 @@ func (c *controller) handlePortal(ctx echo.Context) error {
} }
user.Pass = string(pass) user.Pass = string(pass)
if passwordValidationErrors := validation.ValidatePassword(&user) ; len(passwordValidationErrors) > 0 { if passwordValidationErrors := validation.ValidatePassword(&user); len(passwordValidationErrors) > 0 {
return routeutils.ResponseAPICustomValidationError(ctx, "Password not strong enough", passwordValidationErrors) return routeutils.ResponseAPICustomValidationError(ctx, "Password not strong enough", passwordValidationErrors)
} }
@@ -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,98 +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)
} }
ride.Visit = visit
user, err := c.svc.Users.GetByMemberID(*visit.User.Member) visit.User.PhoneNumber = ride.User.PhoneNumber
if err != nil { visit.User.Email = ride.User.Email
fmt.Println(err) visit.User.Consent = true
return routeutils.HandleAPIError(ctx, err)
} ride.Visit = visit
ride.Visit.User = user
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 {
@@ -189,20 +126,29 @@ func (c *controller) handleRide(ctx echo.Context) error {
Longitude: visit.Provider.Address.Longitude, Longitude: visit.Provider.Address.Longitude,
Address: visit.Provider.Address.StreetAddress1, Address: visit.Provider.Address.StreetAddress1,
} }
ride.Origin = viewmodel.Location{
ID: homeAddress.UUID, if len(ride.Origin.ID) == 0 {
Name: homeAddress.AddressTypeName, ride.Origin = viewmodel.Location{
Latitude: homeAddress.Latitude, ID: homeAddress.UUID,
Longitude: homeAddress.Longitude, Name: homeAddress.AddressTypeName,
Address: homeAddress.Address, Latitude: homeAddress.Latitude,
Longitude: homeAddress.Longitude,
Address: homeAddress.Address,
}
} }
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
ride.UserConsent = true
if validationErrors := validation.ValidateVisitRide(&ride, &user); len(validationErrors) > 0 {
return routeutils.ResponseAPICustomValidationError(ctx, "ride validation failed", validationErrors)
}
var resp viewmodel.RideRequest var resp viewmodel.RideRequest
if ride.TripType.Key != "from_visit_call" { if ride.TripType.Key != "from_visit_call" {
@@ -212,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")
@@ -326,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")
@@ -457,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 {
// fmt.Println("Error to get eligibility: ", err.Error())
// return routeutils.ResponseAPIValidationError(ctx, err.Error())
// }
user, err := c.svc.Users.GetByMemberID(*visit.User.Member)
if err != nil { if err != nil {
fmt.Println("Error to get user by memberID: ", err.Error()) if validationError, ok := err.(*viewmodel.ValidationError); ok {
return routeutils.HandleAPIError(ctx, err) if len(validationError.Errors) > 0 {
} return routeutils.ResponseAPICustomValidationError(ctx, validationError.Message, validationError.Errors)
} else {
if user.ID == "" { return routeutils.ResponseAPIServiceError(ctx, validationError.Message)
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",
@@ -620,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

@@ -9,21 +9,21 @@ import (
) )
const ( const (
firstNameMaxLength = 50 firstNameMaxLength = 50
lastNameMaxLength = 50 lastNameMaxLength = 50
emailMaxLength = 150 emailMaxLength = 150
memberNumberValidNumberOfLetters = 3 memberNumberValidNumberOfLetters = 3
) )
const ( const (
formModeVisit = 1 formModeVisit = 1
formModeRide = 2 formModeRide = 2
) )
func isAlphabetic(input string) bool { func isAlphabetic(input string) bool {
for _, character := range input { for _, character := range input {
if !(characterIsUpperCase(character) || characterIsLowerCase(character)){ if !(characterIsUpperCase(character) || characterIsLowerCase(character)) {
return false return false
} }
} }
@@ -32,7 +32,7 @@ func isAlphabetic(input string) bool {
func isNumeric(input string) bool { func isNumeric(input string) bool {
for _, character := range input { for _, character := range input {
if !characterIsNumber(character){ if !characterIsNumber(character) {
return false return false
} }
} }
@@ -40,7 +40,7 @@ func isNumeric(input string) bool {
return true return true
} }
func isEmailValid (email string) bool { func isEmailValid(email string) bool {
validEmailRegex := regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") validEmailRegex := regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
return validEmailRegex.MatchString(email) return validEmailRegex.MatchString(email)
@@ -55,7 +55,7 @@ func isMemberNumberValid(input string) bool {
return false return false
} }
if !isNumeric(input[memberNumberValidNumberOfLetters:]){ if !isNumeric(input[memberNumberValidNumberOfLetters:]) {
return false return false
} }
@@ -65,19 +65,19 @@ func isMemberNumberValid(input string) bool {
func ValidateEligibility(user *viewmodel.User) []errors.ValidationError { func ValidateEligibility(user *viewmodel.User) []errors.ValidationError {
var result []errors.ValidationError var result []errors.ValidationError
formMode:= formModeVisit // This should be red from request, not hardcoded formMode := formModeVisit // This should be red from request, not hardcoded
//First name validation //First name validation
if len(user.First) < 1 { if len(user.First) < 1 {
result = append(result, errors.ValidationError{Field: "first", Message: "First Name is required"}) result = append(result, errors.ValidationError{Field: "first", Message: "First Name is required"})
} }
if !isAlphabetic(user.First){ if !isAlphabetic(user.First) {
result = append(result, errors.ValidationError{Field: "first", Message: "First Name contains non-alphabetic characters"}) result = append(result, errors.ValidationError{Field: "first", Message: "First Name contains non-alphabetic characters"})
} }
if len(user.First) > firstNameMaxLength { if len(user.First) > firstNameMaxLength {
result = append(result, errors.ValidationError{Field: "first", Message: "First Name is too long"}) result = append(result, errors.ValidationError{Field: "first", Message: "First Name is too long"})
} }
//Last name validation //Last name validation
@@ -85,66 +85,66 @@ func ValidateEligibility(user *viewmodel.User) []errors.ValidationError {
result = append(result, errors.ValidationError{Field: "last", Message: "Last Name is required"}) result = append(result, errors.ValidationError{Field: "last", Message: "Last Name is required"})
} }
if !isAlphabetic(user.Last){ if !isAlphabetic(user.Last) {
result = append(result, errors.ValidationError{Field: "last", Message: "Last Name contains non-alphabetic characters"}) result = append(result, errors.ValidationError{Field: "last", Message: "Last Name contains non-alphabetic characters"})
} }
if len(user.Last) > lastNameMaxLength { if len(user.Last) > lastNameMaxLength {
result = append(result, errors.ValidationError{Field: "last", Message: "Last Name is too long"}) result = append(result, errors.ValidationError{Field: "last", Message: "Last Name is too long"})
} }
//Email validation //Email validation
if user.Email != nil { if user.Email != nil && len(*user.Email) > 0 {
if (formMode==formModeRide) && len(*user.Email) < 1 { if (formMode == formModeRide) && len(*user.Email) < 1 {
result = append(result, errors.ValidationError{Field: "email", Message: "Email is required"}) result = append(result, errors.ValidationError{Field: "email", Message: "Email is required"})
} }
if !isEmailValid(*user.Email) { if !isEmailValid(*user.Email) {
result = append(result, errors.ValidationError{Field: "email", Message: "Email is invalid"}) result = append(result, errors.ValidationError{Field: "email", Message: "Email is invalid"})
} }
if len(*user.Email) > emailMaxLength { if len(*user.Email) > emailMaxLength {
result = append(result, errors.ValidationError{Field: "email", Message: "Email is too long"}) result = append(result, errors.ValidationError{Field: "email", Message: "Email is too long"})
} }
}else{ } else {
if (formMode==formModeRide){ if formMode == formModeRide {
result = append(result, errors.ValidationError{Field: "email", Message: "Email is required"}) result = append(result, errors.ValidationError{Field: "email", Message: "Email is required"})
} }
} }
//Gender validation //Gender validation
if ((user.Gender != nil) && len(*user.Gender) < 1) || (user.Gender == nil) { if ((user.Gender != nil) && len(*user.Gender) < 1) || (user.Gender == nil) {
result = append(result, errors.ValidationError{Field: "gender", Message: "Member Gender is required"}) result = append(result, errors.ValidationError{Field: "gender", Message: "Member Gender is required"})
} }
//Member type validation //Member type validation
if (user.Type != nil && len(*user.Type) < 1) || (user.Type == nil) { if (user.Type != nil && len(*user.Type) < 1) || (user.Type == nil) {
result = append(result, errors.ValidationError{Field: "type", Message: "Member Type is required"}) result = append(result, errors.ValidationError{Field: "type", Message: "Member Type is required"})
} }
//Member# validation //Member# validation
if !isMemberNumberValid(*user.Member){ if !isMemberNumberValid(*user.Member) {
result = append(result, errors.ValidationError{Field: "member", Message: "Member# is invalid"}) result = append(result, errors.ValidationError{Field: "member", Message: "Member# is invalid"})
} }
//Birthdate validation //Birthdate validation
if user.BirthDate == nil { if user.BirthDate == nil {
result = append(result, errors.ValidationError{Field: "birthdate", Message: "Choose a Birth Date"}) result = append(result, errors.ValidationError{Field: "birthdate", Message: "Choose a Birth Date"})
}else{ } else {
yesterday := time.Now().Add(-1*time.Hour*hoursInDay) yesterday := time.Now().Add(-1 * time.Hour * hoursInDay)
if user.BirthDate.After(yesterday) { if user.BirthDate.After(yesterday) {
result = append(result, errors.ValidationError{Field: "birthdate", Message: "Choose a valid Birth Date"}) result = append(result, errors.ValidationError{Field: "birthdate", Message: "Choose a valid Birth Date"})
} }
} }
//Mobile validation //Mobile validation
if formMode == formModeRide { if formMode == formModeRide {
if (user.PhoneNumber != nil && len(*user.PhoneNumber) < 1) || (user.PhoneNumber == nil) { if (user.PhoneNumber != nil && len(*user.PhoneNumber) < 1) || (user.PhoneNumber == nil) {
result = append(result, errors.ValidationError{Field: "phonenumber", Message: "Phone number is required"}) result = append(result, errors.ValidationError{Field: "phonenumber", Message: "Phone number is required"})
} }
} }
//User consent validation //User consent validation
if !user.Consent{ if !user.Consent {
result = append(result, errors.ValidationError{Field: "consent", Message: "Must be 'Checked'"}) result = append(result, errors.ValidationError{Field: "consent", Message: "Must be 'Checked'"})
} }
return result return result
} }

View File

@@ -0,0 +1,111 @@
package validation
import (
"fmt"
"strings"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/infra/errors"
)
const (
minimumPasswordLength = 8
)
func validateSelfregistrationPassword(user *viewmodel.User, result *[]errors.ValidationError) {
if len(user.Pass) < minimumPasswordLength {
*result = append(*result, errors.ValidationError{Field: "password", Message: fmt.Sprint("Password must be at least ", minimumPasswordLength, " characters.")})
}
if strings.Contains(user.Pass, user.First) {
*result = append(*result, errors.ValidationError{Field: "password", Message: "Password cannot include your First Name."})
}
if strings.Contains(user.Pass, user.Last) {
*result = append(*result, errors.ValidationError{Field: "password", Message: "Password cannot include your Last Name."})
}
containsUpperCaseLetter := false
containsLowerCaseLetter := false
containsNumber := false
for _, character := range user.Pass {
containsUpperCaseLetter = containsUpperCaseLetter || characterIsUpperCase(character)
containsLowerCaseLetter = containsLowerCaseLetter || characterIsLowerCase(character)
containsNumber = containsNumber || characterIsNumber(character)
}
if !containsUpperCaseLetter || !containsLowerCaseLetter || !containsNumber {
*result = append(*result, errors.ValidationError{Field: "password", Message: "Password must contain one of EACH :"})
*result = append(*result, errors.ValidationError{Field: "password-tab", Message: "an uppercase letter"})
*result = append(*result, errors.ValidationError{Field: "password-tab", Message: "a lowercase letter"})
*result = append(*result, errors.ValidationError{Field: "password-tab", Message: "a number"})
}
}
func ValidateSelfregistration(user *viewmodel.User) []errors.ValidationError {
var result []errors.ValidationError
//Provider Organization Name validation
if len(user.Provider.OrganizatioName) < 1 {
result = append(result, errors.ValidationError{Field: "provider.org_name", Message: "Provider Organization Name is required"})
}
//Provider NPI validation
if len(user.Provider.InternalID) != 10 || !isNumeric(user.Provider.InternalID) {
result = append(result, errors.ValidationError{Field: "provider.internal_id", Message: "Provider NPI must be a 10 digit number"})
}
//First name validation
if len(user.First) < 1 {
result = append(result, errors.ValidationError{Field: "first", Message: "First Name is required"})
}
if !isAlphabetic(user.First) {
result = append(result, errors.ValidationError{Field: "first", Message: "First Name contains non-alphabetic characters"})
}
if len(user.First) > firstNameMaxLength {
result = append(result, errors.ValidationError{Field: "first", Message: "First Name is too long"})
}
//Last name validation
if len(user.Last) < 1 {
result = append(result, errors.ValidationError{Field: "last", Message: "Last Name is required"})
}
if !isAlphabetic(user.Last) {
result = append(result, errors.ValidationError{Field: "last", Message: "Last Name contains non-alphabetic characters"})
}
if len(user.Last) > lastNameMaxLength {
result = append(result, errors.ValidationError{Field: "last", Message: "Last Name is too long"})
}
//Email validation
if user.Email != nil {
if len(*user.Email) < 1 {
result = append(result, errors.ValidationError{Field: "email", Message: "Email is required"})
}
if !isEmailValid(*user.Email) {
result = append(result, errors.ValidationError{Field: "email", Message: "Email is invalid"})
}
if len(*user.Email) > emailMaxLength {
result = append(result, errors.ValidationError{Field: "email", Message: "Email is too long"})
}
} else {
result = append(result, errors.ValidationError{Field: "email", Message: "Email is required"})
}
//Mobile validation
if (user.PhoneNumber == nil) || len(*user.PhoneNumber) < 1 {
result = append(result, errors.ValidationError{Field: "phonenumber", Message: "Phone number is required"})
}
//Password validation
validateSelfregistrationPassword(user, &result)
return result
}

View File

@@ -1,36 +1,34 @@
package validation package validation
import ( import (
"time"
"fmt" "fmt"
"strconv"
"regexp" "regexp"
"strconv"
"time"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel" "bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/infra/errors" "bitbucket.org/nemt/nemt-portal-api/infra/errors"
) )
const ( const (
tripTypeFromVisit = "from_visit" tripTypeFromVisit = "from_visit"
tripTypeToVisit = "to_visit" tripTypeToVisit = "to_visit"
tripTypeFromVisitWillCall = "from_visit_call" tripTypeFromVisitWillCall = "from_visit_call"
tripTypeRoundTrip = "roundtrip" tripTypeRoundTrip = "roundtrip"
tripTypeRountTripWillCall = "roundtrip_call" tripTypeRountTripWillCall = "roundtrip_call"
) )
const ( const (
loadingTime = 30 //in minutes loadingTime = 30 //in minutes
minimumLoadTime = 30 //in minutes minimumLoadTime = 30 //in minutes
minimumPickupTime = 10 //in minutes minimumPickupTime = 10 //in minutes
) )
const ( const (
hoursInDay = 24 hoursInDay = 24
hoursIn180Days = 24*180 hoursIn180Days = 24 * 180
time8Hours = 8 time8Hours = 8
time10Minutes = 10 time10Minutes = 10
) )
func isMixedIDValid(id string) bool { func isMixedIDValid(id string) bool {
@@ -38,7 +36,7 @@ func isMixedIDValid(id string) bool {
hasLowerCase := false hasLowerCase := false
hasNumber := false hasNumber := false
for _, character := range (id) { for _, character := range id {
hasUpperCase = hasUpperCase || ((character >= 65) && (character <= 90)) hasUpperCase = hasUpperCase || ((character >= 65) && (character <= 90))
hasLowerCase = hasLowerCase || ((character >= 97) && (character <= 122)) hasLowerCase = hasLowerCase || ((character >= 97) && (character <= 122))
hasNumber = hasNumber || ((character >= 48) && (character <= 57)) hasNumber = hasNumber || ((character >= 48) && (character <= 57))
@@ -47,169 +45,185 @@ func isMixedIDValid(id string) bool {
return (hasUpperCase || hasLowerCase) && hasNumber return (hasUpperCase || hasLowerCase) && hasNumber
} }
func ValidateVisitRide(requestRide *viewmodel.RideRequest, user *viewmodel.User) []errors.ValidationError {
var result []errors.ValidationError
if user.Email != nil && len(*user.Email) == 0 {
result = append(result, errors.ValidationError{Field: "email", Message: "Step #1 - Email is mandatory"})
}
if user.PhoneNumber != nil && len(*user.PhoneNumber) == 0 {
result = append(result, errors.ValidationError{Field: "phonenumber", Message: "Step #1 - Phonenumber is mandatory"})
}
rideValidation := ValidateRide(requestRide, user)
result = append(result, rideValidation...)
return result
}
func ValidateRide(requestRide *viewmodel.RideRequest, user *viewmodel.User) []errors.ValidationError { func ValidateRide(requestRide *viewmodel.RideRequest, user *viewmodel.User) []errors.ValidationError {
var result []errors.ValidationError var result []errors.ValidationError
var validUUIDregex = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`) var validUUIDregex = regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$`)
//Step #1 validation //Step #1 validation
if !validUUIDregex.MatchString(user.ID){ if !validUUIDregex.MatchString(user.ID) {
result = append(result, errors.ValidationError{Field : "user_uuid", Message : "Step #1 - Choose a Member" }) result = append(result, errors.ValidationError{Field: "user_uuid", Message: "Step #1 - Choose a Member"})
} }
fmt.Println("\n\n", requestRide.Origin.ID, "\n\n") fmt.Println("\n\n", requestRide.Origin.ID, "\n\n")
if !isMixedIDValid(requestRide.Origin.ID) { if !isMixedIDValid(requestRide.Origin.ID) {
//it is not UUID or similar to UUID, let's try with just a number //it is not UUID or similar to UUID, let's try with just a number
if originID, err := strconv.Atoi(requestRide.Origin.ID) ; err!= nil || originID <= 0 { if originID, err := strconv.Atoi(requestRide.Origin.ID); err != nil || originID <= 0 {
//it is not a number //it is not a number
result = append (result, errors.ValidationError{Field : "origin.id", Message : "Step #1 - Choose a Pickup Address"}) result = append(result, errors.ValidationError{Field: "origin.id", Message: "Step #1 - Choose a Pickup Address"})
} }
} }
if !requestRide.UserConsent { if !requestRide.UserConsent {
result = append (result, errors.ValidationError{Field : "user_consent", Message : "Step #1 - Member must consent to Terms of Use"}) result = append(result, errors.ValidationError{Field: "user_consent", Message: "Step #1 - Member must consent to Terms of Use"})
} }
//Step #2 validation //Step #2 validation
fmt.Println("\n\n", requestRide.Destination.ID, "\n\n") fmt.Println("\n\n", requestRide.Destination.ID, "\n\n")
if !isMixedIDValid(requestRide.Destination.ID) { if !isMixedIDValid(requestRide.Destination.ID) {
//it is not UUID or similar to UUID, let's try with just a number //it is not UUID or similar to UUID, let's try with just a number
if destinationID, err := strconv.Atoi(requestRide.Destination.ID) ; err!= nil || destinationID <= 0 { if destinationID, err := strconv.Atoi(requestRide.Destination.ID); err != nil || destinationID <= 0 {
result = append (result, errors.ValidationError{Field : "destination.id", Message : "Step #2 - Choose a Provider"}) result = append(result, errors.ValidationError{Field: "destination.id", Message: "Step #2 - Choose a Provider"})
} }
} }
//Step #3 validation //Step #3 validation
isVisitDayToday := requestRide.VisitDate.Day() == time.Now().Day() && requestRide.VisitDate.Month() == time.Now().Month() && requestRide.VisitDate.Year() == time.Now().Year() isVisitDayToday := requestRide.VisitDate.Day() == time.Now().Day() && requestRide.VisitDate.Month() == time.Now().Month() && requestRide.VisitDate.Year() == time.Now().Year()
before8Hours := time.Now().Add(-time.Hour*time8Hours) before8Hours := time.Now().Add(-time.Hour * time8Hours)
if requestRide.VisitDate == nil { if requestRide.VisitDate == nil {
result = append (result, errors.ValidationError{Field : "visit_date", Message : "Step #3 - Choose a Date for the Visit"}) result = append(result, errors.ValidationError{Field: "visit_date", Message: "Step #3 - Choose a Date for the Visit"})
}else{ } else {
dayBeforeToday := time.Now().Add(-time.Hour*hoursInDay) dayBeforeToday := time.Now().Add(-time.Hour * hoursInDay)
if requestRide.VisitDate.Before(dayBeforeToday) { if requestRide.VisitDate.Before(dayBeforeToday) {
result = append (result, errors.ValidationError{Field : "visit_date", Message : "Step #3 - Visit cannot occur more than one day before today"}) result = append(result, errors.ValidationError{Field: "visit_date", Message: "Step #3 - Visit cannot occur more than one day before today"})
} }
dayAfter180Days := time.Now().Add(time.Hour*hoursIn180Days) dayAfter180Days := time.Now().Add(time.Hour * hoursIn180Days)
if requestRide.VisitDate.After(dayAfter180Days) { if requestRide.VisitDate.After(dayAfter180Days) {
result = append (result, errors.ValidationError{Field : "visit_date", Message : "Step #3 - Visit cannot occur more than 180 days after today"}) result = append(result, errors.ValidationError{Field: "visit_date", Message: "Step #3 - Visit cannot occur more than 180 days after today"})
} }
if requestRide.VisitTime == nil { if requestRide.VisitTime == nil {
result = append (result, errors.ValidationError{Field : "visit_time", Message : "Step #3 - Choose a Time for the Visit"}) result = append(result, errors.ValidationError{Field: "visit_time", Message: "Step #3 - Choose a Time for the Visit"})
}else{ } else {
if isVisitDayToday && requestRide.VisitTime.Before(before8Hours) { if isVisitDayToday && requestRide.VisitTime.Before(before8Hours) {
result = append (result, errors.ValidationError{Field : "visit_time", Message : "Step #3 - Visit is more than 8 hours in the past"}) result = append(result, errors.ValidationError{Field: "visit_time", Message: "Step #3 - Visit is more than 8 hours in the past"})
} }
} }
} }
//Step #4 validation //Step #4 validation
timeWithDurationAndLoadingTime := requestRide.VisitTime.Add(-time.Duration(requestRide.Duration)*time.Second).Add(-loadingTime*time.Minute) timeWithDurationAndLoadingTime := requestRide.VisitTime.Add(-time.Duration(requestRide.Duration) * time.Second).Add(-loadingTime * time.Minute)
after10Minutes := time.Now().Add(time.Minute*time10Minutes) after10Minutes := time.Now().Add(time.Minute * time10Minutes)
isTripTypeValid := true isTripTypeValid := true
switch requestRide.TripType.Key { switch requestRide.TripType.Key {
case tripTypeToVisit: case tripTypeToVisit:
if requestRide.PickupTime == nil { if requestRide.PickupTime == nil {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Choose a Pickup Time"}) result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Choose a Pickup Time"})
}else{ } else {
if requestRide.PickupTime.After(*requestRide.VisitTime) { if requestRide.PickupTime.After(*requestRide.VisitTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time must occur before Visit Time"}) result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Pickup Time must occur before Visit Time"})
}
if requestRide.PickupTime.After(timeWithDurationAndLoadingTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time less than required time before Visit Time"})
}
if isVisitDayToday && requestRide.PickupTime.Before(before8Hours) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Visit cannot occour in the past"})
}
} }
case tripTypeFromVisit : if requestRide.PickupTime.After(timeWithDurationAndLoadingTime) {
if requestRide.PickupTime == nil { result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Pickup Time less than required time before Visit Time"})
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Choose a Pickup Time"})
}else{
timeWithMinimumPickupTime := time.Now().Add(minimumPickupTime*time.Minute)
if isVisitDayToday && requestRide.PickupTime.Before(timeWithMinimumPickupTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : fmt.Sprint("Step #4 - Time must be more than %d minutes from now",minimumPickupTime)})
}
if requestRide.PickupTime.Before(*requestRide.VisitTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time less than required time after Visit Time"})
}
timeWithMinimumLoadTime := time.Now().Add(minimumLoadTime*time.Minute)
if requestRide.PickupTime.Before(timeWithMinimumLoadTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time less than Minimum Load Time before Visit Time"})
}
} }
case tripTypeFromVisitWillCall: if isVisitDayToday && requestRide.PickupTime.Before(before8Hours) {
//no special validation for this case result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Visit cannot occour in the past"})
}
}
case tripTypeRoundTrip: case tripTypeFromVisit:
if requestRide.PickupTime == nil { if requestRide.PickupTime == nil {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Choose a Pickup Time"}) result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Choose a Pickup Time"})
}else{ } else {
if requestRide.PickupTime.After(*requestRide.VisitTime) { timeWithMinimumPickupTime := time.Now().Add(minimumPickupTime * time.Minute)
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time must occur before Visit Time"}) if isVisitDayToday && requestRide.PickupTime.Before(timeWithMinimumPickupTime) {
} result = append(result, errors.ValidationError{Field: "pickup_time", Message: fmt.Sprint("Step #4 - Time must be more than %d minutes from now", minimumPickupTime)})
if requestRide.PickupTime.After(timeWithDurationAndLoadingTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time less than required time before Visit Time"})
}
if isVisitDayToday && requestRide.PickupTime.Before(after10Minutes) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time must be at least 10 minutes from now"})
}
} }
if requestRide.ReturnTime == nil { if requestRide.PickupTime.Before(*requestRide.VisitTime) {
result = append (result, errors.ValidationError{Field : "return_time", Message : "Step #4 - Choose a Pickup Time"}) result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Pickup Time less than required time after Visit Time"})
}else{
if isVisitDayToday {
if requestRide.ReturnTime.Before(before8Hours) {
result = append (result, errors.ValidationError{Field : "return_time", Message : "Step #4 - Return Time is more than 8 hours after Visit Time"})
}
if requestRide.ReturnTime.Before(after10Minutes) {
result = append (result, errors.ValidationError{Field : "return_time", Message : "Step #4 - Return Time must be at least 10 minutes from now"})
}
}
} }
case tripTypeRountTripWillCall: timeWithMinimumLoadTime := time.Now().Add(minimumLoadTime * time.Minute)
if requestRide.PickupTime == nil { if requestRide.PickupTime.Before(timeWithMinimumLoadTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Choose a Pickup Time"}) result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Pickup Time less than Minimum Load Time before Visit Time"})
}else{ }
if requestRide.PickupTime.After(*requestRide.VisitTime) { }
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time must occur before Visit Time"})
} case tripTypeFromVisitWillCall:
//no special validation for this case
if requestRide.PickupTime.After(timeWithDurationAndLoadingTime) {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Pickup Time less than required time after Visit Time"}) case tripTypeRoundTrip:
} if requestRide.PickupTime == nil {
result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Choose a Pickup Time"})
if isVisitDayToday && requestRide.PickupTime.Before(before8Hours) { } else {
result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Visit cannot occur in the past "}) if requestRide.PickupTime.After(*requestRide.VisitTime) {
} result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Pickup Time must occur before Visit Time"})
} }
default: if requestRide.PickupTime.After(timeWithDurationAndLoadingTime) {
isTripTypeValid = false result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Pickup Time less than required time before Visit Time"})
}
if isVisitDayToday && requestRide.PickupTime.Before(after10Minutes) {
result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Pickup Time must be at least 10 minutes from now"})
}
}
if requestRide.ReturnTime == nil {
result = append(result, errors.ValidationError{Field: "return_time", Message: "Step #4 - Choose a Pickup Time"})
} else {
if isVisitDayToday {
if requestRide.ReturnTime.Before(before8Hours) {
result = append(result, errors.ValidationError{Field: "return_time", Message: "Step #4 - Return Time is more than 8 hours after Visit Time"})
}
if requestRide.ReturnTime.Before(after10Minutes) {
result = append(result, errors.ValidationError{Field: "return_time", Message: "Step #4 - Return Time must be at least 10 minutes from now"})
}
}
}
case tripTypeRountTripWillCall:
if requestRide.PickupTime == nil {
result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Choose a Pickup Time"})
} else {
if requestRide.PickupTime.After(*requestRide.VisitTime) {
result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Pickup Time must occur before Visit Time"})
}
if requestRide.PickupTime.After(timeWithDurationAndLoadingTime) {
result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Pickup Time less than required time after Visit Time"})
}
if isVisitDayToday && requestRide.PickupTime.Before(before8Hours) {
result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Visit cannot occur in the past "})
}
}
default:
isTripTypeValid = false
} }
if !isTripTypeValid { if !isTripTypeValid {
result = append (result, errors.ValidationError{Field : "trip_type.key", Message : "Step #4 - Choose a Trip Type"}) result = append(result, errors.ValidationError{Field: "trip_type.key", Message: "Step #4 - Choose a Trip Type"})
} }
return result return result
} }

View File

@@ -1,35 +1,44 @@
package validation package validation
import ( import (
"time"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel" "bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/infra/errors" "bitbucket.org/nemt/nemt-portal-api/infra/errors"
) )
func ValidateVisit(visit *viewmodel.Visit, user *viewmodel.User) []errors.ValidationError { func ValidateVisit(visit *viewmodel.Visit, user *viewmodel.User) []errors.ValidationError {
var result []errors.ValidationError var result []errors.ValidationError
// Step #1
if len(visit.User.First) == 0 { if len(visit.Provider.ProviderUUID) == 0 {
result = append(result, errors.ValidationError{Field: "first", Message: "Step #1 - First Name is mandatory"}) result = append(result, errors.ValidationError{Field: "provider.id", Message: "Step #1 - Provider is required"})
} }
if len(visit.User.Last) == 0 { // Step #2
result = append(result, errors.ValidationError{Field: "last", Message: "Step #1 - Last Name is mandatory"}) eligibilityResult := ValidateEligibility(&visit.User)
if len(eligibilityResult) > 0 {
result = append(result, eligibilityResult...)
} }
if visit.User.Gender == nil || (*visit.User.Gender != "M" && *visit.User.Gender != "F" && *visit.User.Gender != "U") { //Step #3
result = append(result, errors.ValidationError{Field: "gender", Message: "Step #1 - Gender is mandatory"}) if visit.VisitDatetime.IsZero() {
} result = append(result, errors.ValidationError{Field: "visit_datetime", Message: "Step #3 - Choose a Date for the Visit"})
} else {
isVisitDayToday := visit.VisitDatetime.Day() == time.Now().Day() && visit.VisitDatetime.Month() == time.Now().Month() && visit.VisitDatetime.Year() == time.Now().Year()
before8Hours := time.Now().Add(-time.Hour * time8Hours)
dayBeforeToday := time.Now().Add(-time.Hour * hoursInDay)
if visit.VisitDatetime.Before(dayBeforeToday) {
result = append(result, errors.ValidationError{Field: "visit_datetime", Message: "Step #3 - Visit cannot occur more than one day before today"})
}
if visit.User.Type == nil || (*visit.User.Type != "S" && *visit.User.Gender != "D" && *visit.User.Gender != "U") { dayAfter180Days := time.Now().Add(time.Hour * hoursIn180Days)
result = append(result, errors.ValidationError{Field: "type", Message: "Step #1 - Member Type is mandatory"}) if visit.VisitDatetime.After(dayAfter180Days) {
} result = append(result, errors.ValidationError{Field: "visit_datetime", Message: "Step #3 - Visit cannot occur more than 180 days after today"})
}
if visit.User.Member == nil || len(*visit.User.Member) == 0 { if isVisitDayToday && visit.VisitDatetime.Before(before8Hours) {
result = append(result, errors.ValidationError{Field: "member", Message: "Step #1 - Member # is mandatory"}) result = append(result, errors.ValidationError{Field: "visit_datetime", Message: "Step #3 - Visit is more than 8 hours in the past"})
} }
if visit.User.BirthDate == nil || visit.User.BirthDate.IsZero() {
result = append(result, errors.ValidationError{Field: "birthdate", Message: "Step #1 - Birth Date is mandatory"})
} }
return result return result

BIN
svijetlastrana Executable file

Binary file not shown.