Skip to content

Commit

Permalink
Merge a214b6e into cfb2ca2
Browse files Browse the repository at this point in the history
  • Loading branch information
iampark committed Aug 3, 2015
2 parents cfb2ca2 + a214b6e commit bcbfe73
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 50 deletions.
76 changes: 52 additions & 24 deletions README.md
@@ -1,27 +1,41 @@
Example Uber app for developers
Example Python/Flask Uber App
==============================

[![TravisCI](https://travis-ci.org/uber/Python-Sample-Application.svg?branch=master)](https://travis-ci.org/uber/Python-Sample-Application)
[![Coverage Status](https://coveralls.io/repos/uber/Python-Sample-Application/badge.png)](https://coveralls.io/r/uber/Python-Sample-Application)

https://developer.uber.com/
This is an example Python/Flask application that shows a working example of [Uber's APIs](https://developer.uber.com/), including authentication and REST calls.

What Is This?
-------------
<img src="static/img/auth.png" style="width: 80%;"/>

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.
This sample shows the following API calls at work:

- [User authentication](https://developer.uber.com/v1/auth/)
- [Product query](https://developer.uber.com/v1/endpoints/)
- Time estimates
- Price estimates
- User profile & recent trip history

How To Use This
To see examples of how developers at [OpenTable](http://blog.opentable.com/2014/ride-there-with-uber-in-the-opentable-app/), [Foursquare](https://devblog.uber.com/foursquare-and-uber-the-perfect-ride-for-the-perfect-recommendation/) and [Trulia](https://devblog.uber.com/ubers-api-helps-trulia-customers-get-to-their-dream-homes/) have used Uber in their products, visit our [Developer Showcase](https://developer.uber.com/showcase/) or see [hackathon projects](https://developer.uber.com/showcase/) that have integrated Uber.

Full documentation of the APIs are available on the online [Uber API Documentation](https://developer.uber.com/v1/endpoints/).


Getting started
---------------

1. Navigate over to https://developer.uber.com/, and sign up for an Uber developer account.
2. Register a new Uber application and make your Redirect URI `http://localhost:7000/submit` - ensure that both the `profile` and `history` OAuth scopes are checked.
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`.
4. Run `export UBER_CLIENT_ID="`*{your client id}*`"&&export UBER_CLIENT_SECRET="`*{your client secret}*`"`
1. Visit [https://developer.uber.com/](https://developer.uber.com/) to sign up for an Uber developer account.
2. Register a new Uber application and ensure that:
- Your Redirect URI is `http://localhost:7000/submit`
- Both the `profile` and `history` OAuth scopes are checked
- Copy your Client ID and Client Secret
3. Add your Client ID and Client Secret as local environment variables using the following:
- `export UBER_CLIENT_ID={your client id}`
- `export UBER_CLIENT_SECRET={your client secret}`
4. Review the `config.json` file for any relevant local changes
5. Run `pip install -r requirements.txt` to install dependencies
6. Run `python app.py`
7. Navigate to http://localhost:7000 in your browser
7. Open [http://localhost:7000](http://localhost:7000) in your browser


Testing
Expand All @@ -32,25 +46,39 @@ Testing
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.


Development
-----------

If you want to work on this application we’d love your pull requests and tickets on GitHub!

1. If you open up a ticket, please make sure it describes the problem or feature request fully.
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 `make test`.

Deploy to Heroku
Deploying to Heroku
----------------

Click the button below to set up this sample app on Heroku:

[![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy)

After creating your app on Heroku, you have to configure the redirect URL for your Uber OAuth app. Use a `https://`*{your-app-name}*`.herokuapp.com/submit` URL.
You will also want to configure the heroku environment variable FLASK_DEBUG=False in order to properly serve SSL traffic.
You should see the Heroku new app screen below.

<img src="static/img/heroku.png" style="width: 80%;"/>

Before your app will work, you need to:

1. Specify the Client ID and Secret in the Heroku app
2. Configure your [Uber OAuth app](https://developer.uber.com/apps/) to have a redirect URL of `https://`*{your-app-name}*`.herokuapp.com/submit` URL.
3. Configure the heroku environment variable `FLASK_DEBUG=False` to serve SSL traffic.

Making Requests
Making requests
---------------

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/
The base for all requests is https://api.uber.com/v1/, where `v1` is the specific version of the API you are requestiong.

To see a list of all available endpoints, please visit: [https://developer.uber.com/v1/endpoints/](https://developer.uber.com/v1/endpoints/)

Have an fix or improvement?
-----------

We’d love your pull requests and tickets on GitHub to make this sample code even better.

1. If you open up a ticket, please make sure it describes the problem or feature request fully.
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 `make test`.

For additional help with the Uber APIs, please see [our support page](https://developer.uber.com/support/) or check out our [forum on StackOverflow](http://stackoverflow.com/questions/tagged/uber-api).



41 changes: 29 additions & 12 deletions app.py
Expand Up @@ -2,6 +2,7 @@

import json
import os
import urllib
from urlparse import urlparse

from flask import Flask, render_template, request, redirect, session
Expand Down Expand Up @@ -101,22 +102,25 @@ def products():
Returns all the products currently available in San Francisco.
"""
url = config.get('base_uber_url') + 'products'
headers = generate_ride_headers(session.get('access_token'))
params = {
'latitude': config.get('start_latitude'),
'longitude': config.get('start_longitude'),
}

response = app.requests_session.get(
url,
headers=generate_ride_headers(session.get('access_token')),
headers=headers,
params=params,
)

if response.status_code != 200:
return 'There was an error', response.status_code
return "There was an error: %s" % (response.status_code), response.status_code
return render_template(
'results.html',
endpoint='products',
api_url="%s?%s" % (url, urllib.urlencode(params)),
api_headers=headers,
data=response.text,
)

Expand All @@ -128,23 +132,27 @@ def time():
Returns the time estimates from the given lat/lng given below.
"""
url = config.get('base_uber_url') + 'estimates/time'
headers = generate_ride_headers(session.get('access_token'))
params = {
'start_latitude': config.get('start_latitude'),
'start_longitude': config.get('start_longitude'),
}

response = app.requests_session.get(
url,
headers=generate_ride_headers(session.get('access_token')),
headers=headers,
params=params,
)

if response.status_code != 200:
return 'There was an error', response.status_code
return "There was an error: %s" % (response.status_code), response.status_code
return render_template(
'results.html',
endpoint='time',
api_url="%s?%s" % (url, urllib.urlencode(params)),
api_headers=headers,
data=response.text,

)


Expand All @@ -155,6 +163,7 @@ def price():
Returns the time estimates from the given lat/lng given below.
"""
url = config.get('base_uber_url') + 'estimates/price'
headers = generate_ride_headers(session.get('access_token'))
params = {
'start_latitude': config.get('start_latitude'),
'start_longitude': config.get('start_longitude'),
Expand All @@ -164,39 +173,44 @@ def price():

response = app.requests_session.get(
url,
headers=generate_ride_headers(session.get('access_token')),
headers=headers,
params=params,
)

if response.status_code != 200:
return 'There was an error', response.status_code
return "There was an error: %s" % (response.status_code), response.status_code
return render_template(
'results.html',
endpoint='price',
api_url="%s?%s" % (url, urllib.urlencode(params)),
api_headers=headers,
data=response.text,
)


@app.route('/history', methods=['GET'])
def history():
"""Return the last 5 trips made by the logged in user."""
url = config.get('base_uber_url_v1_1') + 'history'
url = config.get('base_uber_url_v1_2') + 'history'
headers = generate_ride_headers(session.get('access_token'))
params = {
'offset': 0,
'limit': 5,
'limit': 25,
}

response = app.requests_session.get(
url,
headers=generate_ride_headers(session.get('access_token')),
headers=headers,
params=params,
)

if response.status_code != 200:
return 'There was an error', response.status_code
return "There was an error: %s" % (response.status_code), response.status_code
return render_template(
'results.html',
endpoint='history',
api_url="%s?%s" % (url, urllib.urlencode(params)),
api_headers=headers,
data=response.text,
)

Expand All @@ -205,16 +219,19 @@ def history():
def me():
"""Return user information including name, picture and email."""
url = config.get('base_uber_url') + 'me'
headers = generate_ride_headers(session.get('access_token'))
response = app.requests_session.get(
url,
headers=generate_ride_headers(session.get('access_token')),
headers=headers,
)

if response.status_code != 200:
return 'There was an error', response.status_code
return "There was an error: %s" % (response.status_code), response.status_code
return render_template(
'results.html',
endpoint='me',
api_url="%s" % (url),
api_headers=headers,
data=response.text,
)

Expand Down
1 change: 1 addition & 0 deletions config.json
Expand Up @@ -6,6 +6,7 @@
"name": "Sample app",
"base_uber_url": "https://api.uber.com/v1/",
"base_uber_url_v1_1" : "https://api.uber.com/v1.1/",
"base_uber_url_v1_2" : "https://api.uber.com/v1.2/",
"start_latitude": "37.781955",
"start_longitude": "-122.402367",
"end_latitude": "37.744352",
Expand Down
Binary file added static/img/auth.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/img/heroku.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/img/logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions static/style.css
@@ -0,0 +1,15 @@
body {
font-family: ff-clan-web-pro,"Helvetica Neue",Helvetica,sans-serif;
}

h1, h2, h3, h4 {
font-family: ff-clan-web-pro-wide,"Helvetica Neue",Helvetica,sans-serif;
font-weight: 400;
text-transform: uppercase;
color: #222231;
margin-top: 0px;
}

.back {
margin-bottom: 8px;
}
4 changes: 2 additions & 2 deletions static/util.js
@@ -1,7 +1,7 @@
function action(endpoint_name) {
window.location.replace('/' + endpoint_name);
window.location.href = '/' + endpoint_name;
}

function redirect_to_demo(code) {
window.location.replace('/demo');
window.location.href = '/demo';
}
30 changes: 24 additions & 6 deletions templates/demo.html
@@ -1,16 +1,34 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css" media="screen" />
<script src="util.js"></script>
</head>
<body>
{% if token %}
<h1> Congratulations! you have successfully authenticated and your token is: {{ token }} </h1>
{% else %}
<h1> Something went wrong :( </h1>
{% endif %}
<table cellpadding="8">
<tr>
<td width="10%" valign="top">
<img src="img/logo.png" width="60">
</td>
<td width="*" valign="top">
{% if token %}
<h2> Successfully authenticated via the Uber API!</h2>

<h2> Test the following functions of the api! </h2>
<p> Your user token is <i>{{ token }}</i>.</p>

{% else %}
<h1> Something went wrong :( </h1>
{% endif %}

</td>
</tr>
</table>

<hr width="100%">

<p>
Click button below to test API calls:
</p>
<button type="button" onclick="action('products')">PRODUCTS!</button>
<button type="button" onclick="action('time')">TIME ESTIMATES!</button>
<button type="button" onclick="action('price')">PRICE ESTIMATES!</button>
Expand Down
19 changes: 15 additions & 4 deletions templates/results.html
@@ -1,14 +1,25 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css" media="screen" />
<script src="util.js"></script>
</head>

<body>
<button type="button" onclick="redirect_to_demo()">Back</button>
<h1> Welcome to the {{ endpoint }} endpoint!</h1>

<h2> Here is the result of a call to the {{ endpoint }}: </h2>
<button type="button" onclick="redirect_to_demo()" class="back">&lt; GO BACK</button>
<h1> The /{{ endpoint }} endpoint</h1>
<p> Below is a {{request.method}} request/response to the Uber API for the given endpoint.</p>
<h2>Request</h2>
<p>
<i>
{% for key, value in api_headers.iteritems() %}
{{key}}: {{value}}<br>
{% endfor %}
<br>
{{ api_url }}
</i>
</p>
<h2>Response</h2>
<p> {{ data }} </p>
</body>
</html>
2 changes: 1 addition & 1 deletion test/fixtures/history_failure.json
@@ -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.4.0"]}, "method": "GET", "uri": "https://api.uber.com/v1.1/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": ["Mon, 06 Oct 2014 16:58:57 GMT"], "x-uber-app": ["uberex-nonsandbox"], "content-type": ["application/json"]}, "status": {"message": "Unauthorized", "code": 401}, "url": "https://api.uber.com/v1.1/history?limit=5&offset=0"}, "recorded_at": "2014-10-06T16:58:57"}], "recorded_with": "betamax/0.4.0"}
{"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.4.0"]}, "method": "GET", "uri": "https://api.uber.com/v1.2/history?limit=25&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": ["Mon, 06 Oct 2014 16:58:57 GMT"], "x-uber-app": ["uberex-nonsandbox"], "content-type": ["application/json"]}, "status": {"message": "Unauthorized", "code": 401}, "url": "https://api.uber.com/v1.2/history?limit=25&offset=0"}, "recorded_at": "2014-10-06T16:58:57"}], "recorded_with": "betamax/0.4.0"}
2 changes: 1 addition & 1 deletion test/fixtures/history_success.json
@@ -1 +1 @@
{"http_interactions": [{"request": {"body": {"string": "", "encoding": "utf-8"}, "headers": {"Accept": ["*/*"], "Content-Type": ["application/json"], "Accept-Encoding": ["gzip, deflate"], "Authorization": ["bearer MLth87eHvSAaCQ1vn7jTd0xA9Kapo5"], "User-Agent": ["python-requests/2.3.0 CPython/2.7.5 Darwin/13.4.0"]}, "method": "GET", "uri": "https://api.uber.com/v1.1/history?limit=5&offset=0"}, "response": {"body": {"base64_string": "H4sIAAAAAAAAA62TTW7bMBBG78K1aHB+yOH4KkURUBSJGojtxJYWReC7d5SFi9gGaqDRjuLokW++0Yerx+Uwu20UHNyx93OzRRjc626/W18P7tfuPB9Pv932x4c7z2Vezm5rX+3fXtvcJje4yQrKoTa3BdhIzFGFMw9uWXaTlbYWShtz9TFS8Jwq+kKZfW9x7KI1tKpGMcZpfpl3+5XDgFbNRIN7Ox2npc4vn6wC9lRjVcFPFvlsdR57nSahEDisN2qH6QuJE5ndqb0v7XxzBgFdhn+L4QZDRFXQv2LcU9fCwVcu3TPW4jV08hFUEiXRVPIjMYKwUr5BjDCG9FCMAos+IYYb0IyUrd3XwEJhqG0KPvVaPRcZvSaiNbreKcaecW3ybWDMwGiU//cyUmbr0H1gtkPIT3jxBpk0RYGrFkdoHdEGr49sMoheI3dfa5NWiGxm4YEWqVnJd2gZScFI91q2AyRPaOEmZEg5iOjVq/SeRonox1p0DSn4cQT10ssIQkUCyAMv1Bjo1mviMpbSxHeekmcwqoINgRifmh0dCxvr6/9lJEl2n3svtN5lvvy8/AHuOYdTZwQAAA==", "encoding": null}, "headers": {"x-rate-limit-remaining": ["999"], "content-language": ["en"], "content-encoding": ["gzip"], "transfer-encoding": ["chunked"], "strict-transport-security": ["max-age=2592000"], "server": ["nginx"], "connection": ["keep-alive"], "x-rate-limit-reset": ["1412618400"], "x-uber-app": ["uberex-nonsandbox"], "date": ["Mon, 06 Oct 2014 17:12:33 GMT"], "x-rate-limit-limit": ["1000"], "content-type": ["application/json"]}, "status": {"message": "OK", "code": 200}, "url": "https://api.uber.com/v1.1/history?limit=5&offset=0"}, "recorded_at": "2014-10-06T17:12:33"}], "recorded_with": "betamax/0.4.0"}
{"http_interactions": [{"request": {"body": {"string": "", "encoding": "utf-8"}, "headers": {"Accept": ["*/*"], "Content-Type": ["application/json"], "Accept-Encoding": ["gzip, deflate"], "Authorization": ["bearer MLth87eHvSAaCQ1vn7jTd0xA9Kapo5"], "User-Agent": ["python-requests/2.3.0 CPython/2.7.5 Darwin/13.4.0"]}, "method": "GET", "uri": "https://api.uber.com/v1.2/history?limit=25&offset=0"}, "response": {"body": {"base64_string": "H4sIAAAAAAAAA62TTW7bMBBG78K1aHB+yOH4KkURUBSJGojtxJYWReC7d5SFi9gGaqDRjuLokW++0Yerx+Uwu20UHNyx93OzRRjc626/W18P7tfuPB9Pv932x4c7z2Vezm5rX+3fXtvcJje4yQrKoTa3BdhIzFGFMw9uWXaTlbYWShtz9TFS8Jwq+kKZfW9x7KI1tKpGMcZpfpl3+5XDgFbNRIN7Ox2npc4vn6wC9lRjVcFPFvlsdR57nSahEDisN2qH6QuJE5ndqb0v7XxzBgFdhn+L4QZDRFXQv2LcU9fCwVcu3TPW4jV08hFUEiXRVPIjMYKwUr5BjDCG9FCMAos+IYYb0IyUrd3XwEJhqG0KPvVaPRcZvSaiNbreKcaecW3ybWDMwGiU//cyUmbr0H1gtkPIT3jxBpk0RYGrFkdoHdEGr49sMoheI3dfa5NWiGxm4YEWqVnJd2gZScFI91q2AyRPaOEmZEg5iOjVq/SeRonox1p0DSn4cQT10ssIQkUCyAMv1Bjo1mviMpbSxHeekmcwqoINgRifmh0dCxvr6/9lJEl2n3svtN5lvvy8/AHuOYdTZwQAAA==", "encoding": null}, "headers": {"x-rate-limit-remaining": ["999"], "content-language": ["en"], "content-encoding": ["gzip"], "transfer-encoding": ["chunked"], "strict-transport-security": ["max-age=2592000"], "server": ["nginx"], "connection": ["keep-alive"], "x-rate-limit-reset": ["1412618400"], "x-uber-app": ["uberex-nonsandbox"], "date": ["Mon, 06 Oct 2014 17:12:33 GMT"], "x-rate-limit-limit": ["1000"], "content-type": ["application/json"]}, "status": {"message": "OK", "code": 200}, "url": "https://api.uber.com/v1.2/history?limit=25&offset=0"}, "recorded_at": "2014-10-06T17:12:33"}], "recorded_with": "betamax/0.4.0"}

0 comments on commit bcbfe73

Please sign in to comment.