This is the final project of the Udacity Full Stack Web Developer Nanodegree Course.
The object is to build an API from start to finish and host it.
Below are the skills taught in the courses summarized which are part of this final project:
- Coding in Python 3
- Relational Database Architecture
- Modeling Data Objects with SQLAlchemy
- Internet Protocols and Communication
- Developing a Flask API
- Authentication and Access
- Authentication with Auth0
- Authentication in Flask
- Role-Based Access Control (RBAC)
- Testing Flask Applications
- Deploying Applications
The following instructions are based on a linux cli environment and can vary on different operating systems.
Follow the instructions to install the latest version of python for your platform here: docs
It's recommended to work within a virtual environment whenever using Python for projects to keep your dependencies for each project separate and organaized. How to set up the virtual environment you can see here: docs
Once you have setup your venv, you can install the project dependencies inside the root directory by running:
pip3 install -r requirements.txt
This command will install all the required packages included in the requirements.txt file
Authentification Service:
Deployment Service:
You can follow the postgres Documentationto set up your local database
- Main DB
- Test DB
Within the setup.sh file replace the Database Urls with your own Postgres Database Urls
Run the setup.sh file to set the needed environment variables for the following steps.
Execute:
python manage.py db upgrade
to setup the db models.
Due to a hard limit of the web_token expiry of 1 day(dee following thread), it could be necessary to update the JWT Access tokens in the setup.sh file.
Go to the
and use the following login credentials to get new valid tokens:
Admin: capstoneadmin@fsnd.com PW: Capstone!2021 User: capstoneuser@fsnd.com PW: Capstone!2021
In the browser address line you get the a redirection with a new valid access token:
Copy the access token and replace it inside the setup.sh file.
To logout a user use the following link:
To setup the necessary environment variables execute:
source setup.sh
Run the following command from the root folder within your venv:
python tests/test_app.py
Your local installation is set up correctly when all tests pass.
Run the following command from the root folder within your venv:
python app.py
- Podcast - List of Podcasts
- Speaker - List of Speakers/Podcast Guests
- Episode - Podcast Episode with speaker
- Healthcheck GET '/'
- GET '/podcasts', '/speakers' and '/episodes'
- GET '/podcasts/', '/speakers/' and '/episodes/'
- POST '/podcasts', '/speakers' and '/episodes'
- PATCH '/podcasts/', '/speakers/' and '/episodes/'
- DELETE '/podcasts/', '/speakers/' and '/episodes/'
-
Admin
- All permissions from 'Registered User'
- Can update and delete db entries
-
User:
- Can add and search db entries
-
Everyone is able to proceed GET requests (no login necessary)
-
Admin:
- 'post:data'
- 'patch:data'
- 'delete:data'
-
Registered User:
- 'post:data'
- Deployment URL: https://fsnd-capstone2021.herokuapp.com/
- Local Hosted URL: The app is hosted at default http://127.0.0.1:5000/
To successfully run the curl requests, Authentication tokens are provided in the setup.sh file If not already done, run the setup.sh file to set the environment variables.
The following error types are implemented:
- 400: Bad request
- 401: Unauthorized
- 403: Forbidden
- 404: Resource not found
- 422: Not processable
- 500: Internal server error
Errors arre returned in JSON format:
{
"success": false,
"error": 404,
"message": "resource not found"
}
Authentication Errors (401) provide additional info about the error in the form
{
'code': 'authorization_header_missing',
'description': 'Authorization header is expected.'
}
The following samples are using the deployment URL. For local testin you have to replace it with the Local hosted Url.
-
General:
- Fetches the status of the server
- Request Arguments: None
-
Sample:
curl https://fsnd-capstone2021.herokuapp.com/
-
Returns:
{
"success":true,
"message":"Healthy"
}
-
General:
- Fetches all podcasts and counts speakers and episodes per podcast
- Request Arguments: None
-
Sample:
curl https://fsnd-capstone2021.herokuapp.com/podcasts
-
Returns:
{
"success":true,
"podcasts":[{
"id":10,
"author":"Robert Breedlove",
"name":"The 'What is money?' Show",
"image":"https://production.listennotes.com/podcasts/the-what-is-money-show-robert-breedlove-LZHEONsqo51-4XBAzvpCmj0.1400x1400.jpg",
"podcast_link":"https://open.spotify.com/show/25LPvm8EewBGyfQQ1abIsE",
"speakers":2,
"episodes":2
},
...
]
}
-
General:
- Fetches all speakers and counts episodes per speaker
- Request Arguments: None
-
Sample:
curl https://fsnd-capstone2021.herokuapp.com/speakers
-
Returns:
{
"success":true,
"speakers":[{
"id":10,
"name":"Michael Saylor",
"image":"https://unchainedpodcast.com/wp-content/uploads/2021/01/Michael_Saylor.jpg",
"twitter":"https://twitter.com/saylor",
"website":"https://www.hope.com",
"episodes":2},
...
]
}
-
General:
- Fetches all episodes and gets podcast and speaker name
- Request Arguments: None
-
Sample:
curl https://fsnd-capstone2021.herokuapp.com/episodes
-
Returns:
{
"success":true,
"episodes":[{
"id":13,
"title":"The Saylor Series | Episode 1",
"topics":"Bitcoin"
,"podcast_link":"https://open.spotify.com/episode/5vTqGVN513uTGX1yXcKzJu",
"speaker-id":10,
"speaker_name":"Michael Saylor",
"podcast-id":10,
"podcast_name":"The 'What is money?' Show"
},
...
]
}
-
General:
- Fetches podcast of given podcast_id
-
Sample:
curl https://fsnd-capstone2021.herokuapp.com/podcasts/19
-
Returns:
{
"success":true,
"podcast":{
"id":19,
"author":"Robert Breedlove",
"name":"The 'What is money?' Show",
"image":"https://production.listennotes.com/podcasts/the-what-is-money-show-robert-breedlove-LZHEONsqo51-4XBAzvpCmj0.1400x1400.jpg",
"podcast_link":"https://open.spotify.com/show/25LPvm8EewBGyfQQ1abIsE",
"speakers":2,
"episodes":4
}
}
-
General:
- Fetches speaker of given speaker_id
-
Sample:
curl https://fsnd-capstone2021.herokuapp.com/speakers/7
-
Returns:
{
"success":true,
"speaker":{
"id":7,
"name":"Michael Saylor",
"image_link":"https://unchainedpodcast.com/wp-content/uploads/2021/01/Michael_Saylor.jpg",
"twitter_link":"https://twitter.com/saylor",
"website_link":"https://www.hope.com",
"episodes":2
}
}
-
General:
- Fetches episode of given id
-
Sample:
curl https://fsnd-capstone2021.herokuapp.com/episodes/9
-
Returns:
{
"success":true,
"episode":{
"id":9,
"title":"The Saylor Series | Episode 1",
"topics":"Bitcoin","podcast_link":"https://open.spotify.com/episode/5vTqGVN513uTGX1yXcKzJu",
"speaker_id":7,
"speaker_name":"Michael Saylor",
"podcast_id":19,
"podcast_name":"The 'What is money?' Show"
}
}
- General:
- Post new podcast or search podcast depending on request data
Request data {
'author'='Author name', # String
'name'='Podcast Name', # String
'image'='Image Link', # String
'podcast_link'='podcast_link' # String
}
-
Authentification
- needs Bearer Token with Authorization header 'post:data'
-
Sample:
curl --request POST https://fsnd-capstone2021.herokuapp.com/podcasts \ -d '{"author":"Test","name":"Test","image":"Test","podcast_link":"Test"}' \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ADMIN_TOKEN"
-
Returns:
{
"success":true,
"podcast":{
"id":27,
"author":"Test",
"name":"Test",
"image":"Test",
"podcast_link":"Test",
"speakers":0,
"episodes":0
}
}
-
search in Podcasts for author and name
Request data { 'search'='Robert', # String }
-
Authentification
- needs Bearer Token with Authorization header 'post:data'
-
Sample:
curl --request POST https://fsnd-capstone2021.herokuapp.com/podcasts \ -d '{"search":"Robert"}' \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ADMIN_TOKEN"
-
Returns:
{
"success":true,
"author_search":[
{
"id":19,
"author":"Robert Breedlove",
"name":"The 'What is money?' Show",
"image":"https://production.listennotes.com/podcasts/the-what-is-money-show-robert-breedlove-LZHEONsqo51-4XBAzvpCmj0.1400x1400.jpg",
"podcast_link":"https://open.spotify.com/show/25LPvm8EewBGyfQQ1abIsE",
"speakers":2,
"episodes":2
}
],
"name_search":[],
"results":1
}
- General:
- Post new speaker or search speaker depending on request data
Request data {
'name'='Speaker name', # String
'image_link'='Image link', # String
'twitter_link'='Twitter Link', # String
'website_link'='Podcast link' # String
}
-
Authentification
- needs Bearer Token with Authorization header 'post:data'
-
Sample:
curl --request POST https://fsnd-capstone2021.herokuapp.com/speakers \ -d '{"name":"Test","image_link":"Test","twitter_link":"Test","website_link":"Test"}' \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ADMIN_TOKEN"
-
Returns:
{
"success":true,
"speaker":{
"id":13,
"name":"Test",
"image_link":"Test",
"twitter_link":"Test",
"website_link":"Test",
"episodes":0
}
}
-
search in Speakers for Speaker name
Request data { 'search'='Michael', # String }
-
Authentification
- needs Bearer Token with Authorization header 'post:data'
-
Sample:
curl --request POST https://fsnd-capstone2021.herokuapp.com/speakers \ -d '{"search":"Michael"}' \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ADMIN_TOKEN"
-
Returns:
{
"success":true,
"name_search":[
{
"id":7,"name":"Michael Saylor",
"image_link":"https://unchainedpodcast.com/wp-content/uploads/2021/01/Michael_Saylor.jpg",
"twitter_link":"https://twitter.com/saylor",
"website_link":"https://www.hope.com",
"episodes":1
}
],
"results":1
}
- General:
- Post new episode or search speaker depending on request data
Request data {
'title'='Title', # String
'topics'='Topics', # String
'podcast_link'='Podcast link' # String
'podcast_id'= 23, # Integer, ForeignKey Podcast.id
'speaker_id'= 9 # Integer, ForeignKey Speaker.id
}
-
Authentification
- needs Bearer Token with Authorization header 'post:data'
-
Sample:
curl --request POST https://fsnd-capstone2021.herokuapp.com/episodes \ -d '{"title":"Test","topics":"Test","podcast_link":"Test","podcast_id":23,"speaker_id":9}' \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ADMIN_TOKEN"
-
Returns:
{
"success":true,
"episode":{
"id":17,
"title":"Test",
"topics":"Test",
"podcast_link":"Test",
"speaker_id":9,
"speaker_name":"Raoul Pal",
"podcast_id":23,
"podcast_name":"The Defiant"
}
}
-
search in Episodes for Episode title and topics
Request data { 'search'='Ethereum', # String }
-
Authentification
- needs Bearer Token with Authorization header 'post:data'
-
Sample:
curl --request POST https://fsnd-capstone2021.herokuapp.com/episodes \ -d '{"search":"Ethereum"}' \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ADMIN_TOKEN"
-
Returns:
{
"success":true,
"title_search":[
{
"id":11,
"title":"The biggest, clearest bet of all is Ethereum",
"topics":"Ethereum",
"podcast_link":"https://open.spotify.com/episode/77Q1Ngg76PuexnZDRzSniO",
"speaker_id":9,
"speaker_name":"Raoul Pal",
"podcast_id":23,"podcast_name":"The Defiant"
}
],
"topics_search":[
{
"id":11,
"title":"The biggest, clearest bet of all is Ethereum",
"topics":"Ethereum",
"podcast_link":"https://open.spotify.com/episode/77Q1Ngg76PuexnZDRzSniO",
"speaker_id":9,
"speaker_name":"Raoul Pal",
"podcast_id":23,
"podcast_name":"The Defiant"
}
],
"results":2
}
-
General:
- Update podcast of given podcast id
Request data { 'author'='Author name', # String 'name'='Podcast Name', # String 'image'='Image Link', # String 'podcast_link'='podcast_link' # String }
-
Authentification
- needs Bearer Token with Authorization header 'patch:data'
-
Sample:
curl --request PATCH https://fsnd-capstone2021.herokuapp.com/podcasts/19 \ -d '{"author":"Robert Breedlove","name":"The What is money? Show ","image":"Test_Update","podcast_link":"https://open.spotify.com/show/25LPvm8EewBGyfQQ1abIsE"}' \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ADMIN_TOKEN"
-
Returns:
{
"success":true,
"podcast":{
"id":19,
"author":"Robert Breedlove",
"name":"The What is money? Show ",
"image":"https://production.listennotes.com/podcasts/the-what-is-money-show-robert-breedlove-LZHEONsqo51-4XBAzvpCmj0.1400x1400.jpg",
"podcast_link":"https://open.spotify.com/show/25LPvm8EewBGyfQQ1abIsE",
"speakers":2,
"episodes":2
}
}
-
General:
- Update Speaker of given Speaker id
Request data { 'name'='Speaker name', # String 'image_link'='Image link', # String 'twitter_link'='Twitter Link', # String 'website_link'='Podcast link' # String }
-
Authentification
- needs Bearer Token with Authorization header 'patch:data'
-
Sample:
curl --request PATCH https://fsnd-capstone2021.herokuapp.com/speakers/9 \ -d '{"name":"Raoul Pal","image_link":"Test_Update","twitter_link":"https://twitter.com/RaoulGMI","website_link":"https://www.realvision.com/"}' \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ADMIN_TOKEN"
-
Returns:
{
"success":true,
"speaker":{
"id":9,
"name":"Raoul Pal",
"image_link":"Test_Update",
"twitter_link":"https://twitter.com/RaoulGMI",
"website_link":"https://www.realvision.com/",
"episodes":2
}
}
-
General:
- Update Episode of given Episode id
Request data { 'title'='Speaker name', # String 'topics'='Speaker name', # String 'podcast_link'='Podcast link' # String 'podcast_id'= 23, # Integer, ForeignKey Podcast.id 'speaker_id'= 9 # Integer, ForeignKey Speaker.id }
-
Authentification
- needs Bearer Token with Authorization header 'patch:data'
-
Sample:
curl --request PATCH https://fsnd-capstone2021.herokuapp.com/episodes/13 \ -d '{"title":"What traditional Investors think of Bitcoin","topics":"Bitcoin","podcast_link":"Test_Update","podcast_id":21,"speaker_id":9}' \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ADMIN_TOKEN"
-
Returns:
{
"success":true,
"episode":{
"id":13,
"title":"What traditional Investors think of Bitcoin",
"topics":"Bitcoin",
"podcast_link":"Test_Update",
"speaker_id":9,
"speaker_name":"Raoul Pal",
"podcast_id":21,
"podcast_name":"The What Bitcoin Did Podcast"
}
}
-
General:
- DELETE podcast of given podcast id
- All depended Episodes has to be deleted first.
-
Authentification
- needs Bearer Token with Authorization header 'delete:data'
-
Sample:
curl --request DELETE https://fsnd-capstone2021.herokuapp.com/podcasts/28 \ -H "Authorization: Bearer $ADMIN_TOKEN"
-
Returns:
{
"success":true,
"deleted_id":28
}
-
General:
- Delete Speaker of given Speaker id
- All depended Episodes has to be deleted first.
-
Authentification
- needs Bearer Token with Authorization header 'delete:data'
-
Sample:
curl --request DELETE https://fsnd-capstone2021.herokuapp.com/speakers/15 \ -H "Authorization: Bearer $ADMIN_TOKEN"
-
Returns:
{
"success":true,
"deleted_id":15
}
-
General:
- Delete Episode of given Episode id
-
Authentification
- needs Bearer Token with Authorization header 'delete:data'
-
Sample:
curl --request DELETE https://fsnd-capstone2021.herokuapp.com/episodes/18 \ -H "Authorization: Bearer $ADMIN_TOKEN"
-
Returns:
{
"success":true,
"deleted_id":18
}