├── .gitignore ├── Final Project └── FinalProject.pdf ├── Lesson_2 ├── 06_Sending API Requests │ └── api_server.py ├── 10_Requesting from Python Code │ └── geocode.py └── 12_Make_Your_Own_Mashup │ ├── solution_code │ ├── findARestaurant.py │ └── geocode.py │ └── starter_code │ ├── findARestaurant.py │ └── geocode.py ├── Lesson_3 ├── 03_Making an Endpoint with Flask │ ├── Solution Code │ │ └── endpoints_solution.py │ └── Starter Code │ │ ├── endpoints.py │ │ └── endpoints_tester.py ├── 04_Responding to Different Types of Requests │ ├── Solution Code │ │ └── endpoints_project2sol.py │ └── Starter Code │ │ ├── endpoints2.py │ │ └── endpoints_tester2.py ├── 05_Serializing data from the database │ ├── Solution Code │ │ ├── endpoints_project3_solution.py │ │ └── models.py │ └── Starter Code │ │ ├── endpoints_tester3.py │ │ ├── endpointsproject3.py │ │ └── models.py └── 06_Adding Features to your Mashup │ ├── Solution Code │ ├── findARestaurant.py │ ├── models.py │ ├── tester.py │ └── views.py │ └── Starter Code │ ├── findARestaurant.py │ ├── models.py │ ├── tester.py │ └── views.py ├── Lesson_4 ├── .vagrant │ └── machines │ │ └── default │ │ └── virtualbox │ │ ├── action_provision │ │ ├── action_set_name │ │ ├── id │ │ ├── index_uuid │ │ ├── private_key │ │ └── synced_folders ├── 02_Adding Users and Logins │ └── models.py ├── 03 _User Registration │ ├── models.py │ ├── models.pyc │ ├── users.db │ └── views.py ├── 04_Password Protecting a Resource │ ├── models.py │ ├── models.pyc │ ├── users.db │ └── views.py ├── 05_Mom & Pop’s Bagel Shop │ ├── Solution Code │ │ ├── bagelShop.db │ │ ├── bagel_tester.py │ │ ├── models.py │ │ ├── models.pyc │ │ └── views.py │ └── starter_code │ │ ├── bagel_tester.py │ │ ├── models.py │ │ └── views.py ├── 07_Implementing Token-Based Authentication in Flask │ ├── models.py │ ├── models.pyc │ ├── usersWithTokens.db │ └── views.py ├── 08 _Regal Tree Foods │ ├── Solution Code │ │ ├── fruit_tester.py │ │ ├── models.py │ │ ├── models.pyc │ │ ├── regalTree.db │ │ └── views.py │ └── starter_code │ │ ├── fruit_tester.py │ │ ├── models.py │ │ └── views.py ├── 10_Adding OAuth 2.0 for Authentication │ ├── client_secrets.json │ ├── models.py │ ├── models.pyc │ ├── templates │ │ └── clientOAuth.html │ ├── usersWithOAuth.db │ └── views.py ├── 11_Pale Kale Ocean Eats │ ├── Solution Code │ │ ├── client_secrets.json │ │ ├── models.py │ │ ├── models.pyc │ │ ├── paleKale.db │ │ ├── templates │ │ │ └── clientOAuth.html │ │ ├── usersWithOAuth.db │ │ └── views.py │ └── starter_code │ │ ├── templates │ │ └── clientOAuth.html │ │ └── veggie_tester.py ├── 12_Rate Limiting │ ├── hungryclient.py │ └── views.py └── 13_BargainMart │ ├── Solution Code │ ├── hungryclient.py │ ├── models.py │ └── views.py │ └── Starter Code │ ├── hungryclient.py │ ├── models.py │ └── views.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.db 3 | Vagrantfile 4 | *.sh 5 | -------------------------------------------------------------------------------- /Final Project/FinalProject.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Final Project/FinalProject.pdf -------------------------------------------------------------------------------- /Lesson_2/06_Sending API Requests/api_server.py: -------------------------------------------------------------------------------- 1 | #THIS IS A WEBSERVER FOR DEMONSTRATING THE TYPES OF RESPONSES WE SEE FROM AN API ENDPOINT 2 | from flask import Flask 3 | app = Flask(__name__) 4 | 5 | #GET REQUEST 6 | 7 | @app.route('/readHello') 8 | def getRequestHello(): 9 | return "Hi, I got your GET Request!" 10 | 11 | #POST REQUEST 12 | @app.route('/createHello', methods = ['POST']) 13 | def postRequestHello(): 14 | return "I see you sent a POST message :-)" 15 | #UPDATE REQUEST 16 | @app.route('/updateHello', methods = ['PUT']) 17 | def updateRequestHello(): 18 | return "Sending Hello on an PUT request!" 19 | 20 | #DELETE REQUEST 21 | @app.route('/deleteHello', methods = ['DELETE']) 22 | def deleteRequestHello(): 23 | return "Deleting your hard drive.....haha just kidding! I received a DELETE request!" 24 | 25 | if __name__ == '__main__': 26 | app.debug = True 27 | app.run(host='0.0.0.0', port=5000) 28 | 29 | -------------------------------------------------------------------------------- /Lesson_2/10_Requesting from Python Code/geocode.py: -------------------------------------------------------------------------------- 1 | import httplib2 2 | import json 3 | 4 | def getGeocodeLocation(inputString): 5 | # Use Google Maps to convert a location into Latitute/Longitute coordinates 6 | # FORMAT: https://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&key=API_KEY 7 | google_api_key = "PASTE_YOUR_KEY_HERE" 8 | locationString = inputString.replace(" ", "+") 9 | url = ('https://maps.googleapis.com/maps/api/geocode/json?address=%s&key=%s'% (locationString, google_api_key)) 10 | h = httplib2.Http() 11 | result = json.loads(h.request(url,'GET')[1]) 12 | latitude = result['results'][0]['geometry']['location']['lat'] 13 | longitude = result['results'][0]['geometry']['location']['lng'] 14 | return (latitude,longitude) 15 | -------------------------------------------------------------------------------- /Lesson_2/12_Make_Your_Own_Mashup/solution_code/findARestaurant.py: -------------------------------------------------------------------------------- 1 | from geocode import getGeocodeLocation 2 | import json 3 | import httplib2 4 | 5 | import sys 6 | import codecs 7 | sys.stdout = codecs.getwriter('utf8')(sys.stdout) 8 | sys.stderr = codecs.getwriter('utf8')(sys.stderr) 9 | 10 | foursquare_client_id = "PASTE_CLIENT_ID_HERE" 11 | foursquare_client_secret = "PASTE_CLIENT_SECRET_HERE" 12 | 13 | 14 | def findARestaurant(mealType,location): 15 | #1. Use getGeocodeLocation to get the latitude and longitude coordinates of the location string. 16 | latitude, longitude = getGeocodeLocation(location) 17 | #2. Use foursquare API to find a nearby restaurant with the latitude, longitude, and mealType strings. 18 | #HINT: format for url will be something like https://api.foursquare.com/v2/venues/search?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&v=20130815&ll=40.7,-74&query=sushi 19 | url = ('https://api.foursquare.com/v2/venues/search?client_id=%s&client_secret=%s&v=20130815&ll=%s,%s&query=%s' % (foursquare_client_id, foursquare_client_secret,latitude,longitude,mealType)) 20 | h = httplib2.Http() 21 | result = json.loads(h.request(url,'GET')[1]) 22 | 23 | if result['response']['venues']: 24 | #3. Grab the first restaurant 25 | restaurant = result['response']['venues'][0] 26 | venue_id = restaurant['id'] 27 | restaurant_name = restaurant['name'] 28 | restaurant_address = restaurant['location']['formattedAddress'] 29 | address = "" 30 | for i in restaurant_address: 31 | address += i + " " 32 | restaurant_address = address 33 | #4. Get a 300x300 picture of the restaurant using the venue_id (you can change this by altering the 300x300 value in the URL or replacing it with 'orginal' to get the original picture 34 | url = ('https://api.foursquare.com/v2/venues/%s/photos?client_id=%s&v=20150603&client_secret=%s' % ((venue_id,foursquare_client_id,foursquare_client_secret))) 35 | result = json.loads(h.request(url, 'GET')[1]) 36 | #5. Grab the first image 37 | if result['response']['photos']['items']: 38 | firstpic = result['response']['photos']['items'][0] 39 | prefix = firstpic['prefix'] 40 | suffix = firstpic['suffix'] 41 | imageURL = prefix + "300x300" + suffix 42 | else: 43 | #6. if no image available, insert default image url 44 | imageURL = "http://pixabay.com/get/8926af5eb597ca51ca4c/1433440765/cheeseburger-34314_1280.png?direct" 45 | #7. return a dictionary containing the restaurant name, address, and image url 46 | restaurantInfo = {'name':restaurant_name, 'address':restaurant_address, 'image':imageURL} 47 | print "Restaurant Name: %s" % restaurantInfo['name'] 48 | print "Restaurant Address: %s" % restaurantInfo['address'] 49 | print "Image: %s \n" % restaurantInfo['image'] 50 | return restaurantInfo 51 | else: 52 | print "No Restaurants Found for %s" % location 53 | return "No Restaurants Found" 54 | 55 | if __name__ == '__main__': 56 | findARestaurant("Pizza", "Tokyo, Japan") 57 | findARestaurant("Tacos", "Jakarta, Indonesia") 58 | findARestaurant("Tapas", "Maputo, Mozambique") 59 | findARestaurant("Falafel", "Cairo, Egypt") 60 | findARestaurant("Spaghetti", "New Delhi, India") 61 | findARestaurant("Cappuccino", "Geneva, Switzerland") 62 | findARestaurant("Sushi", "Los Angeles, California") 63 | findARestaurant("Steak", "La Paz, Bolivia") 64 | findARestaurant("Gyros", "Sydney, Australia") 65 | 66 | -------------------------------------------------------------------------------- /Lesson_2/12_Make_Your_Own_Mashup/solution_code/geocode.py: -------------------------------------------------------------------------------- 1 | import httplib2 2 | import json 3 | 4 | def getGeocodeLocation(inputString): 5 | # Use Google Maps to convert a location into Latitute/Longitute coordinates 6 | # FORMAT: https://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&key=API_KEY 7 | google_api_key = "PASTE_YOUR_KEY_HERE" 8 | locationString = inputString.replace(" ", "+") 9 | url = ('https://maps.googleapis.com/maps/api/geocode/json?address=%s&key=%s'% (locationString, google_api_key)) 10 | h = httplib2.Http() 11 | result = json.loads(h.request(url,'GET')[1]) 12 | latitude = result['results'][0]['geometry']['location']['lat'] 13 | longitude = result['results'][0]['geometry']['location']['lng'] 14 | return (latitude,longitude) 15 | -------------------------------------------------------------------------------- /Lesson_2/12_Make_Your_Own_Mashup/starter_code/findARestaurant.py: -------------------------------------------------------------------------------- 1 | from geocode import getGeocodeLocation 2 | import json 3 | import httplib2 4 | 5 | import sys 6 | import codecs 7 | sys.stdout = codecs.getwriter('utf8')(sys.stdout) 8 | sys.stderr = codecs.getwriter('utf8')(sys.stderr) 9 | 10 | foursquare_client_id = "PASTE_YOUR_ID_HERE" 11 | foursquare_client_secret = "YOUR_SECRET_HERE" 12 | 13 | 14 | def findARestaurant(mealType,location): 15 | #1. Use getGeocodeLocation to get the latitude and longitude coordinates of the location string. 16 | 17 | #2. Use foursquare API to find a nearby restaurant with the latitude, longitude, and mealType strings. 18 | #HINT: format for url will be something like https://api.foursquare.com/v2/venues/search?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&v=20130815&ll=40.7,-74&query=sushi 19 | 20 | #3. Grab the first restaurant 21 | #4. Get a 300x300 picture of the restaurant using the venue_id (you can change this by altering the 300x300 value in the URL or replacing it with 'orginal' to get the original picture 22 | #5. Grab the first image 23 | #6. If no image is available, insert default a image url 24 | #7. Return a dictionary containing the restaurant name, address, and image url 25 | if __name__ == '__main__': 26 | findARestaurant("Pizza", "Tokyo, Japan") 27 | findARestaurant("Tacos", "Jakarta, Indonesia") 28 | findARestaurant("Tapas", "Maputo, Mozambique") 29 | findARestaurant("Falafel", "Cairo, Egypt") 30 | findARestaurant("Spaghetti", "New Delhi, India") 31 | findARestaurant("Cappuccino", "Geneva, Switzerland") 32 | findARestaurant("Sushi", "Los Angeles, California") 33 | findARestaurant("Steak", "La Paz, Bolivia") 34 | findARestaurant("Gyros", "Sydney, Australia") 35 | -------------------------------------------------------------------------------- /Lesson_2/12_Make_Your_Own_Mashup/starter_code/geocode.py: -------------------------------------------------------------------------------- 1 | import httplib2 2 | import json 3 | 4 | def getGeocodeLocation(inputString): 5 | # Use Google Maps to convert a location into Latitute/Longitute coordinates 6 | # FORMAT: https://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&key=API_KEY 7 | google_api_key = "PASTE_YOUR_KEY_HERE" 8 | locationString = inputString.replace(" ", "+") 9 | url = ('https://maps.googleapis.com/maps/api/geocode/json?address=%s&key=%s'% (locationString, google_api_key)) 10 | h = httplib2.Http() 11 | result = json.loads(h.request(url,'GET')[1]) 12 | latitude = result['results'][0]['geometry']['location']['lat'] 13 | longitude = result['results'][0]['geometry']['location']['lng'] 14 | return (latitude,longitude) 15 | -------------------------------------------------------------------------------- /Lesson_3/03_Making an Endpoint with Flask/Solution Code/endpoints_solution.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | # Create the appropriate app.route functions, 4 | #test and see if they work 5 | 6 | #Make an app.route() decorator here for when the client sends the URI "/puppies" 7 | @app.route('/puppies') 8 | def puppiesFunction(): 9 | return "Yes, puppies!" 10 | 11 | 12 | #Make another app.route() decorator here that takes in an integer named 'id' for when the client visits a URI like "/puppies/5" 13 | @app.route('/puppies/') 14 | def puppiesFunctionId(id): 15 | return "This method will act on the puppy with id %s" % id 16 | 17 | if __name__ == '__main__': 18 | app.debug = True 19 | app.run(host='0.0.0.0', port=5000) 20 | -------------------------------------------------------------------------------- /Lesson_3/03_Making an Endpoint with Flask/Starter Code/endpoints.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | # Create the appropriate app.route functions. Test and see if they work 4 | 5 | #Make an app.route() decorator here for when the client sends the URI "/puppies" 6 | 7 | def puppiesFunction(): 8 | return "Yes, puppies!" 9 | 10 | 11 | #Make another app.route() decorator here that takes in an integer named 'id' for when the client visits a URI like "/puppies/5" 12 | 13 | def puppiesFunctionId(id): 14 | return "This method will act on the puppy with id %s" % id 15 | 16 | if __name__ == '__main__': 17 | app.debug = True 18 | app.run(host='0.0.0.0', port=5000) 19 | -------------------------------------------------------------------------------- /Lesson_3/03_Making an Endpoint with Flask/Starter Code/endpoints_tester.py: -------------------------------------------------------------------------------- 1 | import httplib2 2 | import json 3 | import sys 4 | 5 | print "Running Endpoint Tester....\n" 6 | address = raw_input("Please enter the address of the server you want to access, \n If left blank the connection will be set to 'http://localhost:5000': ") 7 | if address == '': 8 | address = 'http://localhost:5000' 9 | #Making a GET Request 10 | print "Making a GET Request for /puppies..." 11 | try: 12 | url = address + "/puppies" 13 | h = httplib2.Http() 14 | resp, result = h.request(url, 'GET') 15 | if resp['status'] != '200': 16 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 17 | except Exception as err: 18 | print "Test 1 FAILED: Could not make GET Request to web server" 19 | print err.args 20 | sys.exit() 21 | else: 22 | print "Test 1 PASS: Succesfully Made GET Request to /puppies" 23 | 24 | #Making GET Requests to /puppies/id 25 | print "Making GET requests to /puppies/id " 26 | try: 27 | id = 1 28 | while id <= 10: 29 | url = address + "/puppies/%s" % id 30 | h = httplib2.Http() 31 | resp, result = h.request(url, 'GET') 32 | if resp['status'] != '200': 33 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 34 | id = id + 1 35 | 36 | except Exception as err: 37 | print "Test 2 FAILED: Could not make GET Request to /puppies/id" 38 | print err.args 39 | sys.exit() 40 | else: 41 | print "Test 2 PASS: Succesfully Made GET Request to /puppies/id" 42 | print "ALL TESTS PASSED!!" 43 | 44 | 45 | -------------------------------------------------------------------------------- /Lesson_3/04_Responding to Different Types of Requests/Solution Code/endpoints_project2sol.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | 3 | app = Flask(__name__) 4 | 5 | #Make an app.route() decorator here 6 | @app.route("/puppies", methods = ['GET', 'POST']) 7 | def puppiesFunction(): 8 | if request.method == 'GET': 9 | #Call the method to Get all of the puppies 10 | return getAllPuppies() 11 | 12 | elif request.method == 'POST': 13 | #Call the method to make a new puppy 14 | return makeANewPuppy() 15 | 16 | 17 | #Make another app.route() decorator here that takes in an integer id in the 18 | @app.route("/puppies/", methods = ['GET', 'PUT', 'DELETE']) 19 | def puppiesFunctionId(id): 20 | if request.method == 'GET': 21 | #Call the method to get a specific puppy based on their id 22 | return getPuppy(id) 23 | if request.method == 'PUT': 24 | #Call the method to update a puppy 25 | return updatePuppy(id) 26 | elif request.method == 'DELETE': 27 | #Call the method to remove a puppy 28 | return deletePuppy(id) 29 | 30 | 31 | def getAllPuppies(): 32 | return "Getting All the puppies!" 33 | 34 | def makeANewPuppy(): 35 | return "Creating A New Puppy!" 36 | 37 | def getPuppy(id): 38 | return "Getting Puppy with id %s" % id 39 | 40 | def updatePuppy(id): 41 | return "Updating a Puppy with id %s" % id 42 | 43 | def deletePuppy(id): 44 | return "Removing Puppy with id %s" % id 45 | 46 | if __name__ == '__main__': 47 | app.debug = True 48 | app.run(host='0.0.0.0', port=5000) 49 | -------------------------------------------------------------------------------- /Lesson_3/04_Responding to Different Types of Requests/Starter Code/endpoints2.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | app = Flask(__name__) 3 | # Create the appropriate app.route functions, test and see if they work, and paste your URIs in the boxes below. 4 | 5 | #Make an app.route() decorator here 6 | 7 | def puppiesFunction(): 8 | if request.method == 'GET': 9 | #Call the method to Get all of the puppies 10 | 11 | 12 | elif request.method == 'POST': 13 | #Call the method to make a new puppy 14 | 15 | 16 | 17 | #Make another app.route() decorator here that takes in an integer id in the 18 | 19 | def puppiesFunctionId(id): 20 | if request.method == 'GET': 21 | #Call the method to get a specific puppy based on their id 22 | 23 | if request.method == 'PUT': 24 | #Call the method to update a puppy 25 | 26 | elif request.method == 'DELETE': 27 | #Call the method to remove a puppy 28 | 29 | 30 | 31 | def getAllPuppies(): 32 | return "Getting All the puppies!" 33 | 34 | def makeANewPuppy(): 35 | return "Creating A New Puppy!" 36 | 37 | def getPuppy(id): 38 | return "Getting Puppy with id %s" % id 39 | 40 | def updatePuppy(id): 41 | return "Updating a Puppy with id %s" % id 42 | 43 | def deletePuppy(id): 44 | return "Removing Puppy with id %s" % id 45 | -------------------------------------------------------------------------------- /Lesson_3/04_Responding to Different Types of Requests/Starter Code/endpoints_tester2.py: -------------------------------------------------------------------------------- 1 | import httplib2 2 | import json 3 | import sys 4 | 5 | print "Running Endpoint Tester....\n" 6 | address = raw_input("Please enter the address of the server you want to access, \n If left blank the connection will be set to 'http://localhost:5000': ") 7 | if address == '': 8 | address = 'http://localhost:5000' 9 | #Making a GET Request 10 | print "Making a GET Request for /puppies..." 11 | try: 12 | url = address + "/puppies" 13 | h = httplib2.Http() 14 | resp, result = h.request(url, 'GET') 15 | if resp['status'] != '200': 16 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 17 | except Exception as err: 18 | print "Test 1 FAILED: Could not make GET Request to web server" 19 | print err.args 20 | sys.exit() 21 | else: 22 | print "Test 1 PASS: Succesfully Made GET Request to /puppies" 23 | 24 | 25 | #Making a POST Request 26 | print "Making a POST request to /puppies..." 27 | try: 28 | url = address + "/puppies" 29 | h = httplib2.Http() 30 | resp, result = h.request(url, 'POST') 31 | if resp['status'] != '200': 32 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 33 | 34 | except Exception as err: 35 | print "Test 2 FAILED: Could not make POST Request to web server" 36 | print err.args 37 | sys.exit() 38 | else: 39 | print "Test 2 PASS: Succesfully Made POST Request to /puppies" 40 | 41 | 42 | #Making GET Requests to /puppies/id 43 | print "Making GET requests to /puppies/id " 44 | 45 | try: 46 | id = 1 47 | while id <= 10: 48 | url = address + "/puppies/%s" % id 49 | h = httplib2.Http() 50 | resp, result = h.request(url, 'GET') 51 | if resp['status'] != '200': 52 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 53 | id = id + 1 54 | 55 | except Exception as err: 56 | print "Test 3 FAILED: Could not make GET Requests to web server" 57 | print err.args 58 | sys.exit() 59 | else: 60 | print "Test 3 PASS: Succesfully Made GET Request to /puppies/id" 61 | 62 | 63 | 64 | #Making a PUT Request 65 | print "Making PUT requests to /puppies/id " 66 | 67 | try: 68 | id = 1 69 | while id <= 10: 70 | url = address + "/puppies/%s" % id 71 | h = httplib2.Http() 72 | resp, result = h.request(url, 'PUT') 73 | if resp['status'] != '200': 74 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 75 | id = id + 1 76 | 77 | except Exception as err: 78 | print "Test 4 FAILED: Could not make PUT Request to web server" 79 | print err.args 80 | sys.exit() 81 | else: 82 | print "Test 4 PASS: Succesfully Made PUT Request to /puppies/id" 83 | 84 | 85 | #Making a DELETE Request 86 | 87 | print "Making DELETE requests to /puppies/id ... " 88 | 89 | try: 90 | id = 1 91 | while id <= 10: 92 | url = address + "/puppies/%s" % id 93 | h = httplib2.Http() 94 | resp, result = h.request(url, 'DELETE') 95 | if resp['status'] != '200': 96 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 97 | id = id + 1 98 | 99 | except Exception as err: 100 | print "Test 5 FAILED: Could not make DELETE Requests to web server" 101 | print err.args 102 | sys.exit() 103 | else: 104 | print "Test 5 PASS: Succesfully Made DELETE Request to /puppies/id" 105 | print "ALL TESTS PASSED!!" 106 | 107 | 108 | -------------------------------------------------------------------------------- /Lesson_3/05_Serializing data from the database/Solution Code/endpoints_project3_solution.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, jsonify 2 | from sqlalchemy import create_engine 3 | from sqlalchemy.orm import sessionmaker 4 | from models import Base, Puppy 5 | 6 | 7 | engine = create_engine('sqlite:///puppies.db') 8 | Base.metadata.bind = engine 9 | 10 | DBSession = sessionmaker(bind=engine) 11 | session = DBSession() 12 | 13 | app = Flask(__name__) 14 | 15 | # Create the appropriate app.route functions, 16 | #test and see if they work 17 | 18 | 19 | #Make an app.route() decorator here 20 | @app.route("/") 21 | @app.route("/puppies", methods = ['GET', 'POST']) 22 | def puppiesFunction(): 23 | if request.method == 'GET': 24 | #Call the method to Get all of the puppies 25 | return getAllPuppies() 26 | elif request.method == 'POST': 27 | #Call the method to make a new puppy 28 | print "Making a New puppy" 29 | 30 | name = request.args.get('name', '') 31 | description = request.args.get('description', '') 32 | print name 33 | print description 34 | return makeANewPuppy(name, description) 35 | 36 | 37 | 38 | #Make another app.route() decorator here that takes in an integer id in the URI 39 | @app.route("/puppies/", methods = ['GET', 'PUT', 'DELETE']) 40 | #Call the method to view a specific puppy 41 | def puppiesFunctionId(id): 42 | if request.method == 'GET': 43 | return getPuppy(id) 44 | 45 | #Call the method to edit a specific puppy 46 | elif request.method == 'PUT': 47 | name = request.args.get('name', '') 48 | description = request.args.get('description', '') 49 | return updatePuppy(id,name, description) 50 | 51 | #Call the method to remove a puppy 52 | elif request.method == 'DELETE': 53 | return deletePuppy(id) 54 | 55 | def getAllPuppies(): 56 | puppies = session.query(Puppy).all() 57 | return jsonify(Puppies=[i.serialize for i in puppies]) 58 | 59 | def getPuppy(id): 60 | puppy = session.query(Puppy).filter_by(id = id).one() 61 | return jsonify(puppy=puppy.serialize) 62 | 63 | def makeANewPuppy(name,description): 64 | puppy = Puppy(name = name, description = description) 65 | session.add(puppy) 66 | session.commit() 67 | return jsonify(Puppy=puppy.serialize) 68 | 69 | def updatePuppy(id,name, description): 70 | puppy = session.query(Puppy).filter_by(id = id).one() 71 | if not name: 72 | puppy.name = name 73 | if not description: 74 | puppy.description = description 75 | session.add(puppy) 76 | session.commit() 77 | return "Updated a Puppy with id %s" % id 78 | 79 | def deletePuppy(id): 80 | puppy = session.query(Puppy).filter_by(id = id).one() 81 | session.delete(puppy) 82 | session.commit() 83 | return "Removed Puppy with id %s" % id 84 | 85 | 86 | if __name__ == '__main__': 87 | app.debug = False 88 | app.run(host='0.0.0.0', port=5000) 89 | -------------------------------------------------------------------------------- /Lesson_3/05_Serializing data from the database/Solution Code/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy import create_engine 4 | 5 | Base = declarative_base() 6 | 7 | class Puppy(Base): 8 | __tablename__ = 'puppy' 9 | 10 | 11 | name = Column(String(80), nullable = False) 12 | id = Column(Integer, primary_key = True) 13 | description = Column(String(250)) 14 | 15 | @property 16 | def serialize(self): 17 | """Return object data in easily serializeable format""" 18 | return { 19 | 'id': self.id, 20 | 'name': self.name, 21 | 'description' : self.description 22 | } 23 | 24 | 25 | 26 | engine = create_engine('sqlite:///puppies.db') 27 | Base.metadata.create_all(engine) 28 | -------------------------------------------------------------------------------- /Lesson_3/05_Serializing data from the database/Starter Code/endpoints_tester3.py: -------------------------------------------------------------------------------- 1 | import httplib2 2 | import json 3 | import sys 4 | 5 | print "Running Endpoint Tester....\n" 6 | address = raw_input("Please enter the address of the server you want to access, \n If left blank the connection will be set to 'http://localhost:5000': ") 7 | if address == '': 8 | address = 'http://localhost:5000' 9 | 10 | 11 | 12 | #Making a POST Request 13 | print "Making a POST request to /puppies..." 14 | try: 15 | url = address + "/puppies?name=Fido&description=Playful+Little+Puppy" 16 | h = httplib2.Http() 17 | resp, result = h.request(url, 'POST') 18 | obj = json.loads(result) 19 | puppyID = obj['Puppy']['id'] 20 | if resp['status'] != '200': 21 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 22 | 23 | except Exception as err: 24 | print "Test 1 FAILED: Could not make POST Request to web server" 25 | print err.args 26 | sys.exit() 27 | else: 28 | print "Test 1 PASS: Succesfully Made POST Request to /puppies" 29 | 30 | 31 | 32 | 33 | #Making a GET Request 34 | print "Making a GET Request for /puppies..." 35 | try: 36 | url = address + "/puppies" 37 | h = httplib2.Http() 38 | resp, result = h.request(url, 'GET') 39 | if resp['status'] != '200': 40 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 41 | except Exception as err: 42 | print "Test 2 FAILED: Could not make GET Request to web server" 43 | print err.args 44 | sys.exit() 45 | else: 46 | print "Test 2 PASS: Succesfully Made GET Request to /puppies" 47 | 48 | 49 | 50 | 51 | #Making GET Requests to /puppies/id 52 | print "Making GET requests to /puppies/id " 53 | 54 | try: 55 | id = puppyID 56 | url = address + "/puppies/%s" % id 57 | h = httplib2.Http() 58 | resp, result = h.request(url, 'GET') 59 | if resp['status'] != '200': 60 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 61 | 62 | 63 | except Exception as err: 64 | print "Test 3 FAILED: Could not make GET Requests to web server" 65 | print err.args 66 | sys.exit() 67 | else: 68 | print "Test 3 PASS: Succesfully Made GET Request to /puppies/id" 69 | 70 | 71 | 72 | #Making a PUT Request 73 | print "Making PUT requests to /puppies/id " 74 | 75 | try: 76 | id = puppyID 77 | 78 | url = address + "/puppies/%s?name=wilma&description=A+sleepy+bundle+of+joy" % id 79 | h = httplib2.Http() 80 | resp, result = h.request(url, 'PUT') 81 | if resp['status'] != '200': 82 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 83 | 84 | except Exception as err: 85 | print "Test 4 FAILED: Could not make PUT Request to web server" 86 | print err.args 87 | sys.exit() 88 | else: 89 | print "Test 4 PASS: Succesfully Made PUT Request to /puppies/id" 90 | 91 | 92 | #Making a DELETE Request 93 | 94 | print "Making DELETE requests to /puppies/id ... " 95 | 96 | try: 97 | id = puppyID 98 | url = address + "/puppies/%s" % id 99 | h = httplib2.Http() 100 | resp, result = h.request(url, 'DELETE') 101 | if resp['status'] != '200': 102 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 103 | 104 | 105 | except Exception as err: 106 | print "Test 5 FAILED: Could not make DELETE Requests to web server" 107 | print err.args 108 | sys.exit() 109 | else: 110 | print "Test 5 PASS: Succesfully Made DELETE Request to /puppies/id" 111 | print "ALL TESTS PASSED!!" 112 | 113 | 114 | -------------------------------------------------------------------------------- /Lesson_3/05_Serializing data from the database/Starter Code/endpointsproject3.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, jsonify 2 | from sqlalchemy import create_engine 3 | from sqlalchemy.orm import sessionmaker 4 | from models import Base, Puppy 5 | 6 | 7 | engine = create_engine('sqlite:///puppies.db') 8 | Base.metadata.bind = engine 9 | 10 | DBSession = sessionmaker(bind=engine) 11 | session = DBSession() 12 | 13 | app = Flask(__name__) 14 | 15 | # Create the appropriate app.route functions, 16 | #test and see if they work 17 | 18 | 19 | #Make an app.route() decorator here 20 | @app.route("/") 21 | @app.route("/puppies", methods = ['GET', 'POST']) 22 | def puppiesFunction(): 23 | if request.method == 'GET': 24 | #Call the method to Get all of the puppies 25 | return getAllPuppies() 26 | elif request.method == 'POST': 27 | #Call the method to make a new puppy 28 | print "Making a New puppy" 29 | 30 | name = request.args.get('name', '') 31 | description = request.args.get('description', '') 32 | print name 33 | print description 34 | return makeANewPuppy(name, description) 35 | 36 | 37 | 38 | #Make another app.route() decorator here that takes in an integer id in the URI 39 | @app.route("/puppies/", methods = ['GET', 'PUT', 'DELETE']) 40 | #Call the method to view a specific puppy 41 | def puppiesFunctionId(id): 42 | if request.method == 'GET': 43 | return getPuppy(id) 44 | 45 | #Call the method to edit a specific puppy 46 | elif request.method == 'PUT': 47 | name = request.args.get('name', '') 48 | description = request.args.get('description', '') 49 | return updatePuppy(id,name, description) 50 | 51 | #Call the method to remove a puppy 52 | elif request.method == 'DELETE': 53 | return deletePuppy(id) 54 | 55 | def getAllPuppies(): 56 | puppies = session.query(Puppy).all() 57 | return jsonify(Puppies=[i.serialize for i in puppies]) 58 | 59 | def getPuppy(id): 60 | puppy = session.query(Puppy).filter_by(id = id).one() 61 | return jsonify(puppy=puppy.serialize) 62 | 63 | def makeANewPuppy(name,description): 64 | puppy = Puppy(name = name, description = description) 65 | session.add(puppy) 66 | session.commit() 67 | return jsonify(Puppy=puppy.serialize) 68 | 69 | def updatePuppy(id,name, description): 70 | puppy = session.query(Puppy).filter_by(id = id).one() 71 | if not name: 72 | puppy.name = name 73 | if not description: 74 | puppy.description = description 75 | session.add(puppy) 76 | session.commit() 77 | return "Updated a Puppy with id %s" % id 78 | 79 | def deletePuppy(id): 80 | puppy = session.query(Puppy).filter_by(id = id).one() 81 | session.delete(puppy) 82 | session.commit() 83 | return "Removed Puppy with id %s" % id 84 | 85 | 86 | if __name__ == '__main__': 87 | app.debug = False 88 | app.run(host='0.0.0.0', port=5000) 89 | -------------------------------------------------------------------------------- /Lesson_3/05_Serializing data from the database/Starter Code/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy import create_engine 4 | 5 | Base = declarative_base() 6 | 7 | class Puppy(Base): 8 | __tablename__ = 'puppy' 9 | 10 | 11 | name =Column(String(80), nullable = False) 12 | id = Column(Integer, primary_key = True) 13 | description = Column(String(250)) 14 | #Add add a decorator property to serialize data from the database 15 | 16 | 17 | 18 | engine = create_engine('sqlite:///puppies.db') 19 | Base.metadata.create_all(engine) 20 | -------------------------------------------------------------------------------- /Lesson_3/06_Adding Features to your Mashup/Solution Code/findARestaurant.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import httplib2 4 | 5 | import sys 6 | import codecs 7 | sys.stdout = codecs.getwriter('utf8')(sys.stdout) 8 | sys.stderr = codecs.getwriter('utf8')(sys.stderr) 9 | 10 | foursquare_client_id = '' 11 | foursquare_client_secret = '' 12 | google_api_key = '' 13 | 14 | def getGeocodeLocation(inputString): 15 | #Replace Spaces with '+' in URL 16 | locationString = inputString.replace(" ", "+") 17 | url = ('https://maps.googleapis.com/maps/api/geocode/json?address=%s&key=%s'% (locationString, google_api_key)) 18 | h = httplib2.Http() 19 | result = json.loads(h.request(url,'GET')[1]) 20 | #print response 21 | latitude = result['results'][0]['geometry']['location']['lat'] 22 | longitude = result['results'][0]['geometry']['location']['lng'] 23 | return (latitude,longitude) 24 | 25 | #This function takes in a string representation of a location and cuisine type, geocodes the location, and then pass in the latitude and longitude coordinates to the Foursquare API 26 | def findARestaurant(mealType, location): 27 | latitude, longitude = getGeocodeLocation(location) 28 | url = ('https://api.foursquare.com/v2/venues/search?client_id=%s&client_secret=%s&v=20130815&ll=%s,%s&query=%s' % (foursquare_client_id, foursquare_client_secret,latitude,longitude,mealType)) 29 | h = httplib2.Http() 30 | result = json.loads(h.request(url,'GET')[1]) 31 | if result['response']['venues']: 32 | #Grab the first restaurant 33 | restaurant = result['response']['venues'][0] 34 | venue_id = restaurant['id'] 35 | restaurant_name = restaurant['name'] 36 | restaurant_address = restaurant['location']['formattedAddress'] 37 | #Format the Restaurant Address into one string 38 | address = "" 39 | for i in restaurant_address: 40 | address += i + " " 41 | restaurant_address = address 42 | 43 | #Get a 300x300 picture of the restaurant using the venue_id (you can change this by altering the 300x300 value in the URL or replacing it with 'orginal' to get the original picture 44 | url = ('https://api.foursquare.com/v2/venues/%s/photos?client_id=%s&v=20150603&client_secret=%s' % ((venue_id,foursquare_client_id,foursquare_client_secret))) 45 | result = json.loads(h.request(url,'GET')[1]) 46 | #Grab the first image 47 | #if no image available, insert default image url 48 | if result['response']['photos']['items']: 49 | firstpic = result['response']['photos']['items'][0] 50 | prefix = firstpic['prefix'] 51 | suffix = firstpic['suffix'] 52 | imageURL = prefix + "300x300" + suffix 53 | else: 54 | imageURL = "http://pixabay.com/get/8926af5eb597ca51ca4c/1433440765/cheeseburger-34314_1280.png?direct" 55 | 56 | restaurantInfo = {'name':restaurant_name, 'address':restaurant_address, 'image':imageURL} 57 | #print "Restaurant Name: %s " % restaurantInfo['name'] 58 | #print "Restaurant Address: %s " % restaurantInfo['address'] 59 | #print "Image: %s \n " % restaurantInfo['image'] 60 | return restaurantInfo 61 | else: 62 | #print "No Restaurants Found for %s" % location 63 | return "No Restaurants Found" 64 | 65 | if __name__ == '__main__': 66 | findARestaurant("Pizza", "Tokyo, Japan") 67 | findARestaurant("Tacos", "Jakarta, Indonesia") 68 | findARestaurant("Tapas", "Maputo, Mozambique") 69 | findARestaurant("Falafel", "Cairo, Egypt") 70 | findARestaurant("Spaghetti", "New Delhi, India") 71 | findARestaurant("Cappuccino", "Geneva, Switzerland") 72 | findARestaurant("Sushi", "Los Angeles, California") 73 | findARestaurant("Steak", "La Paz, Bolivia") 74 | findARestaurant("Gyros", "Sydney Austrailia") 75 | -------------------------------------------------------------------------------- /Lesson_3/06_Adding Features to your Mashup/Solution Code/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from sqlalchemy import Column,Integer,String 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import relationship, sessionmaker 5 | from sqlalchemy import create_engine 6 | 7 | 8 | 9 | 10 | 11 | Base = declarative_base() 12 | class Restaurant(Base): 13 | __tablename__ = 'restaurant' 14 | id = Column(Integer, primary_key = True) 15 | restaurant_name = Column(String) 16 | restaurant_address = Column(String) 17 | restaurant_image = Column(String) 18 | 19 | 20 | #Add a property decorator to serialize information from this database 21 | @property 22 | def serialize(self): 23 | return { 24 | 'restaurant_name': self.restaurant_name, 25 | 'restaurant_address': self.restaurant_address, 26 | 'restaurant_image' : self.restaurant_image, 27 | 'id' : self.id 28 | 29 | } 30 | 31 | engine = create_engine('sqlite:///restaurants.db') 32 | 33 | 34 | Base.metadata.create_all(engine) 35 | -------------------------------------------------------------------------------- /Lesson_3/06_Adding Features to your Mashup/Solution Code/tester.py: -------------------------------------------------------------------------------- 1 | 2 | import httplib2 3 | import sys 4 | import json 5 | 6 | import sys 7 | import codecs 8 | sys.stdout = codecs.getwriter('utf8')(sys.stdout) 9 | sys.stderr = codecs.getwriter('utf8')(sys.stderr) 10 | 11 | print "Running Endpoint Tester....\n" 12 | address = raw_input("Please enter the address of the server you want to access, \n If left blank the connection will be set to 'http://localhost:5000': ") 13 | if address == '': 14 | address = 'http://localhost:5000' 15 | #TEST ONE -- CREATE NEW RESTAURANTS 16 | try: 17 | print "Test 1: Creating new Restaurants......" 18 | url = address + '/restaurants?location=Buenos+Aires+Argentina&mealType=Sushi' 19 | h = httplib2.Http() 20 | resp, result = h.request(url,'POST') 21 | if resp['status'] != '200': 22 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 23 | print json.loads(result) 24 | 25 | url = address + '/restaurants?location=Denver+Colorado&mealType=Soup' 26 | h = httplib2.Http() 27 | resp, result = h.request(url,'POST') 28 | if resp['status'] != '200': 29 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 30 | print json.loads(result) 31 | 32 | url = address + '/restaurants?location=Prague+Czech+Republic&mealType=Crepes' 33 | h = httplib2.Http() 34 | resp, result = h.request(url,'POST') 35 | if resp['status'] != '200': 36 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 37 | print json.loads(result).iteritems() 38 | 39 | url = address + '/restaurants?location=Shanghai+China&mealType=Sandwiches' 40 | h = httplib2.Http() 41 | resp, result = h.request(url,'POST') 42 | if resp['status'] != '200': 43 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 44 | print json.loads(result) 45 | 46 | url = address + '/restaurants?location=Nairobi+Kenya&mealType=Pizza' 47 | h = httplib2.Http() 48 | resp, result = h.request(url,'POST') 49 | if resp['status'] != '200': 50 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 51 | print json.loads(result) 52 | 53 | except Exception as err: 54 | print "Test 1 FAILED: Could not add new restaurants" 55 | print err.args 56 | sys.exit() 57 | else: 58 | print "Test 1 PASS: Succesfully Made all new restaurants" 59 | 60 | #TEST TWO -- READ ALL RESTAURANTS 61 | try: 62 | print "Attempting Test 2: Reading all Restaurants..." 63 | url = address + "/restaurants" 64 | h = httplib2.Http() 65 | resp, result = h.request(url,'GET') 66 | if resp['status'] != '200': 67 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 68 | all_result = json.loads(result) 69 | print result 70 | 71 | except Exception as err: 72 | print "Test 2 FAILED: Could not retrieve restaurants from server" 73 | print err.args 74 | sys.exit() 75 | else: 76 | print "Test 2 PASS: Succesfully read all restaurants" 77 | #TEST THREE -- READ A SPECIFIC RESTAURANT 78 | try: 79 | print "Attempting Test 3: Reading the last created restaurant..." 80 | result = all_result 81 | restID = result['restaurants'][len(result['restaurants'])-1]['id'] 82 | url = address + "/restaurants/%s" % restID 83 | h = httplib2.Http() 84 | resp, result = h.request(url,'GET') 85 | if resp['status'] != '200': 86 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 87 | print json.loads(result) 88 | 89 | except Exception as err: 90 | print "Test 3 FAILED: Could not retrieve restaurant from server" 91 | print err.args 92 | sys.exit() 93 | else: 94 | print "Test 3 PASS: Succesfully read last restaurant" 95 | 96 | #TEST FOUR -- UPDATE A SPECIFIC RESTAURANT 97 | try: 98 | print "Attempting Test 4: Changing the name, image, and address of the first restaurant to Udacity..." 99 | result = all_result 100 | restID = result['restaurants'][0]['id'] 101 | url = address + "/restaurants/%s?name=Udacity&address=2465+Latham+Street+Mountain+View+CA&image=https://media.glassdoor.com/l/70/82/fc/e8/students-first.jpg" % restID 102 | h = httplib2.Http() 103 | resp, result = h.request(url,'PUT') 104 | if resp['status'] != '200': 105 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 106 | print json.loads(result) 107 | 108 | except Exception as err: 109 | print "Test 4 FAILED: Could not update restaurant from server" 110 | print err.args 111 | sys.exit() 112 | else: 113 | print "Test 4 PASS: Succesfully updated first restaurant" 114 | 115 | #TEST FIVE -- DELETE SECOND RESTARUANT 116 | try: 117 | print "Attempting Test 5: Deleteing the second restaurant from the server..." 118 | result = all_result 119 | restID = result['restaurants'][1]['id'] 120 | url = address + "/restaurants/%s" % restID 121 | h = httplib2.Http() 122 | resp, result = h.request(url,'DELETE') 123 | if resp['status'] != '200': 124 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 125 | print result 126 | 127 | except Exception as err: 128 | print "Test 5 FAILED: Could not delete restaurant from server" 129 | print err.args 130 | sys.exit() 131 | else: 132 | print "Test 5 PASS: Succesfully updated first restaurant" 133 | print "ALL TESTS PASSED!" 134 | 135 | -------------------------------------------------------------------------------- /Lesson_3/06_Adding Features to your Mashup/Solution Code/views.py: -------------------------------------------------------------------------------- 1 | from findARestaurant import findARestaurant 2 | from models import Base, Restaurant 3 | from flask import Flask, jsonify, request 4 | from sqlalchemy.ext.declarative import declarative_base 5 | from sqlalchemy.orm import relationship, sessionmaker 6 | from sqlalchemy import create_engine 7 | 8 | import sys 9 | import codecs 10 | sys.stdout = codecs.getwriter('utf8')(sys.stdout) 11 | sys.stderr = codecs.getwriter('utf8')(sys.stderr) 12 | 13 | 14 | 15 | 16 | #foursquare_client_id = '' 17 | 18 | #foursquare_client_secret = '' 19 | 20 | #google_api_key = '' 21 | 22 | engine = create_engine('sqlite:///restaurants.db') 23 | 24 | Base.metadata.bind = engine 25 | DBSession = sessionmaker(bind=engine) 26 | session = DBSession() 27 | app = Flask(__name__) 28 | 29 | @app.route('/restaurants', methods = ['GET', 'POST']) 30 | def all_restaurants_handler(): 31 | if request.method == 'GET': 32 | # RETURN ALL RESTAURANTS IN DATABASE 33 | restaurants = session.query(Restaurant).all() 34 | return jsonify(restaurants = [i.serialize for i in restaurants]) 35 | 36 | elif request.method == 'POST': 37 | # MAKE A NEW RESTAURANT AND STORE IT IN DATABASE 38 | location = request.args.get('location', '') 39 | mealType = request.args.get('mealType', '') 40 | restaurant_info = findARestaurant(mealType, location) 41 | if restaurant_info != "No Restaurants Found": 42 | restaurant = Restaurant(restaurant_name = unicode(restaurant_info['name']), restaurant_address = unicode(restaurant_info['address']), restaurant_image = restaurant_info['image']) 43 | session.add(restaurant) 44 | session.commit() 45 | return jsonify(restaurant = restaurant.serialize) 46 | else: 47 | return jsonify({"error":"No Restaurants Found for %s in %s" % (mealType, location)}) 48 | 49 | @app.route('/restaurants/', methods = ['GET','PUT', 'DELETE']) 50 | def restaurant_handler(id): 51 | restaurant = session.query(Restaurant).filter_by(id = id).one() 52 | if request.method == 'GET': 53 | #RETURN A SPECIFIC RESTAURANT 54 | return jsonify(restaurant = restaurant.serialize) 55 | elif request.method == 'PUT': 56 | #UPDATE A SPECIFIC RESTAURANT 57 | address = request.args.get('address') 58 | image = request.args.get('image') 59 | name = request.args.get('name') 60 | if address: 61 | restaurant.restaurant_address = address 62 | if image: 63 | restaurant.restaurant_image = image 64 | if name: 65 | restaurant.restaurant_name = name 66 | session.commit() 67 | return jsonify(restaurant = restaurant.serialize) 68 | 69 | elif request.method == 'DELETE': 70 | #DELETE A SPECFIC RESTAURANT 71 | session.delete(restaurant) 72 | session.commit() 73 | return "Restaurant Deleted" 74 | 75 | if __name__ == '__main__': 76 | app.debug = True 77 | app.run(host='0.0.0.0', port=5000) 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Lesson_3/06_Adding Features to your Mashup/Starter Code/findARestaurant.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import httplib2 4 | 5 | import sys 6 | import codecs 7 | sys.stdout = codecs.getwriter('utf8')(sys.stdout) 8 | sys.stderr = codecs.getwriter('utf8')(sys.stderr) 9 | 10 | foursquare_client_id = '' 11 | foursquare_client_secret = '' 12 | google_api_key = '' 13 | 14 | def getGeocodeLocation(inputString): 15 | #Replace Spaces with '+' in URL 16 | locationString = inputString.replace(" ", "+") 17 | url = ('https://maps.googleapis.com/maps/api/geocode/json?address=%s&key=%s'% (locationString, google_api_key)) 18 | h = httplib2.Http() 19 | result = json.loads(h.request(url,'GET')[1]) 20 | #print response 21 | latitude = result['results'][0]['geometry']['location']['lat'] 22 | longitude = result['results'][0]['geometry']['location']['lng'] 23 | return (latitude,longitude) 24 | 25 | #This function takes in a string representation of a location and cuisine type, geocodes the location, and then pass in the latitude and longitude coordinates to the Foursquare API 26 | def findARestaurant(mealType, location): 27 | latitude, longitude = getGeocodeLocation(location) 28 | url = ('https://api.foursquare.com/v2/venues/search?client_id=%s&client_secret=%s&v=20130815&ll=%s,%s&query=%s' % (foursquare_client_id, foursquare_client_secret,latitude,longitude,mealType)) 29 | h = httplib2.Http() 30 | result = json.loads(h.request(url,'GET')[1]) 31 | if result['response']['venues']: 32 | #Grab the first restaurant 33 | restaurant = result['response']['venues'][0] 34 | venue_id = restaurant['id'] 35 | restaurant_name = restaurant['name'] 36 | restaurant_address = restaurant['location']['formattedAddress'] 37 | #Format the Restaurant Address into one string 38 | address = "" 39 | for i in restaurant_address: 40 | address += i + " " 41 | restaurant_address = address 42 | 43 | #Get a 300x300 picture of the restaurant using the venue_id (you can change this by altering the 300x300 value in the URL or replacing it with 'orginal' to get the original picture 44 | url = ('https://api.foursquare.com/v2/venues/%s/photos?client_id=%s&v=20150603&client_secret=%s' % ((venue_id,foursquare_client_id,foursquare_client_secret))) 45 | result = json.loads(h.request(url,'GET')[1]) 46 | #Grab the first image 47 | #if no image available, insert default image url 48 | if result['response']['photos']['items']: 49 | firstpic = result['response']['photos']['items'][0] 50 | prefix = firstpic['prefix'] 51 | suffix = firstpic['suffix'] 52 | imageURL = prefix + "300x300" + suffix 53 | else: 54 | imageURL = "http://pixabay.com/get/8926af5eb597ca51ca4c/1433440765/cheeseburger-34314_1280.png?direct" 55 | 56 | restaurantInfo = {'name':restaurant_name, 'address':restaurant_address, 'image':imageURL} 57 | #print "Restaurant Name: %s " % restaurantInfo['name'] 58 | #print "Restaurant Address: %s " % restaurantInfo['address'] 59 | #print "Image: %s \n " % restaurantInfo['image'] 60 | return restaurantInfo 61 | else: 62 | #print "No Restaurants Found for %s" % location 63 | return "No Restaurants Found" 64 | 65 | if __name__ == '__main__': 66 | findARestaurant("Pizza", "Tokyo, Japan") 67 | findARestaurant("Tacos", "Jakarta, Indonesia") 68 | findARestaurant("Tapas", "Maputo, Mozambique") 69 | findARestaurant("Falafel", "Cairo, Egypt") 70 | findARestaurant("Spaghetti", "New Delhi, India") 71 | findARestaurant("Cappuccino", "Geneva, Switzerland") 72 | findARestaurant("Sushi", "Los Angeles, California") 73 | findARestaurant("Steak", "La Paz, Bolivia") 74 | findARestaurant("Gyros", "Sydney Austrailia") 75 | -------------------------------------------------------------------------------- /Lesson_3/06_Adding Features to your Mashup/Starter Code/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from sqlalchemy import Column,Integer,String 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import relationship, sessionmaker 5 | from sqlalchemy import create_engine 6 | 7 | 8 | 9 | 10 | 11 | Base = declarative_base() 12 | class Restaurant(Base): 13 | __tablename__ = 'restaurant' 14 | id = Column(Integer, primary_key = True) 15 | restaurant_name = Column(String) 16 | restaurant_address = Column(String) 17 | restaurant_image = Column(String) 18 | 19 | 20 | #Add a property decorator to serialize information from this database 21 | @property 22 | def serialize(self): 23 | return { 24 | 'restaurant_name': self.restaurant_name, 25 | 'restaurant_address': self.restaurant_address, 26 | 'restaurant_image' : self.restaurant_image, 27 | 'id' : self.id 28 | 29 | } 30 | 31 | engine = create_engine('sqlite:///restaurants.db') 32 | 33 | 34 | Base.metadata.create_all(engine) 35 | -------------------------------------------------------------------------------- /Lesson_3/06_Adding Features to your Mashup/Starter Code/tester.py: -------------------------------------------------------------------------------- 1 | 2 | import httplib2 3 | import sys 4 | import json 5 | 6 | import sys 7 | import codecs 8 | sys.stdout = codecs.getwriter('utf8')(sys.stdout) 9 | sys.stderr = codecs.getwriter('utf8')(sys.stderr) 10 | 11 | print "Running Endpoint Tester....\n" 12 | address = raw_input("Please enter the address of the server you want to access, \n If left blank the connection will be set to 'http://localhost:5000': ") 13 | if address == '': 14 | address = 'http://localhost:5000' 15 | #TEST ONE -- CREATE NEW RESTAURANTS 16 | try: 17 | print "Test 1: Creating new Restaurants......" 18 | url = address + '/restaurants?location=Buenos+Aires+Argentina&mealType=Sushi' 19 | h = httplib2.Http() 20 | resp, result = h.request(url,'POST') 21 | if resp['status'] != '200': 22 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 23 | print json.loads(result) 24 | 25 | url = address + '/restaurants?location=Denver+Colorado&mealType=Soup' 26 | h = httplib2.Http() 27 | resp, result = h.request(url,'POST') 28 | if resp['status'] != '200': 29 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 30 | print json.loads(result) 31 | 32 | url = address + '/restaurants?location=Prague+Czech+Republic&mealType=Crepes' 33 | h = httplib2.Http() 34 | resp, result = h.request(url,'POST') 35 | if resp['status'] != '200': 36 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 37 | print json.loads(result).iteritems() 38 | 39 | url = address + '/restaurants?location=Shanghai+China&mealType=Sandwiches' 40 | h = httplib2.Http() 41 | resp, result = h.request(url,'POST') 42 | if resp['status'] != '200': 43 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 44 | print json.loads(result) 45 | 46 | url = address + '/restaurants?location=Nairobi+Kenya&mealType=Pizza' 47 | h = httplib2.Http() 48 | resp, result = h.request(url,'POST') 49 | if resp['status'] != '200': 50 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 51 | print json.loads(result) 52 | 53 | except Exception as err: 54 | print "Test 1 FAILED: Could not add new restaurants" 55 | print err.args 56 | sys.exit() 57 | else: 58 | print "Test 1 PASS: Succesfully Made all new restaurants" 59 | 60 | #TEST TWO -- READ ALL RESTAURANTS 61 | try: 62 | print "Attempting Test 2: Reading all Restaurants..." 63 | url = address + "/restaurants" 64 | h = httplib2.Http() 65 | resp, result = h.request(url,'GET') 66 | if resp['status'] != '200': 67 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 68 | all_result = json.loads(result) 69 | print result 70 | 71 | except Exception as err: 72 | print "Test 2 FAILED: Could not retrieve restaurants from server" 73 | print err.args 74 | sys.exit() 75 | else: 76 | print "Test 2 PASS: Succesfully read all restaurants" 77 | #TEST THREE -- READ A SPECIFIC RESTAURANT 78 | try: 79 | print "Attempting Test 3: Reading the last created restaurant..." 80 | result = all_result 81 | restID = result['restaurants'][len(result['restaurants'])-1]['id'] 82 | url = address + "/restaurants/%s" % restID 83 | h = httplib2.Http() 84 | resp, result = h.request(url,'GET') 85 | if resp['status'] != '200': 86 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 87 | print json.loads(result) 88 | 89 | except Exception as err: 90 | print "Test 3 FAILED: Could not retrieve restaurant from server" 91 | print err.args 92 | sys.exit() 93 | else: 94 | print "Test 3 PASS: Succesfully read last restaurant" 95 | 96 | #TEST FOUR -- UPDATE A SPECIFIC RESTAURANT 97 | try: 98 | print "Attempting Test 4: Changing the name, image, and address of the first restaurant to Udacity..." 99 | result = all_result 100 | restID = result['restaurants'][0]['id'] 101 | url = address + "/restaurants/%s?name=Udacity&address=2465+Latham+Street+Mountain+View+CA&image=https://media.glassdoor.com/l/70/82/fc/e8/students-first.jpg" % restID 102 | h = httplib2.Http() 103 | resp, result = h.request(url,'PUT') 104 | if resp['status'] != '200': 105 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 106 | print json.loads(result) 107 | 108 | except Exception as err: 109 | print "Test 4 FAILED: Could not update restaurant from server" 110 | print err.args 111 | sys.exit() 112 | else: 113 | print "Test 4 PASS: Succesfully updated first restaurant" 114 | 115 | #TEST FIVE -- DELETE SECOND RESTARUANT 116 | try: 117 | print "Attempting Test 5: Deleteing the second restaurant from the server..." 118 | result = all_result 119 | restID = result['restaurants'][1]['id'] 120 | url = address + "/restaurants/%s" % restID 121 | h = httplib2.Http() 122 | resp, result = h.request(url,'DELETE') 123 | if resp['status'] != '200': 124 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 125 | print result 126 | 127 | except Exception as err: 128 | print "Test 5 FAILED: Could not delete restaurant from server" 129 | print err.args 130 | sys.exit() 131 | else: 132 | print "Test 5 PASS: Succesfully updated first restaurant" 133 | print "ALL TESTS PASSED!" 134 | 135 | -------------------------------------------------------------------------------- /Lesson_3/06_Adding Features to your Mashup/Starter Code/views.py: -------------------------------------------------------------------------------- 1 | from findARestaurant import findARestaurant 2 | from models import Base, Restaurant 3 | from flask import Flask, jsonify, request 4 | from sqlalchemy.ext.declarative import declarative_base 5 | from sqlalchemy.orm import relationship, sessionmaker 6 | from sqlalchemy import create_engine 7 | 8 | import sys 9 | import codecs 10 | sys.stdout = codecs.getwriter('utf8')(sys.stdout) 11 | sys.stderr = codecs.getwriter('utf8')(sys.stderr) 12 | 13 | 14 | 15 | 16 | #foursquare_client_id = '' 17 | 18 | #foursquare_client_secret = '' 19 | 20 | #google_api_key = '' 21 | 22 | engine = create_engine('sqlite:///restaurants.db') 23 | 24 | Base.metadata.bind = engine 25 | DBSession = sessionmaker(bind=engine) 26 | session = DBSession() 27 | app = Flask(__name__) 28 | 29 | @app.route('/restaurants', methods = ['GET', 'POST']) 30 | def all_restaurants_handler(): 31 | #YOUR CODE HERE 32 | 33 | @app.route('/restaurants/', methods = ['GET','PUT', 'DELETE']) 34 | def restaurant_handler(id): 35 | #YOUR CODE HERE 36 | 37 | if __name__ == '__main__': 38 | app.debug = True 39 | app.run(host='0.0.0.0', port=5000) 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Lesson_4/.vagrant/machines/default/virtualbox/action_provision: -------------------------------------------------------------------------------- 1 | 1.5:d0610c5d-3e5b-4be3-9293-a4bca65b34c8 -------------------------------------------------------------------------------- /Lesson_4/.vagrant/machines/default/virtualbox/action_set_name: -------------------------------------------------------------------------------- 1 | 1442870181 -------------------------------------------------------------------------------- /Lesson_4/.vagrant/machines/default/virtualbox/id: -------------------------------------------------------------------------------- 1 | d0610c5d-3e5b-4be3-9293-a4bca65b34c8 -------------------------------------------------------------------------------- /Lesson_4/.vagrant/machines/default/virtualbox/index_uuid: -------------------------------------------------------------------------------- 1 | fb916ff76f64443c8d38dd8e4e684860 -------------------------------------------------------------------------------- /Lesson_4/.vagrant/machines/default/virtualbox/private_key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEA2rArTSghZ+siPUrvaAUyPbB2G6IScRDGMbiPl2wi4yUgBAE9 3 | lRWqIPbqAt6Vd/VZHIsI9K3qgwW/edGebXHJRZuXRJb1MX8M/JX+YfQChSkpVJV8 4 | khcfW5728TSCKOqrogfVmtFn/t3quILmHIeFWzSWzq/zHqkiGk79c9E1LbJweYpW 5 | 0hr5x1ePiwwe5Ky64vgtF7VoVuouLeWlNTvpM/MHXFy6JjqhSt3WZSwTO6Nq6GE3 6 | 8dhbWjcfb3a4eIxt3aRsYRnlOBNP4znkak+z0Mf4+2XDRVIBOe/R5iON3TvIxS+6 7 | BSJ/A4vHsjdIOfGXCKNFbtudVJTEe/UaqiOaeQIDAQABAoIBAA8b/OHn0+cKJ+N0 8 | OfZF5aK9fmuKVxvYvNjSl5kur3piJkQgkdCrHoYIiUKJfmo7WVzPfl7pZTqIOyJ/ 9 | iSKmqfFVPq5Of8JbtNn+wwShh0sL/P8LFQBC+Ler1AP2TMfm7tTTFb8MmNLTIlR9 10 | Nsjh1CTpDUJahcGojeKjcn9+BN6TROAINgCIyOEDAA10LLWDeOkAHhX+4srt+CJA 11 | J/K5nQSzs+GbwYpdGYodaBKgnDRkhcO3zhS+aqe5YlLGziz2IvBpTWv1bFIGGPa9 12 | Xk7Bsu/dSbwz4hcgvKm22WXiKKSkJO7kkPG3chJ+V6dgIOELqdWR2zBudDD3Mmoi 13 | T/2RKMECgYEA7TV57clf0R7dj8KpLlk5JcksgiG68BdwHF0qp8IXuWFosVByYOdQ 14 | zuFWTR4oOIcOjcM3i6Rp1XSikqerH0o1w9qrHvjIrqMuPOmSpOJGnMab6+Fcv/jD 15 | njlSEmarRJ+cnmTV4IeIPKB85W+tw/5hTD9hTOKzkcboNIhetMPdWssCgYEA7AMZ 16 | HXVZ1clGYIxxvj6NAArPykaojzZjSBQbNHeTd2Fs52LG5DUKKqeNvqWBqKt+lE/Q 17 | 69GzTl6RRKFJSHWOwSkt5C1bmap35tQ0++fsDBiiAp1V//Z1v5Ws8z6XICZIsBWj 18 | IYC/IioLmKogHfJ8rjjEMIDBkI++dowyUgFY40sCgYB2MKT189ZIDgb7MFS3gUFW 19 | MGx60m+gXS1BFLLCL5U+iuhtINY8rkS3L9OeTR5hO/nmbJRqgZsx/hFa3SwIRSOQ 20 | 2I1Wu0LSsP/C3eZxPUVNrC/YUcuy4zIkzg60mG9Rm+2fhkKCw0UAYD6a2xBLjZ3R 21 | Les/Md2jDuZ39lKp4sWSDQKBgD8CoLztrLtYDlMOi/uHxatCN7HAZZY+pgkDXTQH 22 | zLYtq56nD4Sz6G/05fIFS/WVO6krGhMrnFyLAOu6x87xm9hUPy1RMsh266YT98Vd 23 | aPbdyLs7+E7rJQquUktq5Xwu3G8uh6wVLHJZmVj76rE0ITd+blqotYT765TI31Jn 24 | i6y7AoGABEGBFekrJidZCT8zME+1j1vmsRmcUDJE4tfcEfaOEX43r74B+O7KFFYl 25 | NEiMGXclT8kexM0NSmFvkXHldpGJDco1VkPoatoWK7Vr58sOTNnXGc9hIlalzEwq 26 | ZAF1LETo4xYyEmSnIUoc6Ifwgd87iwIAKBIaFeXN0UHPpxQwRjE= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /Lesson_4/.vagrant/machines/default/virtualbox/synced_folders: -------------------------------------------------------------------------------- 1 | {"virtualbox":{"/vagrant":{"guestpath":"/vagrant","hostpath":"/Users/lrnzbr/Dropbox/APIProject/L4","disabled":false}}} -------------------------------------------------------------------------------- /Lesson_4/02_Adding Users and Logins/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import relationship, sessionmaker 4 | from sqlalchemy import create_engine 5 | from passlib.apps import custom_app_context as pwd_context 6 | 7 | Base = declarative_base() 8 | 9 | 10 | class User(Base): 11 | __tablename__ = 'user' 12 | 13 | id = Column(Integer, primary_key=True) 14 | username = Column(String(32), index=True) 15 | password_hash = Column(String(64)) 16 | 17 | def hash_password(self, password): 18 | self.password_hash = pwd_context.encrypt(password) 19 | 20 | def verify_password(self, password): 21 | return pwd_context.verify(password, self.password_hash) 22 | 23 | 24 | engine = create_engine('sqlite:///users.db') 25 | 26 | Base.metadata.create_all(engine) 27 | -------------------------------------------------------------------------------- /Lesson_4/03 _User Registration/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column,Integer,String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import relationship, sessionmaker 4 | from sqlalchemy import create_engine 5 | from passlib.apps import custom_app_context as pwd_context 6 | Base = declarative_base() 7 | 8 | class User(Base): 9 | __tablename__ = 'user' 10 | id = Column(Integer, primary_key=True) 11 | username = Column(String(32), index=True) 12 | password_hash = Column(String(64)) 13 | 14 | def hash_password(self, password): 15 | self.password_hash = pwd_context.encrypt(password) 16 | 17 | def verify_password(self, password): 18 | return pwd_context.verify(password, self.password_hash) 19 | 20 | 21 | engine = create_engine('sqlite:///users.db') 22 | 23 | 24 | Base.metadata.create_all(engine) 25 | 26 | -------------------------------------------------------------------------------- /Lesson_4/03 _User Registration/models.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/03 _User Registration/models.pyc -------------------------------------------------------------------------------- /Lesson_4/03 _User Registration/users.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/03 _User Registration/users.db -------------------------------------------------------------------------------- /Lesson_4/03 _User Registration/views.py: -------------------------------------------------------------------------------- 1 | from models import Base, User 2 | from flask import Flask, jsonify, request, url_for, abort 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import relationship, sessionmaker 5 | from sqlalchemy import create_engine 6 | 7 | from flask import Flask 8 | 9 | engine = create_engine('sqlite:///users.db') 10 | 11 | Base.metadata.bind = engine 12 | DBSession = sessionmaker(bind=engine) 13 | session = DBSession() 14 | app = Flask(__name__) 15 | 16 | @app.route('/api/users', methods = ['POST']) 17 | def new_user(): 18 | username = request.json.get('username') 19 | password = request.json.get('password') 20 | if username is None or password is None: 21 | abort(400) # missing arguments 22 | if session.query(User).filter_by(username = username).first() is not None: 23 | abort(400) # existing user 24 | user = User(username = username) 25 | user.hash_password(password) 26 | session.add(user) 27 | session.commit() 28 | return jsonify({ 'username': user.username }), 201, {'Location': url_for('get_user', id = user.id, _external = True)} 29 | 30 | @app.route('/api/users/') 31 | def get_user(id): 32 | user = session.query(User).filter_by(id=id).one() 33 | if not user: 34 | abort(400) 35 | return jsonify({'username': user.username}) 36 | 37 | 38 | if __name__ == '__main__': 39 | app.debug = True 40 | app.run(host='0.0.0.0', port=5000) 41 | -------------------------------------------------------------------------------- /Lesson_4/04_Password Protecting a Resource/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column,Integer,String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import relationship, sessionmaker 4 | from sqlalchemy import create_engine 5 | from passlib.apps import custom_app_context as pwd_context 6 | Base = declarative_base() 7 | 8 | class User(Base): 9 | __tablename__ = 'user' 10 | id = Column(Integer, primary_key=True) 11 | username = Column(String(32), index=True) 12 | password_hash = Column(String(64)) 13 | 14 | def hash_password(self, password): 15 | self.password_hash = pwd_context.encrypt(password) 16 | 17 | def verify_password(self, password): 18 | return pwd_context.verify(password, self.password_hash) 19 | 20 | 21 | engine = create_engine('sqlite:///users.db') 22 | 23 | 24 | Base.metadata.create_all(engine) 25 | 26 | -------------------------------------------------------------------------------- /Lesson_4/04_Password Protecting a Resource/models.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/04_Password Protecting a Resource/models.pyc -------------------------------------------------------------------------------- /Lesson_4/04_Password Protecting a Resource/users.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/04_Password Protecting a Resource/users.db -------------------------------------------------------------------------------- /Lesson_4/04_Password Protecting a Resource/views.py: -------------------------------------------------------------------------------- 1 | from models import Base, User 2 | from flask import Flask, jsonify, request, url_for, abort, g 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import relationship, sessionmaker 5 | from sqlalchemy import create_engine 6 | 7 | from flask.ext.httpauth import HTTPBasicAuth 8 | auth = HTTPBasicAuth() 9 | 10 | 11 | engine = create_engine('sqlite:///users.db') 12 | 13 | Base.metadata.bind = engine 14 | DBSession = sessionmaker(bind=engine) 15 | session = DBSession() 16 | app = Flask(__name__) 17 | 18 | @auth.verify_password 19 | def verify_password(username, password): 20 | user = session.query(User).filter_by(username = username).first() 21 | if not user or not user.verify_password(password): 22 | return False 23 | g.user = user 24 | return True 25 | 26 | 27 | @app.route('/users', methods = ['POST']) 28 | def new_user(): 29 | username = request.json.get('username') 30 | password = request.json.get('password') 31 | if username is None or password is None: 32 | print "missing arguments" 33 | abort(400) 34 | 35 | if session.query(User).filter_by(username = username).first() is not None: 36 | print "existing user" 37 | user = session.query(User).filter_by(username=username).first() 38 | return jsonify({'message':'user already exists'}), 200#, {'Location': url_for('get_user', id = user.id, _external = True)} 39 | 40 | user = User(username = username) 41 | user.hash_password(password) 42 | session.add(user) 43 | session.commit() 44 | return jsonify({ 'username': user.username }), 201#, {'Location': url_for('get_user', id = user.id, _external = True)} 45 | 46 | @app.route('/api/users/') 47 | def get_user(id): 48 | user = session.query(User).filter_by(id=id).one() 49 | if not user: 50 | abort(400) 51 | return jsonify({'username': user.username}) 52 | 53 | @app.route('/api/resource') 54 | @auth.login_required 55 | def get_resource(): 56 | return jsonify({ 'data': 'Hello, %s!' % g.user.username }) 57 | 58 | 59 | 60 | if __name__ == '__main__': 61 | app.debug = True 62 | app.run(host='0.0.0.0', port=5000) 63 | -------------------------------------------------------------------------------- /Lesson_4/05_Mom & Pop’s Bagel Shop/Solution Code/bagelShop.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/05_Mom & Pop’s Bagel Shop/Solution Code/bagelShop.db -------------------------------------------------------------------------------- /Lesson_4/05_Mom & Pop’s Bagel Shop/Solution Code/bagel_tester.py: -------------------------------------------------------------------------------- 1 | from urllib import urlencode 2 | from httplib2 import Http 3 | import json 4 | import sys 5 | import base64 6 | 7 | 8 | print "Running Endpoint Tester....\n" 9 | address = raw_input("Please enter the address of the server you want to access, \n If left blank the connection will be set to 'http://localhost:5000': ") 10 | if address == '': 11 | address = 'http://localhost:5000' 12 | 13 | 14 | #TEST 1 TRY TO MAKE A NEW USER 15 | try: 16 | 17 | url = address + '/users' 18 | h = Http() 19 | #h.add_credentials('TinnyTim', 'Udacity') 20 | data = dict(username = "TinnyTim", password = "Udacity") 21 | data = json.dumps(data) 22 | resp, content = h.request(url,'POST', body = data, headers = {"Content-Type": "application/json"}) 23 | if resp['status'] != '201' and resp['status'] != '200': 24 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 25 | except Exception as err: 26 | print "Test 1 FAILED: Could not make a new user" 27 | print err.args 28 | sys.exit() 29 | else: 30 | print "Test 1 PASS: Succesfully made a new user" 31 | 32 | #TEST 2 ADD NEW BAGELS TO THE DATABASE 33 | try: 34 | h = Http() 35 | h.add_credentials('TinnyTim','Udacity') 36 | url = address + '/bagels' 37 | data = dict(username = "TinnyTim", password = "Udacity", name = "plain", picture = "http://bonacbagel.weebly.com/uploads/4/0/5/4/40548977/s318635836612132814_p1_i1_w240.jpeg", description = "Old-Fashioned Plain Bagel", price= "$1.99") 38 | resp, content = h.request(url,'POST', body = json.dumps(data), headers = {"Content-Type" : "application/json"}) 39 | if resp['status'] != '200': 40 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 41 | except Exception as err: 42 | print "Test 2 FAILED: Could not add new bagels" 43 | print err.args 44 | sys.exit() 45 | else: 46 | print "Test 2 PASS: Succesfully made new bagels" 47 | 48 | 49 | #TEST 3 TRY TO READ BAGELS WITH INVALID CREDENTIALS 50 | try: 51 | h = Http() 52 | h.add_credentials('TinnyTim','Youdacity') 53 | url = address + '/bagels' 54 | data = dict(username = "Tinny_Tim", password = "youdacity") 55 | resp, content = h.request(url,'GET', urlencode(data)) 56 | if resp['status'] == '200': 57 | raise Exception("Security Flaw: able to log in with invalid credentials") 58 | except Exception as err: 59 | print "Test 3 FAILED" 60 | print err.args 61 | sys.exit() 62 | else: 63 | print "Test 3 PASS: App checks against invalid credentials" 64 | 65 | 66 | #TEST 4 TRY TO READ BAGELS WITH VALID CREDENTIALS 67 | try: 68 | h = Http() 69 | h.add_credentials("TinnyTim", "Udacity") 70 | url = address + '/bagels' 71 | #data = dict(username = "TinnyTim", password = "Udacity") 72 | resp, content = h.request(url,'GET')#, urlencode(data)) 73 | if resp['status'] != '200': 74 | raise Exception("Unable to access /bagels with valid credentials") 75 | except Exception as err: 76 | print "Test 4 FAILED" 77 | print err.args 78 | sys.exit() 79 | else: 80 | print "Test 4 PASS: Logged in User can view /bagels" 81 | print "ALL TESTS PASSED!" -------------------------------------------------------------------------------- /Lesson_4/05_Mom & Pop’s Bagel Shop/Solution Code/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column,Integer,String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import relationship, sessionmaker 4 | from sqlalchemy import create_engine 5 | from passlib.apps import custom_app_context as pwd_context 6 | Base = declarative_base() 7 | 8 | class User(Base): 9 | __tablename__ = 'user' 10 | id = Column(Integer, primary_key=True) 11 | username = Column(String(32), index=True) 12 | password_hash = Column(String(64)) 13 | 14 | def hash_password(self, password): 15 | self.password_hash = pwd_context.encrypt(password) 16 | 17 | def verify_password(self, password): 18 | return pwd_context.verify(password, self.password_hash) 19 | 20 | class Bagel(Base): 21 | __tablename__ = 'bagel' 22 | id = Column(Integer, primary_key=True) 23 | name = Column(String) 24 | picture = Column(String) 25 | description = Column(String) 26 | price = Column(String) 27 | @property 28 | def serialize(self): 29 | """Return object data in easily serializeable format""" 30 | return { 31 | 'name' : self.name, 32 | 'picture' : self.picture, 33 | 'description' : self.description, 34 | 'price' : self.price 35 | } 36 | 37 | 38 | engine = create_engine('sqlite:///bagelShop.db') 39 | 40 | 41 | Base.metadata.create_all(engine) 42 | 43 | -------------------------------------------------------------------------------- /Lesson_4/05_Mom & Pop’s Bagel Shop/Solution Code/models.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/05_Mom & Pop’s Bagel Shop/Solution Code/models.pyc -------------------------------------------------------------------------------- /Lesson_4/05_Mom & Pop’s Bagel Shop/Solution Code/views.py: -------------------------------------------------------------------------------- 1 | from models import Base, User, Bagel 2 | from flask import Flask, jsonify, request, url_for, abort, g 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import relationship, sessionmaker 5 | from sqlalchemy import create_engine 6 | from flask.ext.httpauth import HTTPBasicAuth 7 | 8 | auth = HTTPBasicAuth() 9 | 10 | 11 | engine = create_engine('sqlite:///bagelShop.db') 12 | 13 | Base.metadata.bind = engine 14 | DBSession = sessionmaker(bind=engine) 15 | session = DBSession() 16 | app = Flask(__name__) 17 | 18 | @auth.verify_password 19 | def verify_password(username, password): 20 | print "Looking for user %s" % username 21 | user = session.query(User).filter_by(username = username).first() 22 | if not user: 23 | print "User not found" 24 | return False 25 | elif not user.verify_password(password): 26 | print "Unable to verify password" 27 | return False 28 | else: 29 | g.user = user 30 | return True 31 | 32 | 33 | @app.route('/users', methods = ['POST']) 34 | def new_user(): 35 | username = request.json.get('username') 36 | password = request.json.get('password') 37 | if username is None or password is None: 38 | print "missing arguments" 39 | abort(400) 40 | 41 | user = session.query(User).filter_by(username=username).first() 42 | if user is not None: 43 | print "existing user" 44 | return jsonify({'message':'user already exists'}), 200#, {'Location': url_for('get_user', id = user.id, _external = True)} 45 | 46 | user = User(username = username) 47 | user.hash_password(password) 48 | session.add(user) 49 | session.commit() 50 | return jsonify({ 'username': user.username }), 201#, {'Location': url_for('get_user', id = user.id, _external = True)} 51 | 52 | @app.route('/users/') 53 | def get_user(id): 54 | user = session.query(User).filter_by(id=id).one() 55 | if not user: 56 | abort(400) 57 | return jsonify({'username': user.username}) 58 | 59 | @app.route('/resource') 60 | @auth.login_required 61 | def get_resource(): 62 | return jsonify({ 'data': 'Hello, %s!' % g.user.username }) 63 | 64 | @app.route('/bagels', methods = ['GET','POST']) 65 | @auth.login_required 66 | def showAllBagels(): 67 | if request.method == 'GET': 68 | bagels = session.query(Bagel).all() 69 | return jsonify(bagels = [bagel.serialize for bagel in bagels]) 70 | elif request.method == 'POST': 71 | name = request.json.get('name') 72 | description = request.json.get('description') 73 | picture = request.json.get('picture') 74 | price = request.json.get('price') 75 | newBagel = Bagel(name = name, description = description, picture = picture, price = price) 76 | session.add(newBagel) 77 | session.commit() 78 | return jsonify(newBagel.serialize) 79 | 80 | 81 | 82 | if __name__ == '__main__': 83 | app.debug = True 84 | app.run(host='0.0.0.0', port=5000) 85 | -------------------------------------------------------------------------------- /Lesson_4/05_Mom & Pop’s Bagel Shop/starter_code/bagel_tester.py: -------------------------------------------------------------------------------- 1 | from urllib import urlencode 2 | from httplib2 import Http 3 | import json 4 | import sys 5 | import base64 6 | 7 | 8 | print "Running Endpoint Tester....\n" 9 | address = raw_input("Please enter the address of the server you want to access, \n If left blank the connection will be set to 'http://localhost:5000': ") 10 | if address == '': 11 | address = 'http://localhost:5000' 12 | 13 | 14 | #TEST 1 TRY TO MAKE A NEW USER 15 | try: 16 | 17 | url = address + '/users' 18 | h = Http() 19 | #h.add_credentials('TinnyTim', 'Udacity') 20 | data = dict(username = "TinnyTim", password = "Udacity") 21 | data = json.dumps(data) 22 | resp, content = h.request(url,'POST', body = data, headers = {"Content-Type": "application/json"}) 23 | if resp['status'] != '201' and resp['status'] != '200': 24 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 25 | except Exception as err: 26 | print "Test 1 FAILED: Could not make a new user" 27 | print err.args 28 | sys.exit() 29 | else: 30 | print "Test 1 PASS: Succesfully made a new user" 31 | 32 | #TEST 2 ADD NEW BAGELS TO THE DATABASE 33 | try: 34 | h = Http() 35 | h.add_credentials('TinnyTim','Udacity') 36 | url = address + '/bagels' 37 | data = dict(username = "TinnyTim", password = "Udacity", name = "plain", picture = "http://bonacbagel.weebly.com/uploads/4/0/5/4/40548977/s318635836612132814_p1_i1_w240.jpeg", description = "Old-Fashioned Plain Bagel", price= "$1.99") 38 | resp, content = h.request(url,'POST', body = json.dumps(data), headers = {"Content-Type" : "application/json"}) 39 | if resp['status'] != '200': 40 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 41 | except Exception as err: 42 | print "Test 2 FAILED: Could not add new bagels" 43 | print err.args 44 | sys.exit() 45 | else: 46 | print "Test 2 PASS: Succesfully made new bagels" 47 | 48 | 49 | #TEST 3 TRY TO READ BAGELS WITH INVALID CREDENTIALS 50 | try: 51 | h = Http() 52 | h.add_credentials('TinnyTim','Youdacity') 53 | url = address + '/bagels' 54 | data = dict(username = "Tinny_Tim", password = "youdacity") 55 | resp, content = h.request(url,'GET', urlencode(data)) 56 | if resp['status'] == '200': 57 | raise Exception("Security Flaw: able to log in with invalid credentials") 58 | except Exception as err: 59 | print "Test 3 FAILED" 60 | print err.args 61 | sys.exit() 62 | else: 63 | print "Test 3 PASS: App checks against invalid credentials" 64 | 65 | 66 | #TEST 4 TRY TO READ BAGELS WITH VALID CREDENTIALS 67 | try: 68 | h = Http() 69 | h.add_credentials("TinnyTim", "Udacity") 70 | url = address + '/bagels' 71 | #data = dict(username = "TinnyTim", password = "Udacity") 72 | resp, content = h.request(url,'GET')#, urlencode(data)) 73 | if resp['status'] != '200': 74 | raise Exception("Unable to access /bagels with valid credentials") 75 | except Exception as err: 76 | print "Test 4 FAILED" 77 | print err.args 78 | sys.exit() 79 | else: 80 | print "Test 4 PASS: Logged in User can view /bagels" 81 | print "ALL TESTS PASSED!" -------------------------------------------------------------------------------- /Lesson_4/05_Mom & Pop’s Bagel Shop/starter_code/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column,Integer,String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import relationship, sessionmaker 4 | from sqlalchemy import create_engine 5 | from passlib.apps import custom_app_context as pwd_context 6 | Base = declarative_base() 7 | 8 | #ADD YOUR USER MODEL HERE 9 | 10 | class Bagel(Base): 11 | __tablename__ = 'bagel' 12 | id = Column(Integer, primary_key=True) 13 | name = Column(String) 14 | picture = Column(String) 15 | description = Column(String) 16 | price = Column(String) 17 | @property 18 | def serialize(self): 19 | """Return object data in easily serializeable format""" 20 | return { 21 | 'name' : self.name, 22 | 'picture' : self.picture, 23 | 'description' : self.description, 24 | 'price' : self.price 25 | } 26 | 27 | 28 | engine = create_engine('sqlite:///bagelShop.db') 29 | 30 | 31 | Base.metadata.create_all(engine) 32 | 33 | -------------------------------------------------------------------------------- /Lesson_4/05_Mom & Pop’s Bagel Shop/starter_code/views.py: -------------------------------------------------------------------------------- 1 | from models import Base, User, Bagel 2 | from flask import Flask, jsonify, request, url_for, abort, g 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import relationship, sessionmaker 5 | from sqlalchemy import create_engine 6 | from flask.ext.httpauth import HTTPBasicAuth 7 | 8 | auth = HTTPBasicAuth() 9 | 10 | 11 | engine = create_engine('sqlite:///bagelShop.db') 12 | 13 | Base.metadata.bind = engine 14 | DBSession = sessionmaker(bind=engine) 15 | session = DBSession() 16 | app = Flask(__name__) 17 | 18 | #ADD @auth.verify_password here 19 | 20 | #ADD a /users route here 21 | 22 | 23 | 24 | @app.route('/bagels', methods = ['GET','POST']) 25 | #protect this route with a required login 26 | def showAllBagels(): 27 | if request.method == 'GET': 28 | bagels = session.query(Bagel).all() 29 | return jsonify(bagels = [bagel.serialize for bagel in bagels]) 30 | elif request.method == 'POST': 31 | name = request.json.get('name') 32 | description = request.json.get('description') 33 | picture = request.json.get('picture') 34 | price = request.json.get('price') 35 | newBagel = Bagel(name = name, description = description, picture = picture, price = price) 36 | session.add(newBagel) 37 | session.commit() 38 | return jsonify(newBagel.serialize) 39 | 40 | 41 | 42 | if __name__ == '__main__': 43 | app.debug = True 44 | app.run(host='0.0.0.0', port=5000) 45 | -------------------------------------------------------------------------------- /Lesson_4/07_Implementing Token-Based Authentication in Flask/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column,Integer,String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import relationship, sessionmaker 4 | from sqlalchemy import create_engine 5 | from passlib.apps import custom_app_context as pwd_context 6 | import random, string 7 | from itsdangerous import(TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired) 8 | 9 | Base = declarative_base() 10 | secret_key = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in xrange(32)) 11 | 12 | class User(Base): 13 | __tablename__ = 'user' 14 | id = Column(Integer, primary_key=True) 15 | username = Column(String(32), index=True) 16 | password_hash = Column(String(64)) 17 | 18 | def hash_password(self, password): 19 | self.password_hash = pwd_context.encrypt(password) 20 | 21 | def verify_password(self, password): 22 | return pwd_context.verify(password, self.password_hash) 23 | 24 | def generate_auth_token(self, expiration=600): 25 | s = Serializer(secret_key, expires_in = expiration) 26 | return s.dumps({'id': self.id }) 27 | 28 | @staticmethod 29 | def verify_auth_token(token): 30 | s = Serializer(secret_key) 31 | try: 32 | data = s.loads(token) 33 | except SignatureExpired: 34 | #Valid Token, but expired 35 | return None 36 | except BadSignature: 37 | #Invalid Token 38 | return None 39 | user_id = data['id'] 40 | return user_id 41 | 42 | 43 | engine = create_engine('sqlite:///usersWithTokens.db') 44 | 45 | 46 | Base.metadata.create_all(engine) 47 | 48 | -------------------------------------------------------------------------------- /Lesson_4/07_Implementing Token-Based Authentication in Flask/models.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/07_Implementing Token-Based Authentication in Flask/models.pyc -------------------------------------------------------------------------------- /Lesson_4/07_Implementing Token-Based Authentication in Flask/usersWithTokens.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/07_Implementing Token-Based Authentication in Flask/usersWithTokens.db -------------------------------------------------------------------------------- /Lesson_4/07_Implementing Token-Based Authentication in Flask/views.py: -------------------------------------------------------------------------------- 1 | from models import Base, User 2 | from flask import Flask, jsonify, request, url_for, abort, g 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import relationship, sessionmaker 5 | from sqlalchemy import create_engine 6 | 7 | from flask.ext.httpauth import HTTPBasicAuth 8 | auth = HTTPBasicAuth() 9 | 10 | 11 | engine = create_engine('sqlite:///usersWithTokens.db') 12 | 13 | Base.metadata.bind = engine 14 | DBSession = sessionmaker(bind=engine) 15 | session = DBSession() 16 | app = Flask(__name__) 17 | 18 | 19 | 20 | 21 | 22 | @auth.verify_password 23 | def verify_password(username_or_token, password): 24 | #Try to see if it's a token first 25 | user_id = User.verify_auth_token(username_or_token) 26 | if user_id: 27 | user = session.query(User).filter_by(id = user_id).one() 28 | else: 29 | user = session.query(User).filter_by(username = username_or_token).first() 30 | if not user or not user.verify_password(password): 31 | return False 32 | g.user = user 33 | return True 34 | 35 | 36 | 37 | @app.route('/token') 38 | @auth.login_required 39 | def get_auth_token(): 40 | token = g.user.generate_auth_token() 41 | return jsonify({'token': token.decode('ascii')}) 42 | 43 | 44 | 45 | @app.route('/users', methods = ['POST']) 46 | def new_user(): 47 | username = request.json.get('username') 48 | password = request.json.get('password') 49 | if username is None or password is None: 50 | print "missing arguments" 51 | abort(400) 52 | 53 | if session.query(User).filter_by(username = username).first() is not None: 54 | print "existing user" 55 | user = session.query(User).filter_by(username=username).first() 56 | return jsonify({'message':'user already exists'}), 200#, {'Location': url_for('get_user', id = user.id, _external = True)} 57 | 58 | user = User(username = username) 59 | user.hash_password(password) 60 | session.add(user) 61 | session.commit() 62 | return jsonify({ 'username': user.username }), 201#, {'Location': url_for('get_user', id = user.id, _external = True)} 63 | 64 | @app.route('/api/users/') 65 | def get_user(id): 66 | user = session.query(User).filter_by(id=id).one() 67 | if not user: 68 | abort(400) 69 | return jsonify({'username': user.username}) 70 | 71 | @app.route('/api/resource') 72 | @auth.login_required 73 | def get_resource(): 74 | return jsonify({ 'data': 'Hello, %s!' % g.user.username }) 75 | 76 | 77 | 78 | if __name__ == '__main__': 79 | app.debug = True 80 | #app.config['SECRET_KEY'] = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in xrange(32)) 81 | app.run(host='0.0.0.0', port=5000) 82 | -------------------------------------------------------------------------------- /Lesson_4/08 _Regal Tree Foods/Solution Code/fruit_tester.py: -------------------------------------------------------------------------------- 1 | from urllib import urlencode 2 | from httplib2 import Http 3 | import json 4 | import sys 5 | import base64 6 | 7 | 8 | print "Running Endpoint Tester....\n" 9 | address = raw_input("Please enter the address of the server you want to access, \n If left blank the connection will be set to 'http://localhost:5000': ") 10 | if address == '': 11 | address = 'http://localhost:5000' 12 | 13 | #TEST 1: TRY TO REGISTER A NEW USER 14 | try: 15 | h = Http() 16 | url = address + '/users' 17 | data = dict(username="Peter", password="Pan") 18 | data = json.dumps(data) 19 | resp, content = h.request(url,'POST', body = data, headers = {"Content-Type": "application/json"}) 20 | if resp['status'] != '201' and resp['status'] != '200': 21 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 22 | 23 | except Exception as err: 24 | print "Test 1 FAILED: Could not make a new user" 25 | print err.args 26 | sys.exit() 27 | else: 28 | print "Test 1 PASS: Succesfully made a new user" 29 | 30 | #TEST 2: OBTAIN A TOKEN 31 | try: 32 | h = Http() 33 | h.add_credentials('Peter','Pan') 34 | url = address + '/token' 35 | resp, content = h.request(url,'GET' , headers = {"Content-Type" : "application/json"}) 36 | if resp['status'] != '200': 37 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 38 | new_content = json.loads(content) 39 | if not new_content['token']: 40 | raise Exception('No Token Received!') 41 | token = new_content['token'] 42 | print "received token: %s" % token 43 | except Exception as err: 44 | print "Test 2 FAILED: Could not exchange user credentials for a token" 45 | print err.args 46 | sys.exit() 47 | else: 48 | print "Test 2 PASS: Succesfully obtained token! " 49 | 50 | #TEST 3: TRY TO ADD PRODUCS TO DATABASE 51 | 52 | try: 53 | h = Http() 54 | h.add_credentials(token,'blank') 55 | url = address + '/products' 56 | data = dict(name = "apple", category = "fruit", price= "$.99") 57 | resp, content = h.request(url,'POST', body = json.dumps(data), headers = {"Content-Type" : "application/json"}) 58 | if resp['status'] != '200': 59 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 60 | except Exception as err: 61 | print "Test 3 FAILED: Could not add new products" 62 | print err.args 63 | sys.exit() 64 | else: 65 | print "Test 3 PASS: Succesfully added new products" 66 | 67 | 68 | #TEST 4: TRY ACCESSING ENDPOINT WITH AN INVALID TOKEN 69 | 70 | #TEST 5: TRY TO VIEW ALL PRODUCTS IN DATABASE 71 | 72 | #TEST 6: TRY TO VIEW A SPECIFIC CATEGORY OF PRODUCTS -------------------------------------------------------------------------------- /Lesson_4/08 _Regal Tree Foods/Solution Code/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column,Integer,String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import relationship, sessionmaker 4 | from sqlalchemy import create_engine 5 | from passlib.apps import custom_app_context as pwd_context 6 | import random, string 7 | from itsdangerous import(TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired) 8 | 9 | Base = declarative_base() 10 | secret_key = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in xrange(32)) 11 | 12 | class User(Base): 13 | __tablename__ = 'user' 14 | id = Column(Integer, primary_key=True) 15 | username = Column(String(32), index=True) 16 | password_hash = Column(String(64)) 17 | 18 | def hash_password(self, password): 19 | self.password_hash = pwd_context.encrypt(password) 20 | 21 | def verify_password(self, password): 22 | return pwd_context.verify(password, self.password_hash) 23 | 24 | def generate_auth_token(self, expiration=600): 25 | s = Serializer(secret_key, expires_in = expiration) 26 | return s.dumps({'id': self.id }) 27 | 28 | @staticmethod 29 | def verify_auth_token(token): 30 | s = Serializer(secret_key) 31 | try: 32 | data = s.loads(token) 33 | except SignatureExpired: 34 | #Valid Token, but expired 35 | return None 36 | except BadSignature: 37 | #Invalid Token 38 | return None 39 | user_id = data['id'] 40 | return user_id 41 | 42 | class Product(Base): 43 | __tablename__ = 'product' 44 | id = Column(Integer, primary_key=True) 45 | name = Column(String) 46 | category = Column(String) 47 | price = Column(String) 48 | @property 49 | def serialize(self): 50 | """Return object data in easily serializeable format""" 51 | return { 52 | 'name' : self.name, 53 | 'category' : self.category, 54 | 'price' : self.price 55 | } 56 | 57 | engine = create_engine('sqlite:///regalTree.db') 58 | 59 | 60 | Base.metadata.create_all(engine) 61 | 62 | -------------------------------------------------------------------------------- /Lesson_4/08 _Regal Tree Foods/Solution Code/models.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/08 _Regal Tree Foods/Solution Code/models.pyc -------------------------------------------------------------------------------- /Lesson_4/08 _Regal Tree Foods/Solution Code/regalTree.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/08 _Regal Tree Foods/Solution Code/regalTree.db -------------------------------------------------------------------------------- /Lesson_4/08 _Regal Tree Foods/Solution Code/views.py: -------------------------------------------------------------------------------- 1 | from models import Base, User, Product 2 | from flask import Flask, jsonify, request, url_for, abort, g 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import relationship, sessionmaker 5 | from sqlalchemy import create_engine 6 | 7 | from flask.ext.httpauth import HTTPBasicAuth 8 | auth = HTTPBasicAuth() 9 | 10 | 11 | engine = create_engine('sqlite:///regalTree.db') 12 | 13 | Base.metadata.bind = engine 14 | DBSession = sessionmaker(bind=engine) 15 | session = DBSession() 16 | app = Flask(__name__) 17 | 18 | 19 | 20 | 21 | 22 | @auth.verify_password 23 | def verify_password(username_or_token, password): 24 | #Try to see if it's a token first 25 | user_id = User.verify_auth_token(username_or_token) 26 | if user_id: 27 | user = session.query(User).filter_by(id = user_id).one() 28 | else: 29 | user = session.query(User).filter_by(username = username_or_token).first() 30 | if not user or not user.verify_password(password): 31 | return False 32 | g.user = user 33 | return True 34 | 35 | 36 | 37 | @app.route('/token') 38 | @auth.login_required 39 | def get_auth_token(): 40 | token = g.user.generate_auth_token() 41 | return jsonify({'token': token.decode('ascii')}) 42 | 43 | 44 | 45 | @app.route('/users', methods = ['POST']) 46 | def new_user(): 47 | username = request.json.get('username') 48 | password = request.json.get('password') 49 | if username is None or password is None: 50 | print "missing arguments" 51 | abort(400) 52 | 53 | if session.query(User).filter_by(username = username).first() is not None: 54 | print "existing user" 55 | user = session.query(User).filter_by(username=username).first() 56 | return jsonify({'message':'user already exists'}), 200#, {'Location': url_for('get_user', id = user.id, _external = True)} 57 | 58 | user = User(username = username) 59 | user.hash_password(password) 60 | session.add(user) 61 | session.commit() 62 | return jsonify({ 'username': user.username }), 201#, {'Location': url_for('get_user', id = user.id, _external = True)} 63 | 64 | @app.route('/users/') 65 | def get_user(id): 66 | user = session.query(User).filter_by(id=id).one() 67 | if not user: 68 | abort(400) 69 | return jsonify({'username': user.username}) 70 | 71 | @app.route('/resource') 72 | @auth.login_required 73 | def get_resource(): 74 | return jsonify({ 'data': 'Hello, %s!' % g.user.username }) 75 | 76 | @app.route('/products', methods = ['GET', 'POST']) 77 | @auth.login_required 78 | def showAllProducts(): 79 | if request.method == 'GET': 80 | products = session.query(Product).all() 81 | return jsonify(products = [p.serialize for p in products]) 82 | if request.method == 'POST': 83 | name = request.json.get('name') 84 | category = request.json.get('category') 85 | price = request.json.get('price') 86 | newItem = Product(name = name, category = category, price = price) 87 | session.add(newItem) 88 | session.commit() 89 | return jsonify(newItem.serialize) 90 | 91 | 92 | 93 | @app.route('/products/') 94 | @auth.login_required 95 | def showCategoriedProducts(category): 96 | if category == 'fruit': 97 | fruit_items = session.query(Product).filter_by(category = 'fruit').all() 98 | return jsonify(fruit_products = [f.serialize for f in fruit_items]) 99 | if category == 'legume': 100 | legume_items = session.query(Product).filter_by(category = 'legume').all() 101 | return jsonify(legume_products = [l.serialize for l in legume_items]) 102 | if category == 'vegetable': 103 | vegetable_items = session.query(Product).filter_by(category = 'vegetable').all() 104 | return jsonify(produce_products = [p.serialize for p in produce_items]) 105 | 106 | 107 | 108 | if __name__ == '__main__': 109 | app.debug = True 110 | #app.config['SECRET_KEY'] = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in xrange(32)) 111 | app.run(host='0.0.0.0', port=5000) 112 | -------------------------------------------------------------------------------- /Lesson_4/08 _Regal Tree Foods/starter_code/fruit_tester.py: -------------------------------------------------------------------------------- 1 | from urllib import urlencode 2 | from httplib2 import Http 3 | import json 4 | import sys 5 | import base64 6 | 7 | 8 | print "Running Endpoint Tester....\n" 9 | address = raw_input("Please enter the address of the server you want to access, \n If left blank the connection will be set to 'http://localhost:5000': ") 10 | if address == '': 11 | address = 'http://localhost:5000' 12 | 13 | #TEST 1: TRY TO REGISTER A NEW USER 14 | try: 15 | h = Http() 16 | url = address + '/users' 17 | data = dict(username="Peter", password="Pan") 18 | data = json.dumps(data) 19 | resp, content = h.request(url,'POST', body = data, headers = {"Content-Type": "application/json"}) 20 | if resp['status'] != '201' and resp['status'] != '200': 21 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 22 | 23 | except Exception as err: 24 | print "Test 1 FAILED: Could not make a new user" 25 | print err.args 26 | sys.exit() 27 | else: 28 | print "Test 1 PASS: Succesfully made a new user" 29 | 30 | #TEST 2: OBTAIN A TOKEN 31 | try: 32 | h = Http() 33 | h.add_credentials('Peter','Pan') 34 | url = address + '/token' 35 | resp, content = h.request(url,'GET' , headers = {"Content-Type" : "application/json"}) 36 | if resp['status'] != '200': 37 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 38 | new_content = json.loads(content) 39 | if not new_content['token']: 40 | raise Exception('No Token Received!') 41 | token = new_content['token'] 42 | print "received token: %s" % token 43 | except Exception as err: 44 | print "Test 2 FAILED: Could not exchange user credentials for a token" 45 | print err.args 46 | sys.exit() 47 | else: 48 | print "Test 2 PASS: Succesfully obtained token! " 49 | 50 | #TEST 3: TRY TO ADD PRODUCS TO DATABASE 51 | 52 | try: 53 | h = Http() 54 | h.add_credentials(token,'blank') 55 | url = address + '/products' 56 | data = dict(name = "apple", category = "fruit", price= "$.99") 57 | resp, content = h.request(url,'POST', body = json.dumps(data), headers = {"Content-Type" : "application/json"}) 58 | if resp['status'] != '200': 59 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 60 | except Exception as err: 61 | print "Test 3 FAILED: Could not add new products" 62 | print err.args 63 | sys.exit() 64 | else: 65 | print "Test 3 PASS: Succesfully added new products" 66 | 67 | 68 | #TEST 4: TRY ACCESSING ENDPOINT WITH AN INVALID TOKEN 69 | 70 | #TEST 5: TRY TO VIEW ALL PRODUCTS IN DATABASE 71 | 72 | #TEST 6: TRY TO VIEW A SPECIFIC CATEGORY OF PRODUCTS -------------------------------------------------------------------------------- /Lesson_4/08 _Regal Tree Foods/starter_code/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column,Integer,String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import relationship, sessionmaker 4 | from sqlalchemy import create_engine 5 | from passlib.apps import custom_app_context as pwd_context 6 | import random, string 7 | from itsdangerous import(TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired) 8 | 9 | Base = declarative_base() 10 | 11 | #You will use this secret key to create and verify your tokens 12 | secret_key = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in xrange(32)) 13 | 14 | class User(Base): 15 | __tablename__ = 'user' 16 | id = Column(Integer, primary_key=True) 17 | username = Column(String(32), index=True) 18 | password_hash = Column(String(64)) 19 | 20 | def hash_password(self, password): 21 | self.password_hash = pwd_context.encrypt(password) 22 | 23 | def verify_password(self, password): 24 | return pwd_context.verify(password, self.password_hash) 25 | #Add a method to generate auth tokens here 26 | 27 | #Add a method to verify auth tokens here 28 | 29 | class Product(Base): 30 | __tablename__ = 'product' 31 | id = Column(Integer, primary_key=True) 32 | name = Column(String) 33 | category = Column(String) 34 | price = Column(String) 35 | @property 36 | def serialize(self): 37 | """Return object data in easily serializeable format""" 38 | return { 39 | 'name' : self.name, 40 | 'category' : self.category, 41 | 'price' : self.price 42 | } 43 | 44 | engine = create_engine('sqlite:///regalTree.db') 45 | 46 | 47 | Base.metadata.create_all(engine) 48 | 49 | -------------------------------------------------------------------------------- /Lesson_4/08 _Regal Tree Foods/starter_code/views.py: -------------------------------------------------------------------------------- 1 | from models import Base, User, Product 2 | from flask import Flask, jsonify, request, url_for, abort, g 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import relationship, sessionmaker 5 | from sqlalchemy import create_engine 6 | 7 | from flask.ext.httpauth import HTTPBasicAuth 8 | auth = HTTPBasicAuth() 9 | 10 | 11 | engine = create_engine('sqlite:///regalTree.db') 12 | 13 | Base.metadata.bind = engine 14 | DBSession = sessionmaker(bind=engine) 15 | session = DBSession() 16 | app = Flask(__name__) 17 | 18 | 19 | 20 | 21 | #ADD @auth.verify_password decorator here 22 | 23 | 24 | #add /token route here to get a token for a user with login credentials 25 | 26 | 27 | 28 | 29 | @app.route('/users', methods = ['POST']) 30 | def new_user(): 31 | username = request.json.get('username') 32 | password = request.json.get('password') 33 | if username is None or password is None: 34 | print "missing arguments" 35 | abort(400) 36 | 37 | if session.query(User).filter_by(username = username).first() is not None: 38 | print "existing user" 39 | user = session.query(User).filter_by(username=username).first() 40 | return jsonify({'message':'user already exists'}), 200#, {'Location': url_for('get_user', id = user.id, _external = True)} 41 | 42 | user = User(username = username) 43 | user.hash_password(password) 44 | session.add(user) 45 | session.commit() 46 | return jsonify({ 'username': user.username }), 201#, {'Location': url_for('get_user', id = user.id, _external = True)} 47 | 48 | @app.route('/users/') 49 | def get_user(id): 50 | user = session.query(User).filter_by(id=id).one() 51 | if not user: 52 | abort(400) 53 | return jsonify({'username': user.username}) 54 | 55 | @app.route('/resource') 56 | @auth.login_required 57 | def get_resource(): 58 | return jsonify({ 'data': 'Hello, %s!' % g.user.username }) 59 | 60 | @app.route('/products', methods = ['GET', 'POST']) 61 | @auth.login_required 62 | def showAllProducts(): 63 | if request.method == 'GET': 64 | products = session.query(Product).all() 65 | return jsonify(products = [p.serialize for p in products]) 66 | if request.method == 'POST': 67 | name = request.json.get('name') 68 | category = request.json.get('category') 69 | price = request.json.get('price') 70 | newItem = Product(name = name, category = category, price = price) 71 | session.add(newItem) 72 | session.commit() 73 | return jsonify(newItem.serialize) 74 | 75 | 76 | 77 | @app.route('/products/') 78 | @auth.login_required 79 | def showCategoriedProducts(category): 80 | if category == 'fruit': 81 | fruit_items = session.query(Product).filter_by(category = 'fruit').all() 82 | return jsonify(fruit_products = [f.serialize for f in fruit_items]) 83 | if category == 'legume': 84 | legume_items = session.query(Product).filter_by(category = 'legume').all() 85 | return jsonify(legume_products = [l.serialize for l in legume_items]) 86 | if category == 'vegetable': 87 | vegetable_items = session.query(Product).filter_by(category = 'vegetable').all() 88 | return jsonify(produce_products = [p.serialize for p in produce_items]) 89 | 90 | 91 | 92 | if __name__ == '__main__': 93 | app.debug = True 94 | #app.config['SECRET_KEY'] = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in xrange(32)) 95 | app.run(host='0.0.0.0', port=5000) 96 | -------------------------------------------------------------------------------- /Lesson_4/10_Adding OAuth 2.0 for Authentication/client_secrets.json: -------------------------------------------------------------------------------- 1 | {"web":{"auth_uri":"https://accounts.google.com/o/oauth2/auth","client_secret":"jzaUaC50PpdcVKkIkd9MXCc1","token_uri":"https://accounts.google.com/o/oauth2/token","client_email":"950296359546-tr6a0qci8smluvackvmj51bruuu73qos@developer.gserviceaccount.com","redirect_uris":["http://localhost:5000/oauth2callback"],"client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/950296359546-tr6a0qci8smluvackvmj51bruuu73qos@developer.gserviceaccount.com","client_id":"950296359546-tr6a0qci8smluvackvmj51bruuu73qos.apps.googleusercontent.com","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","javascript_origins":["http://localhost:5000"]}} -------------------------------------------------------------------------------- /Lesson_4/10_Adding OAuth 2.0 for Authentication/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column,Integer,String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import relationship, sessionmaker 4 | from sqlalchemy import create_engine 5 | from passlib.apps import custom_app_context as pwd_context 6 | import random, string 7 | from itsdangerous import(TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired) 8 | 9 | Base = declarative_base() 10 | secret_key = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in xrange(32)) 11 | 12 | class User(Base): 13 | __tablename__ = 'user' 14 | id = Column(Integer, primary_key=True) 15 | username = Column(String(32), index=True) 16 | picture = Column(String) 17 | email = Column(String) 18 | password_hash = Column(String(64)) 19 | 20 | def hash_password(self, password): 21 | self.password_hash = pwd_context.encrypt(password) 22 | 23 | def verify_password(self, password): 24 | return pwd_context.verify(password, self.password_hash) 25 | 26 | def generate_auth_token(self, expiration=600): 27 | s = Serializer(secret_key, expires_in = expiration) 28 | return s.dumps({'id': self.id }) 29 | 30 | @staticmethod 31 | def verify_auth_token(token): 32 | s = Serializer(secret_key) 33 | try: 34 | data = s.loads(token) 35 | except SignatureExpired: 36 | #Valid Token, but expired 37 | return None 38 | except BadSignature: 39 | #Invalid Token 40 | return None 41 | user_id = data['id'] 42 | return user_id 43 | 44 | 45 | engine = create_engine('sqlite:///usersWithOAuth.db') 46 | 47 | 48 | Base.metadata.create_all(engine) 49 | 50 | -------------------------------------------------------------------------------- /Lesson_4/10_Adding OAuth 2.0 for Authentication/models.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/10_Adding OAuth 2.0 for Authentication/models.pyc -------------------------------------------------------------------------------- /Lesson_4/10_Adding OAuth 2.0 for Authentication/templates/clientOAuth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 36 | 37 | 38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |

There's a new Google Sign-In Option!!

56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 109 | 110 | 111 | 127 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /Lesson_4/10_Adding OAuth 2.0 for Authentication/usersWithOAuth.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/10_Adding OAuth 2.0 for Authentication/usersWithOAuth.db -------------------------------------------------------------------------------- /Lesson_4/10_Adding OAuth 2.0 for Authentication/views.py: -------------------------------------------------------------------------------- 1 | from models import Base, User 2 | from flask import Flask, jsonify, request, url_for, abort, g, render_template 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import relationship, sessionmaker 5 | from sqlalchemy import create_engine 6 | 7 | from flask.ext.httpauth import HTTPBasicAuth 8 | import json 9 | 10 | #NEW IMPORTS 11 | from oauth2client.client import flow_from_clientsecrets 12 | from oauth2client.client import FlowExchangeError 13 | import httplib2 14 | from flask import make_response 15 | import requests 16 | 17 | auth = HTTPBasicAuth() 18 | 19 | 20 | engine = create_engine('sqlite:///usersWithOAuth.db') 21 | 22 | Base.metadata.bind = engine 23 | DBSession = sessionmaker(bind=engine) 24 | session = DBSession() 25 | app = Flask(__name__) 26 | 27 | 28 | CLIENT_ID = json.loads( 29 | open('client_secrets.json', 'r').read())['web']['client_id'] 30 | 31 | 32 | 33 | @auth.verify_password 34 | def verify_password(username_or_token, password): 35 | #Try to see if it's a token first 36 | user_id = User.verify_auth_token(username_or_token) 37 | if user_id: 38 | user = session.query(User).filter_by(id = user_id).one() 39 | else: 40 | user = session.query(User).filter_by(username = username_or_token).first() 41 | if not user or not user.verify_password(password): 42 | return False 43 | g.user = user 44 | return True 45 | 46 | @app.route('/clientOAuth') 47 | def start(): 48 | return render_template('clientOAuth.html') 49 | 50 | @app.route('/oauth/', methods = ['POST']) 51 | def login(provider): 52 | #STEP 1 - Parse the auth code 53 | auth_code = request.json.get('auth_code') 54 | print "Step 1 - Complete, received auth code %s" % auth_code 55 | if provider == 'google': 56 | #STEP 2 - Exchange for a token 57 | try: 58 | # Upgrade the authorization code into a credentials object 59 | oauth_flow = flow_from_clientsecrets('client_secrets.json', scope='') 60 | oauth_flow.redirect_uri = 'postmessage' 61 | credentials = oauth_flow.step2_exchange(auth_code) 62 | except FlowExchangeError: 63 | response = make_response(json.dumps('Failed to upgrade the authorization code.'), 401) 64 | response.headers['Content-Type'] = 'application/json' 65 | return response 66 | 67 | # Check that the access token is valid. 68 | access_token = credentials.access_token 69 | url = ('https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=%s' % access_token) 70 | h = httplib2.Http() 71 | result = json.loads(h.request(url, 'GET')[1]) 72 | # If there was an error in the access token info, abort. 73 | if result.get('error') is not None: 74 | response = make_response(json.dumps(result.get('error')), 500) 75 | response.headers['Content-Type'] = 'application/json' 76 | 77 | # # Verify that the access token is used for the intended user. 78 | # gplus_id = credentials.id_token['sub'] 79 | # if result['user_id'] != gplus_id: 80 | # response = make_response(json.dumps("Token's user ID doesn't match given user ID."), 401) 81 | # response.headers['Content-Type'] = 'application/json' 82 | # return response 83 | 84 | # # Verify that the access token is valid for this app. 85 | # if result['issued_to'] != CLIENT_ID: 86 | # response = make_response(json.dumps("Token's client ID does not match app's."), 401) 87 | # response.headers['Content-Type'] = 'application/json' 88 | # return response 89 | 90 | # stored_credentials = login_session.get('credentials') 91 | # stored_gplus_id = login_session.get('gplus_id') 92 | # if stored_credentials is not None and gplus_id == stored_gplus_id: 93 | # response = make_response(json.dumps('Current user is already connected.'), 200) 94 | # response.headers['Content-Type'] = 'application/json' 95 | # return response 96 | print "Step 2 Complete! Access Token : %s " % credentials.access_token 97 | 98 | #STEP 3 - Find User or make a new one 99 | 100 | #Get user info 101 | h = httplib2.Http() 102 | userinfo_url = "https://www.googleapis.com/oauth2/v1/userinfo" 103 | params = {'access_token': credentials.access_token, 'alt':'json'} 104 | answer = requests.get(userinfo_url, params=params) 105 | 106 | data = answer.json() 107 | 108 | name = data['name'] 109 | picture = data['picture'] 110 | email = data['email'] 111 | 112 | 113 | 114 | #see if user exists, if it doesn't make a new one 115 | user = session.query(User).filter_by(email=email).first() 116 | if not user: 117 | user = User(username = name, picture = picture, email = email) 118 | session.add(user) 119 | session.commit() 120 | 121 | 122 | 123 | #STEP 4 - Make token 124 | token = user.generate_auth_token(600) 125 | 126 | 127 | 128 | #STEP 5 - Send back token to the client 129 | return jsonify({'token': token.decode('ascii')}) 130 | 131 | #return jsonify({'token': token.decode('ascii'), 'duration': 600}) 132 | else: 133 | return 'Unrecoginized Provider' 134 | 135 | @app.route('/token') 136 | @auth.login_required 137 | def get_auth_token(): 138 | token = g.user.generate_auth_token() 139 | return jsonify({'token': token.decode('ascii')}) 140 | 141 | 142 | 143 | @app.route('/users', methods = ['POST']) 144 | def new_user(): 145 | username = request.json.get('username') 146 | password = request.json.get('password') 147 | if username is None or password is None: 148 | print "missing arguments" 149 | abort(400) 150 | 151 | if session.query(User).filter_by(username = username).first() is not None: 152 | print "existing user" 153 | user = session.query(User).filter_by(username=username).first() 154 | return jsonify({'message':'user already exists'}), 200#, {'Location': url_for('get_user', id = user.id, _external = True)} 155 | 156 | user = User(username = username) 157 | user.hash_password(password) 158 | session.add(user) 159 | session.commit() 160 | return jsonify({ 'username': user.username }), 201#, {'Location': url_for('get_user', id = user.id, _external = True)} 161 | 162 | @app.route('/api/users/') 163 | def get_user(id): 164 | user = session.query(User).filter_by(id=id).one() 165 | if not user: 166 | abort(400) 167 | return jsonify({'username': user.username}) 168 | 169 | @app.route('/api/resource') 170 | @auth.login_required 171 | def get_resource(): 172 | return jsonify({ 'data': 'Hello, %s!' % g.user.username }) 173 | 174 | 175 | 176 | if __name__ == '__main__': 177 | app.debug = True 178 | #app.config['SECRET_KEY'] = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in xrange(32)) 179 | app.run(host='0.0.0.0', port=5000) 180 | -------------------------------------------------------------------------------- /Lesson_4/11_Pale Kale Ocean Eats/Solution Code/client_secrets.json: -------------------------------------------------------------------------------- 1 | {"web":{"auth_uri":"https://accounts.google.com/o/oauth2/auth","client_secret":"jzaUaC50PpdcVKkIkd9MXCc1","token_uri":"https://accounts.google.com/o/oauth2/token","client_email":"950296359546-tr6a0qci8smluvackvmj51bruuu73qos@developer.gserviceaccount.com","redirect_uris":["http://localhost:5000/oauth2callback"],"client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/950296359546-tr6a0qci8smluvackvmj51bruuu73qos@developer.gserviceaccount.com","client_id":"950296359546-tr6a0qci8smluvackvmj51bruuu73qos.apps.googleusercontent.com","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","javascript_origins":["http://localhost:5000"]}} -------------------------------------------------------------------------------- /Lesson_4/11_Pale Kale Ocean Eats/Solution Code/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column,Integer,String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import relationship, sessionmaker 4 | from sqlalchemy import create_engine 5 | from passlib.apps import custom_app_context as pwd_context 6 | import random, string 7 | from itsdangerous import(TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired) 8 | 9 | Base = declarative_base() 10 | secret_key = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in xrange(32)) 11 | 12 | class User(Base): 13 | __tablename__ = 'user' 14 | id = Column(Integer, primary_key=True) 15 | username = Column(String(32), index=True) 16 | picture = Column(String) 17 | email = Column(String) 18 | password_hash = Column(String(64)) 19 | 20 | def hash_password(self, password): 21 | self.password_hash = pwd_context.encrypt(password) 22 | 23 | def verify_password(self, password): 24 | return pwd_context.verify(password, self.password_hash) 25 | 26 | def generate_auth_token(self, expiration=600): 27 | s = Serializer(secret_key, expires_in = expiration) 28 | return s.dumps({'id': self.id }) 29 | 30 | @staticmethod 31 | def verify_auth_token(token): 32 | s = Serializer(secret_key) 33 | try: 34 | data = s.loads(token) 35 | except SignatureExpired: 36 | #Valid Token, but expired 37 | return None 38 | except BadSignature: 39 | #Invalid Token 40 | return None 41 | user_id = data['id'] 42 | return user_id 43 | 44 | 45 | engine = create_engine('sqlite:///paleKale.db') 46 | 47 | 48 | Base.metadata.create_all(engine) 49 | 50 | -------------------------------------------------------------------------------- /Lesson_4/11_Pale Kale Ocean Eats/Solution Code/models.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/11_Pale Kale Ocean Eats/Solution Code/models.pyc -------------------------------------------------------------------------------- /Lesson_4/11_Pale Kale Ocean Eats/Solution Code/paleKale.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/11_Pale Kale Ocean Eats/Solution Code/paleKale.db -------------------------------------------------------------------------------- /Lesson_4/11_Pale Kale Ocean Eats/Solution Code/templates/clientOAuth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 36 | 37 | 38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |

There's a new Google Sign-In Option!!

56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 109 | 110 | 111 | 127 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /Lesson_4/11_Pale Kale Ocean Eats/Solution Code/usersWithOAuth.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/APIs/92cb54457a26f2a5e9cf3b74bbe2bb0c54d48237/Lesson_4/11_Pale Kale Ocean Eats/Solution Code/usersWithOAuth.db -------------------------------------------------------------------------------- /Lesson_4/11_Pale Kale Ocean Eats/Solution Code/views.py: -------------------------------------------------------------------------------- 1 | from models import Base, User 2 | from flask import Flask, jsonify, request, url_for, abort, g, render_template 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import relationship, sessionmaker 5 | from sqlalchemy import create_engine 6 | 7 | from flask.ext.httpauth import HTTPBasicAuth 8 | import json 9 | 10 | #NEW IMPORTS 11 | from oauth2client.client import flow_from_clientsecrets 12 | from oauth2client.client import FlowExchangeError 13 | import httplib2 14 | from flask import make_response 15 | import requests 16 | 17 | auth = HTTPBasicAuth() 18 | 19 | 20 | engine = create_engine('sqlite:///paleKale.db') 21 | 22 | Base.metadata.bind = engine 23 | DBSession = sessionmaker(bind=engine) 24 | session = DBSession() 25 | app = Flask(__name__) 26 | 27 | 28 | CLIENT_ID = json.loads( 29 | open('client_secrets.json', 'r').read())['web']['client_id'] 30 | 31 | 32 | 33 | @auth.verify_password 34 | def verify_password(username_or_token, password): 35 | #Try to see if it's a token first 36 | user_id = User.verify_auth_token(username_or_token) 37 | if user_id: 38 | user = session.query(User).filter_by(id = user_id).one() 39 | else: 40 | user = session.query(User).filter_by(username = username_or_token).first() 41 | if not user or not user.verify_password(password): 42 | return False 43 | g.user = user 44 | return True 45 | 46 | @app.route('/clientOAuth') 47 | def start(): 48 | return render_template('clientOAuth.html') 49 | 50 | @app.route('/oauth/', methods = ['POST']) 51 | def login(provider): 52 | #STEP 1 - Parse the auth code 53 | auth_code = request.json.get('auth_code') 54 | print "Step 1 - Complete, received auth code %s" % auth_code 55 | if provider == 'google': 56 | #STEP 2 - Exchange for a token 57 | try: 58 | # Upgrade the authorization code into a credentials object 59 | oauth_flow = flow_from_clientsecrets('client_secrets.json', scope='') 60 | oauth_flow.redirect_uri = 'postmessage' 61 | credentials = oauth_flow.step2_exchange(auth_code) 62 | except FlowExchangeError: 63 | response = make_response(json.dumps('Failed to upgrade the authorization code.'), 401) 64 | response.headers['Content-Type'] = 'application/json' 65 | return response 66 | 67 | # Check that the access token is valid. 68 | access_token = credentials.access_token 69 | url = ('https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=%s' % access_token) 70 | h = httplib2.Http() 71 | result = json.loads(h.request(url, 'GET')[1]) 72 | # If there was an error in the access token info, abort. 73 | if result.get('error') is not None: 74 | response = make_response(json.dumps(result.get('error')), 500) 75 | response.headers['Content-Type'] = 'application/json' 76 | 77 | # # Verify that the access token is used for the intended user. 78 | # gplus_id = credentials.id_token['sub'] 79 | # if result['user_id'] != gplus_id: 80 | # response = make_response(json.dumps("Token's user ID doesn't match given user ID."), 401) 81 | # response.headers['Content-Type'] = 'application/json' 82 | # return response 83 | 84 | # # Verify that the access token is valid for this app. 85 | # if result['issued_to'] != CLIENT_ID: 86 | # response = make_response(json.dumps("Token's client ID does not match app's."), 401) 87 | # response.headers['Content-Type'] = 'application/json' 88 | # return response 89 | 90 | # stored_credentials = login_session.get('credentials') 91 | # stored_gplus_id = login_session.get('gplus_id') 92 | # if stored_credentials is not None and gplus_id == stored_gplus_id: 93 | # response = make_response(json.dumps('Current user is already connected.'), 200) 94 | # response.headers['Content-Type'] = 'application/json' 95 | # return response 96 | print "Step 2 Complete! Access Token : %s " % credentials.access_token 97 | 98 | #STEP 3 - Find User or make a new one 99 | 100 | #Get user info 101 | h = httplib2.Http() 102 | userinfo_url = "https://www.googleapis.com/oauth2/v1/userinfo" 103 | params = {'access_token': credentials.access_token, 'alt':'json'} 104 | answer = requests.get(userinfo_url, params=params) 105 | 106 | data = answer.json() 107 | 108 | name = data['name'] 109 | picture = data['picture'] 110 | email = data['email'] 111 | 112 | 113 | 114 | #see if user exists, if it doesn't make a new one 115 | user = session.query(User).filter_by(email=email).first() 116 | if not user: 117 | user = User(username = name, picture = picture, email = email) 118 | session.add(user) 119 | session.commit() 120 | 121 | 122 | 123 | #STEP 4 - Make token 124 | token = user.generate_auth_token(600) 125 | 126 | 127 | 128 | #STEP 5 - Send back token to the client 129 | return jsonify({'token': token.decode('ascii')}) 130 | 131 | #return jsonify({'token': token.decode('ascii'), 'duration': 600}) 132 | else: 133 | return 'Unrecoginized Provider' 134 | 135 | @app.route('/token') 136 | @auth.login_required 137 | def get_auth_token(): 138 | token = g.user.generate_auth_token() 139 | return jsonify({'token': token.decode('ascii')}) 140 | 141 | 142 | 143 | @app.route('/users', methods = ['POST']) 144 | def new_user(): 145 | username = request.json.get('username') 146 | password = request.json.get('password') 147 | if username is None or password is None: 148 | print "missing arguments" 149 | abort(400) 150 | 151 | if session.query(User).filter_by(username = username).first() is not None: 152 | print "existing user" 153 | user = session.query(User).filter_by(username=username).first() 154 | return jsonify({'message':'user already exists'}), 200#, {'Location': url_for('get_user', id = user.id, _external = True)} 155 | 156 | user = User(username = username) 157 | user.hash_password(password) 158 | session.add(user) 159 | session.commit() 160 | return jsonify({ 'username': user.username }), 201#, {'Location': url_for('get_user', id = user.id, _external = True)} 161 | 162 | @app.route('/api/users/') 163 | def get_user(id): 164 | user = session.query(User).filter_by(id=id).one() 165 | if not user: 166 | abort(400) 167 | return jsonify({'username': user.username}) 168 | 169 | @app.route('/api/resource') 170 | @auth.login_required 171 | def get_resource(): 172 | return jsonify({ 'data': 'Hello, %s!' % g.user.username }) 173 | 174 | 175 | 176 | if __name__ == '__main__': 177 | app.debug = True 178 | #app.config['SECRET_KEY'] = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in xrange(32)) 179 | app.run(host='0.0.0.0', port=5000) 180 | -------------------------------------------------------------------------------- /Lesson_4/11_Pale Kale Ocean Eats/starter_code/templates/clientOAuth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 36 | 37 | 38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |

There's a new Google Sign-In Option!!

56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 109 | 110 | 111 | 127 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /Lesson_4/11_Pale Kale Ocean Eats/starter_code/veggie_tester.py: -------------------------------------------------------------------------------- 1 | from httplib2 import Http 2 | import json 3 | import sys 4 | 5 | 6 | print "Running Endpoint Tester....\n" 7 | address = raw_input("Please enter the address of the server you want to access, \n If left blank the connection will be set to 'http://localhost:5000': ") 8 | if address == '': 9 | address = 'http://localhost:5000' 10 | 11 | #GET AUTH CODE 12 | client_url = address + "/clientOAuth" 13 | print "Visit %s in your browser" % client_url 14 | auth_code = "" 15 | while auth_code == "": 16 | auth_code = raw_input("Paste the One-Time Auth Code Here:") 17 | 18 | #TEST ONE GET TOKEN 19 | try: 20 | h = Http() 21 | url = address + "/oauth/google" 22 | data = dict(auth_code = auth_code) 23 | data = json.dumps(data) 24 | resp, content = h.request(url, 'POST', body = data, headers = {"Content-Type": "application/json"}) 25 | if resp['status'] != '200': 26 | raise Exception('Received an unsuccessful status code of %s' % resp['status']) 27 | new_content = json.loads(content) 28 | if not new_content['token']: 29 | raise Exception('No Token Received!') 30 | token = new_content['token'] 31 | except Exception as err: 32 | print "Test 1 FAILED: Could not exchange auth code for a token" 33 | print err.args 34 | sys.exit() 35 | else: 36 | print "Test 1 PASS: Succesfully obtained token! " 37 | 38 | 39 | #ADD TO DB WITH TOKEN 40 | 41 | #READ FROM DB WITH TOKEN 42 | -------------------------------------------------------------------------------- /Lesson_4/12_Rate Limiting/hungryclient.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | from time import sleep 3 | import httplib2 4 | import json 5 | 6 | 7 | h = httplib2.Http() 8 | 9 | url = raw_input("Please enter the uri you want to access, \n If left blank the connection will be set to 'http://localhost:5000/rate-limited': ") 10 | if url == '': 11 | url = 'http://localhost:5000/rate-limited' 12 | 13 | 14 | req_per_minute = float(raw_input("Please specify the number of requests per minute: ") ) 15 | 16 | 17 | interval = (60.0 / req_per_minute) 18 | 19 | def SendRequests(url, req_per_minute): 20 | requests = 0 21 | while requests < req_per_minute: 22 | result = json.loads(h.request(url,'GET')[1]) 23 | #result = h.request(url,'GET')[1] 24 | #print result 25 | if result.get('error') is not None: 26 | print "Error #%s : %s" %(result.get('error'), result.get('data')) 27 | print "Hit rate limit. Waiting 5 seconds and trying again..." 28 | sleep(5) 29 | SendRequests(url, req_per_minute) 30 | else: 31 | print "Number of Requests: ", requests+1 32 | print result.get('response') 33 | requests = requests + 1 34 | sleep(interval) 35 | 36 | print "Sending Requests..." 37 | SendRequests(url, req_per_minute) -------------------------------------------------------------------------------- /Lesson_4/12_Rate Limiting/views.py: -------------------------------------------------------------------------------- 1 | from redis import Redis 2 | redis = Redis() 3 | 4 | import time 5 | from functools import update_wrapper 6 | from flask import request, g 7 | from flask import Flask, jsonify 8 | 9 | app = Flask(__name__) 10 | 11 | 12 | 13 | 14 | 15 | class RateLimit(object): 16 | expiration_window = 10 17 | 18 | def __init__(self, key_prefix, limit, per, send_x_headers): 19 | self.reset = (int(time.time()) // per) * per + per 20 | self.key = key_prefix + str(self.reset) 21 | self.limit = limit 22 | self.per = per 23 | self.send_x_headers = send_x_headers 24 | p = redis.pipeline() 25 | p.incr(self.key) 26 | p.expireat(self.key, self.reset + self.expiration_window) 27 | self.current = min(p.execute()[0], limit) 28 | 29 | remaining = property(lambda x: x.limit - x.current) 30 | over_limit = property(lambda x: x.current >= x.limit) 31 | 32 | def get_view_rate_limit(): 33 | return getattr(g, '_view_rate_limit', None) 34 | 35 | def on_over_limit(limit): 36 | return (jsonify({'data':'You hit the rate limit','error':'429'}),429) 37 | 38 | def ratelimit(limit, per=300, send_x_headers=True, 39 | over_limit=on_over_limit, 40 | scope_func=lambda: request.remote_addr, 41 | key_func=lambda: request.endpoint): 42 | def decorator(f): 43 | def rate_limited(*args, **kwargs): 44 | key = 'rate-limit/%s/%s/' % (key_func(), scope_func()) 45 | rlimit = RateLimit(key, limit, per, send_x_headers) 46 | g._view_rate_limit = rlimit 47 | if over_limit is not None and rlimit.over_limit: 48 | return over_limit(rlimit) 49 | return f(*args, **kwargs) 50 | return update_wrapper(rate_limited, f) 51 | return decorator 52 | 53 | 54 | 55 | 56 | 57 | @app.after_request 58 | def inject_x_rate_headers(response): 59 | limit = get_view_rate_limit() 60 | if limit and limit.send_x_headers: 61 | h = response.headers 62 | h.add('X-RateLimit-Remaining', str(limit.remaining)) 63 | h.add('X-RateLimit-Limit', str(limit.limit)) 64 | h.add('X-RateLimit-Reset', str(limit.reset)) 65 | return response 66 | 67 | @app.route('/rate-limited') 68 | @ratelimit(limit=300, per=30 * 1) 69 | def index(): 70 | return jsonify({'response':'This is a rate limited response'}) 71 | 72 | if __name__ == '__main__': 73 | app.secret_key = 'super_secret_key' 74 | app.debug = True 75 | app.run(host = '0.0.0.0', port = 5000) -------------------------------------------------------------------------------- /Lesson_4/13_BargainMart/Solution Code/hungryclient.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | from time import sleep 3 | import json 4 | import httplib2 5 | 6 | 7 | h = httplib2.Http() 8 | 9 | url = raw_input("Please enter the uri you want to access, \n If left blank the connection will be set to 'http://localhost:5000/catalog': ") 10 | if url == '': 11 | url = 'http://localhost:5000/catalog' 12 | 13 | 14 | req_per_minute = float(raw_input("Please specify the number of requests per minute: ") ) 15 | 16 | 17 | interval = (60.0 / req_per_minute) 18 | 19 | def SendRequests(url, req_per_minute): 20 | requests = 0 21 | while requests < req_per_minute: 22 | result = json.loads(h.request(url,'GET')[1]) 23 | #result = h.request(url,'GET')[1] 24 | #print result 25 | if result.get('error') is not None: 26 | print "Error #%s : %s" %(result.get('error'), result.get('data')) 27 | print "Hit rate limit. Waiting 5 seconds and trying again..." 28 | sleep(5) 29 | SendRequests(url, req_per_minute) 30 | else: 31 | print "Number of Requests: ", requests+1 32 | print result 33 | requests = requests + 1 34 | sleep(interval) 35 | 36 | print "Sending Requests..." 37 | SendRequests(url, req_per_minute) -------------------------------------------------------------------------------- /Lesson_4/13_BargainMart/Solution Code/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column,Integer,String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import relationship, sessionmaker 4 | from sqlalchemy import create_engine 5 | from passlib.apps import custom_app_context as pwd_context 6 | 7 | Base = declarative_base() 8 | 9 | class Item(Base): 10 | __tablename__ = 'item' 11 | id = Column(Integer, primary_key=True) 12 | name = Column(String) 13 | picture = Column(String) 14 | description = Column(String) 15 | price = Column(String) 16 | @property 17 | def serialize(self): 18 | """Return object data in easily serializeable format""" 19 | return { 20 | 'name' : self.name, 21 | 'picture' : self.picture, 22 | 'price' : self.price, 23 | 'description' : self.description 24 | } 25 | 26 | engine = create_engine('sqlite:///bargainMart.db') 27 | 28 | 29 | Base.metadata.create_all(engine) 30 | 31 | -------------------------------------------------------------------------------- /Lesson_4/13_BargainMart/Solution Code/views.py: -------------------------------------------------------------------------------- 1 | from redis import Redis 2 | import time 3 | from functools import update_wrapper 4 | from flask import request, g 5 | from flask import Flask, jsonify 6 | from models import Base, Item 7 | 8 | 9 | from sqlalchemy.ext.declarative import declarative_base 10 | from sqlalchemy.orm import relationship, sessionmaker 11 | from sqlalchemy import create_engine 12 | 13 | import json 14 | 15 | engine = create_engine('sqlite:///bargainMart.db') 16 | 17 | Base.metadata.bind = engine 18 | DBSession = sessionmaker(bind=engine) 19 | session = DBSession() 20 | app = Flask(__name__) 21 | 22 | 23 | 24 | 25 | app = Flask(__name__) 26 | redis = Redis() 27 | 28 | 29 | 30 | 31 | 32 | class RateLimit(object): 33 | expiration_window = 10 34 | 35 | def __init__(self, key_prefix, limit, per, send_x_headers): 36 | self.reset = (int(time.time()) // per) * per + per 37 | self.key = key_prefix + str(self.reset) 38 | self.limit = limit 39 | self.per = per 40 | self.send_x_headers = send_x_headers 41 | p = redis.pipeline() 42 | p.incr(self.key) 43 | p.expireat(self.key, self.reset + self.expiration_window) 44 | self.current = min(p.execute()[0], limit) 45 | 46 | remaining = property(lambda x: x.limit - x.current) 47 | over_limit = property(lambda x: x.current >= x.limit) 48 | 49 | def get_view_rate_limit(): 50 | return getattr(g, '_view_rate_limit', None) 51 | 52 | def on_over_limit(limit): 53 | return (jsonify({'data':'You hit the rate limit','error':'429'}),429) 54 | 55 | def ratelimit(limit, per=300, send_x_headers=True, 56 | over_limit=on_over_limit, 57 | scope_func=lambda: request.remote_addr, 58 | key_func=lambda: request.endpoint): 59 | def decorator(f): 60 | def rate_limited(*args, **kwargs): 61 | key = 'rate-limit/%s/%s/' % (key_func(), scope_func()) 62 | rlimit = RateLimit(key, limit, per, send_x_headers) 63 | g._view_rate_limit = rlimit 64 | if over_limit is not None and rlimit.over_limit: 65 | return over_limit(rlimit) 66 | return f(*args, **kwargs) 67 | return update_wrapper(rate_limited, f) 68 | return decorator 69 | 70 | 71 | 72 | @app.after_request 73 | def inject_x_rate_headers(response): 74 | limit = get_view_rate_limit() 75 | if limit and limit.send_x_headers: 76 | h = response.headers 77 | h.add('X-RateLimit-Remaining', str(limit.remaining)) 78 | h.add('X-RateLimit-Limit', str(limit.limit)) 79 | h.add('X-RateLimit-Reset', str(limit.reset)) 80 | return response 81 | 82 | @app.route('/catalog') 83 | @ratelimit(limit=30, per=60 * 1) 84 | def getCatalog(): 85 | items = session.query(Item).all() 86 | 87 | #Populate an empty database 88 | if items == []: 89 | item1 = Item(name="Pineapple", price="$2.50", picture="https://upload.wikimedia.org/wikipedia/commons/c/cb/Pineapple_and_cross_section.jpg", description="Organically Grown in Hawai'i") 90 | session.add(item1) 91 | item2 = Item(name="Carrots", price = "$1.99", picture = "http://media.mercola.com/assets/images/food-facts/carrot-fb.jpg", description = "High in Vitamin A") 92 | session.add(item2) 93 | item3 = Item(name="Aluminum Foil", price="$3.50", picture = "http://images.wisegeek.com/aluminum-foil.jpg", description = "300 feet long") 94 | session.add(item3) 95 | item4 = Item(name="Eggs", price = "$2.00", picture = "http://whatsyourdeal.com/grocery-coupons/wp-content/uploads/2015/01/eggs.png", description = "Farm Fresh Organic Eggs") 96 | session.add(item4) 97 | item5 = Item(name="Bananas", price = "$2.15", picture = "http://dreamatico.com/data_images/banana/banana-3.jpg", description="Fresh, delicious, and full of potassium") 98 | session.add(item5) 99 | session.commit() 100 | items = session.query(Item).all() 101 | return jsonify(catalog = [i.serialize for i in items]) 102 | 103 | if __name__ == '__main__': 104 | app.secret_key = 'super_secret_key' 105 | app.debug = True 106 | app.run(host = '0.0.0.0', port = 5000) 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /Lesson_4/13_BargainMart/Starter Code/hungryclient.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | from time import sleep 3 | import json 4 | import httplib2 5 | 6 | 7 | h = httplib2.Http() 8 | 9 | url = raw_input("Please enter the uri you want to access, \n If left blank the connection will be set to 'http://localhost:5000/catalog': ") 10 | if url == '': 11 | url = 'http://localhost:5000/catalog' 12 | 13 | 14 | req_per_minute = float(raw_input("Please specify the number of requests per minute: ") ) 15 | 16 | 17 | interval = (60.0 / req_per_minute) 18 | 19 | def SendRequests(url, req_per_minute): 20 | requests = 0 21 | while requests < req_per_minute: 22 | result = json.loads(h.request(url,'GET')[1]) 23 | #result = h.request(url,'GET')[1] 24 | #print result 25 | if result.get('error') is not None: 26 | print "Error #%s : %s" %(result.get('error'), result.get('data')) 27 | print "Hit rate limit. Waiting 5 seconds and trying again..." 28 | sleep(5) 29 | SendRequests(url, req_per_minute) 30 | else: 31 | print "Number of Requests: ", requests+1 32 | print result 33 | requests = requests + 1 34 | sleep(interval) 35 | 36 | print "Sending Requests..." 37 | SendRequests(url, req_per_minute) -------------------------------------------------------------------------------- /Lesson_4/13_BargainMart/Starter Code/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column,Integer,String 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import relationship, sessionmaker 4 | from sqlalchemy import create_engine 5 | from passlib.apps import custom_app_context as pwd_context 6 | 7 | Base = declarative_base() 8 | 9 | class Item(Base): 10 | __tablename__ = 'item' 11 | id = Column(Integer, primary_key=True) 12 | name = Column(String) 13 | picture = Column(String) 14 | description = Column(String) 15 | price = Column(String) 16 | @property 17 | def serialize(self): 18 | """Return object data in easily serializeable format""" 19 | return { 20 | 'name' : self.name, 21 | 'picture' : self.picture, 22 | 'price' : self.price, 23 | 'description' : self.description 24 | } 25 | 26 | engine = create_engine('sqlite:///bargainMart.db') 27 | 28 | 29 | Base.metadata.create_all(engine) 30 | 31 | -------------------------------------------------------------------------------- /Lesson_4/13_BargainMart/Starter Code/views.py: -------------------------------------------------------------------------------- 1 | from redis import Redis 2 | import time 3 | from functools import update_wrapper 4 | from flask import request, g 5 | from flask import Flask, jsonify 6 | from models import Base, Item 7 | 8 | 9 | from sqlalchemy.ext.declarative import declarative_base 10 | from sqlalchemy.orm import relationship, sessionmaker 11 | from sqlalchemy import create_engine 12 | 13 | import json 14 | 15 | engine = create_engine('sqlite:///bargainMart.db') 16 | 17 | Base.metadata.bind = engine 18 | DBSession = sessionmaker(bind=engine) 19 | session = DBSession() 20 | app = Flask(__name__) 21 | 22 | 23 | 24 | 25 | app = Flask(__name__) 26 | #ADD RATE LIMITING CODE HERE 27 | 28 | 29 | 30 | @app.route('/catalog') 31 | def getCatalog(): 32 | items = session.query(Item).all() 33 | 34 | #Populate an empty database 35 | if items == []: 36 | item1 = Item(name="Pineapple", price="$2.50", picture="https://upload.wikimedia.org/wikipedia/commons/c/cb/Pineapple_and_cross_section.jpg", description="Organically Grown in Hawai'i") 37 | session.add(item1) 38 | item2 = Item(name="Carrots", price = "$1.99", picture = "http://media.mercola.com/assets/images/food-facts/carrot-fb.jpg", description = "High in Vitamin A") 39 | session.add(item2) 40 | item3 = Item(name="Aluminum Foil", price="$3.50", picture = "http://images.wisegeek.com/aluminum-foil.jpg", description = "300 feet long") 41 | session.add(item3) 42 | item4 = Item(name="Eggs", price = "$2.00", picture = "http://whatsyourdeal.com/grocery-coupons/wp-content/uploads/2015/01/eggs.png", description = "Farm Fresh Organic Eggs") 43 | session.add(item4) 44 | item5 = Item(name="Bananas", price = "$2.15", picture = "http://dreamatico.com/data_images/banana/banana-3.jpg", description="Fresh, delicious, and full of potassium") 45 | session.add(item5) 46 | session.commit() 47 | items = session.query(Item).all() 48 | return jsonify(catalog = [i.serialize for i in items]) 49 | 50 | if __name__ == '__main__': 51 | app.secret_key = 'super_secret_key' 52 | app.debug = True 53 | app.run(host = '0.0.0.0', port = 5000) 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # APIs 2 | ### This is the code Repo for ud388 - Designing RESTful APIs 3 | 4 | This code base was meant to supplement the Udacity course for designing RESTful APIs. Within each directory you will find sample and solution code to the exercises in this course. Some of this code will require some modification from the user before it is executable on your machine. 5 | 6 | ## API Keys for third party providers 7 | This course uses the Google Maps and Foursquare APIs. You will need to create developer accounts and private keys in order to properly use code snippets that rely on these APIs. 8 | 9 | ## Python Libraries 10 | The code in this repository assumes the following python libraries are installed: 11 | * Flask 12 | * SQLAlchemy 13 | * httplib 14 | * requests 15 | * oauth2client 16 | * redis 17 | * passlib 18 | * itsdangerous 19 | * flask-httpauth 20 | 21 | ## Installing Redis 22 | wget http://download.redis.io/redis-stable.tar.gz 23 | tar xvzf redis-stable.tar.gz 24 | cd redis-stable 25 | make install 26 | --------------------------------------------------------------------------------