initial commit 2

This commit is contained in:
Senad Uka
2018-04-25 13:16:36 +02:00
parent c1520d169c
commit 99c10b75fb
167 changed files with 25057 additions and 0 deletions

View File

@@ -0,0 +1,125 @@
package authenticateroute
import (
"sync"
"time"
b64 "encoding/base64"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/infra/auth"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"github.com/labstack/echo"
)
var (
instance *controller
once sync.Once
)
type controller struct {
cfg *config.Config
svc *applicationservice.Service
}
func controllerInstance(cfg *config.Config, svc *applicationservice.Service) *controller {
once.Do(func() {
instance = &controller{
cfg: cfg,
svc: svc,
}
})
return instance
}
func (c *controller) handlePortal(ctx echo.Context) error {
err := auth.ValidateAppKey(ctx, c.cfg)
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 user.Email == nil || len(*user.Email) == 0 || len(user.Pass) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "email and pass are required", false)
}
pass, err := b64.StdEncoding.DecodeString(user.Pass)
if err != nil {
return routeutils.ResponseAPIAuthError(ctx, "Invalid email or password", false)
}
user.Pass = string(pass)
user, err = c.svc.Users.FullLogin("email", *user.Email, user.Pass, "SP")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if user.ID == "" {
return routeutils.ResponseAPIAuthError(ctx, "Invalid email or password", false)
}
token, err := auth.GenerateToken(c.cfg, user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
response := viewmodel.AuthResponse{
Token: token,
ServerTime: time.Now().Unix(),
ValidTime: time.Now().Add(auth.TokenExpiration).Unix(),
User: user,
}
return routeutils.ResponseAPIOK(ctx, response)
}
func (c *controller) handleMember(ctx echo.Context) error {
err := auth.ValidateAppKey(ctx, c.cfg)
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 user.PhoneNumber == nil || len(*user.PhoneNumber) == 0 || len(user.Pass) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "phonenumber and pass are required", false)
}
pass, err := b64.StdEncoding.DecodeString(user.Pass)
if err != nil {
return routeutils.ResponseAPIAuthError(ctx, "Invalid phonenumber or password", false)
}
user.Pass = string(pass)
user, err = c.svc.Users.FullLogin("phone_number", *user.PhoneNumber, user.Pass, "US")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if user.ID == "" {
return routeutils.ResponseAPIAuthError(ctx, "Invalid phonenumber or password", false)
}
token, err := auth.GenerateToken(c.cfg, user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
response := viewmodel.AuthResponse{
Token: token,
ServerTime: time.Now().Unix(),
ValidTime: time.Now().Add(auth.TokenExpiration).Unix(),
User: user,
}
return routeutils.ResponseAPIOK(ctx, response)
}

View File

@@ -0,0 +1,20 @@
package authenticateroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
portalRoute = "/portal"
memberRoute = "/member"
)
// Register authenticate route
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service) {
ctrl := controllerInstance(cfg, svc)
r.POST(portalRoute, ctrl.handlePortal)
r.POST(memberRoute, ctrl.handleMember)
}

View File

@@ -0,0 +1,17 @@
package docsroute
import (
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
rootRoute = "/"
yamlRoute = "/yaml"
)
// Register registers the handlers for doc routes
func Register(r *echo.Group, cfg *config.Config) {
r.Static(yamlRoute, cfg.App.Docs.YAMLPath)
r.Static(rootRoute, cfg.App.Docs.SwaggerPath)
}

View File

@@ -0,0 +1,49 @@
package eligibilityroute
import (
"sync"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/third/eligibility/bcbsi"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"github.com/labstack/echo"
)
var (
instance *controller
once sync.Once
)
type controller struct {
cfg *config.Config
svc *applicationservice.Service
bcbsi *bcbsi.Service
}
func controllerInstance(cfg *config.Config, svc *applicationservice.Service) *controller {
once.Do(func() {
instance = &controller{
cfg: cfg,
svc: svc,
bcbsi: bcbsi.New(cfg),
}
})
return instance
}
func (c *controller) handleEligibility(ctx echo.Context) error {
var eligibility viewmodel.Eligibility
if err := ctx.Bind(&eligibility); err != nil {
return routeutils.HandleAPIError(ctx, err)
}
ret, err := c.bcbsi.BXE.CheckEligibility(eligibility)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, ret)
}

View File

@@ -0,0 +1,17 @@
package eligibilityroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
rootRoute = ""
)
// Register authenticate route
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service) {
ctrl := controllerInstance(cfg, svc)
r.POST(rootRoute, ctrl.handleEligibility)
}

View File

@@ -0,0 +1,290 @@
package externalroute
import (
"fmt"
"strings"
"sync"
"time"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
"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"
)
var (
instance *controller
once sync.Once
)
type controller struct {
cfg *config.Config
svc *applicationservice.Service
tnc *tncservice.Service
notification *notificationservice.Service
}
func controllerInstance(cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service, notification *notificationservice.Service) *controller {
once.Do(func() {
instance = &controller{
cfg: cfg,
svc: svc,
tnc: tnc,
notification: notification,
}
})
return instance
}
func (c *controller) getDriverNotification(ride viewmodel.Ride, notificationType string, subject string, message string, to string, from string) viewmodel.Notification {
retVal := viewmodel.Notification{
Type: notificationType,
Message: message,
Subject: subject,
Ride: ride,
User: ride.User,
CreatedUser: ride.CreatedUser,
To: to,
From: from,
}
return retVal
}
func (c *controller) handleCancel(ctx echo.Context) error {
var rideUUID = ctx.Param("ride_uuid")
var userUUID = ctx.Param("user_uuid")
ride, err := c.svc.Rides.GetByUUIDAndUserUUID(rideUUID, userUUID)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
lyftRide := viewmodel.RideRequest{RideID: ride.InternalID}
if ride.Status.Key == "scheduled" && !strings.Contains(ride.InternalID, "s_") {
lyftRide.RideID = "s_" + ride.InternalID
}
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID {
if err = c.tnc.Lyft.CancelRide(lyftRide); err != nil {
if err.Error() != "ride_not_found" {
fmt.Println("Error to cancel with Lyft: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
}
} else {
fmt.Println("In Production")
if err = c.tnc.LyftProd.CancelRide(lyftRide); err != nil {
fmt.Println("Error to cancel with Lyft: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
}
err = c.svc.Rides.UpdateStatus(rideUUID, "canceled")
if err != nil {
fmt.Println("Error: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
// ride.Status.Key = "canceled"
// err = c.svc.Notification.SendNotification(ride.Status.Key, ride, viewmodel.RideRequest{})
// if err != nil {
// fmt.Println("Error to notify: ", err.Error())
// }
ctx.Response().Header().Set("Content-Type", "application/json")
return routeutils.ResponseNoContent(ctx, nil)
}
func (c *controller) handleMessage(ctx echo.Context) error {
var rideUUID = ctx.Param("ride_uuid")
var userUUID = ctx.Param("user_uuid")
ride, err := c.svc.Rides.GetByUUIDAndUserUUID(rideUUID, userUUID)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
message := make(map[string]string)
err = ctx.Bind(&message)
if err != nil || message["message"] == "" {
return routeutils.ResponseAPIValidationError(ctx, "message body param is mandatory")
}
if err != nil || message["to"] == "" {
return routeutils.ResponseAPIValidationError(ctx, "to param is mandatory")
}
notifications := make([]viewmodel.Notification, 0)
if message["to"] == "driver" {
notifications = append(notifications, c.getDriverNotification(ride, "sms", "A message to driver", message["message"], ride.Driver.PhoneNumber, *ride.User.PhoneNumber))
} else {
notifications = append(notifications, c.getDriverNotification(ride, "sms", "A message to dispatcher", message["message"], *ride.CreatedUser.PhoneNumber, *ride.User.PhoneNumber))
notifications = append(notifications, c.getDriverNotification(ride, "email", "A message to dispatcher", message["message"], *ride.CreatedUser.Email, *ride.User.Email))
}
notifications, err = c.svc.Notification.SendNotifications(notifications)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, notifications)
}
func (c *controller) handle(ctx echo.Context) error {
var rideUUID = ctx.Param("ride_uuid")
var userUUID = ctx.Param("user_uuid")
ride, err := c.svc.Rides.GetByUUIDAndUserUUID(rideUUID, userUUID)
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" {
var lyftRide viewmodel.RideRequest
var err error
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
} else {
fmt.Println("In Production")
lyftRide, err = c.tnc.LyftProd.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
}
if err != nil {
fmt.Println("Error: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
ride.Route.Location = ride.Route.Origin
ride.Route.Location.Address = ""
ride.Route.Location.Name = ""
switch ride.Status.Key {
case "accepted":
if lyftRide.Origin.ETASeconds != nil {
ride.Route.ETA = *lyftRide.Origin.ETASeconds
} else {
ride.Route.ETA = 0
if c.cfg.App.Debug {
ride.Route.ETA = 350
}
}
case "pickedUp":
case "arrived":
if lyftRide.Destination.ETASeconds != nil {
ride.Route.ETA = *lyftRide.Destination.ETASeconds
} else {
ride.Route.ETA = 0
if c.cfg.App.Debug {
ride.Route.ETA = 350
}
}
}
if lyftRide.Location.Latitude != 0 && lyftRide.Location.Longitude != 0 {
ride.Route.Location.Latitude = lyftRide.Location.Latitude
ride.Route.Location.Longitude = lyftRide.Location.Longitude
ride.Route.Location.Bearing = lyftRide.Location.Bearing
}
}
ride.InternalID = ""
ride.User.Gender = nil
ride.User.Email = nil
ride.User.PhoneNumber = nil
ride.Passenger = viewmodel.UserLyft{}
// ride.Visit = viewmodel.Visit{
// ExternalID: ride.Visit.ExternalID,
// }
ride.CreatedUser = viewmodel.User{}
return routeutils.ResponseAPIOK(ctx, ride)
}
func (c *controller) handleReady(ctx echo.Context) error {
var rideUUID = ctx.Param("ride_uuid")
var userUUID = ctx.Param("user_uuid")
ride, err := c.svc.Rides.GetByUUIDAndUserUUID(rideUUID, userUUID)
if err != nil {
fmt.Println("Error: ", err.Error())
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 nextRide viewmodel.Ride
if ride.TripType.Key == "from_visit_call" && ride.Status.Key == "willCall" {
nextRide = ride
} else if ride.Visit.TripType.Key == "roundtrip_call" && ride.TripType.Key == "to_visit" {
roundTripRide, err := c.svc.Rides.GetByVisitUUIDAndTripType(ride.Visit.UUID, "from_visit_call", authUser)
if err != nil {
fmt.Println("Error to get next ride: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
nextRide = roundTripRide
} else {
fmt.Println("Error on get next ride ")
return routeutils.ResponseAPINotFoundError(ctx)
}
var lyftRide viewmodel.RideRequest
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
} else {
fmt.Println("In Production")
lyftRide, err = c.tnc.LyftProd.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
}
if err != nil {
fmt.Println("Error: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
lyftRide.RideID = ""
lyftRide.Destination = ride.Route.Destination
lyftRide.Origin = ride.Route.Origin
name := nextRide.User.Name
names := strings.Split(name, " ")
lyftRide.Passenger.FirstName = names[0]
lyftRide.Passenger.LastName = " "
lyftRide.Passenger.PhoneNumber = *nextRide.User.PhoneNumber
lyftRide.RideType = "lyft"
if c.cfg.LyftProd.UserUUID != nextRide.CreatedUser.ID {
lyftRide, err = c.tnc.Lyft.RequestRide(lyftRide)
} else {
fmt.Println("In Production")
lyftRide, err = c.tnc.LyftProd.RequestRide(lyftRide)
}
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
currentTime := time.Now()
lyftRide.PickupTime = &currentTime
lyftRide.ReturnTime = &currentTime
nextRide.PickupTime = &currentTime
nextRide, err = c.svc.Rides.UpdateNewRide(nextRide, lyftRide, authUser)
if err != nil {
fmt.Println("Error: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
go func() {
err = c.svc.Notification.SendNotification(lyftRide.Status, nextRide, lyftRide)
if err != nil {
fmt.Println("Error to notify user: ", err.Error())
}
}()
return routeutils.ResponseAPIOK(ctx, nextRide)
}

View File

@@ -0,0 +1,25 @@
package externalroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
rootRoute = "/:ride_uuid/:user_uuid"
cancelRoute = "/:ride_uuid/:user_uuid/cancel"
messageRoute = "/:ride_uuid/:user_uuid/message"
readyRoute = "/:ride_uuid/:user_uuid/ready"
)
// Register authenticate route
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service, notification *notificationservice.Service) {
ctrl := controllerInstance(cfg, svc, tnc, notification)
r.GET(rootRoute, ctrl.handle)
r.PUT(cancelRoute, ctrl.handleCancel)
r.POST(messageRoute, ctrl.handleMessage)
r.POST(readyRoute, ctrl.handleReady)
}

View File

@@ -0,0 +1,27 @@
package healthroute
import (
"net/http"
"sync"
"github.com/labstack/echo"
)
var (
instance *controller
once sync.Once
)
type controller struct {
}
func controllerInstance() *controller {
once.Do(func() {
instance = &controller{}
})
return instance
}
func (c *controller) handle(ctx echo.Context) error {
return ctx.String(http.StatusOK, "OK")
}

View File

@@ -0,0 +1,15 @@
package healthroute
import (
"github.com/labstack/echo"
)
const (
rootRoute = "/"
)
func Register(r *echo.Group) {
ctrl := controllerInstance()
r.GET(rootRoute, ctrl.handle)
}

View File

@@ -0,0 +1,193 @@
package lyfthookroute
import (
"fmt"
"io/ioutil"
"strings"
"sync"
"time"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
"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"
"github.com/pquerna/ffjson/ffjson"
)
var (
instance *controller
once sync.Once
)
type controller struct {
cfg *config.Config
svc *applicationservice.Service
tnc *tncservice.Service
notification *notificationservice.Service
}
func controllerInstance(cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service, notification *notificationservice.Service) *controller {
once.Do(func() {
instance = &controller{
cfg: cfg,
svc: svc,
tnc: tnc,
notification: notification,
}
})
return instance
}
func (c *controller) handleStateChange(ctx echo.Context) error {
var rideUUID = ctx.Param("ride_uuid")
var status = ctx.Param("status")
user, err := c.svc.Users.GetByUUID("573c70ff-733d-11e7-ba8f-0a6ad3fcdeaa", "")
if err != nil {
fmt.Println("Error: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
ride, err := c.svc.Rides.GetByUUID(rideUUID, user)
if err != nil {
fmt.Println("Error: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
// var lyftRide viewmodel.RideRequest
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID {
_, err = c.tnc.Lyft.GetRideStatus(viewmodel.RideRequest{RideID: ride.InternalID}, status)
go func() {
secondCall := func() {
ride.InternalID = "s_" + ride.InternalID
c.tnc.Lyft.GetRideStatus(viewmodel.RideRequest{RideID: ride.InternalID}, status)
}
time.AfterFunc(1*time.Second, secondCall)
}()
} else {
_, err = c.tnc.LyftProd.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
}
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
// if ride.Route.ETA ==
// UUID, _ := uuid.NewV4()
// hook := viewmodel.WebhookResponse{
// Event: lyftRide,
// EventID: UUID.String(),
// EventType: "ride.status.updated",
// HREF: "Test",
// OccurredAt: time.Now().UTC().Format("2006-01-02T15:04:05-0700"),
// }
// ride, err = c.svc.Rides.Update(hook)
// if err != nil {
// fmt.Println("Error: ", err.Error())
// return routeutils.HandleAPIError(ctx, err)
// }
// lyftRide.Driver.FirstName = ride.Driver.FirstName
// lyftRide.Driver.LastName = ride.Driver.LastName
// lyftRide.Status = ride.Status.Key
// lyftRide.Vehicle.Color = ride.Vehicle.Color
// lyftRide.Vehicle.LicensePlate = ride.Vehicle.LicensePlate
// lyftRide.Vehicle.Make = ride.Vehicle.Make
// lyftRide.Vehicle.Model = ride.Vehicle.Model
// lyftRide.Destination.ETASeconds = &ride.Route.ETA
// if lyftRide.Status == "canceled" {
// lyftRide.CanceledBy = "driver"
// }
// if ride.Status.Key == "accepted" || ride.Status.Key == "pickedUp" || ride.Status.Key == "arrived" {
// ride.Route.Location = ride.Route.Origin
// ride.Route.Location.Address = ""
// ride.Route.Location.Name = ""
// switch ride.Status.Key {
// case "accepted":
// if lyftRide.Origin.ETASeconds != nil {
// ride.Route.ETA = *lyftRide.Origin.ETASeconds
// } else {
// ride.Route.ETA = 0
// if c.cfg.App.Debug {
// ride.Route.ETA = 350
// }
// }
// case "pickedUp":
// case "arrived":
// if lyftRide.Destination.ETASeconds != nil {
// ride.Route.ETA = *lyftRide.Destination.ETASeconds
// } else {
// ride.Route.ETA = 0
// if c.cfg.App.Debug {
// ride.Route.ETA = 350
// }
// }
// }
// if lyftRide.Location.Latitude != 0 && lyftRide.Location.Longitude != 0 {
// ride.Route.Location.Latitude = lyftRide.Location.Latitude
// ride.Route.Location.Longitude = lyftRide.Location.Longitude
// ride.Route.Location.Bearing = lyftRide.Location.Bearing
// }
// }
// err = c.svc.Notification.SendNotification(status, ride, lyftRide)
// if err != nil {
// fmt.Println("Error: ", err.Error())
// return routeutils.HandleAPIError(ctx, err)
// }
return routeutils.ResponseAPIOK(ctx, ride)
}
func (c *controller) handle(ctx echo.Context) error {
fmt.Println("RECEIVING HOOK: ")
body, err := ioutil.ReadAll(ctx.Request().Body)
if err != nil {
fmt.Println("Error to read the body of the webhook: ", err.Error())
} else {
fmt.Println(string(body))
}
var requestRide viewmodel.WebhookResponse
err = ffjson.Unmarshal(body, &requestRide)
if err != nil {
fmt.Println(err)
return routeutils.ResponseAPIValidationError(ctx, "invalid parameters")
}
defer ctx.Request().Body.Close()
// err = ctx.Bind(&requestRide)
// if err != nil {
// fmt.Println(err)
// return routeutils.ResponseAPIValidationError(ctx, "invalid parameters")
// }
requestRide.Event.RideID = strings.Replace(requestRide.Event.RideID, "s_", "", -1)
requestRide.Event.RideID = strings.Replace(requestRide.Event.RideID, "sandboxevent-", "", -1)
ride, err := c.svc.Rides.Update(requestRide)
if err != nil {
fmt.Println("Error: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
switch requestRide.EventType {
case "ride.status.updated":
go func() {
if err := c.svc.Notification.SendNotification(requestRide.Event.Status, ride, requestRide.Event); err != nil {
fmt.Println("Error: ", err.Error())
}
}()
default:
fmt.Println(requestRide.EventType, " :", requestRide.Event.Status)
}
ctx.Response().Header().Set("Content-Type", "application/json")
return routeutils.ResponseAPIOK(ctx, ride)
}

View File

@@ -0,0 +1,26 @@
package lyfthookroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
rootRoute = ""
stateChangeRoute = "/:ride_uuid/:status"
)
// Register authenticate route
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service, notification *notificationservice.Service) {
ctrl := controllerInstance(cfg, svc, tnc, notification)
r.POST(rootRoute, ctrl.handle)
if cfg.App.Debug {
r.GET(stateChangeRoute, ctrl.handleStateChange)
}
}

View File

@@ -0,0 +1,156 @@
package notificationroute
import (
"fmt"
"net/http"
"sync"
"time"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
"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/gorilla/websocket"
"github.com/labstack/echo"
)
var (
instance *controller
once sync.Once
upgrader = websocket.Upgrader{}
)
type controller struct {
cfg *config.Config
svc *applicationservice.Service
tnc *tncservice.Service
notification *notificationservice.Service
}
func controllerInstance(cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service, notification *notificationservice.Service) *controller {
once.Do(func() {
instance = &controller{
cfg: cfg,
svc: svc,
tnc: tnc,
notification: notification,
}
})
upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
EnableCompression: true,
Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) {
fmt.Println("Error on websocket: ", status, " - ", reason)
},
}
go svc.Notification.DeliverMessage()
return instance
}
func (c *controller) handle(ctx echo.Context) error {
return routeutils.ResponseAPIOK(ctx, "OK")
}
func (c *controller) handleNotification(ctx echo.Context) error {
sRead := ctx.QueryParam("read")
isRead := (sRead == "true")
userUUID, err := routeutils.GetAndValidateStringParam(ctx, "user_uuid", "User ID is mandatory")
if err != nil {
return err
}
contactType, err := routeutils.GetAndValidateStringParam(ctx, "status", "User ID is mandatory")
if err != nil {
return err
}
n, err := c.svc.Notification.GetByUserUUIDAndReadStatus(viewmodel.User{ID: userUUID}, contactType, isRead)
if err != nil {
fmt.Println("Error to get notifications: ", err.Error())
}
return routeutils.ResponseAPIOK(ctx, n)
}
func (c *controller) handleSocket(ctx echo.Context) error {
ws, err := upgrader.Upgrade(ctx.Response(), ctx.Request(), nil)
if err != nil {
return err
}
defer ws.Close()
userUUID := ctx.QueryParam("id")
if userUUID == "" {
fmt.Println("User not found")
}
user, err := c.svc.Users.GetByUUID(userUUID, "SP")
if err != nil {
fmt.Println("User not found on our system: ", err.Error())
}
if user.ID == "" {
user, err = c.svc.Users.GetByUUID(userUUID, "US")
if err != nil {
fmt.Println("User not found on our system: ", err.Error())
}
}
_, err = c.svc.Notification.Subscribe(user, ws)
if err != nil {
fmt.Println("Error to subscribe user: ", err.Error())
}
n, err := c.svc.Notification.GetByUserUUIDAndReadStatus(user, "app", false)
if err != nil {
fmt.Println("Error to get notifications: ", err.Error())
}
loc, _ := time.LoadLocation("America/Chicago")
for _, i := range n {
m := viewmodel.Message{
DeliveryID: user.ID,
NotificationID: i.UUID,
CreateDate: i.Created.In(loc),
Read: i.Read,
Content: viewmodel.MessageContent{
Payload: i.Ride,
Subject: i.Subject,
Content: i.Message,
Type: i.MessageType,
},
}
if err := c.svc.Notification.SendMessage(m); err != nil {
fmt.Println("Error to send message: ", err.Error())
}
}
for {
var m viewmodel.Message
if err := ws.ReadJSON(&m); err != nil {
_, ok := err.(*websocket.CloseError)
if !ok {
fmt.Println("Error to read message: ", err.Error())
}
}
if m.Content.Type == "disable-notification" {
if err := c.svc.Notification.ReadStatus(m.NotificationID, true); err != nil {
fmt.Println("Error to disable notification: ", err.Error())
}
} else {
if err := c.svc.Notification.SendMessage(m); err != nil {
fmt.Println("Error to send message: ", err.Error())
}
}
}
}

View File

@@ -0,0 +1,25 @@
package notificationroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
rootRoute = ""
userNotificationRoute = "/:user_uuid/:status"
webhookRoute = "/ws"
)
// Register authenticate route
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service, notification *notificationservice.Service) {
ctrl := controllerInstance(cfg, svc, tnc, notification)
r.GET(rootRoute, ctrl.handle)
r.GET(webhookRoute, ctrl.handleSocket)
r.GET(userNotificationRoute, ctrl.handleNotification)
}

View File

@@ -0,0 +1,304 @@
package organizationroute
import (
"sync"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/domain"
"bitbucket.org/nemt/nemt-portal-api/infra/auth"
"bitbucket.org/nemt/nemt-portal-api/infra/cache"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"github.com/labstack/echo"
)
var (
instance *controller
once sync.Once
)
type controller struct {
cfg *config.Config
svc *applicationservice.Service
}
func controllerInstance(cfg *config.Config, svc *applicationservice.Service) *controller {
once.Do(func() {
instance = &controller{
cfg: cfg,
svc: svc,
}
})
return instance
}
func (c *controller) handleTypes(ctx echo.Context) error {
cache := cache.Instance(c.cfg)
cacheKey := ctx.Request().Method + ctx.Request().URL.EscapedPath() + ctx.Request().URL.RawQuery
resp := []viewmodel.OrganizationType{}
err := cache.GetStruct(cacheKey, &resp)
if err != nil {
if err != domain.ErrCacheMiss {
ctx.Logger().Errorf(domain.LogProblemGettingFromCache, err)
}
resp, err = c.svc.Organization.GetAllTypes()
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
cache.SetStruct(cacheKey, resp)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleAddOrganization(ctx echo.Context) error {
var org viewmodel.Organization
err := ctx.Bind(&org)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
authUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
org.Author.ID = authUser.ID
org.LastEditor.ID = authUser.ID
resp, err := c.svc.Organization.AddOrganization(org, authUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handle(ctx echo.Context) error {
orgType, _ := routeutils.GetAndValidateStringQueryParam(ctx, "type", "Type is mandatory")
resp, err := c.svc.Organization.GetByType(orgType)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleDetail(ctx echo.Context) error {
orgUUID, err := routeutils.GetAndValidateStringParam(ctx, "org_uuid", "Org ID is mandatory")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
resp, err := c.svc.Organization.GetByUUID(orgUUID)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleParent(ctx echo.Context) error {
var parent viewmodel.Organization
err := ctx.Bind(&parent)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
orgUUID, err := routeutils.GetAndValidateStringParam(ctx, "org_uuid", "Org ID is mandatory")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
resp, err := c.svc.Organization.SetParentOrganization(orgUUID, parent.UUID)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleChild(ctx echo.Context) error {
var child viewmodel.Organization
err := ctx.Bind(&child)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
orgUUID, err := routeutils.GetAndValidateStringParam(ctx, "org_uuid", "Org ID is mandatory")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
_, err = c.svc.Organization.SetParentOrganization(child.UUID, orgUUID)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
resp, err := c.svc.Organization.GetByUUID(orgUUID)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleNameSearch(ctx echo.Context) error {
name, err := routeutils.GetAndValidateStringQueryParam(ctx, "name", "Name is mandatory")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
searchType := ""
searchType, _ = routeutils.GetAndValidateStringQueryParam(ctx, "type", "Type is mandatory")
cache := cache.Instance(c.cfg)
cacheKey := ctx.Request().Method + ctx.Request().URL.RawPath + ctx.Request().URL.RawQuery
resp := []viewmodel.Organization{}
err = cache.GetStruct(cacheKey, &resp)
if err != nil {
if err != domain.ErrCacheMiss {
ctx.Logger().Errorf(domain.LogProblemGettingFromCache, err)
}
resp, err = c.svc.Organization.GetByName(name, searchType)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
cache.SetStruct(cacheKey, resp)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleRemoveAddress(ctx echo.Context) error {
var address viewmodel.OrganizationAddress
err := ctx.Bind(&address)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
orgUUID, err := routeutils.GetAndValidateStringParam(ctx, "org_uuid", "Org ID is mandatory")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
uInt, err := auth.GetTokenDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
createdUser := uInt.(map[string]interface{})
address.UpdatedUser.ID = createdUser["useruuid"].(string)
err = c.svc.Organization.InactivateOrganizationAddress(orgUUID, address)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
resp, err := c.svc.Organization.GetByUUID(orgUUID)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleAddAddress(ctx echo.Context) error {
var address viewmodel.OrganizationAddress
err := ctx.Bind(&address)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
orgUUID, err := routeutils.GetAndValidateStringParam(ctx, "org_uuid", "Org ID is mandatory")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
uInt, err := auth.GetTokenDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
createdUser := uInt.(map[string]interface{})
address.CreatedUser.ID = createdUser["useruuid"].(string)
address.UpdatedUser.ID = address.CreatedUser.ID
_, err = c.svc.Organization.SetOrganizationAddress(orgUUID, address)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
resp, err := c.svc.Organization.GetByUUID(orgUUID)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleRemoveContact(ctx echo.Context) error {
var contact viewmodel.OrganizationContact
err := ctx.Bind(&contact)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
orgUUID, err := routeutils.GetAndValidateStringParam(ctx, "org_uuid", "Org ID is mandatory")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
uInt, err := auth.GetTokenDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
createdUser := uInt.(map[string]interface{})
contact.UpdatedUser.ID = createdUser["useruuid"].(string)
err = c.svc.Organization.InactivateOrganizationContact(orgUUID, contact)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
resp, err := c.svc.Organization.GetByUUID(orgUUID)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleAddContact(ctx echo.Context) error {
var contact viewmodel.OrganizationContact
err := ctx.Bind(&contact)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
orgUUID, err := routeutils.GetAndValidateStringParam(ctx, "org_uuid", "Org ID is mandatory")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
uInt, err := auth.GetTokenDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
createdUser := uInt.(map[string]interface{})
contact.CreatedUser.ID = createdUser["useruuid"].(string)
contact.UpdatedUser.ID = contact.CreatedUser.ID
_, err = c.svc.Organization.SetOrganizationContact(orgUUID, contact)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
resp, err := c.svc.Organization.GetByUUID(orgUUID)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}

View File

@@ -0,0 +1,39 @@
package organizationroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
rootRoute = ""
detailRoute = "/:org_uuid"
parentRoute = "/:org_uuid/parent"
childRoute = "/:org_uuid/child"
addressRoute = "/:org_uuid/address"
contactRoute = "/:org_uuid/contact"
typeRoute = "/type"
nameRoute = "/name"
)
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service) {
ctrl := controllerInstance(cfg, svc)
r.GET(rootRoute, ctrl.handle)
r.POST(rootRoute, ctrl.handleAddOrganization)
r.GET(detailRoute, ctrl.handleDetail)
r.GET(typeRoute, ctrl.handleTypes)
r.GET(nameRoute, ctrl.handleNameSearch)
r.POST(parentRoute, ctrl.handleParent)
r.POST(childRoute, ctrl.handleChild)
r.POST(addressRoute, ctrl.handleAddAddress)
r.PUT(addressRoute, ctrl.handleRemoveAddress)
r.POST(contactRoute, ctrl.handleAddContact)
r.PUT(contactRoute, ctrl.handleRemoveContact)
}

View File

@@ -0,0 +1,55 @@
package placesroute
import (
"sync"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"github.com/labstack/echo"
"golang.org/x/net/context"
"googlemaps.github.io/maps"
)
var (
instance *controller
once sync.Once
)
type controller struct {
cfg *config.Config
svc *applicationservice.Service
maps *maps.Client
}
func controllerInstance(cfg *config.Config, svc *applicationservice.Service) *controller {
once.Do(func() {
c, _ := maps.NewClient(maps.WithClientIDAndSignature("gme-bluecrossandblue1", "msqgD-jdqCyR0M_1u5C1HION5iI="))
instance = &controller{
cfg: cfg,
svc: svc,
maps: c,
}
})
return instance
}
func (c *controller) handle(ctx echo.Context) error {
name, err := routeutils.GetAndValidateStringQueryParam(ctx, "name", "name is mandatory")
if err != nil {
return err
}
req := maps.PlaceAutocompleteRequest{
Input: name,
Language: "en",
}
resp, err := c.maps.PlaceAutocomplete(context.Background(), &req)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}

View File

@@ -0,0 +1,17 @@
package placesroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
rootRoute = ""
)
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service) {
ctrl := controllerInstance(cfg, svc)
r.GET(rootRoute, ctrl.handle)
}

View File

@@ -0,0 +1,103 @@
package profileroute
import (
"sync"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/domain"
"bitbucket.org/nemt/nemt-portal-api/infra/cache"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"github.com/labstack/echo"
)
var (
instance *controller
once sync.Once
)
type controller struct {
cfg *config.Config
svc *applicationservice.Service
}
func controllerInstance(cfg *config.Config, svc *applicationservice.Service) *controller {
once.Do(func() {
instance = &controller{
cfg: cfg,
svc: svc,
}
})
return instance
}
func (c *controller) handle(ctx echo.Context) error {
cache := cache.Instance(c.cfg)
cacheKey := ctx.Request().Method + ctx.Request().URL.EscapedPath() + ctx.Request().URL.RawQuery
resp := []viewmodel.Profile{}
err := cache.GetStruct(cacheKey, &resp)
if err != nil {
if err != domain.ErrCacheMiss {
ctx.Logger().Errorf(domain.LogProblemGettingFromCache, err)
}
resp, err = c.svc.Profile.GetVisibles(true)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
cache.SetStruct(cacheKey, resp)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleKey(ctx echo.Context) error {
key, err := routeutils.GetAndValidateStringParam(ctx, "key", "Key is mandatory")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
cache := cache.Instance(c.cfg)
cacheKey := ctx.Request().Method + ctx.Request().URL.EscapedPath() + ctx.Request().URL.RawQuery
resp := viewmodel.Profile{}
err = cache.GetStruct(cacheKey, &resp)
if err != nil {
if err != domain.ErrCacheMiss {
ctx.Logger().Errorf(domain.LogProblemGettingFromCache, err)
}
resp, err = c.svc.Profile.GetByKey(key)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
cache.SetStruct(cacheKey, resp)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleOrg(ctx echo.Context) error {
orgType, err := routeutils.GetAndValidateStringParam(ctx, "org_key", "Org is mandatory")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
cache := cache.Instance(c.cfg)
cacheKey := ctx.Request().Method + ctx.Request().URL.EscapedPath() + ctx.Request().URL.RawQuery
resp := []viewmodel.Profile{}
err = cache.GetStruct(cacheKey, &resp)
if err != nil {
if err != domain.ErrCacheMiss {
ctx.Logger().Errorf(domain.LogProblemGettingFromCache, err)
}
resp, err = c.svc.Profile.GetByOrganizationType(orgType)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
cache.SetStruct(cacheKey, resp)
}
return routeutils.ResponseAPIOK(ctx, resp)
}

View File

@@ -0,0 +1,21 @@
package profileroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
rootRoute = ""
keyRoute = "/:key"
orgRoute = "/org/:org_key"
)
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service) {
ctrl := controllerInstance(cfg, svc)
r.GET(rootRoute, ctrl.handle)
r.GET(keyRoute, ctrl.handleKey)
r.GET(orgRoute, ctrl.handleOrg)
}

View File

@@ -0,0 +1,178 @@
package providerroute
import (
"sync"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/third/npd"
"bitbucket.org/nemt/nemt-portal-api/application/third/npd/npdmodel"
"bitbucket.org/nemt/nemt-portal-api/infra/auth"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"github.com/labstack/echo"
)
var (
instance *controller
once sync.Once
)
type controller struct {
cfg *config.Config
svc *applicationservice.Service
npd *npd.Service
}
func controllerInstance(cfg *config.Config, svc *applicationservice.Service) *controller {
once.Do(func() {
instance = &controller{
cfg: cfg,
svc: svc,
npd: npd.New(cfg),
}
})
return instance
}
func (c *controller) handle(ctx echo.Context) error {
var providers []npdmodel.ProviderResponse
if err := ctx.Bind(&providers); err != nil {
return routeutils.HandleAPIError(ctx, err)
}
authUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
entities, err := c.svc.Provider.Save(providers, authUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, entities)
}
func (c *controller) handleParticipating(ctx echo.Context) error {
query := ctx.QueryParam("query")
var lat float64
var long float64
var distance int64
distance = 10
planCode := ""
productID := ""
mukID := ""
providerID := ""
sort := "distance"
lat, err := routeutils.GetAndValidateFloatQueryParam(ctx, "lat", "latitude is mandatory")
if err != nil {
lat = 0
}
long, err = routeutils.GetAndValidateFloatQueryParam(ctx, "long", "longitude is mandatory")
if err != nil {
long = 0
}
planCode, _ = routeutils.GetAndValidateStringQueryParam(ctx, "plan_code", "Plan Code is mandatory")
productID, _ = routeutils.GetAndValidateStringQueryParam(ctx, "product_id", "Product ID is mandatory")
providerID, _ = routeutils.GetAndValidateStringQueryParam(ctx, "provider_id", "Provider ID is mandatory")
distance, err = routeutils.GetAndValidateIntQueryParam(ctx, "distance", "distance is mandatory")
if err != nil {
distance = 1000000
}
sort, err = routeutils.GetAndValidateStringQueryParam(ctx, "sort", "sort is mandatory")
if err != nil || (sort != "distance" && sort != "name") {
sort = "distance"
}
mukID, _ = routeutils.GetAndValidateStringQueryParam(ctx, "muk_id", "Muk ID is mandatory")
authUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
// if (lat != 0 && long == 0) || (lat == 0 && long != 0) {
// return routeutils.ResponseAPIFieldValidationError(ctx, "coordinates", "Invalid coordinates")
// } else {
// lat = 41.886406
// long = -87.624225
// }
lat = 40.442875
long = -80.003112
if len(mukID) > 0 {
query = ""
providerID = ""
}
providers, err := c.svc.Provider.Get(query, lat, long, distance, planCode, productID, mukID, providerID, sort, authUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, providers)
}
func (c *controller) handleList(ctx echo.Context) error {
name := ctx.QueryParam("name")
searchBy := ctx.QueryParam("searchBy")
// lat, err := routeutils.GetAndValidateFloatQueryParam(ctx, "lat", "latitude is mandatory")
// if err != nil {
// return err
// }
// long, err := routeutils.GetAndValidateFloatQueryParam(ctx, "long", "longitude is mandatory")
// if err != nil {
// return err
// }
distance, err := routeutils.GetAndValidateIntQueryParam(ctx, "distance", "distance is mandatory")
if err != nil {
distance = 10
}
limit, err := routeutils.GetAndValidateIntQueryParam(ctx, "limit", "limit is mandatory")
if err != nil {
limit = 50
}
sortBy, err := routeutils.GetAndValidateStringQueryParam(ctx, "sortby", "limit is mandatory")
if err != nil || (sortBy != "distance" && sortBy != "name") {
sortBy = "distance"
}
providerParams := npdmodel.ProviderSearchParams{
Name: name,
SearchBy: searchBy,
Latitude: 40.442875,
Longitude: -80.003112,
Distance: distance,
Limit: limit,
Offset: 0,
SortBy: sortBy,
FacetAcceptNewPatients: "Y",
FacetExtendedOfficeHours: "Y",
FacetGender: "Y",
FacetHospitalAffiliations: "Y",
FacetLanguage: "Y",
FacetOfficeLanguages: "Y",
FacetProviderAffiliations: "Y",
FacetProviderEntityName: "Y",
FacetSummaryScore: "Y",
}
providers, err := c.npd.Provider.GetProviders(providerParams)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, providers)
}

View File

@@ -0,0 +1,20 @@
package providerroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
rootRoute = ""
participatingroute = "/participating"
)
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service) {
ctrl := controllerInstance(cfg, svc)
r.GET(rootRoute, ctrl.handleList)
r.GET(participatingroute, ctrl.handleParticipating)
r.POST(rootRoute, ctrl.handle)
}

50
server/router/router.go Normal file
View File

@@ -0,0 +1,50 @@
package router
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/authenticateroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/docsroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/eligibilityroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/externalroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/healthroute"
"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/organizationroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/placesroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/profileroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/providerroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/tncroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/twilioroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/usersroute"
"bitbucket.org/nemt/nemt-portal-api/server/router/visitroute"
"github.com/labstack/echo"
)
// Register registers the API routes
func Register(e *echo.Echo, cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service, notification *notificationservice.Service) {
healthroute.Register(e.Group("/health"))
prefixGroup := e.Group(cfg.HTTP.Prefix)
if cfg.App.Debug {
docsroute.Register(prefixGroup.Group("/docs"), cfg)
}
notificationroute.Register(prefixGroup.Group("/notification"), cfg, svc, tnc, notification)
twilioroute.Register(prefixGroup.Group("/twilio"), cfg, svc, tnc, notification)
lyfthookroute.Register(prefixGroup.Group("/lyfthook"), cfg, svc, tnc, notification)
externalroute.Register(prefixGroup.Group("/ext"), cfg, svc, tnc, notification)
authenticateroute.Register(prefixGroup.Group("/authenticate"), cfg, svc)
appGroup := prefixGroup.Group("/" + cfg.App.Name)
usersroute.Register(appGroup.Group("/users"), cfg, svc)
eligibilityroute.Register(appGroup.Group("/eligibility"), cfg, svc)
tncroute.Register(appGroup.Group("/rides"), cfg, svc, tnc, notification)
visitroute.Register(appGroup.Group("/visits"), cfg, svc)
providerroute.Register(appGroup.Group("/provider"), cfg, svc)
placesroute.Register(appGroup.Group("/places"), cfg, svc)
profileroute.Register(appGroup.Group("/profile"), cfg, svc)
organizationroute.Register(appGroup.Group("/organization"), cfg, svc)
}

View File

@@ -0,0 +1,130 @@
package routeutils
import (
"strconv"
"strings"
"bitbucket.org/nemt/nemt-portal-api/infra/errors"
"github.com/labstack/echo"
)
// GetAndValidateIntParam gets the integer param value and validates it, returning a validation error in case it's invalid
func GetAndValidateIntParam(c echo.Context, paramName string, errorMessage string) (ID int64, err error) {
IDstr := c.Param(paramName)
if strings.TrimSpace(IDstr) == "" {
err = errors.NewValidationError(paramName, errorMessage)
}
ID, err = strconv.ParseInt(IDstr, 10, 64)
if ID == 0 {
err = errors.NewValidationError(paramName, errorMessage)
}
return
}
// GetAndValidateIntQueryParam gets the integer param value and validates it, returning a validation error in case it's invalid
func GetAndValidateIntQueryParam(c echo.Context, paramName string, errorMessage string) (ID int64, err error) {
IDstr := c.QueryParam(paramName)
if strings.TrimSpace(IDstr) == "" {
err = errors.NewValidationError(paramName, errorMessage)
}
ID, err = strconv.ParseInt(IDstr, 10, 64)
if ID == 0 {
err = errors.NewValidationError(paramName, errorMessage)
}
return
}
// GetAndValidateFloatParam gets the integer param value and validates it, returning a validation error in case it's invalid
func GetAndValidateFloatQueryParam(c echo.Context, paramName string, errorMessage string) (ID float64, err error) {
IDstr := c.QueryParam(paramName)
if strings.TrimSpace(IDstr) == "" {
err = errors.NewValidationError(paramName, errorMessage)
}
ID, err = strconv.ParseFloat(IDstr, 64)
if ID == 0 {
err = errors.NewValidationError(paramName, errorMessage)
}
return
}
// GetAndValidateStringQueryParam gets the integer param value and validates it, returning a validation error in case it's invalid
func GetAndValidateStringQueryParam(c echo.Context, paramName string, errorMessage string) (ID string, err error) {
ID = c.QueryParam(paramName)
if strings.TrimSpace(ID) == "" {
err = errors.NewValidationError(paramName, errorMessage)
}
return
}
// GetAndValidateStringParam gets the string param value and validates it, returning a validation error in case it's invalid
func GetAndValidateStringParam(c echo.Context, paramName string, errorMessage string) (ID string, err error) {
ID = c.Param(paramName)
if strings.TrimSpace(ID) == "" {
err = errors.NewValidationError(paramName, errorMessage)
}
return
}
// GetPagingParams gets the standard paging params from the URL, returning a validation error in case it's invalid
func GetPagingParams(c echo.Context) (page int64, quantity int64) {
pg := c.QueryParam("page")
ipp := c.QueryParam("quantity")
page, _ = strconv.ParseInt(pg, 10, 64)
quantity, _ = strconv.ParseInt(ipp, 10, 64)
if page < 1 {
page = 1
}
if quantity < 1 || quantity > 1000 {
quantity = 10
}
return
}
// GetMultipleQueryParam returns a list of values for multiple query params
func GetMultipleQueryParam(c echo.Context, paramName string) (retVal []string) {
retVal = make([]string, 0)
for k, param := range map[string][]string(c.QueryParams()) {
if k == paramName {
for _, value := range param {
retVal = append(retVal, value)
}
break
}
}
return retVal
}
// GetMultipleIntQueryParam returns a list of integer values from multiple query params
func GetMultipleIntQueryParam(c echo.Context, paramName string) (retVal []int64, err error) {
retVal = make([]int64, 0)
for _, value := range GetMultipleQueryParam(c, paramName) {
intValue, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return retVal, errors.Wrap(err)
}
retVal = append(retVal, intValue)
}
return retVal, nil
}

View File

@@ -0,0 +1,204 @@
package routeutils
import (
"database/sql"
"fmt"
"net"
"net/http"
"os/exec"
"reflect"
"strings"
"bitbucket.org/nemt/nemt-portal-api/infra/errors"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/go-sql-driver/mysql"
"github.com/labstack/echo"
)
// resultWrapper has fields for standard message responses
type resultWrapper struct {
Error bool `json:"error,omitempty"`
Message string `json:"message,omitempty"`
Redirect bool `json:"redirect,omitempty"`
Data interface{} `json:"data,omitempty"`
}
// ResponseNoContent returns a standard API success with no content response
func ResponseNoContent(c echo.Context, data interface{}) error {
return c.NoContent(http.StatusNoContent)
}
// ResponseAPIOK returns a standard API success response
func ResponseAPIOK(c echo.Context, data interface{}) error {
return c.JSON(http.StatusOK, data)
}
// ResponseAPIError returns a standard API error to the response
func ResponseAPIError(c echo.Context, status int, message string, redirect bool) error {
returnValue := resultWrapper{
Error: true,
Message: message,
Redirect: redirect,
}
return c.JSON(status, returnValue)
}
// ResponseAPIAuthError returns a standard API auth error to the response
func ResponseAPIAuthError(c echo.Context, message string, redirect bool) error {
return ResponseAPIError(c, http.StatusUnauthorized, message, redirect)
}
// ResponseAPIServiceError returns a standard API service unavailable error to the response
func ResponseAPIServiceError(c echo.Context, message string) error {
return ResponseAPIError(c, http.StatusServiceUnavailable, message, false)
}
// ResponseAPIValidationError returns a standard API validation error to the response
func ResponseAPIValidationError(c echo.Context, message string) error {
return ResponseAPIError(c, http.StatusUnprocessableEntity, message, false)
}
// ResponseAPIFieldValidationError returns a standard API field validation error to the response
func ResponseAPIFieldValidationError(c echo.Context, field string, message string) error {
err := errors.NewValidationError(field, message)
return HandleAPIError(c, err)
}
// ResponseAPINotFoundError returns a standard API not found error to the response
func ResponseAPINotFoundError(c echo.Context) error {
return ResponseAPIError(c, http.StatusNotFound, "Not Found", false)
}
func ignoreDefaultWrappedErrors(c echo.Context, errorToHandle *errors.WrappedError, handler func(echo.Context, error) error) error {
err := errorToHandle.GetOriginalError()
if err != nil {
switch e := (*err).(type) {
case *errors.ValidationError:
case *errors.NotAuthorizedError:
case *errors.HTTPError:
case awserr.Error:
default:
if e != sql.ErrNoRows {
c.Logger().Errorf("%s", e.Error())
}
}
return handler(c, *err)
}
return nil
}
// HandleAPIError applies the default error handling to the response
func HandleAPIError(c echo.Context, errorToHandle error) (err error) {
statusCode := http.StatusServiceUnavailable
errorMessage := "Service Unavailable"
if errorToHandle != nil {
logger := c.Logger()
errorString := errorToHandle.Error()
switch e := errorToHandle.(type) {
case *errors.WrappedError:
return ignoreDefaultWrappedErrors(c, e, HandleAPIError)
case *errors.ApplicationError:
logger.Errorf("Application Error: %s - %v", e.Path, e.Message)
case *errors.ValidationError:
return ResponseAPIValidationError(c, errorString)
case *errors.NotAuthorizedError:
statusCode = http.StatusUnauthorized
errorMessage = errorString
case *errors.HTTPError:
statusCode = e.Status
errorMessage = e.Message
case *mysql.MySQLError:
logger.Errorf("MySQL Error: %v - %v", e.Number, e.Message)
errorMessage = "Service temporarily unavailable"
case net.Error:
logger.Errorf("Network/Connection error: %v", e.Error())
errorMessage = "Service temporarily unavailable"
case *echo.HTTPError:
statusCode = e.Code
errorMessage = fmt.Sprint(e.Message)
case *exec.ExitError:
logger.Errorf("Exec Error: %s - %v", e.ProcessState, e.Error())
default:
if e == sql.ErrNoRows {
logger.Debugf("SQL Error: %v", e.Error())
return ResponseAPINotFoundError(c)
} else if e == sql.ErrTxDone || strings.HasPrefix(errorString, "sql: ") {
logger.Errorf("SQL Error: %v", e.Error())
return ResponseAPIServiceError(c, "Service temporarily unavailable")
}
logger.Errorf("%v: %v", reflect.TypeOf(e), e.Error())
}
}
return ResponseAPIError(c, statusCode, errorMessage, false)
}
// HandleHTMLError applies the default error handling to the response
func HandleHTMLError(c echo.Context, errorToHandle error) error {
statusCode := http.StatusServiceUnavailable
errorMessage := "Service Unavailable"
if errorToHandle != nil {
logger := c.Logger()
errorString := errorToHandle.Error()
switch e := errorToHandle.(type) {
case *errors.WrappedError:
return ignoreDefaultWrappedErrors(c, e, HandleHTMLError)
case *errors.ApplicationError:
logger.Errorf("Application Error: %s - %v", e.Path, e.Message)
case *errors.ValidationError:
return ResponseAPIValidationError(c, errorString)
case *errors.NotAuthorizedError:
return ResponseAPIError(c, http.StatusUnauthorized, errorString, false)
case *errors.HTTPError:
errorMessage = e.Message
case *mysql.MySQLError:
logger.Errorf("MySQL Error: %v - %v", e.Number, e.Message)
errorMessage = "Service temporarily unavailable"
case net.Error:
logger.Errorf("Network/Connection error: %v", e.Error())
errorMessage = "Service temporarily unavailable"
case *echo.HTTPError:
logger.Errorf("%v: %v - %v", reflect.TypeOf(e), e.Code, e.Message)
statusCode = e.Code
default:
if e == sql.ErrNoRows {
logger.Debugf("SQL Error: %v", e.Error())
statusCode = http.StatusNotFound
errorMessage = "Not Found"
} else if e == sql.ErrTxDone || strings.HasPrefix(errorString, "sql: ") {
logger.Errorf("SQL Error: %v", e.Error())
errorMessage = "Service temporarily unavailable"
} else {
logger.Errorf("%v: %v", reflect.TypeOf(e), e.Error())
}
}
}
return c.HTML(statusCode, errorMessage)
}

View File

@@ -0,0 +1,872 @@
package tncroute
import (
"fmt"
"net/http"
"strconv"
"strings"
"sync"
"time"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/infra/auth"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"github.com/labstack/echo"
uuid "github.com/satori/go.uuid"
"google.golang.org/api/googleapi/transport"
urlshortener "google.golang.org/api/urlshortener/v1"
"github.com/gorilla/websocket"
)
var (
instance *controller
once sync.Once
upgrader = websocket.Upgrader{}
)
type controller struct {
cfg *config.Config
svc *applicationservice.Service
tnc *tncservice.Service
notification *notificationservice.Service
}
func controllerInstance(cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service, notification *notificationservice.Service) *controller {
once.Do(func() {
instance = &controller{
cfg: cfg,
svc: svc,
tnc: tnc,
notification: notification,
}
})
return instance
}
func (c *controller) handleSocket(ctx echo.Context) error {
ws, err := upgrader.Upgrade(ctx.Response(), ctx.Request(), nil)
if err != nil {
return err
}
defer ws.Close()
for {
// Write
err := ws.WriteMessage(websocket.TextMessage, []byte("Hello, Client!"))
if err != nil {
ctx.Logger().Error(err)
}
// Read
_, msg, err := ws.ReadMessage()
if err != nil {
ctx.Logger().Error(err)
}
fmt.Printf("%s\n", msg)
}
}
func (c *controller) handleMessage(ctx echo.Context) error {
rideID := ctx.Param("ride_uuid")
if rideID == "" {
return routeutils.ResponseAPIValidationError(ctx, "rideID param is mandatory")
}
message := make(map[string]string)
err := ctx.Bind(&message)
if err != nil || message["message"] == "" {
return routeutils.ResponseAPIValidationError(ctx, "message body param is mandatory")
}
user, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
ride, err := c.svc.Rides.GetByUUID(rideID, user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
notifications := make([]viewmodel.Notification, 0)
notifications = append(notifications, c.svc.Notification.GetNotification(ride, "sms", "A message to member", message["message"], false, "message"))
notifications = append(notifications, c.svc.Notification.GetNotification(ride, "app", "A message to member", message["message"], false, "message"))
notifications, err = c.svc.Notification.SendNotifications(notifications)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, notifications)
}
func (c *controller) getURLShortened(ride viewmodel.Ride) (string, error) {
const (
url = "https://portal.bcbsinstitute.com/#/ride/%s/%s"
)
svc, err := urlshortener.New(&http.Client{
Transport: &transport.APIKey{Key: c.cfg.GoogleShortener.APIKey},
})
if err != nil {
return "", err
}
userURL := fmt.Sprintf(url, ride.UUID, ride.User.ID)
shortURL, err := svc.Url.Insert(&urlshortener.Url{
Kind: "urlshortener#url", // Not really needed
LongUrl: userURL,
}).Do()
if err != nil {
return "", err
}
return shortURL.Id, nil
}
func (c *controller) handleShare(ctx echo.Context) error {
const (
message = "%s shared a link with you for an upcoming ride: %s"
)
rideID := ctx.Param("ride_uuid")
if rideID == "" {
return routeutils.ResponseAPIValidationError(ctx, "rideID param is mandatory")
}
user, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
ride, err := c.svc.Rides.GetByUUID(rideID, user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
url, err := c.getURLShortened(ride)
if err != nil {
fmt.Println("Error to short url: ", err)
}
notifications := make([]viewmodel.Notification, 0)
notifications = append(notifications, c.svc.Notification.GetNotification(ride, "sms", "A ride was shared with you", fmt.Sprintf(message, ride.CreatedUser.Name, url), false, "message"))
notifications = append(notifications, c.svc.Notification.GetNotification(ride, "email", "A ride was shared with you", fmt.Sprintf(message, ride.CreatedUser.Name, url), false, "message"))
notifications = append(notifications, c.svc.Notification.GetNotification(ride, "app", "A ride was shared with you", fmt.Sprintf(message, ride.CreatedUser.Name, url), false, "message"))
notifications, err = c.svc.Notification.SendNotifications(notifications)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, notifications)
}
func (c *controller) handleMessageDriver(ctx echo.Context) error {
rideID := ctx.Param("ride_uuid")
if rideID == "" {
return routeutils.ResponseAPIValidationError(ctx, "rideID param is mandatory")
}
message := make(map[string]string)
err := ctx.Bind(&message)
if err != nil || message["message"] == "" {
return routeutils.ResponseAPIValidationError(ctx, "message body param is mandatory")
}
user, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
ride, err := c.svc.Rides.GetByUUID(rideID, user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
notifications := make([]viewmodel.Notification, 0)
if ride.Status.Key == "accepted" || ride.Status.Key == "arrived" || ride.Status.Key == "pickedUp" {
notifications = append(notifications, c.getDriverNotification(ride, "sms", "A message to driver", message["message"], ride.Driver.PhoneNumber, *ride.CreatedUser.PhoneNumber))
notifications, err = c.svc.Notification.SendNotifications(notifications)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
}
return routeutils.ResponseAPIOK(ctx, notifications)
}
func (c *controller) handleList(ctx echo.Context) error {
user, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
entity, err := c.svc.Rides.GetAll(user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
ctx.Response().Header().Set("Content-Type", "application/json")
return routeutils.ResponseAPIOK(ctx, entity)
}
func (c *controller) handle(ctx echo.Context) error {
var requestRide viewmodel.RideRequest
err := ctx.Bind(&requestRide)
if err != nil {
fmt.Println(err)
return routeutils.ResponseAPIValidationError(ctx, "invalid parameters")
}
user, err := c.svc.Users.GetByUUID(requestRide.UserUUID, "US")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if user.ID == "" {
return routeutils.ResponseAPIValidationError(ctx, "User not found")
}
createdUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
name := user.Name
names := strings.Split(name, " ")
requestRide.Passenger.FirstName = names[0]
requestRide.Passenger.LastName = " "
requestRide.Passenger.PhoneNumber = *user.PhoneNumber
var resp viewmodel.RideRequest
if requestRide.TripType.Key != "from_visit_call" {
if requestRide.TripType.Key == "from_visit" {
newOrigin := requestRide.Origin
requestRide.Origin = requestRide.Destination
requestRide.Destination = newOrigin
}
if c.cfg.LyftProd.UserUUID != createdUser.ID {
resp, err = c.tnc.Lyft.RequestRide(requestRide)
} else {
fmt.Println("In Production")
resp, err = c.tnc.LyftProd.RequestRide(requestRide)
}
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if requestRide.TripType.Key == "from_visit" {
newOrigin := resp.Origin
resp.Origin = resp.Destination
resp.Destination = newOrigin
}
resp.RideID = strings.Replace(resp.RideID, "s_", "", -1)
} else {
resp = requestRide
UUID, _ := uuid.NewV4()
resp.RideID = UUID.String()
}
if resp.Status == "scheduled" || requestRide.TripType.Key == "from_visit_call" {
if requestRide.PickupTime == nil {
currentDate := time.Now()
requestRide.PickupTime = &currentDate
}
requestMS := (requestRide.PickupTime.UnixNano() / int64(time.Millisecond))
generateDate := time.Now()
generateMS := (generateDate.UnixNano() / int64(time.Millisecond))
resp.RequestAt = requestRide.PickupTime
resp.RequestAtMS = &requestMS
resp.GeneratedAt = &generateDate
resp.GeneratedAtMS = &generateMS
}
resp.Passenger.FirstName = names[0]
resp.Passenger.LastName = names[len(names)-1]
resp.Passenger.PhoneNumber = *user.PhoneNumber
if resp.Passenger.ImageURL == nil {
imageURL := " "
resp.Passenger.ImageURL = &imageURL
}
resp.UserUUID = requestRide.UserUUID
if requestRide.TripType.Key == "from_visit" {
resp.Origin.Name = requestRide.Destination.Name
resp.Origin.ID = requestRide.Destination.ID
resp.Destination.Name = requestRide.Origin.Name
resp.Destination.ID = requestRide.Origin.ID
} else {
resp.Origin.Name = requestRide.Origin.Name
resp.Origin.ID = requestRide.Origin.ID
resp.Destination.Name = requestRide.Destination.Name
resp.Destination.ID = requestRide.Destination.ID
}
resp.Distance = requestRide.Distance
resp.Duration = requestRide.Duration
resp.ETA = requestRide.ETA
resp.PickupTime = requestRide.PickupTime
resp.VisitDate = requestRide.VisitDate
resp.VisitTime = requestRide.VisitTime
resp.VisitExternalID = requestRide.VisitExternalID
resp.CreateUserUUID = createdUser.ID
resp.Visit.TripType = requestRide.TripType
if resp.TripType.Key == "from_visit_call" {
resp.Status = "willCall"
resp.Passenger.UserID = &resp.UserUUID
}
resp.TripType.Key = requestRide.TripType.Key
if requestRide.TripType.Key == "roundtrip" || requestRide.TripType.Key == "roundtrip_call" {
resp.TripType.Key = "to_visit"
} else if requestRide.TripType.Key == "from_visit_call" {
resp.TripType.Key = "from_visit_call"
}
entity, err := c.svc.Rides.Save(resp)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
go func() {
err = c.svc.Notification.SendNotification(resp.Status, entity, resp)
if err != nil {
fmt.Println("Error to notify user: ", err.Error())
}
}()
if requestRide.TripType.Key == "roundtrip" || requestRide.TripType.Key == "roundtrip_call" {
newRide := requestRide
if requestRide.TripType.Key == "roundtrip" {
destination := newRide.Origin
newRide.Origin = newRide.Destination
newRide.Destination = destination
if requestRide.ReturnTime != nil {
newRide.PickupTime = requestRide.ReturnTime
}
scheduledRide := make(map[string]interface{})
scheduledRide["timestamp_ms"] = requestRide.PickupTime.Unix()
newRide.ScheduledPickupRange = scheduledRide
newRide.Passenger.FirstName = names[0]
newRide.Passenger.LastName = " "
newRide.Passenger.PhoneNumber = *user.PhoneNumber
if c.cfg.LyftProd.UserUUID != createdUser.ID {
newRide, err = c.tnc.Lyft.RequestRide(newRide)
} else {
fmt.Println("In Production")
newRide, err = c.tnc.LyftProd.RequestRide(newRide)
}
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
destination = newRide.Origin
newRide.Origin = newRide.Destination
newRide.Destination = destination
if newRide.Error != "" {
fmt.Println("Error to schedule a ride on lyft: ", newRide.Error, newRide.ErrorDescription)
} else {
fmt.Println("Ride Scheduled: ", newRide.Status)
}
newRide.TripType.Key = "from_visit"
} else {
newRide.TripType.Key = "from_visit_call"
newRide.Status = "willCall"
newRide.RideID = entity.UUID
}
newRide.Visit = entity.Visit
newRide.PickupTime = requestRide.ReturnTime
requestMS := (newRide.PickupTime.UnixNano() / int64(time.Millisecond))
generateDate := time.Now()
generateMS := (generateDate.UnixNano() / int64(time.Millisecond))
newRide.RequestAt = newRide.PickupTime
newRide.RequestAtMS = &requestMS
newRide.GeneratedAt = &generateDate
newRide.GeneratedAtMS = &generateMS
newRide.Passenger.FirstName = names[0]
newRide.Passenger.LastName = names[len(names)-1]
newRide.Passenger.PhoneNumber = *user.PhoneNumber
newRide.Passenger.UserID = &requestRide.UserUUID
if newRide.Passenger.ImageURL == nil {
imageURL := " "
newRide.Passenger.ImageURL = &imageURL
}
newRide.UserUUID = requestRide.UserUUID
newRide.Origin.Name = requestRide.Destination.Name
newRide.Origin.ID = requestRide.Destination.ID
newRide.Destination.Name = requestRide.Origin.Name
newRide.Destination.ID = requestRide.Origin.ID
newRide.Distance = requestRide.Distance
newRide.Duration = requestRide.Duration
newRide.ETA = requestRide.ETA
newRide.VisitDate = requestRide.VisitDate
newRide.VisitTime = requestRide.VisitTime
newRide.VisitExternalID = requestRide.VisitExternalID
newRide.CreateUserUUID = createdUser.ID
roudtripRide, err := c.svc.Rides.Save(newRide)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
go func() {
err = c.svc.Notification.SendNotification(newRide.Status, roudtripRide, newRide)
if err != nil {
fmt.Println("Error to notify user: ", err.Error())
}
}()
}
ctx.Response().Header().Set("Content-Type", "application/json")
return routeutils.ResponseAPIOK(ctx, entity)
}
func (c *controller) getDriverNotification(ride viewmodel.Ride, notificationType string, subject string, message string, to string, from string) viewmodel.Notification {
retVal := viewmodel.Notification{
Type: notificationType,
Message: message,
Subject: subject,
Ride: ride,
User: ride.User,
CreatedUser: ride.CreatedUser,
To: to,
From: from,
}
return retVal
}
func (c *controller) handleRawLyft(ctx echo.Context) error {
rideUUID := ctx.Param("ride_uuid")
if rideUUID == "" {
return routeutils.ResponseAPIValidationError(ctx, "ride_uuid param is mandatory")
}
user, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
ride, err := c.svc.Rides.GetByUUID(rideUUID, user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
var lyftRide viewmodel.RideRequest
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
} else {
fmt.Println("In Production")
lyftRide, err = c.tnc.LyftProd.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
}
if err != nil {
fmt.Println("Error getting lyft details: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, lyftRide)
}
func (c *controller) handleRideETA(ctx echo.Context) error {
rideUUID := ctx.Param("ride_uuid")
if rideUUID == "" {
return routeutils.ResponseAPIValidationError(ctx, "ride_uuid param is mandatory")
}
user, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
ride, err := c.svc.Rides.GetByUUID(rideUUID, user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if ride.Status.Key == "accepted" || ride.Status.Key == "pickedUp" || ride.Status.Key == "arrived" {
var lyftRide viewmodel.RideRequest
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
} else {
fmt.Println("In Production")
lyftRide, err = c.tnc.LyftProd.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
}
if err != nil {
fmt.Println("Error getting lyft details: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
ride.Route.Location = ride.Route.Origin
ride.Route.Location.Address = ""
ride.Route.Location.Name = ""
switch lyftRide.Status {
case "scheduled":
ride.Route.ETA = ride.PickupTime.UnixNano() / int64(time.Second)
case "accepted":
if lyftRide.Origin.ETASeconds != nil {
ride.Route.ETA = *lyftRide.Origin.ETASeconds
} else {
ride.Route.ETA = 0
if c.cfg.App.Debug {
ride.Route.ETA = 350
}
}
case "arrived", "pickedUp":
if lyftRide.Destination.ETASeconds != nil {
ride.Route.ETA = *lyftRide.Destination.ETASeconds
} else {
ride.Route.ETA = 0
if c.cfg.App.Debug {
ride.Route.ETA = 350
}
}
}
if lyftRide.Location.Latitude != 0 && lyftRide.Location.Longitude != 0 {
ride.Route.Location = lyftRide.Location
}
}
return routeutils.ResponseAPIOK(ctx, ride)
}
func (c *controller) handleCancel(ctx echo.Context) error {
var requestRide viewmodel.RideRequest
rideUUID := ctx.Param("ride_uuid")
if rideUUID == "" {
return routeutils.ResponseAPIValidationError(ctx, "ride_uuid param is mandatory")
}
user, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
ride, err := c.svc.Rides.GetByUUID(rideUUID, user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
requestRide.RideID = ride.InternalID
if ride.Status.Key == "scheduled" && !strings.Contains(ride.InternalID, "s_") {
requestRide.RideID = "s_" + ride.InternalID
}
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID {
err = c.tnc.Lyft.CancelRide(requestRide)
if err != nil && err.Error() == "ride_not_found" {
err = nil
}
} else {
fmt.Println("In Production")
err = c.tnc.LyftProd.CancelRide(requestRide)
}
if err != nil {
fmt.Println("Error: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
err = c.svc.Rides.UpdateStatus(rideUUID, "canceled")
if err != nil {
fmt.Println("Error: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
// ride.Status.Key = "canceled"
// go func() {
// err = c.svc.Notification.SendNotification(ride.Status.Key, ride, viewmodel.RideRequest{})
// if err != nil {
// fmt.Println("Error: ", err.Error())
// }
// }()
ctx.Response().Header().Set("Content-Type", "application/json")
return routeutils.ResponseNoContent(ctx, nil)
}
func (c *controller) handleETA(ctx echo.Context) error {
sLat := ctx.Param("lat")
if sLat == "" {
return routeutils.ResponseAPIValidationError(ctx, "lat param is mandatory")
}
sLog := ctx.Param("log")
if sLog == "" {
return routeutils.ResponseAPIValidationError(ctx, "log param is mandatory")
}
sDestLat := ctx.Param("destlat")
if sLat == "" {
return routeutils.ResponseAPIValidationError(ctx, "lat param is mandatory")
}
sDestLog := ctx.Param("destlong")
if sLog == "" {
return routeutils.ResponseAPIValidationError(ctx, "log param is mandatory")
}
lat, err := strconv.ParseFloat(strings.TrimSpace(sLat), 64)
if err != nil {
return routeutils.ResponseAPIValidationError(ctx, "lat invalid")
}
log, err := strconv.ParseFloat(strings.TrimSpace(sLog), 64)
if err != nil {
return routeutils.ResponseAPIValidationError(ctx, "log invalid")
}
destlat, err := strconv.ParseFloat(strings.TrimSpace(sDestLat), 64)
if err != nil {
return routeutils.ResponseAPIValidationError(ctx, "destination lat invalid")
}
destlog, err := strconv.ParseFloat(strings.TrimSpace(sDestLog), 64)
if err != nil {
return routeutils.ResponseAPIValidationError(ctx, "destination log invalid")
}
params := map[string]interface{}{}
params["endLat"] = destlat
params["endLng"] = destlog
params["rideType"] = "lyft"
resp, err := c.tnc.Lyft.GetCost(lat, log, params)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleRide(ctx echo.Context) error {
rideUUID := ctx.Param("ride_uuid")
if rideUUID == "" {
return routeutils.ResponseAPIValidationError(ctx, "ride_uuid param is mandatory")
}
user, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
ride, err := c.svc.Rides.GetByUUID(rideUUID, user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if ride.Status.Key == "accepted" || ride.Status.Key == "pickedUp" || ride.Status.Key == "arrived" {
var lyftRide viewmodel.RideRequest
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
} else {
fmt.Println("In Production")
lyftRide, err = c.tnc.LyftProd.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
}
if err != nil {
fmt.Println("Error getting lyft details: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
ride.Route.Location = ride.Route.Origin
ride.Route.Location.Address = ""
ride.Route.Location.Name = ""
switch lyftRide.Status {
case "scheduled":
ride.Route.ETA = ride.PickupTime.UnixNano() / int64(time.Second)
case "accepted":
if lyftRide.Origin.ETASeconds != nil {
ride.Route.ETA = *lyftRide.Origin.ETASeconds
} else {
ride.Route.ETA = 0
if c.cfg.App.Debug {
ride.Route.ETA = 350
}
}
case "arrived":
if lyftRide.Destination.ETASeconds != nil {
ride.Route.ETA = *lyftRide.Destination.ETASeconds
} else {
ride.Route.ETA = 0
}
case "pickedUp":
if lyftRide.Destination.ETASeconds != nil {
ride.Route.ETA = *lyftRide.Destination.ETASeconds
} else {
ride.Route.ETA = 0
if c.cfg.App.Debug {
ride.Route.ETA = 350
}
}
}
if lyftRide.Location.Latitude != 0 && lyftRide.Location.Longitude != 0 {
ride.Route.Location = lyftRide.Location
}
}
return routeutils.ResponseAPIOK(ctx, ride)
}
func (c *controller) handleDrivers(ctx echo.Context) error {
sLat := ctx.Param("lat")
if sLat == "" {
return routeutils.ResponseAPIValidationError(ctx, "lat param is mandatory")
}
sLog := ctx.Param("log")
if sLog == "" {
return routeutils.ResponseAPIValidationError(ctx, "log param is mandatory")
}
lat, err := strconv.ParseFloat(strings.TrimSpace(sLat), 64)
if err != nil {
return routeutils.ResponseAPIValidationError(ctx, "lat invalid")
}
log, err := strconv.ParseFloat(strings.TrimSpace(sLog), 64)
if err != nil {
return routeutils.ResponseAPIValidationError(ctx, "log invalid")
}
resp, err := c.tnc.Lyft.GetDrivers(lat, log)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleTypes(ctx echo.Context) error {
sLat := ctx.Param("lat")
if sLat == "" {
return routeutils.ResponseAPIValidationError(ctx, "lat param is mandatory")
}
sLog := ctx.Param("log")
if sLog == "" {
return routeutils.ResponseAPIValidationError(ctx, "log param is mandatory")
}
lat, err := strconv.ParseFloat(strings.TrimSpace(sLat), 64)
if err != nil {
return routeutils.ResponseAPIValidationError(ctx, "lat invalid")
}
log, err := strconv.ParseFloat(strings.TrimSpace(sLog), 64)
if err != nil {
return routeutils.ResponseAPIValidationError(ctx, "log invalid")
}
resp, err := c.tnc.Lyft.GetTypes(lat, log, nil)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleReady(ctx echo.Context) error {
var rideUUID = ctx.Param("ride_uuid")
user, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
ride, err := c.svc.Rides.GetByUUID(rideUUID, user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
var nextRide viewmodel.Ride
if ride.TripType.Key == "from_visit_call" && ride.Status.Key == "willCall" {
nextRide = ride
} else if ride.Visit.TripType.Key == "roundtrip_call" && ride.TripType.Key == "to_visit" {
roundTripRide, err := c.svc.Rides.GetByVisitUUIDAndTripType(ride.Visit.UUID, "from_visit_call", user)
if err != nil {
fmt.Println("Error to get next ride: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
nextRide = roundTripRide
} else {
fmt.Println("Error on get next ride ")
return routeutils.ResponseAPINotFoundError(ctx)
}
var lyftRide viewmodel.RideRequest
if ride.CreatedUser.ID != c.cfg.LyftProd.UserUUID {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
} else {
fmt.Println("In Production")
lyftRide, err = c.tnc.LyftProd.GetRideDetails(viewmodel.RideRequest{RideID: ride.InternalID})
}
if err != nil {
fmt.Println("Error: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
lyftRide.RideID = ""
lyftRide.Destination = ride.Route.Destination
lyftRide.Origin = ride.Route.Origin
name := nextRide.User.Name
names := strings.Split(name, " ")
lyftRide.Passenger.FirstName = names[0]
lyftRide.Passenger.LastName = " "
lyftRide.Passenger.PhoneNumber = *nextRide.User.PhoneNumber
lyftRide.RideType = "lyft"
if c.cfg.LyftProd.UserUUID != nextRide.CreatedUser.ID {
lyftRide, err = c.tnc.Lyft.RequestRide(lyftRide)
} else {
fmt.Println("In Production")
lyftRide, err = c.tnc.LyftProd.RequestRide(lyftRide)
}
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
currentTime := time.Now()
lyftRide.PickupTime = &currentTime
lyftRide.ReturnTime = &currentTime
nextRide.PickupTime = &currentTime
nextRide, err = c.svc.Rides.UpdateNewRide(nextRide, lyftRide, user)
if err != nil {
fmt.Println("Error: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
go func() {
err = c.svc.Notification.SendNotification(lyftRide.Status, nextRide, lyftRide)
if err != nil {
fmt.Println("Error to notify user: ", err.Error())
}
}()
return routeutils.ResponseAPIOK(ctx, nextRide)
}

View File

@@ -0,0 +1,52 @@
package tncroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
rootRoute = ""
rideRoute = "/:ride_uuid"
rawRideRoute = "/:ride_uuid/raw"
cancelRoute = "/:ride_uuid/cancel"
shareRoute = "/:ride_uuid/share"
readyRoute = "/:ride_uuid/ready"
etaDetailsRoute = "/:ride_uuid/eta"
etaRoute = "/eta/:lat/:log/:destlat/:destlong"
ridersRoute = "/drivers/:lat/:log"
typesRoute = "/types/:lat/:log"
messageRoute = "/:ride_uuid/message"
messageDriverRoute = "/:ride_uuid/message/driver"
wsRoute = "/ws"
)
// Register authenticate route
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service, notification *notificationservice.Service) {
ctrl := controllerInstance(cfg, svc, tnc, notification)
r.GET(rootRoute, ctrl.handleList)
r.POST(rootRoute, ctrl.handle)
r.GET(rideRoute, ctrl.handleRide)
r.POST(cancelRoute, ctrl.handleCancel)
r.POST(readyRoute, ctrl.handleReady)
r.POST(messageRoute, ctrl.handleMessage)
r.POST(shareRoute, ctrl.handleShare)
r.POST(messageDriverRoute, ctrl.handleMessageDriver)
r.GET(etaRoute, ctrl.handleETA)
r.GET(etaDetailsRoute, ctrl.handleRideETA)
r.GET(ridersRoute, ctrl.handleDrivers)
r.GET(typesRoute, ctrl.handleTypes)
r.GET(wsRoute, ctrl.handleSocket)
if cfg.App.Debug {
r.GET(rawRideRoute, ctrl.handleRawLyft)
}
}

View File

@@ -0,0 +1,363 @@
package twilioroute
import (
"fmt"
"log"
"net/http"
"strings"
"sync"
"time"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
"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/gorilla/websocket"
"github.com/labstack/echo"
"github.com/ttacon/libphonenumber"
)
var (
instance *controller
once sync.Once
upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
)
type controller struct {
cfg *config.Config
svc *applicationservice.Service
tnc *tncservice.Service
notification *notificationservice.Service
broadcast chan viewmodel.SocketMessage
clients map[*websocket.Conn]viewmodel.SocketMessage
}
func controllerInstance(cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service, notification *notificationservice.Service) *controller {
once.Do(func() {
instance = &controller{
cfg: cfg,
svc: svc,
tnc: tnc,
notification: notification,
broadcast: make(chan viewmodel.SocketMessage),
clients: make(map[*websocket.Conn]viewmodel.SocketMessage),
}
go instance.handleMessages()
})
return instance
}
type Response struct {
Message *Message `xml:"Message,omitempty"`
}
type Message struct {
To string `xml:"to,attr"`
Body string `xml:"Body,omitempty"`
}
func (c *controller) getDriverNotification(ride viewmodel.Ride, notificationType string, subject string, message string, to string, from string) viewmodel.Notification {
retVal := viewmodel.Notification{
Type: notificationType,
Message: message,
Subject: subject,
Ride: ride,
User: ride.User,
CreatedUser: ride.CreatedUser,
To: to,
From: from,
}
return retVal
}
func (c *controller) handleTwilio(ctx echo.Context) error {
var twilioResponse viewmodel.TwilioWebhook
err := ctx.Bind(&twilioResponse)
if err != nil {
fmt.Println("Error binding response: ", err.Error())
return routeutils.HandleAPIError(ctx, err)
}
// bResp, _ := json.MarshalIndent(twilioResponse, "", "\t")
// fmt.Println("Twilio Request")
// fmt.Println(string(bResp))
var resp Response
requestMessage := strings.ToUpper(strings.TrimSpace(twilioResponse.Body))
if requestMessage != "CANCEL RIDE" && requestMessage != "YES" && requestMessage != "NO" && !strings.HasPrefix(requestMessage, "DRIVER") && !strings.HasPrefix(requestMessage, "MEMBER") && requestMessage != "I AM READY" {
lastRide, err := c.svc.Rides.GetLastRideByPhoneNumber(twilioResponse.From, "", "")
if err != nil {
fmt.Println("Error to get the ride: ", err.Error())
}
notifications := make([]viewmodel.Notification, 0)
notifications = append(notifications, c.svc.Notification.GetNotification(lastRide, "sms", "Message to Dispatcher", twilioResponse.Body, true, "message"))
notifications = append(notifications, c.svc.Notification.GetNotification(lastRide, "app", "Message to Dispatcher", twilioResponse.Body, true, "message"))
notifications, err = c.svc.Notification.SendNotifications(notifications)
if err != nil {
fmt.Println("Error to notify: ", err.Error())
}
} else {
toNumber := twilioResponse.From
var message string
var lastRide viewmodel.Ride
isDriver := false
num, _ := libphonenumber.Parse(twilioResponse.From, "US")
if strings.HasPrefix(requestMessage, "MEMBER") {
isDriver = true
lastRide, err = c.svc.Rides.GetLastRideByDriversNumber(twilioResponse.From)
if err != nil {
fmt.Println("Error to get last driver ride: ", err.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))
}
} else {
lastRide, err = c.svc.Rides.GetLastRideByPhoneNumber(twilioResponse.From, "", "")
if err != nil {
fmt.Println("Error to get last ride: ", err.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))
}
}
if !isDriver {
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" {
var readyRide viewmodel.Ride
authUser, err := c.svc.Users.GetByUUID(lastRide.CreatedUser.ID, "")
if err != nil {
fmt.Println("Error to get created user: ", err.Error())
}
if lastRide.TripType.Key == "to_visit" {
readyRide, err = c.svc.Rides.GetByVisitUUIDAndTripType(lastRide.Visit.UUID, "from_visit_call", authUser)
if err != nil || readyRide.UUID == "" {
message = fmt.Sprintf("We received a request to send the return ride from you at %s, but you do not have the a return ride prepared.", libphonenumber.Format(num, libphonenumber.NATIONAL))
}
} else {
readyRide = lastRide
}
switch readyRide.Status.Key {
case "pending", "accepted", "scheduled":
message = "We already request your return ride, you should receive updates soon."
case "willCall":
var lyftRide viewmodel.RideRequest
lyftRide.RideID = ""
lyftRide.Destination = readyRide.Route.Destination
lyftRide.Origin = readyRide.Route.Origin
name := readyRide.User.Name
names := strings.Split(name, " ")
lyftRide.Passenger.FirstName = names[0]
lyftRide.Passenger.LastName = " "
lyftRide.Passenger.PhoneNumber = *readyRide.User.PhoneNumber
lyftRide.RideType = "lyft"
if c.cfg.LyftProd.UserUUID != readyRide.CreatedUser.ID {
lyftRide, err = c.tnc.Lyft.RequestRide(lyftRide)
} else {
fmt.Println("In Production")
lyftRide, err = c.tnc.LyftProd.RequestRide(lyftRide)
}
if err != nil {
fmt.Println("Error to create a lyft ride on twilio: ", err.Error())
message = "There was a problem to call your ride"
}
readyRide, err = c.svc.Rides.UpdateNewRide(readyRide, lyftRide, authUser)
if err != nil {
fmt.Println("Error to update ride: ", err.Error())
message = "There was a problem to call your ride"
}
go func() {
err = c.svc.Notification.SendNotification(readyRide.Status.Key, readyRide, lyftRide)
if err != nil {
fmt.Println("Error to notify user: ", err.Error())
}
}()
message = fmt.Sprintf("Ride requested at %s, for pickup at %s.", lyftRide.RequestAt.Format("03:04 PM"), readyRide.Route.Destination.Name)
}
}
}
if requestMessage == "CANCEL RIDE" && lastRide.UUID != "" {
var visitDate time.Time
if lastRide.VisitTime != nil {
visitDate = *lastRide.VisitTime
} else {
visitDate = *lastRide.VisitDate
}
var pickupDate time.Time
if lastRide.PickupTime != nil {
pickupDate = *lastRide.PickupTime
}
message = fmt.Sprintf("Are you sure you want to cancel a ride to %s visit to %s scheduled for %s?\nReply YES to confirm this cancellation", visitDate.Format("01/02/2006 03:04 PM"), lastRide.Route.Destination.Name, pickupDate.Format("03:04 PM"))
}
if requestMessage == "YES" && lastRide.UUID != "" {
var lyftRide viewmodel.RideRequest
if lastRide.CreatedUser.ID != c.cfg.LyftProd.UserUUID {
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: lastRide.InternalID})
if err != nil {
fmt.Println("Error: ", err.Error())
message = "There was a problem to find your ride"
}
if err = c.tnc.Lyft.CancelRide(lyftRide); err != nil {
fmt.Println("Error to cancel with Lyft: ", err.Error())
message = "There was a problem to cancel your ride with our transportation provider"
}
} else {
fmt.Println("In Production")
lyftRide, err = c.tnc.LyftProd.GetRideDetails(viewmodel.RideRequest{RideID: lastRide.InternalID})
if err != nil {
fmt.Println("Error: ", err.Error())
message = "There was a problem to find your ride"
}
if err = c.tnc.LyftProd.CancelRide(lyftRide); err != nil {
fmt.Println("Error to cancel with Lyft: ", err.Error())
message = "There was a problem to cancel your ride with our transportation provider"
}
}
if err = c.svc.Rides.UpdateStatus(lastRide.UUID, "canceled"); err != nil {
fmt.Println("Error to cancel the ride: ", err.Error())
message = "There was a problem to cancel your ride on our systems"
}
var dateToFormat time.Time
if lastRide.VisitTime != nil {
dateToFormat = *lastRide.VisitTime
} else {
dateToFormat = *lastRide.VisitDate
}
var pickupDate time.Time
if lastRide.PickupTime != nil {
pickupDate = *lastRide.PickupTime
}
lastRide.Status.Key = "cancelled"
message = fmt.Sprintf("A ride to a %s visit to %s scheduled for %s has been cancelled.", dateToFormat.Format("01/02/2006 03:04 PM"), lastRide.Route.Destination.Name, pickupDate.Format("03:04 PM"))
}
// if requestMessage == "NO" && lastRide.UUID != "" {
// var dateToFormat time.Time
// if lastRide.VisitTime != nil {
// dateToFormat = *lastRide.VisitTime
// } else {
// dateToFormat = *lastRide.VisitDate
// }
// message = fmt.Sprintf("You ride is still scheduled on %s at %s", lastRide.Route.Destination.Name, dateToFormat.Format("01/02/2006 03:04 PM"))
// }
if strings.HasPrefix(requestMessage, "DRIVER") && lastRide.UUID != "" {
driverMessage := strings.Replace(requestMessage, "DRIVER", "", -1)
driverMessage = strings.TrimSpace(driverMessage) + "\nReply MEMBER and your message to send a message to the member."
notifications := make([]viewmodel.Notification, 0)
notifications = append(notifications, c.getDriverNotification(lastRide, "sms", "Message to the driver", driverMessage, lastRide.Driver.PhoneNumber, twilioResponse.To))
notifications = append(notifications, c.getDriverNotification(lastRide, "sms", "Message to the driver", requestMessage, *lastRide.CreatedUser.PhoneNumber, *lastRide.User.PhoneNumber))
notifications = append(notifications, c.getDriverNotification(lastRide, "email", "Message to the driver", driverMessage, *lastRide.CreatedUser.Email, *lastRide.User.Email))
notifications, err := c.svc.Notification.SendNotifications(notifications)
if err != nil {
message = "There was a problem to send a message to the driver."
fmt.Println("Error to send drivers notification: ", err.Error())
}
message = "Your message was sent to the driver via SMS."
}
} else {
driverMessage := strings.Replace(requestMessage, "MEMBER", "", -1)
driverMessage = strings.TrimSpace(driverMessage) + "\nReply DRIVER and your message to send a message to the driver."
userPhoneNumber := ""
createUserPhoneNumber := ""
userEmail := ""
createdUserEmail := ""
if lastRide.User.PhoneNumber != nil {
userPhoneNumber = *lastRide.User.PhoneNumber
}
if lastRide.CreatedUser.PhoneNumber != nil {
createUserPhoneNumber = *lastRide.CreatedUser.PhoneNumber
}
if lastRide.User.Email != nil {
userEmail = *lastRide.User.Email
}
if lastRide.CreatedUser.Email != nil {
createdUserEmail = *lastRide.CreatedUser.Email
}
notifications := make([]viewmodel.Notification, 0)
notifications = append(notifications, c.getDriverNotification(lastRide, "sms", "Message to the member from the driver", driverMessage, userPhoneNumber, twilioResponse.To))
notifications = append(notifications, c.getDriverNotification(lastRide, "sms", "Message to the member from the driver", requestMessage, createUserPhoneNumber, userPhoneNumber))
notifications = append(notifications, c.getDriverNotification(lastRide, "email", "Message to the member from the driver", driverMessage, createdUserEmail, userEmail))
notifications, err := c.svc.Notification.SendNotifications(notifications)
if err != nil {
message = "There was a problem to send a message to the member."
fmt.Println("Error to send member notification: ", err.Error())
}
message = "Your message was sent to the member via SMS."
}
resp.Message = &Message{
To: toNumber,
Body: message,
}
}
return ctx.XML(http.StatusOK, resp)
}
func (c *controller) handleMessages() {
for {
msg := <-c.broadcast
for client := range c.clients {
if c.clients[client].From == msg.To {
err := client.WriteJSON(msg)
if err != nil {
log.Printf("error: %v", err)
client.Close()
delete(c.clients, client)
}
}
}
}
}
func (c *controller) handleSocket(ctx echo.Context) error {
ws, err := upgrader.Upgrade(ctx.Response(), ctx.Request(), nil)
if err != nil {
return err
}
defer ws.Close()
c.clients[ws] = viewmodel.SocketMessage{Filled: false}
for {
var msg viewmodel.SocketMessage
err := ws.ReadJSON(&msg)
if err != nil {
log.Printf("error: %v", err)
delete(c.clients, ws)
break
}
msg.Filled = true
c.clients[ws] = msg
}
return nil
}

View File

@@ -0,0 +1,21 @@
package twilioroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
rootRoute = ""
wsRoute = "/ws"
)
// Register authenticate route
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service, notification *notificationservice.Service) {
ctrl := controllerInstance(cfg, svc, tnc, notification)
r.POST(rootRoute, ctrl.handleTwilio)
r.GET(wsRoute, ctrl.handleSocket)
}

View File

@@ -0,0 +1,444 @@
package usersroute
import (
b64 "encoding/base64"
"fmt"
"math/rand"
"sync"
"time"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/domain"
"bitbucket.org/nemt/nemt-portal-api/infra/auth"
"bitbucket.org/nemt/nemt-portal-api/infra/cache"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"github.com/labstack/echo"
)
var (
instance *controller
once sync.Once
)
type controller struct {
svc *applicationservice.Service
cfg *config.Config
}
func controllerInstance(svc *applicationservice.Service, cfg *config.Config) *controller {
once.Do(func() {
instance = &controller{
svc: svc,
cfg: cfg,
}
})
return instance
}
func (c *controller) handleGetAll(ctx echo.Context) error {
page, quantity := routeutils.GetPagingParams(ctx)
list, err := c.svc.Users.GetAll(quantity, page)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, list)
}
func (c *controller) handleGetByID(ctx echo.Context) error {
userID, err := routeutils.GetAndValidateStringParam(ctx, "user_uuid", "mandatory field")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
authUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
item, err := c.svc.Users.GetByUUID(userID, "US")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
rides, err := c.svc.Rides.GetByUserUUID(userID, authUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
item.Rides = rides
return routeutils.ResponseAPIOK(ctx, item)
}
func (c *controller) handlePortalAddress(ctx echo.Context) error {
userID, err := routeutils.GetAndValidateStringParam(ctx, "user_uuid", "mandatory field")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
item, err := c.svc.Users.GetByUUID(userID, "")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
createdUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if item.ID == "" {
return routeutils.ResponseAPIValidationError(ctx, "User not found")
} else {
var Address viewmodel.Address
if err := ctx.Bind(&Address); err != nil {
return routeutils.HandleAPIError(ctx, err)
}
Address.User = item
Address.CreatedUserUUID = createdUser.ID
Address, err = c.svc.Users.SaveAddress(Address)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
Address.User, err = c.svc.Users.GetByUUID(userID, "")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, Address)
}
}
func (c *controller) handleRemoveAddress(ctx echo.Context) error {
addressID, err := routeutils.GetAndValidateStringParam(ctx, "address_uuid", "mandatory field")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
err = c.svc.Users.RemoveAddress(addressID)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseNoContent(ctx, addressID)
}
func (c *controller) handleMemberAddress(ctx echo.Context) error {
userID, err := routeutils.GetAndValidateStringParam(ctx, "user_uuid", "mandatory field")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
item, err := c.svc.Users.GetByUUID(userID, "")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
createdUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if item.ID == "" {
return routeutils.ResponseAPIValidationError(ctx, "User not found")
} else {
var Address viewmodel.Address
if err := ctx.Bind(&Address); err != nil {
return routeutils.HandleAPIError(ctx, err)
}
Address.User = item
Address.CreatedUserUUID = createdUser.ID
Address, err = c.svc.Users.SaveAddress(Address)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
Address.User, err = c.svc.Users.GetByUUID(userID, "US")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, Address)
}
}
func (c *controller) handleMemberDetail(ctx echo.Context) error {
userID, err := routeutils.GetAndValidateStringParam(ctx, "user_uuid", "mandatory field")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
authUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
item, err := c.svc.Users.GetByUUID(userID, "US")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
rides, err := c.svc.Rides.GetByUserUUID(userID, authUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
item.Rides = rides
if item.ID == "" {
return routeutils.ResponseAPIValidationError(ctx, "User not found")
} else {
return routeutils.ResponseAPIOK(ctx, item)
}
}
func (c *controller) handlePortalDetail(ctx echo.Context) error {
userID, err := routeutils.GetAndValidateStringParam(ctx, "user_uuid", "mandatory field")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
item, err := c.svc.Users.GetByUUID(userID, "SP")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if item.ID == "" {
return routeutils.ResponseAPIValidationError(ctx, "User not found")
} else {
return routeutils.ResponseAPIOK(ctx, item)
}
}
func (c *controller) stringWithCharset(length int, charset string) string {
b := make([]byte, length)
var seededRand *rand.Rand = rand.New(
rand.NewSource(time.Now().UnixNano()))
for i := range b {
b[i] = charset[seededRand.Intn(len(charset))]
}
return string(b)
}
func (c *controller) generatePassword(n int) string {
const (
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
)
return c.stringWithCharset(n, charset)
}
func (c *controller) handleGetMember(ctx echo.Context) error {
users, err := c.svc.Users.GetUsersByProfile("US")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
authUser, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
rides, err := c.svc.Rides.GetAll(authUser)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
for i := range users {
for _, r := range rides {
if r.User.ID == users[i].ID {
users[i].Rides = append(users[i].Rides, r)
}
}
}
return routeutils.ResponseAPIOK(ctx, users)
}
func (c *controller) handleGetPortal(ctx echo.Context) error {
users, err := c.svc.Users.GetUsersByProfile("")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, users)
}
func (c *controller) handleMember(ctx echo.Context) error {
var user viewmodel.User
if err := ctx.Bind(&user); err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if user.PhoneNumber == nil && user.Email == nil || len(*user.PhoneNumber) == 0 && len(*user.Email) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "phonenumber or email is required", false)
}
if len(user.Pass) == 0 {
user.Pass = c.generatePassword(8)
} else {
pass, err := b64.StdEncoding.DecodeString(user.Pass)
if err != nil {
return routeutils.ResponseAPIAuthError(ctx, "Invalid password", false)
}
user.Pass = string(pass)
}
if user.BirthDate == nil || user.BirthDate.IsZero() {
return routeutils.ResponseAPIAuthError(ctx, "birthdate is required", false)
}
if user.Member == nil || len(*user.Member) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "member is required", false)
}
if user.Gender == nil || len(*user.Gender) == 0 || (*user.Gender != "M" && *user.Gender != "F" && *user.Gender != "U") {
return routeutils.ResponseAPIAuthError(ctx, "gender is required", false)
}
if len(user.Name) == 0 && len(user.First) == 0 && len(user.Last) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "name is required", false)
}
if len(user.First) != 0 && len(user.Last) != 0 {
user.Name = fmt.Sprintf("%s %s", user.First, user.Last)
}
profile, err := c.svc.Profile.GetByKey("US")
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
user.Profiles = append(user.Profiles, profile)
user, err = c.svc.Users.Create(user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, user)
}
func (c *controller) handleBulkPortal(ctx echo.Context) error {
var users []viewmodel.User
if err := ctx.Bind(&users); err != nil {
return routeutils.HandleAPIError(ctx, err)
}
for i, _ := range users {
if len(users[i].Profiles) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "profile is required", false)
}
if users[i].PhoneNumber == nil || len(*users[i].PhoneNumber) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "phonenumber is required", false)
}
if users[i].Email == nil || len(*users[i].Email) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "email is required", false)
}
if len(users[i].Pass) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "password is required", false)
}
pass, err := b64.StdEncoding.DecodeString(users[i].Pass)
if err != nil {
return routeutils.ResponseAPIAuthError(ctx, "Invalid password", false)
}
users[i].Pass = string(pass)
if len(users[i].Name) == 0 && len(users[i].First) == 0 && len(users[i].Last) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "name is required", false)
}
if len(users[i].First) != 0 && len(users[i].Last) != 0 {
users[i].Name = fmt.Sprintf("%s %s", users[i].First, users[i].Last)
}
}
returnUser, err := c.svc.Users.CreateBulk(users)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, returnUser)
}
func (c *controller) handlePortal(ctx echo.Context) error {
var user viewmodel.User
if err := ctx.Bind(&user); err != nil {
return routeutils.HandleAPIError(ctx, err)
}
if len(user.Profiles) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "profile is required", false)
}
if user.PhoneNumber == nil || len(*user.PhoneNumber) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "phonenumber is required", false)
}
if user.Email == nil || len(*user.Email) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "email is required", false)
}
if len(user.Pass) == 0 {
return routeutils.ResponseAPIAuthError(ctx, "password is required", false)
}
pass, err := b64.StdEncoding.DecodeString(user.Pass)
if err != nil {
return routeutils.ResponseAPIAuthError(ctx, "Invalid password", false)
}
user.Pass = string(pass)
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)
}
user, err = c.svc.Users.Create(user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, user)
}
func (c *controller) handleContactType(ctx echo.Context) error {
cache := cache.Instance(c.cfg)
cacheKey := ctx.Request().Method + ctx.Request().URL.EscapedPath() + ctx.Request().URL.RawQuery
resp := []viewmodel.ContactType{}
err := cache.GetStruct(cacheKey, &resp)
if err != nil {
if err != domain.ErrCacheMiss {
ctx.Logger().Errorf(domain.LogProblemGettingFromCache, err)
}
resp, err = c.svc.Users.GetContactType()
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
cache.SetStruct(cacheKey, resp)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleTokenInfo(ctx echo.Context) error {
user, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, user)
}

View File

@@ -0,0 +1,54 @@
package usersroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
rootRoute = "/"
idRoute = "/:user_uuid/"
memberRoute = "/member"
memberDetailRoute = "/member/:user_uuid"
memberAddressRoute = "/member/:user_uuid/address"
memberRemoveAddressRoute = "/member/:user_uuid/address/:address_uuid"
userRoute = "/portal"
userDetailRoute = "/portal/:user_uuid"
userAddressRoute = "/portal/:user_uuid/address"
userRemoveAddressRoute = "/portal/:user_uuid/address/:address_uuid"
portalRoute = "/portal"
portalBulkRoute = "/portal/bulk"
contacttypeRoute = "/contacttype"
tokenInfoRoute = "/tokeninfo"
)
// Register registers the routes in the echo group
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service) {
ctrl := controllerInstance(svc, cfg)
r.GET(rootRoute, ctrl.handleGetAll)
r.GET(idRoute, ctrl.handleGetByID)
r.POST(memberAddressRoute, ctrl.handleMemberAddress)
r.PUT(memberRemoveAddressRoute, ctrl.handleRemoveAddress)
r.GET(memberDetailRoute, ctrl.handleMemberDetail)
r.GET(memberRoute, ctrl.handleGetMember)
r.GET(userDetailRoute, ctrl.handlePortalDetail)
r.GET(userRoute, ctrl.handleGetPortal)
r.POST(userAddressRoute, ctrl.handlePortalAddress)
r.PUT(userRemoveAddressRoute, ctrl.handleRemoveAddress)
//Can be cached
r.GET(contacttypeRoute, ctrl.handleContactType)
r.POST(memberRoute, ctrl.handleMember)
r.POST(portalRoute, ctrl.handlePortal)
r.POST(portalBulkRoute, ctrl.handleBulkPortal)
if cfg.App.Debug {
r.GET(tokenInfoRoute, ctrl.handleTokenInfo)
}
}

View File

@@ -0,0 +1,64 @@
package visitroute
import (
"sync"
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/infra/auth"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
"github.com/labstack/echo"
)
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,
}
})
return instance
}
func (c *controller) handleGetByID(ctx echo.Context) error {
visit_uuid := ctx.Param("visit_uuid")
if visit_uuid == "" {
return routeutils.ResponseAPIValidationError(ctx, "visit_uuid param is mandatory")
}
user, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
resp, err := c.svc.Visits.GetByUUID(visit_uuid, user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}
func (c *controller) handleGetAll(ctx echo.Context) error {
user, err := auth.GetUserDetail(ctx, c.cfg)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
resp, err := c.svc.Visits.GetAll(user)
if err != nil {
return routeutils.HandleAPIError(ctx, err)
}
return routeutils.ResponseAPIOK(ctx, resp)
}

View File

@@ -0,0 +1,20 @@
package visitroute
import (
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
"github.com/labstack/echo"
)
const (
rootRoute = "/"
idRoute = "/:visit_uuid"
)
// Register registers the routes in the echo group
func Register(r *echo.Group, cfg *config.Config, svc *applicationservice.Service) {
ctrl := controllerInstance(svc, cfg)
r.GET(rootRoute, ctrl.handleGetAll)
r.GET(idRoute, ctrl.handleGetByID)
}