Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions uberhook/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2017 orijtech. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package uberhook_test

import (
"fmt"
"log"
"net/http"

"golang.org/x/crypto/acme/autocert"

"github.com/orijtech/otils"
"github.com/orijtech/uber/uberhook"
)

func Example_Server() {
webhook, err := uberhook.New()
if err != nil {
log.Fatal(err)
}

mux := http.NewServeMux()
mux.Handle("/", webhook.Middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
event, err := uberhook.FparseEvent(r.Body)

if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
log.Printf("Got an event from Uber: %#v\n", event)
fmt.Fprintf(w, "Successfully retrieved event!\n")
})))

go func() {
nonHTTPSHandler := otils.RedirectAllTrafficTo("https://uberhook.example.com")
if err := http.ListenAndServe(":80", nonHTTPSHandler); err != nil {
log.Fatal(err)
}
}()

domains := []string{
"uberhook.example.com",
"www.uberhook.example.com",
}

log.Fatal(http.Serve(autocert.NewListener(domains...), mux))
}
125 changes: 125 additions & 0 deletions uberhook/uberhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright 2017 orijtech. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package uberhook

import (
"encoding/json"
"errors"
"io"
"io/ioutil"
"net/http"
"sync"

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

type Event struct {
ID string `json:"event_id"`
TimeUnix int64 `json:"event_time"`
Type string `json:"event_type"`

Meta *Meta `json:"meta"`

URL string `json:"resource_href"`
}

type Status string

type Meta struct {
UserID string `json:"user_id"`
ResourceID string `json:"resource_id"`
Status uber.Status `json:"status"`
}

type Webhook struct {
sync.RWMutex
oauthConfig *oauth2.OAuth2AppConfig
}

var _ authmid.Authenticator = (*Webhook)(nil)

var (
errBlankClientID = errors.New("expecting a non-blank clientID")
errBlankClientSecret = errors.New("expecting a non-blank clientSecret")
)

func (v *Webhook) HeaderValues(hdr http.Header) ([]string, []string, error) {
return nil, nil, nil
}

func (v *Webhook) LookupAPIKey(hdr http.Header) (string, error) {
// Uber doesn't include the APIKey as part of the header signatures
// at least as of `Sat 10 Jun 2017 01:20:15 MDT` so send back a blank.
// Reference: https://developer.uber.com/docs/riders/guides/webhooks
// has no mention of client_id in there, only client_secret
return "", nil
}

func (v *Webhook) LookupSecret(apiKey string) ([]byte, error) {
var err error = errBlankClientSecret
var secret []byte
if v != nil {
v.RLock()
if v.oauthConfig != nil {
secret = []byte(v.oauthConfig.ClientSecret)
err = nil
}
v.RUnlock()
}
return secret, err
}

func (v *Webhook) Signature(hdr http.Header) (string, error) {
return hdr.Get("X-Uber-Signature"), nil
}

func New() (*Webhook, error) {
oauth2Config, err := oauth2.OAuth2ConfigFromEnv()
if err != nil {
return nil, err
}
return &Webhook{oauthConfig: oauth2Config}, nil
}

func (w *Webhook) Middleware(next http.Handler) http.Handler {
return authmid.Middleware(w, next)
}

var _ authmid.ExcludeMethodAndPather = (*Webhook)(nil)

// Uber's webhook signature verification only consists of (clientSecret, webhookBody)
func (w *Webhook) ExcludeMethodAndPath() bool { return true }

var blankEvent Event
var (
errBlankEvent = errors.New("expecting a non-blank event")
)

func FparseEvent(r io.Reader) (*Event, error) {
blob, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
ev := new(Event)
if err := json.Unmarshal(blob, ev); err != nil {
return nil, err
}
if *ev == blankEvent {
return nil, errBlankEvent
}
return ev, nil
}
2 changes: 0 additions & 2 deletions v1/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import (
"github.com/orijtech/otils"
)

type Status string

type Trip struct {
// Status of the activity. As per API v1.2,
// it only return "completed" for now.
Expand Down
53 changes: 53 additions & 0 deletions v1/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2017 orijtech. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package uber

type Status string

const (
// The request is matching to
// the most efficient available driver.
StatusProcessing Status = "processing"

// The request was unfulfilled because
// no drivers were available.
StatusNoDriversAvailable Status = "no_drivers_available"

// The request has been accepted by a driver and
// is "en route" to the start location
// (i.e. start_latitude and start_longitude).
// This state can occur multiple times in case of
// a driver re-assignment.
StatusAccepted Status = "accepted"

// The driver has arrived or will be shortly.
StatusArriving Status = "arriving"

// The request is "en route" from the
// start location to the end location.
StatusInProgress Status = "in_progress"

// The request has been canceled by the driver.
StatusDriverCanceled Status = "driver_canceled"

// The request has been canceled by the rider.
StatusRiderCanceled Status = "rider_canceled"

// The request has been completed by the driver.
StatusCompleted Status = "completed"

// The receipt for the trip is ready.
StatusReceiptReady Status = "ready"
)