Skip to content

Commit 22f6469

Browse files
committed
Sample Python/Flask app using the Uber api
The goal of the project is to provide a working example of the Uber api. It is intended to be simple, easy to understand and a platform off of which developers can build their own applications. For more information about how to use this example, please read the README.md file for more information and for instructions for how to run this project.
0 parents  commit 22f6469

18 files changed

+462
-0
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
Example Uber app for developers
2+
==============================
3+
4+
https://developer.uber.com/
5+
6+
What Is This?
7+
-------------
8+
9+
This is a simple Python/Flask application intended to provide a working example of Uber's external API. The goal of these endpoints is to be simple, well-documented and to provide a base for developers to develop other applications off of.
10+
11+
12+
How To Use This
13+
---------------
14+
15+
1. Navigate over to https://developer.uber.com/, and sign up for an Uber developer account.
16+
2. Register a new Uber application and make your Redirect URI http://localhost:7000/submit
17+
3. Fill in the relevant information in the config.json file in the root folder and add your client id and secret as the environment variables UBER_CLIENT_ID AND UBER_CLIENT_SECRET. Run ‘export UBER_CLIENT_ID=”YOUR_CLIENT_ID”&&export UBER_CLIENT_SECRET=”YOUR_CLIENT_SECRET”’
18+
4. Run ‘pip install -r requirements.txt’ to install dependencies
19+
5. Run ‘python app.py’
20+
21+
22+
Testing
23+
-------
24+
25+
1. Install the dependencies with ‘pip install -r requirements.txt’
26+
2. Run the command ‘nosetests -v’
27+
3. If you delete the fixtures, or decide to add some of your own, you’ll have to re-generate them, and the way this is done is by running the app, getting an auth_token from the main page of the app. Paste that token in place of the ‘test_auth_token’ at the top of the test_endpoints.py file, then run the tests.
28+
29+
30+
Development
31+
-----------
32+
33+
If you want to work on this application we’d love your pull requests and tickets on GitHub!
34+
35+
1. If you open up a ticket, please make sure it describes the problem or feature request fully.
36+
2. If you send us a pull request, make sure you add a test for what you added, and make sure the full test suite runs with nosetests -v.
37+
38+
39+
Making Requests
40+
---------------
41+
42+
The base for all requests is https://api.uber.com/v1/, to find a list of all available endpoints, please visit: https://developer.uber.com/v1/endpoints/

app.py

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
from __future__ import absolute_import
2+
from flask import Flask, render_template, request, redirect, session
3+
from rauth import OAuth2Service
4+
5+
import requests
6+
import os
7+
import json
8+
9+
app = Flask(__name__, static_folder='static', static_url_path='')
10+
app.requests_session = requests.Session()
11+
app.secret_key = os.urandom(24)
12+
13+
14+
with open('config.json') as f:
15+
config = json.load(f)
16+
17+
18+
def generate_oauth_service():
19+
"""Prepares the OAuth2Service that is used to make requests later."""
20+
return OAuth2Service(
21+
client_id=os.environ.get('UBER_CLIENT_ID'),
22+
client_secret=os.environ.get('UBER_CLIENT_SECRET'),
23+
name=config.get('name'),
24+
authorize_url=config.get('authorize_url'),
25+
access_token_url=config.get('access_token_url'),
26+
base_url=config.get('base_url'),
27+
)
28+
29+
30+
def generate_ride_headers(token):
31+
"""Generates the header object that is used to make api requests."""
32+
return {
33+
'Authorization': 'bearer %s' % token,
34+
'Content-Type': 'application/json',
35+
}
36+
37+
38+
@app.route('/health', methods=['GET'])
39+
def health():
40+
"""Used to check the status of this application."""
41+
return ';-)'
42+
43+
44+
@app.route('/', methods=['GET'])
45+
def signup():
46+
"""The first step in the three-legged OAuth handshake.
47+
48+
You should navigate here first. It will redirect to login.uber.com.
49+
"""
50+
params = {
51+
'response_type': 'code',
52+
'redirect_uri': config.get('redirect_uri'),
53+
'scope': config.get('scopes'),
54+
}
55+
url = generate_oauth_service().get_authorize_url(**params)
56+
return redirect(url)
57+
58+
59+
@app.route('/submit', methods=['GET'])
60+
def submit():
61+
"""The other two steps in the three-legged Oauth handshake.
62+
63+
Your redirect uri will redirect you here, where you will exchange
64+
a code that can be used to obtain an access token for the logged-in use.
65+
"""
66+
params = {
67+
'redirect_uri': config.get('redirect_uri'),
68+
'code': request.args.get('code'),
69+
'grant_type': 'authorization_code'
70+
}
71+
72+
response = app.requests_session.post(
73+
config.get('access_token_url'),
74+
auth=(
75+
os.environ.get('UBER_CLIENT_ID'),
76+
os.environ.get('UBER_CLIENT_SECRET')
77+
),
78+
data=params,
79+
)
80+
session['access_token'] = response.json().get('access_token')
81+
return render_template(
82+
'success.html',
83+
token=response.json().get('access_token')
84+
)
85+
86+
87+
@app.route('/demo', methods=['GET'])
88+
def demo():
89+
"""Demo.html is a template that calls the other routes in this example."""
90+
return render_template('demo.html', token=session.get('access_token'))
91+
92+
93+
@app.route('/products', methods=['GET'])
94+
def products():
95+
'''Example call to the products endpoint.
96+
97+
Returns all the products currently available in San Francisco.
98+
'''
99+
url = config.get('base_uber_url') + 'products'
100+
params = {
101+
'latitude': config.get('start_latitude'),
102+
'longitude': config.get('start_longitude'),
103+
}
104+
105+
response = app.requests_session.get(
106+
url,
107+
headers=generate_ride_headers(session.get('access_token')),
108+
params=params,
109+
)
110+
111+
if response.status_code != 200:
112+
return 'There was an error', response.status_code
113+
return render_template(
114+
'results.html',
115+
endpoint='products',
116+
data=response.text,
117+
)
118+
119+
120+
@app.route('/time', methods=['GET'])
121+
def time():
122+
"""Example call to the time estimates endpoint.
123+
124+
Returns the time estimates from the given lat/lng given below.
125+
"""
126+
url = config.get('base_uber_url') + 'estimates/time'
127+
params = {
128+
'start_latitude': config.get('start_latitude'),
129+
'start_longitude': config.get('start_longitude'),
130+
}
131+
132+
response = app.requests_session.get(
133+
url,
134+
headers=generate_ride_headers(session.get('access_token')),
135+
params=params,
136+
)
137+
138+
if response.status_code != 200:
139+
return 'There was an error', response.status_code
140+
return render_template(
141+
'results.html',
142+
endpoint='time',
143+
data=response.text,
144+
)
145+
146+
147+
@app.route('/price', methods=['GET'])
148+
def price():
149+
"""Example call to the time estimates endpoint.
150+
151+
Returns the time estimates from the given lat/lng given below.
152+
"""
153+
url = config.get('base_uber_url') + 'estimates/price'
154+
params = {
155+
'start_latitude': config.get('start_latitude'),
156+
'start_longitude': config.get('start_longitude'),
157+
'end_latitude': config.get('end_latitude'),
158+
'end_longitude': config.get('end_longitude'),
159+
}
160+
161+
response = app.requests_session.get(
162+
url,
163+
headers=generate_ride_headers(session.get('access_token')),
164+
params=params,
165+
)
166+
167+
if response.status_code != 200:
168+
return 'There was an error', response.status_code
169+
return render_template(
170+
'results.html',
171+
endpoint='price',
172+
data=response.text,
173+
)
174+
175+
176+
@app.route('/history', methods=['GET'])
177+
def history():
178+
"""Returns the last 5 trips made by the logged in user."""
179+
url = config.get('base_uber_url') + 'history'
180+
params = {
181+
'offset': 0,
182+
'limit': 5,
183+
}
184+
185+
response = app.requests_session.get(
186+
url,
187+
headers=generate_ride_headers(session.get('access_token')),
188+
params=params,
189+
)
190+
191+
if response.status_code != 200:
192+
return 'There was an error', response.status_code
193+
return render_template(
194+
'results.html',
195+
endpoint='history',
196+
data=response.text,
197+
)
198+
199+
200+
@app.route('/me', methods=['GET'])
201+
def me():
202+
"""Returns user information including name, picture and email."""
203+
url = config.get('base_uber_url') + 'me'
204+
response = app.requests_session.get(
205+
url,
206+
headers=generate_ride_headers(session.get('access_token')),
207+
)
208+
209+
if response.status_code != 200:
210+
return 'There was an error', response.status_code
211+
return render_template(
212+
'results.html',
213+
endpoint='me',
214+
data=response.text,
215+
)
216+
217+
218+
if __name__ == '__main__':
219+
app.run(port=7000)

config.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"redirect_uri": "http://localhost:7000/submit",
3+
"access_token_url": "https://login.uber.com/oauth/token",
4+
"authorize_url": "https://login.uber.com/oauth/authorize",
5+
"base_url": "https://login.uber.com/",
6+
"scopes": "profile history",
7+
"name": "Sample app",
8+
"base_uber_url": "https://api.uber.com/v1/",
9+
"start_latitude": "37.781955",
10+
"start_longitude": "-122.402367",
11+
"end_latitude": "37.744352",
12+
"end_longitude": "-122.416743"
13+
}

requirements.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Flask==0.10.1
2+
Jinja2==2.7.3
3+
MarkupSafe==0.23
4+
Werkzeug==0.9.6
5+
betamax==0.4.0
6+
gnureadline==6.3.3
7+
itsdangerous==0.24
8+
nose==1.3.3
9+
rauth==0.7.0
10+
requests==2.3.0
11+
wsgiref==0.1.2

static/util.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
function action(endpoint_name) {
2+
window.location.replace('/' + endpoint_name);
3+
}
4+
5+
function redirect_to_demo(code) {
6+
window.location.replace('/demo');
7+
}

templates/demo.html

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<script src="util.js"></script>
5+
</head>
6+
<body>
7+
{% if token %}
8+
<h1> Congratulations! you have successfully authenticated and your token is: {{ token }} </h1>
9+
{% else %}
10+
<h1> Something went wrong :( </h1>
11+
{% endif %}
12+
13+
<h2> Test the following functions of the api! </h2>
14+
<button type="button" onclick="action('products')">PRODUCTS!</button>
15+
<button type="button" onclick="action('time')">TIME ESTIMATES!</button>
16+
<button type="button" onclick="action('price')">PRICE ESTIMATES!</button>
17+
<button type="button" onclick="action('history')">HISTORY!</button>
18+
<button type="button" onclick="action('me')">ME!</button>
19+
</body>
20+
</html>

templates/results.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<script src="util.js"></script>
5+
</head>
6+
7+
<body>
8+
<button type="button" onclick="redirect_to_demo()">Back</button>
9+
<h1> Welcome to the {{ endpoint }} endpoint!</h1>
10+
11+
<h2> Here is the result of a call to the {{ endpoint }}: </h2>
12+
<p> {{ data }}
13+
</body>
14+
</html>

templates/success.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<script src="util.js"></script>
5+
<script>redirect_to_demo('{{ token }}')</script>
6+
</head>
7+
<body></body>
8+
</html>

test/fixtures/history_failure.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"http_interactions": [{"request": {"body": {"string": "", "encoding": "utf-8"}, "headers": {"Accept": ["*/*"], "Content-Type": ["application/json"], "Accept-Encoding": ["gzip, deflate"], "Authorization": ["bearer NOT_A_CODE"], "User-Agent": ["python-requests/2.3.0 CPython/2.7.5 Darwin/13.2.0"]}, "method": "GET", "uri": "https://api.uber.com/v1/history?limit=5&offset=0"}, "response": {"body": {"string": "{\"message\":\"Invalid OAuth 2.0 credentials provided.\",\"code\":\"unauthorized\"}", "encoding": null}, "headers": {"content-length": ["75"], "server": ["nginx"], "connection": ["keep-alive"], "date": ["Fri, 01 Aug 2014 20:22:08 GMT"], "x-uber-app": ["uberex-nonsandbox"], "content-type": ["application/json"]}, "status": {"message": "Unauthorized", "code": 401}, "url": "https://api.uber.com/v1/history?limit=5&offset=0"}, "recorded_at": "2014-08-01T20:22:08"}], "recorded_with": "betamax/0.4.0"}

test/fixtures/history_success.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"http_interactions": [{"request": {"body": {"string": "", "encoding": "utf-8"}, "headers": {"Accept": ["*/*"], "Content-Type": ["application/json"], "Accept-Encoding": ["gzip, deflate"], "Authorization": ["bearer 42Kq726Vv6lzJ0TMhXWsgUulVjRsxh"], "User-Agent": ["python-requests/2.3.0 CPython/2.7.5 Darwin/13.2.0"]}, "method": "GET", "uri": "https://api.uber.com/v1/history?limit=5&offset=0"}, "response": {"body": {"base64_string": "H4sIAAAAAAAAA62VUWvcMAzHv0rIc1xsybaseyuDbU9jUNjLGMWJnTZwd+lyydgo/e7TraXd7dJe1u3NEEey9Pv/pduy6aftWK58VfZtu8ty1FW57jadnFxVXne7sR9+lKvPt+VujOO0K1fyz+ZmncecyqpMciFum1yu4MwGBq+Nx6qcpi7JTTKhBY+tYjakLDk5Oc9KAxsOZOrWWQkiIYbxcuw2EsZY7Vmz51CVN0Ofpma8/BVrqvPwXS7nbfr9qtEIcvU+xLpv4tj123J1W67lNE5JQiKdkSXPTIal0HW/vXr4ogzIs7UP6ANJuTGlIe/2NQZbfMxDXI+xOP+Wt1Ouiou4Ld4OUmy3a/qqeHNe3t2/5vms5MARW4SZrCZYZgA+yKpAF8aM18XFOOQ8PpN0yF+nvPujY2jdXbWAEhptHEqLHylFTE1os1Ym1kLJglDyiRRlxoQYHaZ6hlIADEyLKAWwDuTqaUqktRedzPRLe2IQhR30C/4TJQb0diapQDKMVlTzmzReCykABOIFkIxokow1AcwjpBCw8dS0ygk8sZJuVPSxVgJKN7mus4E5K1Hw5JZBIjGkFu+egiQW1+iClrcdWcmQgWDtoah1KC76SUT9SRz0Qfz1L5ayJBIBu1fTUXbtvfVMh7T0Mokce0pa5ywswIVnyM44zfYJl28ptRGtqhtyyno0KmhtxGKoM1MMNsUZTxEGApnACyYfoVQqRjmFSyYf7cPON8xRcF4+PcmbjEzrZT3bj+KXhp9kRdwPmiNS4ivQJhyMXFJgXjX8pDpgXAjKaJEPSOKHFRUsNT6ZWjWNrZVt66xqTFE50OhdFs9yngEl2yT4haC0lpl7GhQxMnsAseBRvwAt2f2EeuLknVayJYt3/XR1Yl+8zMkGH9h6Pb8aCeAgrehcVOyAi/dxc7O77ob8t8tqv4kd3n25+wkeOPQwfwgAAA==", "encoding": null}, "headers": {"x-rate-limit-remaining": ["4990"], "content-language": ["en"], "content-encoding": ["gzip"], "transfer-encoding": ["chunked"], "strict-transport-security": ["max-age=2592000"], "server": ["nginx"], "connection": ["keep-alive"], "x-rate-limit-reset": ["1406926800"], "x-uber-app": ["uberex-nonsandbox"], "date": ["Fri, 01 Aug 2014 20:22:09 GMT"], "x-rate-limit-limit": ["5000"], "content-type": ["application/json"]}, "status": {"message": "OK", "code": 200}, "url": "https://api.uber.com/v1/history?limit=5&offset=0"}, "recorded_at": "2014-08-01T20:22:09"}], "recorded_with": "betamax/0.4.0"}

0 commit comments

Comments
 (0)