initial commit 2
This commit is contained in:
125
server/router/authenticateroute/controller.go
Normal file
125
server/router/authenticateroute/controller.go
Normal 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)
|
||||
}
|
||||
20
server/router/authenticateroute/router.go
Normal file
20
server/router/authenticateroute/router.go
Normal 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)
|
||||
}
|
||||
17
server/router/docsroute/router.go
Normal file
17
server/router/docsroute/router.go
Normal 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)
|
||||
}
|
||||
49
server/router/eligibilityroute/controller.go
Normal file
49
server/router/eligibilityroute/controller.go
Normal 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)
|
||||
}
|
||||
17
server/router/eligibilityroute/router.go
Normal file
17
server/router/eligibilityroute/router.go
Normal 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)
|
||||
}
|
||||
290
server/router/externalroute/controller.go
Normal file
290
server/router/externalroute/controller.go
Normal 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 = ¤tTime
|
||||
lyftRide.ReturnTime = ¤tTime
|
||||
nextRide.PickupTime = ¤tTime
|
||||
|
||||
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)
|
||||
}
|
||||
25
server/router/externalroute/router.go
Normal file
25
server/router/externalroute/router.go
Normal 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)
|
||||
}
|
||||
27
server/router/healthroute/controller.go
Normal file
27
server/router/healthroute/controller.go
Normal 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")
|
||||
}
|
||||
15
server/router/healthroute/router.go
Normal file
15
server/router/healthroute/router.go
Normal 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)
|
||||
}
|
||||
193
server/router/lyfthookroute/controller.go
Normal file
193
server/router/lyfthookroute/controller.go
Normal 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)
|
||||
}
|
||||
26
server/router/lyfthookroute/router.go
Normal file
26
server/router/lyfthookroute/router.go
Normal 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)
|
||||
}
|
||||
}
|
||||
156
server/router/notificationroute/controller.go
Normal file
156
server/router/notificationroute/controller.go
Normal 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
server/router/notificationroute/router.go
Normal file
25
server/router/notificationroute/router.go
Normal 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)
|
||||
}
|
||||
304
server/router/organizationroute/controller.go
Normal file
304
server/router/organizationroute/controller.go
Normal 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)
|
||||
}
|
||||
39
server/router/organizationroute/router.go
Normal file
39
server/router/organizationroute/router.go
Normal 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)
|
||||
}
|
||||
55
server/router/placesroute/controller.go
Normal file
55
server/router/placesroute/controller.go
Normal 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)
|
||||
}
|
||||
17
server/router/placesroute/router.go
Normal file
17
server/router/placesroute/router.go
Normal 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)
|
||||
}
|
||||
103
server/router/profileroute/controller.go
Normal file
103
server/router/profileroute/controller.go
Normal 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)
|
||||
}
|
||||
21
server/router/profileroute/router.go
Normal file
21
server/router/profileroute/router.go
Normal 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)
|
||||
}
|
||||
178
server/router/providerroute/controller.go
Normal file
178
server/router/providerroute/controller.go
Normal 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)
|
||||
}
|
||||
20
server/router/providerroute/router.go
Normal file
20
server/router/providerroute/router.go
Normal 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
50
server/router/router.go
Normal 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)
|
||||
}
|
||||
130
server/router/routeutils/request.go
Normal file
130
server/router/routeutils/request.go
Normal 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
|
||||
}
|
||||
204
server/router/routeutils/response.go
Normal file
204
server/router/routeutils/response.go
Normal 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)
|
||||
}
|
||||
872
server/router/tncroute/controller.go
Normal file
872
server/router/tncroute/controller.go
Normal 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 = ¤tDate
|
||||
}
|
||||
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 = ¤tTime
|
||||
lyftRide.ReturnTime = ¤tTime
|
||||
nextRide.PickupTime = ¤tTime
|
||||
|
||||
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)
|
||||
}
|
||||
52
server/router/tncroute/router.go
Normal file
52
server/router/tncroute/router.go
Normal 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)
|
||||
}
|
||||
|
||||
}
|
||||
363
server/router/twilioroute/controller.go
Normal file
363
server/router/twilioroute/controller.go
Normal 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
|
||||
}
|
||||
21
server/router/twilioroute/router.go
Normal file
21
server/router/twilioroute/router.go
Normal 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)
|
||||
}
|
||||
444
server/router/usersroute/controller.go
Normal file
444
server/router/usersroute/controller.go
Normal 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)
|
||||
}
|
||||
54
server/router/usersroute/router.go
Normal file
54
server/router/usersroute/router.go
Normal 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)
|
||||
}
|
||||
}
|
||||
64
server/router/visitroute/controller.go
Normal file
64
server/router/visitroute/controller.go
Normal 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)
|
||||
}
|
||||
20
server/router/visitroute/router.go
Normal file
20
server/router/visitroute/router.go
Normal 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)
|
||||
}
|
||||
73
server/server.go
Normal file
73
server/server.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
|
||||
"bitbucket.org/nemt/nemt-portal-api/application/entitymapping"
|
||||
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
|
||||
"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
|
||||
"bitbucket.org/nemt/nemt-portal-api/domain/contract"
|
||||
"bitbucket.org/nemt/nemt-portal-api/domain/service"
|
||||
"bitbucket.org/nemt/nemt-portal-api/infra/config"
|
||||
"bitbucket.org/nemt/nemt-portal-api/infra/errors"
|
||||
"bitbucket.org/nemt/nemt-portal-api/infra/logger"
|
||||
"bitbucket.org/nemt/nemt-portal-api/server/router"
|
||||
"bitbucket.org/nemt/nemt-portal-api/server/serverconfig"
|
||||
"github.com/labstack/echo"
|
||||
)
|
||||
|
||||
var (
|
||||
instance *Server
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// Server runs the application
|
||||
type Server struct {
|
||||
srv *echo.Echo
|
||||
svc *service.Service
|
||||
cfg *config.Config
|
||||
log *logger.Logger
|
||||
cache contract.CacheManager
|
||||
}
|
||||
|
||||
// Instance returns an instance of Service
|
||||
func Instance(svc *service.Service, cfg *config.Config, log *logger.Logger, cache contract.CacheManager) *Server {
|
||||
once.Do(func() {
|
||||
instance = &Server{
|
||||
srv: echo.New(),
|
||||
svc: svc,
|
||||
cfg: cfg,
|
||||
log: log,
|
||||
cache: cache,
|
||||
}
|
||||
})
|
||||
return instance
|
||||
}
|
||||
|
||||
// Run setup and executes the server
|
||||
func (s *Server) Run() error {
|
||||
s.srv.HideBanner = true
|
||||
|
||||
s.srv.Debug = s.cfg.App.Debug
|
||||
|
||||
err := serverconfig.SetMiddlewares(s.srv, s.cfg, s.log, s.svc)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
entityMapper := entitymapping.New()
|
||||
notificationService := notificationservice.New(s.svc, entityMapper, s.cfg, s.cache)
|
||||
appService := applicationservice.New(s.svc, entityMapper, notificationService, s.cfg)
|
||||
tncService := tncservice.New(s.svc, entityMapper, s.cfg, notificationService)
|
||||
|
||||
router.Register(s.srv, s.cfg, appService, tncService, notificationService)
|
||||
|
||||
err = s.srv.Start(fmt.Sprintf(":%d", s.cfg.HTTP.Port))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
25
server/serverconfig/auth.go
Normal file
25
server/serverconfig/auth.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package serverconfig
|
||||
|
||||
import (
|
||||
"bitbucket.org/nemt/nemt-portal-api/infra/auth"
|
||||
"bitbucket.org/nemt/nemt-portal-api/infra/config"
|
||||
"bitbucket.org/nemt/nemt-portal-api/infra/errors"
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
)
|
||||
|
||||
func setJWTMiddleware(e *echo.Echo, cfg *config.Config) error {
|
||||
key, err := auth.GetCertPublicKey(cfg.HTTP.Auth.CertificatePath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
e.Pre(middlewareErrorWrapper(middleware.JWTWithConfig(middleware.JWTConfig{
|
||||
TokenLookup: "header:" + auth.AppTokenHeaderName,
|
||||
Skipper: authSkipper,
|
||||
SigningKey: key,
|
||||
SigningMethod: auth.TokenSigningMethod.Name,
|
||||
})))
|
||||
|
||||
return nil
|
||||
}
|
||||
93
server/serverconfig/authorization.go
Normal file
93
server/serverconfig/authorization.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package serverconfig
|
||||
|
||||
import (
|
||||
"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/infra/logger"
|
||||
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
|
||||
"github.com/casbin/casbin"
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
// Config defines the config for CasbinAuth middleware.
|
||||
Config struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper middleware.Skipper
|
||||
|
||||
// Enforcer CasbinAuth main rule.
|
||||
// Required.
|
||||
Enforcer *casbin.Enforcer
|
||||
|
||||
Application *config.Config
|
||||
|
||||
Svc *applicationservice.Service
|
||||
Logger *logger.Logger
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultConfig is the default CasbinAuth middleware config.
|
||||
DefaultConfig = Config{
|
||||
Skipper: middleware.DefaultSkipper,
|
||||
}
|
||||
)
|
||||
|
||||
// MiddlewareWithConfig returns a CasbinAuth middleware with config.
|
||||
// See `Middleware()`.
|
||||
func MiddlewareWithConfig(cfg *config.Config, svc *applicationservice.Service, log *logger.Logger) echo.MiddlewareFunc {
|
||||
|
||||
config := &DefaultConfig
|
||||
|
||||
config.Svc = svc
|
||||
config.Logger = log
|
||||
config.Application = cfg
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) || config.CheckPermission(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
return routeutils.HandleHTMLError(c, echo.ErrForbidden)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setAuthorizationMiddleware(e *echo.Echo, log *logger.Logger, cfg *config.Config, svc *applicationservice.Service) {
|
||||
e.Use(MiddlewareWithConfig(cfg, svc, log))
|
||||
}
|
||||
|
||||
// CheckPermission checks the user/method/path combination from the request.
|
||||
// Returns true (permission granted) or false (permission forbidden)
|
||||
func (a *Config) CheckPermission(c echo.Context) bool {
|
||||
user, err := auth.GetUserDetail(c, a.Application)
|
||||
if err != nil {
|
||||
a.Logger.Warningf("Cannot get user details. %v ", err)
|
||||
}
|
||||
method := c.Request().Method
|
||||
path := c.Request().URL.Path
|
||||
//objectOrganization := a.organizationGoverningObject(c, user)
|
||||
|
||||
return a.Enforcer.Enforce(user, path, method)
|
||||
}
|
||||
|
||||
func (a *Config) organizationGoverningObject(c echo.Context, userDetails viewmodel.User) (result viewmodel.Organization) {
|
||||
|
||||
existingUser := strings.Contains(c.Request().URL.Path, "/users") && len(c.ParamValues()) > 0
|
||||
newUser := strings.Contains(c.Request().URL.Path, "/users") && len(c.ParamValues()) <= 0
|
||||
|
||||
switch {
|
||||
case existingUser:
|
||||
user, _ := a.Svc.Users.GetByUUID(c.ParamValues()[0], "")
|
||||
result = user.Organizations[0]
|
||||
case newUser:
|
||||
result = userDetails.Organizations[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
11
server/serverconfig/authorization_model.conf
Normal file
11
server/serverconfig/authorization_model.conf
Normal file
@@ -0,0 +1,11 @@
|
||||
[request_definition]
|
||||
r = role, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = role, obj, act
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
|
||||
|
||||
[matchers]
|
||||
m = keymatch(r.role, p.role) && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*")
|
||||
10
server/serverconfig/bodylimit.go
Normal file
10
server/serverconfig/bodylimit.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package serverconfig
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
)
|
||||
|
||||
func setBodyLimitMiddleware(e *echo.Echo) {
|
||||
e.Use(middleware.BodyLimit("4M"))
|
||||
}
|
||||
204
server/serverconfig/cache.go
Normal file
204
server/serverconfig/cache.go
Normal file
@@ -0,0 +1,204 @@
|
||||
package serverconfig
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
|
||||
"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"
|
||||
"github.com/labstack/echo/middleware"
|
||||
)
|
||||
|
||||
// bodyResponseWriter implements the http.ResponseWriter interface
|
||||
type bodyResponseWriter struct {
|
||||
io.Writer
|
||||
http.ResponseWriter
|
||||
}
|
||||
|
||||
func (w *bodyResponseWriter) WriteHeader(code int) {
|
||||
w.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (w *bodyResponseWriter) Write(b []byte) (int, error) {
|
||||
return w.Writer.Write(b)
|
||||
}
|
||||
|
||||
func (w *bodyResponseWriter) Flush() {
|
||||
w.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
func (w *bodyResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return w.ResponseWriter.(http.Hijacker).Hijack()
|
||||
}
|
||||
|
||||
func (w *bodyResponseWriter) CloseNotify() <-chan bool {
|
||||
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
// CacheConfig defines the config for Cache middleware.
|
||||
type CacheConfig struct {
|
||||
// Skipper determines if the request should skip this middleware
|
||||
Skipper middleware.Skipper
|
||||
// Cache expiration/lifetime
|
||||
Expiration time.Duration
|
||||
// VaryByQuery contains a list of query parameters to include in cache key
|
||||
VaryByQuery []string
|
||||
}
|
||||
|
||||
// DefaultCacheConfig is the default Cache middleware config.
|
||||
var DefaultCacheConfig = CacheConfig{
|
||||
Skipper: middleware.DefaultSkipper,
|
||||
}
|
||||
|
||||
// CacheMiddleware returns a middleware that protects requests agains Cache attacks.
|
||||
func CacheMiddleware(cfg *config.Config) echo.MiddlewareFunc {
|
||||
config := DefaultCacheConfig
|
||||
config.Expiration = cfg.Cache.DefaultExpiration
|
||||
|
||||
return CacheMiddlewareWithConfig(cfg, config)
|
||||
}
|
||||
|
||||
// CacheMiddlewareWithConfig returns a Cache middleware with config.
|
||||
// See: `CacheMiddleware()`.
|
||||
func CacheMiddlewareWithConfig(cfg *config.Config, config CacheConfig) echo.MiddlewareFunc {
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultCacheConfig.Skipper
|
||||
}
|
||||
|
||||
if config.Expiration < 0 {
|
||||
config.Expiration = cfg.Cache.DefaultExpiration
|
||||
}
|
||||
|
||||
cache := cache.Instance(cfg)
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
req := c.Request()
|
||||
|
||||
skip := config.Skipper(c) || req.Method != echo.GET
|
||||
|
||||
if !skip {
|
||||
for _, val := range req.Header["Cache-Control"] {
|
||||
if val == "no-cache" {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if skip {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
res := c.Response()
|
||||
|
||||
var cacheKey = getCacheKey(c, config)
|
||||
var contentTypeCacheKey = fmt.Sprintf("%s-content-type", cacheKey)
|
||||
var contentType = "application/json"
|
||||
|
||||
output, err := cache.GetItem(cacheKey)
|
||||
if err == nil {
|
||||
var responseStatus = http.StatusOK
|
||||
|
||||
if len(output) == 0 {
|
||||
responseStatus = http.StatusNoContent
|
||||
}
|
||||
|
||||
contentType, err = cache.GetString(contentTypeCacheKey)
|
||||
if err != nil {
|
||||
return routeutils.HandleAPIError(c, err)
|
||||
}
|
||||
|
||||
expiration, err := cache.GetExpiration(cacheKey)
|
||||
if err != nil {
|
||||
return routeutils.HandleAPIError(c, err)
|
||||
}
|
||||
|
||||
if expiration > 0 {
|
||||
res.Header()["Cache-Control"] = []string{fmt.Sprintf("max-age=%v", expiration.Seconds())}
|
||||
}
|
||||
|
||||
return c.Blob(responseStatus, contentType, []byte(output))
|
||||
} else if err == domain.ErrCacheMiss {
|
||||
resBody := new(bytes.Buffer)
|
||||
|
||||
mw := io.MultiWriter(res.Writer, resBody)
|
||||
|
||||
writer := &bodyResponseWriter{Writer: mw, ResponseWriter: res.Writer}
|
||||
|
||||
res.Writer = writer
|
||||
|
||||
err = next(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
headers := writer.Header()
|
||||
|
||||
cache.SetExpiration(cacheKey, config.Expiration)
|
||||
|
||||
if config.Expiration != 0 {
|
||||
res.Header()["Cache-Control"] = []string{fmt.Sprintf("max-age=%v", config.Expiration.Seconds())}
|
||||
}
|
||||
|
||||
contentTypeHeader, ok := headers[echo.HeaderContentType]
|
||||
if ok {
|
||||
contentType = contentTypeHeader[0]
|
||||
}
|
||||
|
||||
err = cache.SetItem(cacheKey, resBody.Bytes())
|
||||
if err != nil {
|
||||
return routeutils.HandleAPIError(c, err)
|
||||
}
|
||||
err = cache.SetString(contentTypeCacheKey, contentType)
|
||||
if err != nil {
|
||||
return routeutils.HandleAPIError(c, err)
|
||||
}
|
||||
} else if err != nil {
|
||||
return routeutils.HandleAPIError(c, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getCacheKey(c echo.Context, config CacheConfig) string {
|
||||
var req = c.Request()
|
||||
|
||||
var re = regexp.MustCompile("(?i)[^a-z0-9_]+")
|
||||
|
||||
var key = req.URL.Path
|
||||
|
||||
if len(config.VaryByQuery) > 0 {
|
||||
var query = "q"
|
||||
|
||||
for _, queryKey := range config.VaryByQuery {
|
||||
for k, v := range req.URL.Query() {
|
||||
if k == queryKey {
|
||||
query = fmt.Sprintf("%s-%s-%s", query, k, v)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query = strings.Trim(re.ReplaceAllString(query, "-"), "-")
|
||||
|
||||
if strings.TrimSpace(query) != "" {
|
||||
key = fmt.Sprintf("%s-%v", key, query)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Trim(re.ReplaceAllString(key, "-"), "-")
|
||||
}
|
||||
17
server/serverconfig/cors.go
Normal file
17
server/serverconfig/cors.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package serverconfig
|
||||
|
||||
import (
|
||||
"bitbucket.org/nemt/nemt-portal-api/infra/config"
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
)
|
||||
|
||||
func setCORSMiddleware(e *echo.Echo, cfg *config.Config) {
|
||||
middlewareConfig := middleware.DefaultCORSConfig
|
||||
|
||||
if !cfg.App.Debug {
|
||||
middlewareConfig.AllowOrigins = cfg.HTTP.Auth.FrontendURLs
|
||||
}
|
||||
|
||||
e.Pre(middleware.CORSWithConfig(middlewareConfig))
|
||||
}
|
||||
10
server/serverconfig/gzip.go
Normal file
10
server/serverconfig/gzip.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package serverconfig
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
)
|
||||
|
||||
func setGZIPMiddleware(e *echo.Echo) {
|
||||
e.Use(middleware.Gzip())
|
||||
}
|
||||
151
server/serverconfig/log.go
Normal file
151
server/serverconfig/log.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package serverconfig
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"bitbucket.org/nemt/nemt-portal-api/infra/logger"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/gommon/log"
|
||||
)
|
||||
|
||||
type echoLogger struct {
|
||||
*logger.Logger
|
||||
}
|
||||
|
||||
// Printj prints a message in json format
|
||||
func (l *echoLogger) Printj(data log.JSON) {
|
||||
bytes, _ := json.Marshal(data)
|
||||
l.Logger.Print(string(bytes))
|
||||
}
|
||||
|
||||
// Debugj logs a debug message in json format
|
||||
func (l *echoLogger) Debugj(data log.JSON) {
|
||||
bytes, _ := json.Marshal(data)
|
||||
l.Logger.Debug(string(bytes))
|
||||
}
|
||||
|
||||
// Infoj logs an info message in json format
|
||||
func (l *echoLogger) Infoj(data log.JSON) {
|
||||
bytes, _ := json.Marshal(data)
|
||||
l.Logger.Info(string(bytes))
|
||||
}
|
||||
|
||||
// Warnj logs a warning message in json format
|
||||
func (l *echoLogger) Warnj(data log.JSON) {
|
||||
bytes, _ := json.Marshal(data)
|
||||
l.Logger.Warn(string(bytes))
|
||||
}
|
||||
|
||||
// Errorj logs an error message in json format
|
||||
func (l *echoLogger) Errorj(data log.JSON) {
|
||||
bytes, _ := json.Marshal(data)
|
||||
l.Logger.Error(string(bytes))
|
||||
}
|
||||
|
||||
// Panicj logs a panic message in json format
|
||||
func (l *echoLogger) Panicj(data log.JSON) {
|
||||
bytes, _ := json.Marshal(data)
|
||||
l.Logger.Panic(string(bytes))
|
||||
}
|
||||
|
||||
// Fatalj logs a fatal message in json format
|
||||
func (l *echoLogger) Fatalj(data log.JSON) {
|
||||
bytes, _ := json.Marshal(data)
|
||||
l.Logger.Fatal(string(bytes))
|
||||
}
|
||||
|
||||
// Level returns the echo logging level
|
||||
func (l *echoLogger) Level() log.Lvl {
|
||||
return log.Lvl(l.Logger.Level)
|
||||
}
|
||||
|
||||
// SetLevel sets the echo logging level
|
||||
func (l *echoLogger) SetLevel(level log.Lvl) {
|
||||
l.Logger.Level = logrus.Level(level)
|
||||
}
|
||||
|
||||
// Output returns the echo logging output
|
||||
func (l *echoLogger) Output() io.Writer {
|
||||
return l.Writer()
|
||||
}
|
||||
|
||||
// SetOutput sets the echo logging output
|
||||
func (l *echoLogger) SetOutput(output io.Writer) {
|
||||
l.Logger.Logger.Out = output
|
||||
}
|
||||
|
||||
// Prefix returns the echo logging output
|
||||
func (l *echoLogger) Prefix() string {
|
||||
return l.Logger.Data["app"].(string)
|
||||
}
|
||||
|
||||
// SetPrefix returns the echo logging output
|
||||
func (l *echoLogger) SetPrefix(prefix string) {
|
||||
l.Logger.Data["app"] = prefix
|
||||
}
|
||||
|
||||
func loggerMiddlewareHandler(c echo.Context, next echo.HandlerFunc, log *logger.Logger) error {
|
||||
req := c.Request()
|
||||
res := c.Response()
|
||||
start := time.Now()
|
||||
|
||||
err := next(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stop := time.Now()
|
||||
|
||||
p := req.URL.Path
|
||||
if p == "" {
|
||||
p = "/"
|
||||
}
|
||||
|
||||
bytesIn := req.Header.Get(echo.HeaderContentLength)
|
||||
if bytesIn == "" {
|
||||
bytesIn = "0"
|
||||
}
|
||||
|
||||
requestID := req.Header.Get(echo.HeaderXRequestID)
|
||||
|
||||
if requestID == "" {
|
||||
requestID = res.Header().Get(echo.HeaderXRequestID)
|
||||
}
|
||||
|
||||
log.WithFields(map[string]interface{}{
|
||||
"id": requestID,
|
||||
"time_rfc3339": time.Now().Format(time.RFC3339),
|
||||
"remote_ip": c.RealIP(),
|
||||
"host": req.Host,
|
||||
"uri": req.RequestURI,
|
||||
"method": req.Method,
|
||||
"path": p,
|
||||
"referer": req.Referer(),
|
||||
"user_agent": req.UserAgent(),
|
||||
"status": res.Status,
|
||||
"latency": strconv.FormatInt(stop.Sub(start).Nanoseconds()/1000, 10),
|
||||
"latency_human": stop.Sub(start).String(),
|
||||
"bytes_in": bytesIn,
|
||||
"bytes_out": strconv.FormatInt(res.Size, 10),
|
||||
"headers": req.Header,
|
||||
"query": req.URL.Query(),
|
||||
}).Info("Handled request")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loggerMiddleware(log *logger.Logger) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
return loggerMiddlewareHandler(c, next, log)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setLogMiddleware(e *echo.Echo, log *logger.Logger) {
|
||||
e.Logger = &echoLogger{log}
|
||||
e.Pre(loggerMiddleware(log))
|
||||
}
|
||||
73
server/serverconfig/ratelimit.go
Normal file
73
server/serverconfig/ratelimit.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package serverconfig
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
)
|
||||
|
||||
// RateLimitMiddlewareConfig defines the config for RateLimit middleware.
|
||||
type RateLimitMiddlewareConfig struct {
|
||||
Skipper middleware.Skipper
|
||||
// Max number of allowed requests on a timeframe
|
||||
MaxRequests int64
|
||||
// The timeframe duration for the requests count
|
||||
BucketDuration time.Duration
|
||||
// Error response message
|
||||
ResponseMessage string
|
||||
// Error response
|
||||
ResponseStatus int
|
||||
}
|
||||
|
||||
// DefaultRateLimitMiddlewareConfig is the default RateLimit middleware config.
|
||||
var DefaultRateLimitMiddlewareConfig = RateLimitMiddlewareConfig{
|
||||
Skipper: middleware.DefaultSkipper,
|
||||
MaxRequests: 10,
|
||||
BucketDuration: time.Second,
|
||||
ResponseMessage: "Too Many Requests",
|
||||
ResponseStatus: http.StatusServiceUnavailable,
|
||||
}
|
||||
|
||||
// RateLimitMiddleware returns a middleware that protects the API agains massive requests.
|
||||
func RateLimitMiddleware() echo.MiddlewareFunc {
|
||||
return RateLimitMiddlewareWithConfig(DefaultRateLimitMiddlewareConfig)
|
||||
}
|
||||
|
||||
// RateLimitMiddlewareWithConfig returns a RateLimitMiddleware with configuration parameters.
|
||||
// See: `RateLimitMiddleware()`.
|
||||
func RateLimitMiddlewareWithConfig(config RateLimitMiddlewareConfig) echo.MiddlewareFunc {
|
||||
if config.MaxRequests == 0 {
|
||||
config.MaxRequests = DefaultRateLimitMiddlewareConfig.MaxRequests
|
||||
}
|
||||
|
||||
if config.BucketDuration == 0 {
|
||||
config.BucketDuration = DefaultRateLimitMiddlewareConfig.BucketDuration
|
||||
}
|
||||
|
||||
// limiter := tollbooth.NewLimiterExpiringBuckets(config.MaxRequests, config.BucketDuration, time.Hour, time.Second*0)
|
||||
// limiter.Message = config.ResponseMessage
|
||||
// limiter.StatusCode = config.ResponseStatus
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
// err := tollbooth.LimitByRequest(limiter, c.Request())
|
||||
// if err != nil {
|
||||
// tollbooth.SetResponseHeaders(limiter, c.Response().Writer)
|
||||
// return errors.NewHTTPError(limiter.StatusCode, limiter.Message)
|
||||
// }
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setRateLimitMiddleware(e *echo.Echo) {
|
||||
e.Pre(RateLimitMiddleware())
|
||||
}
|
||||
10
server/serverconfig/recover.go
Normal file
10
server/serverconfig/recover.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package serverconfig
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
)
|
||||
|
||||
func setRecoverMiddleware(e *echo.Echo) {
|
||||
e.Use(middleware.Recover())
|
||||
}
|
||||
10
server/serverconfig/requestid.go
Normal file
10
server/serverconfig/requestid.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package serverconfig
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
)
|
||||
|
||||
func setRequestIDMiddleware(e *echo.Echo) {
|
||||
e.Use(middleware.RequestID())
|
||||
}
|
||||
15
server/serverconfig/route.go
Normal file
15
server/serverconfig/route.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package serverconfig
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo"
|
||||
"github.com/labstack/echo/middleware"
|
||||
)
|
||||
|
||||
func setRouteMiddleware(e *echo.Echo) {
|
||||
middlewareConfig := middleware.DefaultTrailingSlashConfig
|
||||
middlewareConfig.RedirectCode = http.StatusPermanentRedirect
|
||||
|
||||
e.Use(middleware.AddTrailingSlashWithConfig(middlewareConfig))
|
||||
}
|
||||
28
server/serverconfig/serverconfig.go
Normal file
28
server/serverconfig/serverconfig.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package serverconfig
|
||||
|
||||
import (
|
||||
"bitbucket.org/nemt/nemt-portal-api/domain/service"
|
||||
"bitbucket.org/nemt/nemt-portal-api/infra/config"
|
||||
"bitbucket.org/nemt/nemt-portal-api/infra/errors"
|
||||
"bitbucket.org/nemt/nemt-portal-api/infra/logger"
|
||||
"github.com/labstack/echo"
|
||||
)
|
||||
|
||||
// SetMiddlewares attaches middlewares to server
|
||||
func SetMiddlewares(server *echo.Echo, cfg *config.Config, log *logger.Logger, svc *service.Service) error {
|
||||
setRecoverMiddleware(server)
|
||||
setGZIPMiddleware(server)
|
||||
setRequestIDMiddleware(server)
|
||||
setLogMiddleware(server, log)
|
||||
setCORSMiddleware(server, cfg)
|
||||
setBodyLimitMiddleware(server)
|
||||
setRateLimitMiddleware(server)
|
||||
//setAuthorizationMiddleware(server, log, svc)
|
||||
|
||||
err := setJWTMiddleware(server, cfg)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
50
server/serverconfig/util.go
Normal file
50
server/serverconfig/util.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package serverconfig
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
|
||||
"github.com/labstack/echo"
|
||||
)
|
||||
|
||||
// authSkipper is the default skipper for authentication and authorization
|
||||
func authSkipper(ctx echo.Context) bool {
|
||||
path := ctx.Request().URL.Path
|
||||
return (strings.HasPrefix(path, "/health") ||
|
||||
strings.HasPrefix(path, "/v1/authenticate") ||
|
||||
strings.HasPrefix(path, "/v1/docs") ||
|
||||
strings.HasPrefix(path, "/v1/twilio") ||
|
||||
strings.HasPrefix(path, "/v1/twilio/ws") ||
|
||||
strings.Contains(path, "/v1/ext") ||
|
||||
strings.Contains(path, "/v1/notification/ws") ||
|
||||
strings.HasPrefix(path, "/v1/lyfthook") ||
|
||||
strings.HasPrefix(path, "/v1/docs"))
|
||||
}
|
||||
|
||||
// appSkipper is the default skipper for the application routes
|
||||
func appSkipper(ctx echo.Context) bool {
|
||||
path := ctx.Request().URL.Path
|
||||
return strings.HasPrefix(path, "/v1/twilio")
|
||||
}
|
||||
|
||||
// docsSkipper is the default skipper for documentation
|
||||
func docsSkipper(ctx echo.Context) bool {
|
||||
path := ctx.Request().URL.Path
|
||||
return strings.HasPrefix(path, "/v1/docs")
|
||||
}
|
||||
|
||||
// middlewareErrorWrapper wraps a middleware and applies the default error handling to response
|
||||
func middlewareErrorWrapper(middleware echo.MiddlewareFunc) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
handlerFunc := middleware(next)
|
||||
|
||||
return func(c echo.Context) error {
|
||||
err := handlerFunc(c)
|
||||
if err != nil {
|
||||
return routeutils.HandleAPIError(c, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user