commit 8bb8d90a88c416dfb7c2f0bedf894d6597b71e35 Author: Arnaud Delcasse Date: Mon Dec 12 00:06:27 2022 +0100 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..0bbebeb --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# COOPGO Standard Covoiturage + +Implementation of the [Standard Covoiturage](https://github.com/fabmob/standard-covoiturage/) specification. This library is part of the COOPGO Technical Platform and provides cleint and server implementations of the standard. + diff --git a/bookings.go b/bookings.go new file mode 100644 index 0000000..2f8c904 --- /dev/null +++ b/bookings.go @@ -0,0 +1,34 @@ +package standardcovoiturage + +import "time" + +type BookingStatus int64 + +const ( + BookingWaitingConfirmation BookingStatus = iota + BookingConfirmed + BookingCancelled + BookingCompletedPendingValidation + BookingValidated +) + +type Booking struct { + ID string `json:"id"` // TODO check uuidv4 + Driver User `json:"driver"` + Passenger User `json:"passenger"` + PassengerPickupDate time.Time `json:"passengerPickupDate"` + PassengerPickupLat float64 `json:"passengerPickupLat"` + PassengerPickupLng float64 `json:"passengerPickupLng"` + PassengerDropLat float64 `json:"passengerDropLat"` + PassengerDropLng float64 `json:"passengerDropLng"` + PassengerPickupAddress *string `json:"passengerPickupAddress,omitempty"` + PassengerDropAddress *string `json:"passengerDropAddress,omitempty"` + Status BookingStatus `json:"status"` + Distance *int64 `json:"distance,omitempty"` + Duration *time.Duration `json:"duration,omitempty"` + WebUrl *string `json:"webUrl,omitempty"` + Price Price `json:"price"` + Car *Car `json:"car"` + DriverJourneyID string `json:"driverJourneyId"` + PassengerJourneyID string `json:"passengerJourneyId"` +} diff --git a/carpool-bookings.go b/carpool-bookings.go new file mode 100644 index 0000000..4738358 --- /dev/null +++ b/carpool-bookings.go @@ -0,0 +1,50 @@ +package standardcovoiturage + +import "time" + +type CarpoolBookingStatus int64 + +const ( + CarpoolBookingWaitingConfirmation CarpoolBookingStatus = iota + CarpoolBookingConfirmed + CarpoolBookingCancelled + CarpoolBookingCompletedPendingValidation + CarpoolBookingValidated +) + +type CarpoolBookingEventData struct { + CarpoolBooking + DriverCarpoolBooking + PassengerCarpoolBooking +} + +type CarpoolBookingEvent struct { + ID string `json:"id"` // TODO validate UUID + IDToken string `json:"idToken"` + Data CarpoolBookingEventData `json:"data"` +} + +type CarpoolBooking struct { + ID string `json:"id"` + PassengerPickupDate time.Time `json:"passengerPickupDate"` + PassengerPickupLat float64 `json:"passengerPickupLat"` + PassengerPickupLng float64 `json:"passengerPickupLng"` + PassengerDropLat float64 `json:"passengerDropLat"` + PassengerDropLng float64 `json:"passengerDropLng"` + PassengerPickupAddress *string `json:"passengerPickupAddress,omitempty"` + PassengerDropAddress *string `json:"passengerDropAddress,omitempty"` + Status CarpoolBookingStatus `json:"status"` + Distance *int64 `json:"distance,omitempty"` + Duration *time.Duration `json:"duration,omitempty"` + WebUrl string `json:"webUrl"` +} + +type PassengerCarpoolBooking struct { + Passenger User `json:"passenger"` +} + +type DriverCarpoolBooking struct { + Driver User `json:"driver"` + Price Price `json:"price"` + Car *Car `json:"car,omitempty"` +} diff --git a/cars.go b/cars.go new file mode 100644 index 0000000..ed888e4 --- /dev/null +++ b/cars.go @@ -0,0 +1,6 @@ +package standardcovoiturage + +type Car struct { + Model *string `json:"model,omitempty"` + Brand *string `json:"brand,omitempty"` +} diff --git a/cmd/standardcovoiturage-to-rdex-gateway/README.md b/cmd/standardcovoiturage-to-rdex-gateway/README.md new file mode 100644 index 0000000..8bea258 --- /dev/null +++ b/cmd/standardcovoiturage-to-rdex-gateway/README.md @@ -0,0 +1,3 @@ +# Standard covoiturage to RDEX Gateway + +Standard covoiturage to RDEX Gateway is a converter from the new standard covoiturage to RDEX API calls for searching carpool journeys. \ No newline at end of file diff --git a/cmd/standardcovoiturage-to-rdex-gateway/go.mod b/cmd/standardcovoiturage-to-rdex-gateway/go.mod new file mode 100644 index 0000000..76584cf --- /dev/null +++ b/cmd/standardcovoiturage-to-rdex-gateway/go.mod @@ -0,0 +1,15 @@ +module git.coopgo.io/coopgo-platform/standard-covoiturage/cmd/standardcovoiturage-to-rdex-bridge + +replace git.coopgo.io/coopgo-platform/standard-covoiturage => ../../ + +go 1.18 + +require ( + git.coopgo.io/coopgo-platform/standard-covoiturage v0.0.0 + gitlab.scity.coop/go-libs/rdex-golang v0.0.0-20210202230228-0ccbfcbe2163 +) + +require ( + github.com/gorilla/schema v1.2.0 // indirect + golang.org/x/crypto v0.1.0 // indirect +) diff --git a/cmd/standardcovoiturage-to-rdex-gateway/go.sum b/cmd/standardcovoiturage-to-rdex-gateway/go.sum new file mode 100644 index 0000000..3db48bb --- /dev/null +++ b/cmd/standardcovoiturage-to-rdex-gateway/go.sum @@ -0,0 +1,6 @@ +github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= +github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= +gitlab.scity.coop/go-libs/rdex-golang v0.0.0-20210202230228-0ccbfcbe2163 h1:F0XUsnMwOtDLaS1riH/fEopzly+KdwZpUHIvL0f/RHQ= +gitlab.scity.coop/go-libs/rdex-golang v0.0.0-20210202230228-0ccbfcbe2163/go.mod h1:EOh0Z3TK1/Z48oDidtIV3reDcflpYCIdZk+2Mj76a/4= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= diff --git a/cmd/standardcovoiturage-to-rdex-gateway/main.go b/cmd/standardcovoiturage-to-rdex-gateway/main.go new file mode 100644 index 0000000..2f68d40 --- /dev/null +++ b/cmd/standardcovoiturage-to-rdex-gateway/main.go @@ -0,0 +1,117 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + standardcovoiturage "git.coopgo.io/coopgo-platform/standard-covoiturage" + "gitlab.scity.coop/go-libs/rdex-golang" +) + +func main() { + handler := CarpoolHandler{} + server := standardcovoiturage.NewServer(handler) + server.AddOperator("fakeoperator", "mysupersecretapikey") + + http.Handle("/", server) + + err := http.ListenAndServe(":3333", nil) + panic(err) +} + +type CarpoolHandler struct{} + +func (h CarpoolHandler) GetDriverJourneys(ctx context.Context, departureLat float64, departureLng float64, arrivalLat float64, arrivalLng float64, departureDate time.Time, timeDelta *time.Duration, departureRadius *float64, count *int64) ([]standardcovoiturage.DriverJourney, error) { + carpoolrequest := "https://api.rdex.ridygo.fr/journeys.json" + + client := &http.Client{} + req, err := http.NewRequest("GET", carpoolrequest, nil) + if err != nil { + fmt.Println(err) + } + + req.URL.RawQuery = fmt.Sprintf( + "p[driver][state]=1&frequency=punctual&p[passenger][state]=0&p[from][latitude]=%f&p[from][longitude]=%f&p[to][latitude]=%f&p[to][longitude]=%f&p[outward][mindate]=%s&p[outward][maxdate]=%s", + departureLat, departureLng, + arrivalLat, arrivalLng, + departureDate.Format("2006-01-02"), departureDate.Format("2006-01-02")) + + req.Header.Set("X-API-KEY", "123456") + resp, err := client.Do(req) + if err != nil { + fmt.Println(err) + } + + var carpoolresults []rdex.RDEXJourney + + if err == nil && resp.StatusCode == http.StatusOK { + err = json.NewDecoder(resp.Body).Decode(&carpoolresults) + if err != nil { + fmt.Println(err) + } + + if carpoolresults == nil { + carpoolresults = []rdex.RDEXJourney{} + } + } else { + carpoolresults = []rdex.RDEXJourney{} + } + + results := []standardcovoiturage.DriverJourney{} + for _, c := range carpoolresults { + + journey := standardcovoiturage.DriverJourney{ + AvailableSteats: c.Driver.Seats, + DriverTrip: standardcovoiturage.DriverTrip{ + Trip: standardcovoiturage.Trip{ + Operator: c.Operator, + WebUrl: c.URL, + PassengerPickupLat: c.From.Latitude, + PassengerPickupLng: c.From.Longitude, + }, + Driver: standardcovoiturage.User{ + ID: *c.Driver.UUID, + Operator: c.Operator, + Alias: *c.Driver.Alias, + Picture: c.Driver.Image, + }, + }, + } + + results = append(results, journey) + } + + return results, nil +} + +// func (h CarpoolHandler) GetDriverJourneys(ctx context.Context, departureLat float64, departureLng float64, arrivalLat float64, arrivalLng float64, departureDate time.Time, timeDelta *time.Duration, departureRadius *float64, count *int64) ([]standardcovoiturage.DriverJourney, error) { +// availableSeats := int64(1) + +// return []standardcovoiturage.DriverJourney{ +// { +// AvailableSteats: &availableSeats, +// DriverTrip: standardcovoiturage.DriverTrip{ +// Driver: standardcovoiturage.User{ +// ID: "1234567890", +// Operator: "fakeoperator", +// Alias: "Fake user", +// }, +// Trip: standardcovoiturage.Trip{ +// Operator: "fakeoperator", +// PassengerPickupLat: 2.0, +// PassengerPickupLng: 2.0, +// PassengerDropLat: 3.0, +// PassengerDropLng: 3.0, +// Duration: 3600, +// }, +// }, +// JourneySchedule: standardcovoiturage.JourneySchedule{ +// PassengerPickupDate: time.Now(), +// Type: standardcovoiturage.Planned, +// }, +// }, +// }, nil +// } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..5d69ce0 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module git.coopgo.io/coopgo-platform/standard-covoiturage + +go 1.18 + +require golang.org/x/crypto v0.1.0 + +require ( + github.com/gorilla/schema v1.2.0 // indirect + gitlab.scity.coop/go-libs/rdex-golang v0.0.0-20210202230228-0ccbfcbe2163 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3db48bb --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= +github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= +gitlab.scity.coop/go-libs/rdex-golang v0.0.0-20210202230228-0ccbfcbe2163 h1:F0XUsnMwOtDLaS1riH/fEopzly+KdwZpUHIvL0f/RHQ= +gitlab.scity.coop/go-libs/rdex-golang v0.0.0-20210202230228-0ccbfcbe2163/go.mod h1:EOh0Z3TK1/Z48oDidtIV3reDcflpYCIdZk+2Mj76a/4= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= diff --git a/journeys.go b/journeys.go new file mode 100644 index 0000000..9459a95 --- /dev/null +++ b/journeys.go @@ -0,0 +1,58 @@ +package standardcovoiturage + +import ( + "fmt" + "time" +) + +type JourneyScheduleType int64 + +const ( + Planned JourneyScheduleType = iota + Dynamic + Line +) + +func (t JourneyScheduleType) MarshalJSON() ([]byte, error) { + types := map[JourneyScheduleType]string{ + Planned: "\"PLANNED\"", + Dynamic: "\"DYNAMIC\"", + Line: "\"Line\"", + } + + return []byte(types[t]), nil +} + +type JSONTime time.Time + +func (t JSONTime) MarshalJSON() ([]byte, error) { + //do your serializing here + stamp := fmt.Sprintf("%v", time.Time(t).Unix()) + return []byte(stamp), nil +} + +type JourneySchedule struct { + ID *string `json:"id,omitempty"` + PassengerPickupDate JSONTime `json:"passengerPickupDate"` + PassengerDepartureDate *JSONTime `json:"passengerDepartureDate,omitempty"` + DriverDepartureDate *JSONTime `json:"driverDepartureDate,omitempty"` + WebUrl *string `json:"webUrl,omitempty"` + Type JourneyScheduleType `json:"type"` +} + +type DriverJourney struct { + DriverTrip + JourneySchedule + + AvailableSteats *int `json:"requestedSeats,omitempty"` + Price *Price `json:"price,omitempty"` +} + +type PassengerJourney struct { + PassengerTrip + JourneySchedule + + //TODO how to handle requested driverDepartureDate + + RequestedSteats *int64 `json:"requestedSeats,omitempty"` +} diff --git a/preferences.go b/preferences.go new file mode 100644 index 0000000..74b6ddf --- /dev/null +++ b/preferences.go @@ -0,0 +1,9 @@ +package standardcovoiturage + +type Preferences struct { + Smoking *bool `json:"smoking,omitempty"` + Animals *bool `json:"animals,omitempty"` + Music *bool `json:"music,omitempty"` + IsTalker *bool `json:"isTalker,omitempty"` + LuggageSize *int64 `json:"luggageSize,omitempty"` +} diff --git a/prices.go b/prices.go new file mode 100644 index 0000000..37166e6 --- /dev/null +++ b/prices.go @@ -0,0 +1,15 @@ +package standardcovoiturage + +type PriceType int64 + +const ( + Free PriceType = iota + Paying + Unknown +) + +type Price struct { + Type *PriceType `json:"type,omitempty"` + Amount *float64 `json:"amount,omitempty"` + Currency *string `json:"currency,omitempty"` +} diff --git a/schedules.go b/schedules.go new file mode 100644 index 0000000..0a8561c --- /dev/null +++ b/schedules.go @@ -0,0 +1,19 @@ +package standardcovoiturage + +type Day int64 + +const ( + Monday Day = iota + Tuesday + Wednesday + Thursday + Friday + Saturday + Sunday +) + +type Schedule struct { + PassengerPickupDay *Day `json:"passengerPickupDay,omitempty"` + + JourneySchedules *[]JourneySchedule `json:"journeySchedules,omitempty"` +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..251d310 --- /dev/null +++ b/server.go @@ -0,0 +1,346 @@ +package standardcovoiturage + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "strings" + "time" + + "github.com/gorilla/schema" + "golang.org/x/crypto/bcrypt" +) + +const OperatorContextKey = "operator" + +type Handler interface { + GetDriverJourneys(ctx context.Context, departureLat float64, departureLng float64, arrivalLat float64, arrivalLng float64, departureDate time.Time, timeDelta *time.Duration, departureRadius *float64, arrivalRadius *float64, count *int64) ([]DriverJourney, error) + GetPassengerJourneys(ctx context.Context, departureLat float64, departureLng float64, arrivalLat float64, arrivalLng float64, departureDate time.Time, timeDelta *time.Duration, departureRadius *float64, arrivalRadius *float64, count *int64) ([]PassengerJourney, error) + GetDriverRegularTrips(ctx context.Context, departureLat float64, departureLng float64, arrivalLat float64, arrivalLng float64, departureTimeOfDay string, departureWeekDays *[]string, timeDelta *time.Duration, departureRadius *float64, arrivalRadius *float64, minDepartureDate *time.Time, maxDepartureDate *time.Time, count *int64) ([]DriverTrip, error) + GetPassengerRegularTrips(ctx context.Context, departureLat float64, departureLng float64, arrivalLat float64, arrivalLng float64, departureTimeOfDay string, departureWeekDays *[]string, timeDelta *time.Duration, departureRadius *float64, arrivalRadius *float64, minDepartureDate *time.Time, maxDepartureDate *time.Time, count *int64) ([]PassengerTrip, error) +} + +type AuthorizedOperator struct { + Operator string + ApiKey string // encoded using bcrypt +} + +type GetDriverJourneysRequest struct { + DepartureLat float64 `schema:"departureLat,required"` + DepartureLng float64 `schema:"departureLng,required"` + ArrivalLat float64 `schema:"arrivalLat,required"` + ArrivalLng float64 `schema:"arrivalLng,required"` + DepartureDate int64 `schema:"departureDate,required"` + TimeDelta *int64 `schema:"timeDelta"` + DepartureRadius *float64 `schema:"departureRadius"` + ArrivalRadius *float64 `schema:"arrivalRadius"` + Count *int64 `schema:"count"` +} + +type GetPassengerJourneysRequest struct { + DepartureLat float64 `schema:"departureLat,required"` + DepartureLng float64 `schema:"departureLng,required"` + ArrivalLat float64 `schema:"arrivalLat,required"` + ArrivalLng float64 `schema:"arrivalLng,required"` + DepartureDate int64 `schema:"departureDate,required"` + TimeDelta *int64 `schema:"timeDelta"` + DepartureRadius *float64 `schema:"departureRadius"` + ArrivalRadius *float64 `schema:"arrivalRadius"` + Count *int64 `schema:"count"` +} + +type GetDriverRegularTripsRequest struct { + DepartureLat float64 `schema:"departureLat,required"` + DepartureLng float64 `schema:"departureLng,required"` + ArrivalLat float64 `schema:"arrivalLat,required"` + ArrivalLng float64 `schema:"arrivalLng,required"` + DepartureTimeOfDay string `schema:"departureTimeOfDay,required"` + DepartureWeekDays *[]string `schema:"departureWeekdays"` + TimeDelta *int64 `schema:"timeDelta"` + DepartureRadius *float64 `schema:"departureRadius"` + ArrivalRadius *float64 `schema:"arrivalRadius"` + MinDepartureDate *int64 `schema:"minDepartureDate"` + MaxDepartureDate *int64 `schema:"maxDepartureDate"` + Count *int64 `schema:"count"` +} + +type GetPassengerRegularTripsRequest struct { + DepartureLat float64 `schema:"departureLat,required"` + DepartureLng float64 `schema:"departureLng,required"` + ArrivalLat float64 `schema:"arrivalLat,required"` + ArrivalLng float64 `schema:"arrivalLng,required"` + DepartureTimeOfDay string `schema:"departureTimeOfDay,required"` + DepartureWeekDays *[]string `schema:"departureWeekdays"` + TimeDelta *int64 `schema:"timeDelta"` + DepartureRadius *float64 `schema:"departureRadius"` + ArrivalRadius *float64 `schema:"arrivalRadius"` + MinDepartureDate *int64 `schema:"minDepartureDate"` + MaxDepartureDate *int64 `schema:"maxDepartureDate"` + Count *int64 `schema:"count"` +} + +type Server struct { + Handler Handler + AuthorizedOperators []AuthorizedOperator +} + +func NewServer(handler Handler) *Server { + return &Server{ + Handler: handler, + } +} + +func (s *Server) FindApiKey(key string) (operator string, err error) { + for _, o := range s.AuthorizedOperators { + if e := bcrypt.CompareHashAndPassword([]byte(o.ApiKey), []byte(key)); e == nil { + return o.Operator, nil + } + } + + return "", errors.New("operator not found") +} + +func (s *Server) AddOperator(operator string, apiKey string) error { + encryptedKey, err := bcrypt.GenerateFromPassword([]byte(apiKey), bcrypt.DefaultCost) + if err != nil { + return err + } + s.AuthorizedOperators = append(s.AuthorizedOperators, AuthorizedOperator{ + Operator: operator, + ApiKey: string(encryptedKey), + }) + + return nil +} + +func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + apiKey := r.Header.Get("X-Api-Key") + operator, err := s.FindApiKey(apiKey) + if err != nil { + fmt.Println(err) + w.WriteHeader(http.StatusUnauthorized) + return + } + + ctx := context.WithValue(r.Context(), OperatorContextKey, operator) + + p := strings.Split(r.URL.Path, "/")[1:] + n := len(p) + switch { + case r.Method == "GET" && n == 1 && p[0] == "driver_journeys": + s.getDriverJourneys(w, r.WithContext(ctx)) + return + case r.Method == "GET" && n == 1 && p[0] == "passenger_journeys": + s.getPassengerJourneys(w, r.WithContext(ctx)) + return + case r.Method == "GET" && n == 1 && p[0] == "driver_regular_trips": + s.getDriverRegularTrips(w, r.WithContext(ctx)) + return + case r.Method == "GET" && n == 1 && p[0] == "passenger_regular_trips": + s.getPassengerRegularTrips(w, r.WithContext(ctx)) + return + default: + w.WriteHeader(http.StatusNotFound) + } +} + +func (s *Server) getDriverJourneys(w http.ResponseWriter, r *http.Request) { + var request GetDriverJourneysRequest + + var decoder = schema.NewDecoder() + + if err := decoder.Decode(&request, r.URL.Query()); err != nil { + fmt.Println(err) + badRequest(w, fmt.Errorf("could not parse the request : %s", err)) + return + } + + departureDate := time.Unix(request.DepartureDate, 0) + var timeDelta *time.Duration + timeDelta = nil + if request.TimeDelta != nil { + duration := time.Duration(*request.TimeDelta) + timeDelta = &duration + } + + driverJourneys, err := s.Handler.GetDriverJourneys( + r.Context(), + request.DepartureLat, + request.DepartureLng, + request.ArrivalLat, + request.ArrivalLng, + departureDate, + timeDelta, + request.DepartureRadius, + request.ArrivalRadius, + request.Count) + + if err != nil { + fmt.Println(err) + jsonResponse(w, err, http.StatusInternalServerError) + return + } + + jsonResponse(w, driverJourneys, http.StatusOK) +} + +func (s *Server) getPassengerJourneys(w http.ResponseWriter, r *http.Request) { + var request GetPassengerJourneysRequest + + var decoder = schema.NewDecoder() + + if err := decoder.Decode(&request, r.URL.Query()); err != nil { + fmt.Println(err) + badRequest(w, fmt.Errorf("could not parse the request : %s", err)) + return + } + + departureDate := time.Unix(request.DepartureDate, 0) + var timeDelta *time.Duration + timeDelta = nil + if request.TimeDelta != nil { + duration := time.Duration(*request.TimeDelta) + timeDelta = &duration + } + + passengerJourneys, err := s.Handler.GetPassengerJourneys( + r.Context(), + request.DepartureLat, + request.DepartureLng, + request.ArrivalLat, + request.ArrivalLng, + departureDate, + timeDelta, + request.DepartureRadius, + request.ArrivalRadius, + request.Count) + + if err != nil { + fmt.Println(err) + jsonResponse(w, err, http.StatusInternalServerError) + return + } + + jsonResponse(w, passengerJourneys, http.StatusOK) +} + +func (s *Server) getDriverRegularTrips(w http.ResponseWriter, r *http.Request) { + var request GetDriverRegularTripsRequest + + var decoder = schema.NewDecoder() + + if err := decoder.Decode(&request, r.URL.Query()); err != nil { + fmt.Println(err) + badRequest(w, fmt.Errorf("could not parse the request : %s", err)) + return + } + + var minDepartureDate *time.Time + if request.MinDepartureDate != nil { + d := time.Unix(*request.MinDepartureDate, 0) + minDepartureDate = &d + } + + var maxDepartureDate *time.Time + if request.MinDepartureDate != nil { + d := time.Unix(*request.MinDepartureDate, 0) + maxDepartureDate = &d + } + + var timeDelta *time.Duration + timeDelta = nil + if request.TimeDelta != nil { + duration := time.Duration(*request.TimeDelta) + timeDelta = &duration + } + + driverJourneys, err := s.Handler.GetDriverRegularTrips( + r.Context(), + request.DepartureLat, + request.DepartureLng, + request.ArrivalLat, + request.ArrivalLng, + request.DepartureTimeOfDay, + request.DepartureWeekDays, + timeDelta, + request.DepartureRadius, + request.ArrivalRadius, + minDepartureDate, + maxDepartureDate, + request.Count) + + if err != nil { + fmt.Println(err) + jsonResponse(w, err, http.StatusInternalServerError) + return + } + + jsonResponse(w, driverJourneys, http.StatusOK) +} + +func (s *Server) getPassengerRegularTrips(w http.ResponseWriter, r *http.Request) { + var request GetPassengerRegularTripsRequest + + var decoder = schema.NewDecoder() + + if err := decoder.Decode(&request, r.URL.Query()); err != nil { + fmt.Println(err) + badRequest(w, fmt.Errorf("could not parse the request : %s", err)) + return + } + + var minDepartureDate *time.Time + if request.MinDepartureDate != nil { + d := time.Unix(*request.MinDepartureDate, 0) + minDepartureDate = &d + } + + var maxDepartureDate *time.Time + if request.MinDepartureDate != nil { + d := time.Unix(*request.MinDepartureDate, 0) + maxDepartureDate = &d + } + + var timeDelta *time.Duration + timeDelta = nil + if request.TimeDelta != nil { + duration := time.Duration(*request.TimeDelta) + timeDelta = &duration + } + + passengerTrips, err := s.Handler.GetPassengerRegularTrips( + r.Context(), + request.DepartureLat, + request.DepartureLng, + request.ArrivalLat, + request.ArrivalLng, + request.DepartureTimeOfDay, + request.DepartureWeekDays, + timeDelta, + request.DepartureRadius, + request.ArrivalRadius, + minDepartureDate, + maxDepartureDate, + request.Count) + + if err != nil { + fmt.Println(err) + jsonResponse(w, err, http.StatusInternalServerError) + return + } + + jsonResponse(w, passengerTrips, http.StatusOK) +} + +func jsonResponse(w http.ResponseWriter, response any, statuscode int) { + w.WriteHeader(http.StatusBadRequest) + w.Header().Set("Content-Type", "application/json") + err := json.NewEncoder(w).Encode(response) + fmt.Println(err) +} + +func badRequest(w http.ResponseWriter, err error) { + jsonResponse(w, map[string]any{"error": err.Error()}, http.StatusBadRequest) +} diff --git a/trips.go b/trips.go new file mode 100644 index 0000000..0ae8424 --- /dev/null +++ b/trips.go @@ -0,0 +1,43 @@ +package standardcovoiturage + +import "time" + +type Trip struct { + Operator string `json:"operator"` + PassengerPickupLat float64 `json:"passengerPickupLat"` + PassengerPickupLng float64 `json:"passengerPickupLng"` + PassengerDropLat float64 `json:"passengerDropLat"` + PassengerDropLng float64 `json:"passengerDropLng"` + Duration time.Duration `json:"duration"` + PassengerPickupAddress *string `json:"passengerPickupAddress,omitempty"` + PassengerDropAddress *string `json:"passengerDropAddress,omitempty"` + Distance *int64 `json:"distance,omitempty"` + DriverDepartureLat *float64 `json:"driverDepartureLat,omitempty"` + DriverDepartureLng *float64 `json:"driverDepartureLng,omitempty"` + DriverArrivalLat *float64 `json:"driverArrivalLat,omitempty"` + DriverArrivalLng *float64 `json:"driverArrivalLng,omitempty"` + DriverDepartureAddress *string `json:"driverDepartureAddress,omitempty"` + DriverArrivalAddress *string `json:"driverArrivalAddress,omitempty"` + JourneyPolyline *string `json:"journeyPolyline,omitempty"` + //WebUrl *string `json:"webUrl,omitempty"` + Preferences *Preferences `json:"preferences,omitempty"` +} + +type DriverTrip struct { + Driver User `json:"driver"` + DepartureToPickupWalkingDistance *int64 `json:"departureToPickupWalkingDistance,omitempty"` + DepartureToPickupWalkingDuration *time.Duration `json:"departureToPickupWalkingDuration,omitempty"` + DepartureToPickupWalkingPolyline *string `json:"departureToPickupWalkingPolyline,omitempty"` + DropoffToArrivalWalkingDistance *int64 `json:"dropoffToArrivalWalkingDistance,omitempty"` + DropoffToArrivalWalkingDuration *time.Duration `json:"dropoffToArrivalWalkingDuration,omitempty"` + DropoffToArrivalWalkingPolyline *string `json:"dropoffToArrivalWalkingPolyline,omitempty"` + Car *Car `json:"car,omitempty"` + + Trip +} + +type PassengerTrip struct { + Passenger User `json:"passenger"` + + Trip +} diff --git a/users.go b/users.go new file mode 100644 index 0000000..cfeb6cd --- /dev/null +++ b/users.go @@ -0,0 +1,21 @@ +package standardcovoiturage + +type Gender int64 + +const ( + Female Gender = iota + Male + Other +) + +type User struct { + ID string `json:"id"` + Operator string `json:"operator"` + Alias string `json:"alias"` + FirstName *string `json:"firstName,omitempty"` + LastName *string `json:"lastName,omitempty"` + Grade *int64 `json:"grade,omitempty"` + Picture *string `json:"picture,omitempty"` + Gender *Gender `json:"gender,omitempty"` + VerifiedIdentity *bool `json:"verifiedIdentity,omitempty"` +}