diff --git a/application/applicationservice/notification.go b/application/applicationservice/notification.go index 4d4ea65..1368b74 100644 --- a/application/applicationservice/notification.go +++ b/application/applicationservice/notification.go @@ -550,6 +550,56 @@ func (s *notificationService) ReadStatus(notificationUUID string, isRead bool) e 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 func (s *notificationService) SendNotifications(notifications []viewmodel.Notification) ([]viewmodel.Notification, error) { if len(notifications) > 0 { diff --git a/authorization_policy.csv b/authorization_policy.csv index 0f3e882..e6aa442 100644 --- a/authorization_policy.csv +++ b/authorization_policy.csv @@ -113,3 +113,10 @@ p, BCBSIAD, *, bcbsi, *, *, *, /v1/nemt/eligibility, POST p, BDCAD, *, techsupport, *, *, *, /v1/nemt/eligibility, POST p, PLANAD, *, plan, *, *, *, /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 \ No newline at end of file diff --git a/data/datamysql/zipcode.go b/data/datamysql/zipcode.go index 872e2e2..1b34d9f 100644 --- a/data/datamysql/zipcode.go +++ b/data/datamysql/zipcode.go @@ -62,4 +62,4 @@ func (c *zipcodeRepo) GetAll() ([]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)) -} \ No newline at end of file +} diff --git a/server/router/routeutils/response.go b/server/router/routeutils/response.go index c68fed6..2aab6da 100644 --- a/server/router/routeutils/response.go +++ b/server/router/routeutils/response.go @@ -39,7 +39,7 @@ func ResponseAPIErrorWithData(c echo.Context, status int, message string, redire Error: true, Message: message, Redirect: redirect, - Data: data, + Data: data, } return c.JSON(status, returnValue) @@ -92,11 +92,12 @@ func ResponseAPINotFoundError(c echo.Context) error { 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 { - 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 { return ResponseAPIError(c, http.StatusForbidden, message, false) } diff --git a/server/router/selfregisterroute/controller.go b/server/router/selfregisterroute/controller.go index 5a2631f..51f2b53 100644 --- a/server/router/selfregisterroute/controller.go +++ b/server/router/selfregisterroute/controller.go @@ -16,6 +16,12 @@ import ( "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" +) + var ( instance *controller once sync.Once @@ -49,44 +55,20 @@ func (c *controller) handle(ctx echo.Context) error { 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) if err != nil { return routeutils.ResponseAPIValidationError(ctx, "Invalid password") } user.Pass = string(pass) - if passwordValidationErrors := validation.ValidatePassword(&user); len(passwordValidationErrors) > 0 { - return routeutils.ResponseAPICustomValidationError(ctx, "Password not strong enough", passwordValidationErrors) - } - - if len(user.Name) == 0 && len(user.First) == 0 && len(user.Last) == 0 { - return routeutils.ResponseAPIValidationError(ctx, "name is required") + if validationErrors := validation.ValidateSelfregistration(&user); len(validationErrors) > 0 { + return routeutils.ResponseAPICustomValidationError(ctx, "Self registration failed", validationErrors) } if len(user.First) != 0 && len(user.Last) != 0 { 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) if err != nil { fmt.Println("Error to create organization", err) @@ -139,5 +121,31 @@ func (c *controller) handle(ctx echo.Context) error { 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 { + return routeutils.HandleAPIError(ctx, err) + } + + //Send sms notification to Authorized user + notification = viewmodel.Notification{ + Type: applicationservice.NOtificationTypeSMS, + To: *user.PhoneNumber, + Message: notificationSmsBody, + } + + notification, err = c.svc.Notification.SendNotificationWithoutWritingToDatabase(notification) + if err != nil { + return routeutils.HandleAPIError(ctx, err) + } + return routeutils.ResponseAPIOK(ctx, user) } diff --git a/server/router/usersroute/controller.go b/server/router/usersroute/controller.go index ce5f1c6..17b057b 100644 --- a/server/router/usersroute/controller.go +++ b/server/router/usersroute/controller.go @@ -1,15 +1,15 @@ package usersroute import ( + "bytes" b64 "encoding/base64" + "encoding/json" "fmt" "math/rand" + "net/http" + "strings" "sync" "time" - "net/http" - "encoding/json" - "bytes" - "strings" "bitbucket.org/nemt/nemt-portal-api/application/applicationservice" "bitbucket.org/nemt/nemt-portal-api/application/third/eligibility/bcbsi" @@ -19,8 +19,8 @@ import ( "bitbucket.org/nemt/nemt-portal-api/infra/cache" "bitbucket.org/nemt/nemt-portal-api/infra/config" "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/validation" "github.com/labstack/echo" "golang.org/x/net/context" "googlemaps.github.io/maps" @@ -346,7 +346,7 @@ func (c *controller) handleMember(ctx echo.Context) error { } //Validate member - if validationErrors := validation.ValidateEligibility(&user) ; len(validationErrors) > 0 { + if validationErrors := validation.ValidateEligibility(&user); len(validationErrors) > 0 { return routeutils.ResponseAPICustomValidationError(ctx, "eligibility validation failed", validationErrors) } @@ -409,44 +409,44 @@ func (c *controller) handleMember(ctx echo.Context) error { 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) - } + 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) + return routeutils.ResponseAPINotEligibleError(ctx) } req, _ := http.NewRequest("POST", c.cfg.Eligibility.Url, bytes.NewBuffer(eligibilityJson)) req.Header.Add("App", c.cfg.HTTP.Auth.AppKey) req.Header.Add("Token", ctx.Request().Header.Get("Token")) req.Header.Add("Content-Type", "application/json") - + resp, err := client.Do(req) if err != nil { - return routeutils.HandleAPIError(ctx, err) + return routeutils.ResponseAPINotEligibleError(ctx) } defer resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode > 300 { - return routeutils.ResponseAPINotEligibleWithMessageError(ctx, "Cannot check eligibility") + return routeutils.ResponseAPINotEligibleError(ctx) } eligibilityResponse := viewmodel.Interchange271{} decoder := json.NewDecoder(resp.Body) err = decoder.Decode(&eligibilityResponse) if err != nil { - return routeutils.ResponseAPINotEligibleWithMessageError(ctx, "Cannot check eligibility") + return routeutils.ResponseAPINotEligibleError(ctx) } //================================================================ if len(eligibilityResponse.Division.HealthCareEligibilityResponse.LoopHL0030) < 1 { - return routeutils.ResponseAPINotEligibleWithMessageError(ctx, "Cannot check eligibility") + return routeutils.ResponseAPINotEligibleError(ctx) } address := viewmodel.Address{} @@ -467,17 +467,17 @@ func (c *controller) handleMember(ctx echo.Context) error { 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") + 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) + return routeutils.HandleAPIError(ctx, err) } r := &maps.GeocodingRequest{ @@ -487,7 +487,7 @@ func (c *controller) handleMember(ctx echo.Context) error { result, err := googleMapsAPI.Geocode(context.Background(), r) if err != nil { fmt.Println("Error to instantiate googles api: ", err.Error()) - return routeutils.HandleAPIError(ctx,err) + return routeutils.HandleAPIError(ctx, err) } if len(result) > 0 { @@ -497,7 +497,7 @@ func (c *controller) handleMember(ctx echo.Context) error { _, 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) } } @@ -597,7 +597,7 @@ func (c *controller) handlePortal(ctx echo.Context) error { } 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) } diff --git a/server/validation/selfregister.go b/server/validation/selfregister.go new file mode 100644 index 0000000..b2cf5fa --- /dev/null +++ b/server/validation/selfregister.go @@ -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 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 +} diff --git a/server/validation/tnc.go b/server/validation/tnc.go index 14352da..15c8e73 100644 --- a/server/validation/tnc.go +++ b/server/validation/tnc.go @@ -1,36 +1,34 @@ package validation import ( - - "time" "fmt" - "strconv" "regexp" + "strconv" + "time" "bitbucket.org/nemt/nemt-portal-api/application/viewmodel" "bitbucket.org/nemt/nemt-portal-api/infra/errors" - ) const ( - tripTypeFromVisit = "from_visit" - tripTypeToVisit = "to_visit" - tripTypeFromVisitWillCall = "from_visit_call" - tripTypeRoundTrip = "roundtrip" - tripTypeRountTripWillCall = "roundtrip_call" + tripTypeFromVisit = "from_visit" + tripTypeToVisit = "to_visit" + tripTypeFromVisitWillCall = "from_visit_call" + tripTypeRoundTrip = "roundtrip" + tripTypeRountTripWillCall = "roundtrip_call" ) const ( - loadingTime = 30 //in minutes - minimumLoadTime = 30 //in minutes - minimumPickupTime = 10 //in minutes + loadingTime = 30 //in minutes + minimumLoadTime = 30 //in minutes + minimumPickupTime = 10 //in minutes ) const ( - hoursInDay = 24 - hoursIn180Days = 24*180 - time8Hours = 8 - time10Minutes = 10 + hoursInDay = 24 + hoursIn180Days = 24 * 180 + time8Hours = 8 + time10Minutes = 10 ) func isMixedIDValid(id string) bool { @@ -38,7 +36,7 @@ func isMixedIDValid(id string) bool { hasLowerCase := false hasNumber := false - for _, character := range (id) { + for _, character := range id { hasUpperCase = hasUpperCase || ((character >= 65) && (character <= 90)) hasLowerCase = hasLowerCase || ((character >= 97) && (character <= 122)) hasNumber = hasNumber || ((character >= 48) && (character <= 57)) @@ -53,163 +51,162 @@ func ValidateRide(requestRide *viewmodel.RideRequest, user *viewmodel.User) []er //Step #1 validation - if !validUUIDregex.MatchString(user.ID){ - result = append(result, errors.ValidationError{Field : "user_uuid", Message : "Step #1 - Choose a Member" }) + if !validUUIDregex.MatchString(user.ID) { + result = append(result, errors.ValidationError{Field: "user_uuid", Message: "Step #1 - Choose a Member"}) } fmt.Println("\n\n", requestRide.Origin.ID, "\n\n") if !isMixedIDValid(requestRide.Origin.ID) { //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 - 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 { - 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 fmt.Println("\n\n", requestRide.Destination.ID, "\n\n") if !isMixedIDValid(requestRide.Destination.ID) { //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 { - result = append (result, errors.ValidationError{Field : "destination.id", Message : "Step #2 - Choose a Provider"}) - } + 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"}) + } } //Step #3 validation 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 { - result = append (result, errors.ValidationError{Field : "visit_date", Message : "Step #3 - Choose a Date for the Visit"}) - }else{ - dayBeforeToday := time.Now().Add(-time.Hour*hoursInDay) + result = append(result, errors.ValidationError{Field: "visit_date", Message: "Step #3 - Choose a Date for the Visit"}) + } else { + dayBeforeToday := time.Now().Add(-time.Hour * hoursInDay) 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) { - 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 { - result = append (result, errors.ValidationError{Field : "visit_time", Message : "Step #3 - Choose a Time for the Visit"}) - }else{ + result = append(result, errors.ValidationError{Field: "visit_time", Message: "Step #3 - Choose a Time for the Visit"}) + } else { 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 - timeWithDurationAndLoadingTime := requestRide.VisitTime.Add(-time.Duration(requestRide.Duration)*time.Second).Add(-loadingTime*time.Minute) - after10Minutes := time.Now().Add(time.Minute*time10Minutes) + timeWithDurationAndLoadingTime := requestRide.VisitTime.Add(-time.Duration(requestRide.Duration) * time.Second).Add(-loadingTime * time.Minute) + after10Minutes := time.Now().Add(time.Minute * time10Minutes) isTripTypeValid := true switch requestRide.TripType.Key { - case tripTypeToVisit: - if requestRide.PickupTime == nil { - result = append (result, errors.ValidationError{Field : "pickup_time", Message : "Step #4 - Choose a Pickup Time"}) - }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 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 tripTypeToVisit: + 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"}) } - case tripTypeFromVisit : - if requestRide.PickupTime == nil { - 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"}) - } + 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"}) } - case tripTypeFromVisitWillCall: - //no special validation for this case + if isVisitDayToday && requestRide.PickupTime.Before(before8Hours) { + result = append(result, errors.ValidationError{Field: "pickup_time", Message: "Step #4 - Visit cannot occour in the past"}) + } + } - case tripTypeRoundTrip: - 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 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"}) - } + case tripTypeFromVisit: + if requestRide.PickupTime == nil { + 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.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"}) - } - } + 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"}) } - 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 "}) - } + 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: + //no special validation for this case + + case tripTypeRoundTrip: + 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"}) } - default: - isTripTypeValid = false + 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 { + 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 { - 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 -} \ No newline at end of file +}