Skip to content

Commit 18b827c

Browse files
committed
delivery: implemented Client.ListDeliveries
Updates #32 * Implemented Client.ListDeliveries. * Introduced scope oauth2.ScopeDelivery. NB: * ListDeliveries hits the /v1 API instead of /v1.2 Sample usage: ```go func main() { client, err := uber.NewClientFromOAuth2File(os.ExpandEnv("$HOME/.uber/credentials.json")) if err != nil { log.Fatal(err) } delivRes, err := client.ListDeliveries(&uber.DeliveryListRequest{ Status: uber.StatusCompleted, StartOffset: 20, }) if err != nil { log.Fatal(err) } itemCount := uint64(0) for page := range delivRes.Pages { if page.Err != nil { fmt.Printf("Page #%d err: %v", page.PageNumber, page.Err) } for i, delivery := range page.Deliveries { fmt.Printf("\t(%d): %#v\n", i, delivery) itemCount += 1 } if itemCount >= 10 { delivRes.Cancel() } } } ```
1 parent 294214b commit 18b827c

6 files changed

Lines changed: 301 additions & 13 deletions

File tree

cmd/uber/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func authorize() {
4747
scopes := []string{
4848
oauth2.ScopeProfile, oauth2.ScopeRequest,
4949
oauth2.ScopeHistory, oauth2.ScopePlaces,
50-
oauth2.ScopeRequestReceipt,
50+
oauth2.ScopeRequestReceipt, oauth2.ScopeDelivery,
5151
}
5252

5353
token, err := oauth2.AuthorizeByEnvApp(scopes...)

example_test.go

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,6 @@ func Example_client_EstimatePrice() {
144144
cancelPaging()
145145
}
146146
}
147-
// Output:
148-
// WW
149147
}
150148

151149
func Example_client_EstimateTime() {
@@ -184,8 +182,6 @@ func Example_client_EstimateTime() {
184182
cancelPaging()
185183
}
186184
}
187-
// Output:
188-
// WW
189185
}
190186

191187
func Example_client_RetrieveMyProfile() {
@@ -456,8 +452,6 @@ func Example_client_ListProducts() {
456452
for i, product := range products {
457453
fmt.Printf("#%d: ID: %q Product: %#v\n", i, product.ID, product)
458454
}
459-
// Output:
460-
// WW
461455
}
462456

463457
func Example_client_ProductByID() {
@@ -473,3 +467,32 @@ func Example_client_ProductByID() {
473467

474468
fmt.Printf("The Product information: %#v\n", product)
475469
}
470+
471+
func Example_client_ListDeliveries() {
472+
client, err := uber.NewClientFromOAuth2File(os.ExpandEnv("$HOME/.uber/credentials.json"))
473+
if err != nil {
474+
log.Fatal(err)
475+
}
476+
477+
delivRes, err := client.ListDeliveries(&uber.DeliveryListRequest{
478+
Status: uber.StatusCompleted,
479+
StartOffset: 20,
480+
})
481+
if err != nil {
482+
log.Fatal(err)
483+
}
484+
485+
itemCount := uint64(0)
486+
for page := range delivRes.Pages {
487+
if page.Err != nil {
488+
fmt.Printf("Page #%d err: %v", page.PageNumber, page.Err)
489+
}
490+
for i, delivery := range page.Deliveries {
491+
fmt.Printf("\t(%d): %#v\n", i, delivery)
492+
itemCount += 1
493+
}
494+
if itemCount >= 10 {
495+
delivRes.Cancel()
496+
}
497+
}
498+
}

oauth2/oauth2.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ const (
177177
// including pickup, destination and real-time
178178
// location for all of your future rides.
179179
ScopeAllTrips = "all_trips"
180+
181+
// ScopeDelivery is a privileged scope that gives
182+
// access to the authenticated user's deliveries.
183+
ScopeDelivery = "delivery"
180184
)
181185

182186
func AuthorizeByEnvApp(scopes ...string) (*oauth2.Token, error) {

v1/client.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,22 @@ func (c *Client) baseURL() string {
8080
}
8181
}
8282

83+
// Some endpoints require us to hit /v1 instead of /v1.2 as in Client.baseURL.
84+
// These endpoints include:
85+
// + ListDeliveries --> /v1/deliveries at least as of "Tue 4 Jul 2017 23:17:14 MDT"
86+
func (c *Client) legacyV1BaseURL() string {
87+
// Setting the baseURLs in here to ensure that no-one mistakenly
88+
// directly invokes baseURL or sandboxBaseURL.
89+
c.RLock()
90+
defer c.RUnlock()
91+
92+
if c.sandboxed {
93+
return "https://sandbox-api.uber.com/v1"
94+
} else { // Invoking the production endpoint
95+
return "https://api.uber.com/v1"
96+
}
97+
}
98+
8399
func NewClient(tokens ...string) (*Client, error) {
84100
if token := otils.FirstNonEmptyString(tokens...); token != "" {
85101
return &Client{token: token}, nil

v1/deliveries.go

Lines changed: 151 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import (
2020
"errors"
2121
"fmt"
2222
"net/http"
23+
"net/url"
2324
"strings"
25+
"time"
2426

2527
"github.com/orijtech/otils"
2628
)
@@ -98,7 +100,7 @@ type Phone struct {
98100

99101
type CurrencyCode string
100102

101-
type DeliveryResponse struct {
103+
type Delivery struct {
102104
ID string `json:"delivery_id"`
103105
Fee float32 `json:"fee"`
104106
QuoteID string `json:"quote_id"`
@@ -196,7 +198,7 @@ func (e *Endpoint) Validate() error {
196198
return nil
197199
}
198200

199-
func (c *Client) RequestDelivery(req *DeliveryRequest) (*DeliveryResponse, error) {
201+
func (c *Client) RequestDelivery(req *DeliveryRequest) (*Delivery, error) {
200202
if err := req.Validate(); err != nil {
201203
return nil, err
202204
}
@@ -215,7 +217,7 @@ func (c *Client) RequestDelivery(req *DeliveryRequest) (*DeliveryResponse, error
215217
if err != nil {
216218
return nil, err
217219
}
218-
dRes := new(DeliveryResponse)
220+
dRes := new(Delivery)
219221
if err := json.Unmarshal(blob, dRes); err != nil {
220222
return nil, err
221223
}
@@ -240,3 +242,149 @@ func (c *Client) CancelDelivery(deliveryID string) error {
240242
_, _, err = c.doHTTPReq(httpReq)
241243
return err
242244
}
245+
246+
type DeliveryListRequest struct {
247+
Status Status `json:"status,omitempty"`
248+
LimitPerPage int64 `json:"limit"`
249+
MaxPageNumber int64 `json:"max_page,omitempty"`
250+
StartOffset int64 `json:"offset"`
251+
252+
ThrottleDurationMs int64 `json:"throttle_duration_ms"`
253+
}
254+
255+
type DeliveryThread struct {
256+
Pages chan *DeliveryPage `json:"-"`
257+
Cancel func()
258+
}
259+
260+
type DeliveryPage struct {
261+
Err error `json:"error"`
262+
PageNumber int64 `json:"page_number,omitempty"`
263+
Deliveries []*Delivery `json:"deliveries,omitempty"`
264+
}
265+
266+
type recvDelivery struct {
267+
Count int64 `json:"count"`
268+
NextPageQuery string `json:"next_page"`
269+
PreviousPageQuery string `json:"previous_page"`
270+
Deliveries []*Delivery `json:"deliveries"`
271+
}
272+
273+
type deliveryPager struct {
274+
Offset int64 `json:"offset"`
275+
Limit int64 `json:"limit"`
276+
Status Status `json:"status"`
277+
}
278+
279+
const (
280+
NoThrottle = -1
281+
282+
defaultThrottleDurationMs = 150 * time.Millisecond
283+
)
284+
285+
// ListDeliveries requires authorization with OAuth2.0 with
286+
// the delivery scope set.
287+
func (c *Client) ListDeliveries(dReq *DeliveryListRequest) (*DeliveryThread, error) {
288+
if dReq == nil {
289+
dReq = &DeliveryListRequest{Status: StatusReceiptReady}
290+
}
291+
292+
baseURL := c.legacyV1BaseURL()
293+
fullURL := fmt.Sprintf("%s/deliveries", baseURL)
294+
qv, err := otils.ToURLValues(&deliveryPager{
295+
Limit: dReq.LimitPerPage,
296+
Status: dReq.Status,
297+
Offset: dReq.StartOffset,
298+
})
299+
if err != nil {
300+
return nil, err
301+
}
302+
303+
if len(qv) > 0 {
304+
fullURL = fmt.Sprintf("%s/deliveries?%s", baseURL, qv.Encode())
305+
}
306+
307+
parsedURL, err := url.Parse(fullURL)
308+
if err != nil {
309+
return nil, err
310+
}
311+
parsedBaseURL, err := url.Parse(baseURL)
312+
if err != nil {
313+
return nil, err
314+
}
315+
316+
var errsList []string
317+
if want, got := parsedBaseURL.Scheme, parsedURL.Scheme; got != want {
318+
errsList = append(errsList, fmt.Sprintf("gotScheme=%q wantBaseScheme=%q", got, want))
319+
}
320+
if want, got := parsedBaseURL.Host, parsedURL.Host; got != want {
321+
errsList = append(errsList, fmt.Sprintf("gotHost=%q wantBaseHost=%q", got, want))
322+
}
323+
if len(errsList) > 0 {
324+
return nil, errors.New(strings.Join(errsList, "\n"))
325+
}
326+
327+
maxPage := dReq.MaxPageNumber
328+
pageExceeded := func(pageNumber int64) bool {
329+
return maxPage > 0 && pageNumber >= maxPage
330+
}
331+
332+
fullDeliveriesBaseURL := fmt.Sprintf("%s/deliveries", baseURL)
333+
resChan := make(chan *DeliveryPage)
334+
cancelChan, cancelFn := makeCancelParadigm()
335+
336+
go func() {
337+
defer close(resChan)
338+
339+
pageNumber := int64(0)
340+
throttleDurationMs := defaultThrottleDurationMs
341+
if dReq.ThrottleDurationMs == NoThrottle {
342+
throttleDurationMs = 0
343+
} else {
344+
throttleDurationMs = time.Duration(dReq.ThrottleDurationMs) * time.Millisecond
345+
}
346+
347+
for {
348+
page := &DeliveryPage{PageNumber: pageNumber}
349+
350+
req, err := http.NewRequest("GET", fullURL, nil)
351+
if err != nil {
352+
page.Err = err
353+
resChan <- page
354+
return
355+
}
356+
357+
slurp, _, err := c.doReq(req)
358+
if err != nil {
359+
page.Err = err
360+
resChan <- page
361+
return
362+
}
363+
364+
recv := new(recvDelivery)
365+
if err := json.Unmarshal(slurp, recv); err != nil {
366+
page.Err = err
367+
resChan <- page
368+
return
369+
}
370+
371+
page.Deliveries = recv.Deliveries
372+
resChan <- page
373+
pageNumber += 1
374+
pageToken := recv.NextPageQuery
375+
if pageExceeded(pageNumber) || pageToken == "" || len(recv.Deliveries) == 0 {
376+
return
377+
}
378+
379+
fullURL = fmt.Sprintf("%s?%s", fullDeliveriesBaseURL, pageToken)
380+
381+
select {
382+
case <-cancelChan:
383+
return
384+
case <-time.After(throttleDurationMs):
385+
}
386+
}
387+
}()
388+
389+
return &DeliveryThread{Cancel: cancelFn, Pages: resChan}, nil
390+
}

0 commit comments

Comments
 (0)