The Casting API Backend project is the Capstone Project for the Udacity Full Stack NanoDegree. The project is to practice the skills learned during the Full Stack Web Developer Nanodegree Program. The syllabus of the Udacity FSND is here. The skills used in the Casting API project are described below.
- Flask
- PostgreSQL
- SQLAlchemy
- Flask-CORS(Cross-Origin Resource Sharing)
- Unittest
- Auth0 and RBAC(role-based access control)
- Docker Compose
- Heroku Deployment
- CI/CD by CircleCI
The project URL is
http://duckcastingapi.herokuapp.com/
The usage of the API is described in the Introduction part and the Endpoints part.
The casting agency app helps users find the actor for roles in the movie. The app is used to store movies, actors, and roles associated with the movies. The details of database models and endpoints are described below. Users can send a post request with the movie data including title, company, release date, and description in order to store movie data. Users can send a post request with the actor data including name, age, gender, and location in order to store actor data. Users can send a post request with the role data in order to store role data. Users can also retrieve filtered actors by query string to find talents for hire. Users can also retrieve filtered roles by query string to find proper roles for an actor. Users can update movies and actor's details and also delete a movie and an actor by sending requests to the appropriate endpoints. If an actor is determined for a role, the user can update role data associated with the actor. The API also implements RBAC(role-based access control) by using Auth 0. Only produce executives can access all methods. Casting assistants and Casting directors are given limited access permissions. The details of permissions are described below.
You are able to use Docker Compose. First you should install docker and docker compose. Instructions for installing docker for your platform can be found in the docker docs and the docker compose docs. Then run in the project directory :
docker-compose build
docker-compose up -d
Check http://localhost:5000/ and then you will see 'hello world!'.
You can check the logs via docker-compose logs -f.
If you want to delete docker images and volumes, run
docker stop castingapi_web_1
docker stop castingapi_db_1
docker rm castingapi_web_1
docker rm castingapi_db_1
docker volume rm castingapi_postgres_data
We recommend working within a virtual environment. Instructions for setting up a virtual enviornment for your platform can be found in the python docs
On macOS and Linux:
python3 -m pip install --user virtualenv
python3 -m venv env
source env/bin/activateOn Windows:
py -m pip install --user virtualenv
py -m venv env
.\env\Scripts\activate
Once you have your virtual environment setup and running, install dependencies by running:
pip install -r requirements.txtThis will install all of the required packages we selected within the requirements.txt file.
-
Flask is a lightweight backend microservices framework.
-
SQLAlchemy is the Python SQL toolkit and ORM.
From the folder in terminal run
On macOS and Linux:
export APP_SETTINGS="config.DevelopmentConfig"
export DATABASE_URL="postgresql:///castingapi"
python manage.py db init
python manage.py db migrate
python manage.py db upgradeOn Windows:
set APP_SETTINGS=config.DevelopmentConfig;
set DATABASE_URL=postgresql:///castingapi;
python manage.py db init
python manage.py db migrate
python manage.py db upgradeFrom within the app directory first ensure you are working using your created virtual environment.
To run the server, execute:
source env/bin/activate
export APP_SETTINGS='config.DevelopmentConfig'
export DATABASE_URL='postgresql:///castingapi'
export AUTH0_DOMAIN='dev-hrmvva9b.us.auth0.com'
export ALGORITHMS='RS256'
export API_AUDIENCE='casting'
python3 app.pyor, execute:
source setup.sh
python3 app.pySetting the APP_SETTINGS variable will detect file changes and restart the server automatically.
Setting the DATABASE_URL variable will set SQLALCHEMY_DATABASE_URI in config.py
- There are 3 roles and 12 permissions. They are described below. Test Tokens are in setup.sh.
- The assistant role is granted to casting assistants. It contains
get:actors,get:movies,get:rolespermissions.
- The director role is granted to casting directors. It contains all permissions assistants have and
patch:actors,patch:movies,patch:roles,post:actors,post:roles,delete:actors,delete:roles. - In short, Director has all permissions without
post:moviesanddelete:movies.
- The producer role is granted to executive producers. It contains all permissions directors have and
post:movies,delete:movies.
| Column | Type | Description |
|---|---|---|
| id | int | primary key |
| title | string | not null |
| release_date | date | not null, input string format "%Y-%m-%d" ex)"2020-11-12" |
| company | string | not null |
| description | string |
- General:
- Fetches a dictionary of movies
- Query parameters are
title,min_release_date,max_release_date,release_date,comapny,description,search_term,page,page_size search_termis for title search and it is case insensitive.- Returns : movies, total_movies, success, page
- The return
moviesis paginated with pagesize andtotal_moviesis the number of movies without pagination. (default page is 1 and default page_size is 10)
- Request
curl http://127.0.0.1:5000/movies?min_release_date=2022-01-01
- Response
{
"movies": [
{
"company": "BBC Films",
"description": "Vincent's life is on hold until he finds his wife's killer. Alice, his neighbor, is convinced she can make him happy. She decides to invent a culprit, so that Vincent can find revenge and leave the past behind. But there is no ideal culprit and no perfect crime.",
"id": 1203,
"release_date": "2022-10-21",
"title": "A Crime"
},
{
"company": "ABC Productions",
"description": "The wife and mistress of the sadistic dean of an exclusive prep school conspire to murder him.",
"id": 1204,
"release_date": "2022-05-05",
"title": "Diabolique"
},
{
"company": "ABC Productions",
"description": "A story of slavery, set in the southern U.S. in the 1930s.",
"id": 1206,
"release_date": "2022-07-15",
"title": "Manderlay"
},
{
"company": "Jadran Film",
"description": "The life of Jesus Christ, his journey through life as he faces the struggles all humans do, and his final temptation on the cross.",
"id": 1208,
"release_date": "2023-10-01",
"title": "The Last Temptation of Christ"
},
{
"company": "Jadran Film",
"description": "Three young soldiers who participated in a military operation that went wrong, and where one of their comrades had been killed before their eyes, are placed in a luxury hotel to prevent a scandal. Despite the help of a young military psychiatrist, the young trio denies any trauma suffered, but they seem to hold a very different secret truth.",
"id": 1209,
"release_date": "2023-01-01",
"title": "Palace Beach Hotel"
},
{
"company": "Met film",
"description": "When violent conflict breaks out between greedy railroaders and a tribe of Mescalero Apaches, only two men, destined to be blood brothers, can prevent all-out war: chief's son Winnetou and German engineer Old Shatterhand.",
"id": 1214,
"release_date": "2022-02-02",
"title": "Winnetou"
},
{
"company": "Met film",
"description": "Daredevil mountain climbers on their attempt to break yet another speed climbing record.",
"id": 1215,
"release_date": "2023-09-09",
"title": "Am Limit"
},
{
"company": "Netflix",
"description": "Anna life stroy",
"id": 1216,
"release_date": "2024-06-06",
"title": "Mein Gott, Anna!"
},
{
"company": "Netflix",
"description": "In a trendy restaurant, public prosecutor Manuel Bacher and his colleagues toast his promotion. On the way home, he meets a quarrelling junkie couple in a park. Manuel wants to help the woman, who is beaten and strangled, and intervenes.",
"id": 1217,
"release_date": "2024-07-17",
"title": "Momentversagen"
},
{
"company": "Matrix film",
"description": "Just Fake one",
"id": 1218,
"release_date": "2024-08-28",
"title": "Fake Movie"
}
],
"success": true,
"page": 1,
"total_movies": 11
}
- General:
- Fetches a dictionary of roles of a movie
- Returns : roles, movie_id, success
- Request
curl http://127.0.0.1:5000/movies/1/roles
- Response
{
"movie_id": 1,
"roles": [
{
"actor_id": null,
"description": null,
"gender": "male",
"id": 1,
"max_age": 30,
"min_age": 25,
"name": "kimmich"
},
{
"actor_id": null,
"description": null,
"gender": "male",
"id": 2,
"max_age": 35,
"min_age": 30,
"name": "revan"
}
],
"success": true
}
- We can create movie data by the api.
- Bearer Token having
post:moviespermission is needed. - Create:
- Create a movie by sending
title, release_date, company, description. The argumentdescriptioncan be null. - Request Arguments : title, release_date, company, description
- Returns : success, id
- Create a movie by sending
- Request
curl \
-X POST http://127.0.0.1:5000/movies \
-d '{"title": "Test movie", "release_date": "2020-11-12", "company": "Test company", "description" : "fun movie"}' \
-H "Content-Type:application/json" \
-H "Authorization: Bearer {TOKEN}"
- Response
{
"success": true,
"id": {movie_id}
}
- We can modify movie data by the api.
- Bearer Token having
patch:moviespermission is needed. - Modify:
- Modify a movie by sending
title, release_date, company, description. - Request Arguments : title, release_date, company, description
- Returns : success, id
- Modify a movie by sending
- Request
curl \
-X PATCH http://127.0.0.1:5000/movies/{movie_id} \
-d '{"title": "changed test movie"}' \
-H "Content-Type:application/json" \
-H "Authorization: Bearer {TOKEN}"
- Response
{
"success": true,
"id": {movie_id}
}
- We can delete movie data by the api.
- Bearer Token having
delete:moviespermission is needed. - Delete:
- Request Arguments : None
- Returns : success, id
- If the given ID is not valid, returns 422 error.
- Request
curl \
-X DELETE http://127.0.0.1:5000/movies/{movie_id} \
-H "Authorization: Bearer {TOKEN}"
- Response
{
"success": true,
"id": {movie_id}
}
| Column | Type | Description |
|---|---|---|
| id | int | primary key |
| movie_id | int | not null, foreign key |
| actor_id | int | foreign key |
| name | string | not null |
| gender | enum | not null, one of ['male','female'] |
| min_age | int | not null |
| max_age | int | not null |
| description | string |
- General:
- Fetches a dictionary of roles
- Query parameters are
movie_id,actor_id,name,gender,min_age,max_age,description,page,page_size - Returns : roles, total_roles, success, page
- The return
rolesis paginated with pagesize andtotal_rolesis the number of roles without pagination. (default page is 1 and default page_size is 10)
- Request
curl http://127.0.0.1:5000/roles?gender=male
- Response
{
"roles": [
{
"description": null,
"gender": "male",
"id": 1,
"max_age": 30,
"min_age": 25,
"movie_id": 1,
"actor_id": null,
"name": "kimmich"
},
{
"description": null,
"gender": "male",
"id": 2,
"max_age": 35,
"min_age": 30,
"movie_id": 1,
"actor_id": null,
"name": "revan"
},
{
"description": null,
"gender": "male",
"id": 3,
"max_age": 25,
"min_age": 15,
"movie_id": 2,
"actor_id": null,
"name": "jack"
},
{
"description": null,
"gender": "male",
"id": 7,
"max_age": 60,
"min_age": 50,
"movie_id": 4,
"actor_id": null,
"name": "park"
},
{
"description": null,
"gender": "male",
"id": 8,
"max_age": 80,
"min_age": 70,
"movie_id": 4,
"actor_id": null,
"name": "park"
}
],
"page": 1,
"success": true,
"total_roles": 5
}
- We can create role data by the api.
- Bearer Token having
post:rolespermission is needed. - Create:
- Create a role by sending
movie_id, actor_id, name, gender, min_age, max_age, description. The argumentactor_id, descriptioncan be null. - Request Arguments : movie_id, actor_id, name, gender, min_age, max_age, description
- Returns : success, id
- Create a role by sending
- Request
curl \
-X POST http://127.0.0.1:5000/roles \
-d '{"movie_id": 1, "name": "jack", "gender": "male", "min_age": 30, "max_age": 35, "description": "jack is strong man"}' \
-H "Content-Type:application/json" \
-H "Authorization: Bearer {TOKEN}"
- Response
{
"success": true,
"id": {role_id}
}
- We can modify role data by the api.
- Bearer Token having
patch:rolespermission is needed. - Modify:
- Modify a role by sending
movie_id, actor_id, name, gender, min_age, max_age, description. - Request Arguments : movie_id, actor_id, name, gender, min_age, max_age, description
- Returns : success, id
- Modify a role by sending
- Request
curl \
-X PATCH http://127.0.0.1:5000/roles/{role_id} \
-d '{"actor_id": 1}' \
-H "Content-Type:application/json" \
-H "Authorization: Bearer {TOKEN}"
- Response
{
"success": true,
"id": {role_id}
}
- We can delete role data by the api.
- Bearer Token having
delete:rolespermission is needed. - Delete:
- Request Arguments : None
- Returns : success, id
- If the given ID is not valid, returns 422 error.
- Request
curl \
-X DELETE http://127.0.0.1:5000/roles/{role_id} \
-H "Authorization: Bearer {TOKEN}"
- Response
{
"success": true,
"id": {role_id}
}
| Column | Type | Description |
|---|---|---|
| id | int | primary key |
| name | string | not null |
| age | int | not null |
| gender | enum | not null, one of GENDER_TYPE |
| location | string | not null |
| passport | boolean | default false |
| driver_license | boolean | default false |
| ethnicity | enum | one of ETHNICITY_TYPE |
| hair_color | enum | one of HAIR_COLOR_TYPE |
| eye_color | enum | one of EYE_COLOR_TYPE |
| body_type | enum | one of BODY_TYPE |
| height | int | Centimetre (cm) |
| description | string | |
| image_link | string | URL |
| phone | string | ex)'+1-202-555-0169' |
| string | ex)'[email protected]' |
GENDER_TYPE = [ 'male', 'female' ]
ETHNICITY_TYPE = [ 'asian', 'black', 'latino', 'middle eastern', 'south asian', 'southeast asian', 'white' ]
HAIR_COLOR_TYPE = [ 'black', 'brown', 'blond', 'auburn', 'chestnut', 'red', 'gray', 'white', 'bald' ]
EYE_COLOR_TYPE = [ 'amber', 'blue', 'brown', 'gray', 'green', 'hazel', 'red', 'violet' ]
BODY_TYPE = [ 'average', 'slim', 'athletic', 'muscular', 'curvy', 'heavyset', 'plus-sized' ]
- General:
- Fetches a dictionary of actors
- Query parameters are
name,age,min_age,max_age,gender,location,passport,driver_license,ethnicity,hair_color,eye_color,body_type,height,min_height,max_height,description,image_link,phone,email,search_term,page,page_size. - By
search_termquery parameter, we are able to search name. - Returns : actors, success, page, total_actors
- The return
actorsis paginated with pagesize andtotal_actorsis the number of actors without pagination. (default page is 1 and default page_size is 10)
- Request
curl http://127.0.0.1:5000/actors?ethnicity=asian&gender=female
- Response
{
"actors": [
{
"age": 40,
"body_type": "slim",
"description": null,
"driver_license": false,
"email": "[email protected]",
"ethnicity": "asian",
"eye_color": "gray",
"gender": "female",
"hair_color": "gray",
"height": 189,
"id": 9,
"image_link": null,
"location": "KA",
"name": "Meret Becker",
"passport": false,
"phone": "+1-202-555-0169"
}
],
"success": true,
"page": 1,
"total_actors": 1
}
- General:
- Fetches a dictionary of roles of an actor
- Returns : roles, actor_id, success
- Request
curl http://127.0.0.1:5000/actors/1/roles
- Response
{
"actor_id": 1,
"roles": [
{
"description": null,
"gender": "male",
"id": 1,
"max_age": 30,
"min_age": 25,
"movie_id": 1,
"name": "kimmich"
},
{
"description": null,
"gender": "male",
"id": 2,
"max_age": 35,
"min_age": 30,
"movie_id": 2,
"name": "revan"
}
],
"success": true
}
- We can create actor data by the api.
- Bearer Token having
post:actorspermission is needed. - Create:
- Create an actor by sending
name,age,gender,location,passport,driver_license,ethnicity,hair_color,eye_color,body_type,height,description,image_link,phone,email. The argumentsname,age,gender,locationcan not be null. - Request Arguments : name, age, gender, location, passport, driver_license, ethnicity, hair_color, eye_color, body_type, height, description, image_link, phone, email, description
- Returns : success, id
- Create an actor by sending
- Request
curl \
-X POST http://127.0.0.1:5000/actors \
-d '{"name": "Sofia", "age": 40, "gender": "female", "location": "LA", "passport": true, "driver_license": true, "ethnicity": "white", "hair_color": "brown", "eye_color": "brown", "body_type": "slim", "height": 165, "description": "this is fake actor", "phone": "+1-202-555-0169", "email": "[email protected]"}' \
-H "Content-Type:application/json" \
-H "Authorization: Bearer {TOKEN}"
- Response
{
"success": true,
"id": {actor_id}
}
- We can modify actor data by the api.
- Bearer Token having
patch:actorspermission is needed. - Modify:
- Modify an actor by sending
name,age,gender,location,passport,driver_license,ethnicity,hair_color,eye_color,body_type,height,description,image_link,phone,email. - Request Arguments : name, age, gender, location, passport, driver_license, ethnicity, hair_color, eye_color, body_type, height, description, image_link, phone, email, description
- Returns : success, id
- Modify an actor by sending
- Request
curl \
-X PATCH http://127.0.0.1:5000/actors/{actor_id} \
-d '{"passport": false}' \
-H "Content-Type:application/json" \
-H "Authorization: Bearer {TOKEN}"
- Response
{
"success": true,
"id": {role_id}
}
- We can delete actor data by the api.
- Bearer Token having
delete:actorspermission is needed. - Delete:
- Request Arguments : None
- Returns : success, id
- If the given ID is not valid, returns 422 error.
- Request
curl \
-X DELETE http://127.0.0.1:5000/actors/{actor_id} \
-H "Authorization: Bearer {TOKEN}"
- Response
{
"success": true,
"id": {role_id}
}
- List of errors
| Error Code | Message |
|---|---|
| 400 | Bad request |
| 401 | Unauthorized error |
| 404 | Not found |
| 422 | Unprocessable |
| 500 | Internal server error |
- Response
{
"success": false,
"error": 500,
"message": "Internal server error"
}
To run the tests, run
source setup.sh
export DATABASE_URL=postgresql:///castingapi_test
dropdb castingapi_test
createdb castingapi_test
python manage.py db upgrade
python3 test_case.py
python3 test_app_by_assistant_token.py
python3 test_app_by_director_token.py
python3 test_app_by_producer_token.py
You can find tokens in setup.sh.
- http://duckcastingapi.herokuapp.com/
- Test Tokens are in
setup.sh