Skip to content

Commit

Permalink
driver: implement trips from the new API
Browse files Browse the repository at this point in the history
Updates #49

Implements method
- DriverTrips which
  correlates with https://developer.uber.com/docs/drivers/references/api/v1/partners-trips-get
and allows one to query for driver trips within a period or paginate
within them.

* Also refactored to reuse DriverPayments which only varies from
DriverTrips by populating Payments instead of Trips; the rest
of the pagination and usage is the same

Sample usage
```go
package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/orijtech/uber/v1"
)

func main() {
	client, err := uber.NewClientFromOAuth2File(os.ExpandEnv("$HOME/.uber/credentials.json"))
	if err != nil {
		log.Fatal(err)
	}
	aWeekAgo := time.Now().Add(-1 * time.Hour * 7 * 24)
	yesterday := time.Now().Add(-1 * time.Hour * 24)
	trips, err := client.ListDriverTrips(&uber.DriverInfoQuery{
		StartDate:     &aWeekAgo,
		EndDate:       &yesterday,
		MaxPageNumber: 2,
		Offset:        4,
	})
	if err != nil {
		log.Fatal(err)
	}
	for page := range trips.Pages {
		if page.Err != nil {
			fmt.Printf("%d err: %v\n", page.PageNumber, page.Err)
			continue
		}
		for i, trip := range page.Trips {
			fmt.Printf("\t%d:: DriverID: %q\nFare: %.2f\n%#v\n", i, trip.DriverID, trip.Fare, trip)
		}
		fmt.Println()
	}
}
```
  • Loading branch information
odeke-em committed Aug 22, 2017
1 parent 80c9009 commit 1c064b6
Show file tree
Hide file tree
Showing 10 changed files with 671 additions and 75 deletions.
30 changes: 29 additions & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ func Example_client_ListDriverPayments() {
}
aWeekAgo := time.Now().Add(-1 * time.Hour * 7 * 24)
yesterday := time.Now().Add(-1 * time.Hour * 24)
payments, err := client.ListDriverPayments(&uber.DriverPaymentsQuery{
payments, err := client.ListDriverPayments(&uber.DriverInfoQuery{
StartDate: &aWeekAgo,
EndDate: &yesterday,
})
Expand All @@ -536,3 +536,31 @@ func Example_client_ListDriverPayments() {
fmt.Println()
}
}

func Example_client_ListDriverTrips() {
client, err := uber.NewClientFromOAuth2File(os.ExpandEnv("$HOME/.uber/credentials.json"))
if err != nil {
log.Fatal(err)
}
aWeekAgo := time.Now().Add(-1 * time.Hour * 7 * 24)
yesterday := time.Now().Add(-1 * time.Hour * 24)
trips, err := client.ListDriverTrips(&uber.DriverInfoQuery{
StartDate: &aWeekAgo,
EndDate: &yesterday,
MaxPageNumber: 2,
Offset: 4,
})
if err != nil {
log.Fatal(err)
}
for page := range trips.Pages {
if page.Err != nil {
fmt.Printf("%d err: %v\n", page.PageNumber, page.Err)
continue
}
for i, trip := range page.Trips {
fmt.Printf("\t%d:: DriverID: %q\nFare: %.2f\n%#v\n", i, trip.DriverID, trip.Fare, trip)
}
fmt.Println()
}
}
10 changes: 6 additions & 4 deletions v1/deliveries.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,22 @@ type Item struct {
}

type Endpoint struct {
Location *Location `json:"location"`
Location *Location `json:"location,omitempty"`
Contact *Contact `json:"contact,omitempty"`

// Special instructions for the endpoint. This field
// is optional and it is limited to 256 characters.
SpecialInstructions otils.NullableString `json:"special_instructions,omitempty"`

SignatureRequired bool `json:"signature_required"`
SignatureRequired bool `json:"signature_required,omitempty"`

// Indicates if the delivery includes alcohol. This
// feature is only available to whitelisted businesses.
IncludesAlcohol bool `json:"includes_alcohol"`
IncludesAlcohol bool `json:"includes_alcohol,omitempty"`

ETAMinutes int `json:"eta"`
ETAMinutes int `json:"eta,omitempty"`

TimestampUnix int64 `json:"timestamp,omitempty"`
}

type Contact struct {
Expand Down
148 changes: 81 additions & 67 deletions v1/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,70 @@ const (
defaultThrottleDuration = 150 * time.Millisecond
)

type DriverInfoResponse struct {
Cancel func()
Pages <-chan *DriverInfoPage
}

type driverInfoWrap struct {
Count int `json:"count"`
Limit int `json:"limit"`
Offset int `json:"offset"`
Payments []*Payment `json:"payments"`
Trips []*Trip `json:"trips"`
}

func (dpq *DriverInfoQuery) toRealDriverQuery() *realDriverQuery {
rdpq := &realDriverQuery{
Offset: dpq.Offset,
}
if dpq.StartDate != nil {
rdpq.StartTimeUnix = dpq.StartDate.Unix()
}
if dpq.EndDate != nil {
rdpq.EndTimeUnix = dpq.EndDate.Unix()
}
return rdpq
}

// realDriverQuery because it is the 1-to-1 match
// with the fields sent it to query for the payments.
// DriverQuery is just there for convenience and
// easy API usage from callers e.g passing in a date without
// having to worry about its exact Unix timestamp.
type realDriverQuery struct {
Offset int `json:"offset,omitempty"`
LimitPerPage int `json:"limit,omitempty"`
StartTimeUnix int64 `json:"from_time,omitempty"`
EndTimeUnix int64 `json:"to_time,omitempty"`
}

type DriverInfoQuery struct {
Offset int `json:"offset,omitempty"`

// LimitPerPage is the number of items to retrieve per page.
// Default is 5, maximum is 50.
LimitPerPage int `json:"limit,omitempty"`

StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`

MaxPageNumber int `json:"max_page_number,omitempty"`

Throttle time.Duration `json:"throttle,omitempty"`
}

type DriverInfoPage struct {
PageNumber int `json:"page_number,omitempty"`
Payments []*Payment `json:"payments,omitempty"`
Trips []*Trip `json:"trips,omitempty"`
Err error `json:"error"`
}

func (c *Client) ListDriverTrips(dpq *DriverInfoQuery) (*DriverInfoResponse, error) {
return c.listDriverInfo(dpq, "/partners/trips")
}

// DriverPayments returns the payments for the given driver.
// Payments are available at this endpoint in near real-time. Some entries,
// such as "device_subscription" will appear on a periodic basis when actually
Expand All @@ -63,9 +127,13 @@ const (
// there will be no payments associated and the response will always be an empty
// array. Drivers working for fleet managers will receive payments from the fleet
// manager and not from Uber.
func (c *Client) ListDriverPayments(dpq *DriverPaymentsQuery) (*DriverPaymentsResponse, error) {
func (c *Client) ListDriverPayments(dpq *DriverInfoQuery) (*DriverInfoResponse, error) {
return c.listDriverInfo(dpq, "/partners/payments")
}

func (c *Client) listDriverInfo(dpq *DriverInfoQuery, path string) (*DriverInfoResponse, error) {
if dpq == nil {
dpq = new(DriverPaymentsQuery)
dpq = new(DriverInfoQuery)
}

throttleDuration := dpq.Throttle
Expand All @@ -80,22 +148,22 @@ func (c *Client) ListDriverPayments(dpq *DriverPaymentsQuery) (*DriverPaymentsRe
return maxPageNumber > 0 && pageNumber >= maxPageNumber
}

baseURL := fmt.Sprintf("%s/partners/payments", c.baseURL(driverV1API))
rdpq := dpq.toRealDriverPaymentsQuery()
baseURL := fmt.Sprintf("%s%s", c.baseURL(driverV1API), path)
rdpq := dpq.toRealDriverQuery()
limitPerPage := rdpq.LimitPerPage
if limitPerPage <= 0 {
limitPerPage = defaultDriverPaymentsLimitPerPage
}

cancelChan, cancelFn := makeCancelParadigm()
resChan := make(chan *DriverPaymentsPage)
resChan := make(chan *DriverInfoPage)
go func() {
defer close(resChan)

pageNumber := 0

for {
curPage := new(DriverPaymentsPage)
curPage := new(DriverInfoPage)
curPage.PageNumber = pageNumber

qv, err := otils.ToURLValues(rdpq)
Expand Down Expand Up @@ -123,17 +191,21 @@ func (c *Client) ListDriverPayments(dpq *DriverPaymentsQuery) (*DriverPaymentsRe
return
}

recv := new(driverPaymentsWrap)
recv := new(driverInfoWrap)
if err := json.Unmarshal(blob, recv); err != nil {
curPage.Err = err
resChan <- curPage
return
}
if len(recv.Payments) == 0 { // No payments sent back, so sign that we are at the end

// No payments nor trips sent back, so a sign that we are at the end
if len(recv.Payments) == 0 && len(recv.Trips) == 0 {
return
}

curPage.Trips = recv.Trips
curPage.Payments = recv.Payments

resChan <- curPage

pageNumber += 1
Expand All @@ -151,68 +223,10 @@ func (c *Client) ListDriverPayments(dpq *DriverPaymentsQuery) (*DriverPaymentsRe
}
}()

resp := &DriverPaymentsResponse{
resp := &DriverInfoResponse{
Cancel: cancelFn,
Pages: resChan,
}

return resp, nil
}

type DriverPaymentsResponse struct {
Cancel func()
Pages <-chan *DriverPaymentsPage
}

type driverPaymentsWrap struct {
Count int `json:"count"`
Limit int `json:"limit"`
Offset int `json:"offset"`
Payments []*Payment `json:"payments"`
}

func (dpq *DriverPaymentsQuery) toRealDriverPaymentsQuery() *realDriverPaymentsQuery {
rdpq := &realDriverPaymentsQuery{
Offset: dpq.Offset,
}
if dpq.StartDate != nil {
rdpq.StartTimeUnix = dpq.StartDate.Unix()
}
if dpq.EndDate != nil {
rdpq.EndTimeUnix = dpq.EndDate.Unix()
}
return rdpq
}

// realDriverPaymentsQuery because it is the 1-to-1 match
// with the fields sent it to query for the payments.
// DriverPaymentsQuery is just there for convenience and
// easy API usage from callers e.g passing in a date without
// having to worry about its exact Unix timestamp.
type realDriverPaymentsQuery struct {
Offset int `json:"offset,omitempty"`
LimitPerPage int `json:"limit,omitempty"`
StartTimeUnix int64 `json:"from_time,omitempty"`
EndTimeUnix int64 `json:"to_time,omitempty"`
}

type DriverPaymentsQuery struct {
Offset int `json:"offset,omitempty"`

// LimitPerPage is the number of items to retrieve per page.
// Default is 5, maximum is 50.
LimitPerPage int `json:"limit,omitempty"`

StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`

MaxPageNumber int `json:"max_page_number,omitempty"`

Throttle time.Duration `json:"throttle,omitempty"`
}

type DriverPaymentsPage struct {
PageNumber int `json:"page_number,omitempty"`
Payments []*Payment `json:"payments,omitempty"`
Err error `json:"error"`
}
19 changes: 19 additions & 0 deletions v1/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,30 @@ type Trip struct {

ProductID string `json:"product_id,omitempty"`
RequestID string `json:"request_id,omitempty"`
TripID string `json:"trip_id,omitempty"`
DriverID string `json:"driver_id,omitempty"`

Unit string `json:"distance_unit,omitempty"`

Duration otils.NullableFloat64 `json:"duration,omitempty"`
DurationEstimate otils.NullableFloat64 `json:"duration_estimate,omitempty"`

Distance otils.NullableFloat64 `json:"distance,omitempty"`
DistanceEstimate otils.NullableFloat64 `json:"distance_estimate,omitempty"`

VehicleID otils.NullableString `json:"vehicle_id,omitempty"`

SurgeMultiplier otils.NullableFloat64 `json:"surge_multiplier,omitempty"`
Fare otils.NullableFloat64 `json:"fare,omitempty"`
Dropoff *Endpoint `json:"dropoff,omitempty"`
Pickup *Endpoint `json:"pickup,omitempty"`
StatusChanges []*StatusChange `json:"status_changes,omitempty"`
CurrencyCode CurrencyCode `json:"currency_code,omitempty"`
}

type StatusChange struct {
Status Status `json:"status,omitempty"`
TimestampUnix int64 `json:"timestamp,omitempty"`
}

type Place struct {
Expand Down
87 changes: 87 additions & 0 deletions v1/testdata/driver_trips_0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
"count": 1200,
"limit": 2,
"trips": [
{
"fare": 6.2,
"dropoff": {
"timestamp": 1502844378
},
"vehicle_id": "0082b54a-6a5e-4f6b-b999-b0649f286381",
"distance": 0.37,
"start_city": {
"latitude": 38.3498,
"display_name": "Charleston, WV",
"longitude": -81.6326
},
"status_changes": [
{
"status": "accepted",
"timestamp": 1502843899
},
{
"status": "driver_arrived",
"timestamp": 1502843900
},
{
"status": "trip_began",
"timestamp": 1502843903
},
{
"status": "completed",
"timestamp": 1502844378
}
],
"surge_multiplier": 1,
"pickup": {
"timestamp": 1502843903
},
"driver_id": "8LvWuRAq2511gmr8EMkovekFNa2848lyMaQevIto-aXmnK9oKNRtfTxYLgPq9OSt8EzAu5pDB7XiaQIrcp-zXgOA5EyK4h00U6D1o7aZpXIQah--U77Eh7LEBiksj2rahB==",
"status": "completed",
"duration": 475,
"trip_id": "b5613b6a-fe74-4704-a637-50f8d51a8bb1",
"currency_code": "USD"
},
{
"fare": 8.2,
"dropoff": {
"timestamp": 1502846443
},
"vehicle_id": "f227de83-0f6a-4422-a733-1e8b781b6ff7",
"distance": 2.11,
"start_city": {
"latitude": 38.3498,
"display_name": "Charleston, WV",
"longitude": -81.6326
},
"status_changes": [
{
"status": "accepted",
"timestamp": 1502844744
},
{
"status": "driver_arrived",
"timestamp": 1502843900
},
{
"status": "trip_began",
"timestamp": 1502843903
},
{
"status": "completed",
"timestamp": 1502844378
}
],
"surge_multiplier": 1.93,
"pickup": {
"timestamp": 1502843903
},
"driver_id": "8LvWuRAq2511gmr8EMkovekFNa2848lyMaQevIto-aXmnK9oKNRtfTxYLgPq9OSt8EzAu5pDB7XiaQIrcp-zXgOA5EyK4h00U6D1o7aZpXIQah--U77Eh7LEBiksj2rahB==",
"status": "completed",
"duration": 475,
"trip_id": "b5613b6a-fe74-4704-a637-50f8d51a8bb1",
"currency_code": "USD"
}
],
"offset": 0
}
1 change: 1 addition & 0 deletions v1/testdata/driver_trips_10.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Loading

0 comments on commit 1c064b6

Please sign in to comment.