def setUp(self): """Define test variables and initialize app.""" self.app = app.create_app() self.client = self.app.test_client self.database_name = "castapp_test" self.database_path = settings.LOCAL_DATABASE_PATH setup_db(self.app, self.database_path) # binds the app to the current context with self.app.app_context(): self.db = SQLAlchemy() self.db.init_app(self.app) # create all tables self.db.create_all()
def setUp(self): """Define test variables and initialize app.""" self.app = app.create_app() self.client = self.app.test_client self.assistant_accesstoken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IkJpc1RBYmdXS1YtXzEyeXpkNE1PVSJ9.eyJodHRwczovL2Nhc3QtYXBwLmhlcm9rdWFwcC5jb20vcm9sZXMiOlsiYXNzaXN0YW50Il0sImlzcyI6Imh0dHBzOi8vYXV0dW1uLXZvaWNlLTA2NjYudXMuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDYwYjEyYzFhZTIwMGNiMDA3MDk3NmI1OSIsImF1ZCI6WyJodHRwczovL2Nhc3QtYXBwLmhlcm9rdWFwcC5jb20vYXBpIiwiaHR0cHM6Ly9hdXR1bW4tdm9pY2UtMDY2Ni51cy5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNjI1OTUyMTE2LCJleHAiOjE2MjYwMzg1MTYsImF6cCI6ImY3WkxVMkRtV2VSY0x1aWt5RUtqcWswODkzS0EyTWJqIiwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCIsInBlcm1pc3Npb25zIjpbImdldDphY3Rvci1pZCIsImdldDptb3ZpZS1pZCIsInBhdGNoOmFjdG9yLXB1Ymxpc2giLCJwYXRjaDphY3RvcnMiLCJwYXRjaDphY3Rvci11bnB1Ymxpc2giLCJwYXRjaDptb3ZpZS1wdWJsaXNoIiwicGF0Y2g6bW92aWVzIiwicGF0Y2g6bW92aWUtdW5wdWJsaXNoIl19.Z_KnnFNMwV90_5XLRHCh3bu3jp-FDRlLRIi_e1a6oUhhwUKg5DryXgrPOh6OboawMH7nJEQBpoi88IOtqm9V4TODnvfk2Ml6oaKqgVH_NjRR0qiUhyhc8q4l0aEhjShkc5wtX7PgcKS0nO3zZSdc3BrU6RKklDoh5Q6ieI4St7io502LBF8wT_rybHQZyC40bsdMTlzSbqpHHwVwV26v8BKKy_-ku4hBQF-6opSD_42UsaK1G8p1vOF_BiehVH-pdUa5OrnxZ0wmAf2OmAu4Z71tt-iqGXruBGIaf0R9Z0tHiJ7tpf7s9J9jcr-hJcv3iToJyK8ao3lBcf3OAW_eAA" self.director_accesstoken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IkJpc1RBYmdXS1YtXzEyeXpkNE1PVSJ9.eyJodHRwczovL2Nhc3QtYXBwLmhlcm9rdWFwcC5jb20vcm9sZXMiOlsiZGlyZWN0b3IiXSwiaXNzIjoiaHR0cHM6Ly9hdXR1bW4tdm9pY2UtMDY2Ni51cy5hdXRoMC5jb20vIiwic3ViIjoiYXV0aDB8NjBiMTI2NDg5MjNmZTIwMDZmMDgwOWZlIiwiYXVkIjpbImh0dHBzOi8vY2FzdC1hcHAuaGVyb2t1YXBwLmNvbS9hcGkiLCJodHRwczovL2F1dHVtbi12b2ljZS0wNjY2LnVzLmF1dGgwLmNvbS91c2VyaW5mbyJdLCJpYXQiOjE2MjU5OTg4NDMsImV4cCI6MTYyNjA4NTI0MywiYXpwIjoiZjdaTFUyRG1XZVJjTHVpa3lFS2pxazA4OTNLQTJNYmoiLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIiwicGVybWlzc2lvbnMiOlsiZGVsZXRlOmFjdG9ycyIsImRlbGV0ZTptb3ZpZXMiLCJnZXQ6YWN0b3ItaWQiLCJnZXQ6bW92aWUtaWQiLCJwb3N0OmFjdG9ycyIsInBvc3Q6bW92aWVzIl19.r9wFhvCuvnKwVvz4tWiPyBiQItERyfGLGVBZwwSgIDk6LbWV3jc2_h_7tjpSL3ANED_vJpWyzL2OzVyjFYok1r9HDfWI-nF_aTByn4hDKw3unQ9qYFiu7uj7sQhIlN8l4aJDzj29NAWbZe3Hq1ktmr1cK38xwQEkxFNBKyaKzQiXEAFHtLh4s8zd5WU38x2jjCU1rA4ZN23RzPGrYGHhcFFtx5LwzGgdw717xKZ7v_c-yRDd8J8miKslOE7AJgCQbfZDMMFomAE8l4bGOB_LKbZmcBAPmORbfFViX5PzTX4bLBhyxe-maAg7leKHzI1Y3vFGxukEh-mWueegripVsg" self.database_name = "castapp_test" self.database_path = settings.LOCAL_DATABASE_PATH setup_db(self.app, self.database_path) # binds the app to the current context with self.app.app_context(): self.db = SQLAlchemy() self.db.init_app(self.app) # create all tables self.db.create_all()
def create_app(): app = Flask(__name__) setup_db(app) CORS(app, resource={r"*/api/*": {"origins": "*"}}) @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type, Authorization, true') response.headers.add('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE, OPTIONS') response.headers.add('Access-Control-Allow-Credentials', 'true') return response default_dish_image_link = "https://unsplash.com/photos/1Rm9GLHV0UA" #default_restaurant_image_link = "https://unsplash.com/photos/26T6EAsQCiA" def get_formatted_dish(dish_id): formatted_dish = {} dish = Dish.query.get(dish_id) if dish is None: abort(404) try: dish_category = Category.query.get(dish.category_id) dish_restaurant = Restaurant.query.get(dish.restaurant_id) formatted_dish = { 'id': dish.id, 'name': dish.name, 'restaurant_id': dish.restaurant_id, 'category_id': dish.category_id, 'rating': dish.rating, 'price': float(dish.price), 'image_link': dish.image_link or default_dish_image_link, 'restaurant_name': dish_restaurant.name, 'category': dish_category.category } return formatted_dish except Exception: abort(422) @app.route('/') def hello_what_to_eat(): return 'Hello, What To Eat!' @app.route('/dishes', methods=['GET']) def get_dishes(): all_dishes = Dish.query.order_by('id').all() if all_dishes is None: abort(404) try: formatted_all_dishes = [dish.format() for dish in all_dishes] return jsonify({"success": True, "dishes": formatted_all_dishes}) except Exception: abort(404) @app.route('/dishes', methods=['POST']) @requires_auth("post:dishes") def create_dish(token): body = request.get_json() if body is None: abort(400) try: new_name = body.get('name') new_restaurant_id = body.get('restaurant_id') new_category_id = body.get('category_id') new_rating = body.get('rating') new_price = body.get('price') new_image_link = body.get('image_link') new_dish = Dish(name=new_name, restaurant_id=new_restaurant_id, category_id=new_category_id, rating=new_rating, price=new_price, image_link=new_image_link) new_dish.insert() all_dishes = Dish.query.order_by('id').all() formatted_all_dishes = [dish.format() for dish in all_dishes] return jsonify({ "success": True, "new_dish": new_dish.format(), "dishes": formatted_all_dishes }) except Exception: abort(422) @app.route('/dishes/<int:dish_id>', methods=['GET']) def get_dish_item(dish_id): formatted_dish = get_formatted_dish(dish_id) if formatted_dish is None: abort(404) return jsonify({"success": True, "dish": formatted_dish}) @app.route('/dishes/<int:dish_id>', methods=['PATCH']) @requires_auth("patch:dishes") def update_dish(token, dish_id): body = request.get_json() if body is None: abort(400) try: new_name = body.get('name') new_restaurant_id = body.get('restaurant_id') new_category_id = body.get('category_id') new_rating = body.get('rating') new_price = body.get('price') new_image_link = body.get('image_link') dish = Dish.query.get(dish_id) if dish is None: abort(404) if new_name is not None: dish.name = new_name if new_restaurant_id is not None: dish.restaurant_id = new_restaurant_id if new_category_id is not None: dish.category_id = new_category_id if new_rating is not None: dish.rating = new_rating if new_price is not None: dish.price = new_price if new_image_link is not None: dish.image_link = new_image_link dish.update() new_dish = get_formatted_dish(dish_id) return jsonify({"success": True, "dish": new_dish}) except Exception: abort(404) @app.route('/dishes/<int:dish_id>', methods=['DELETE']) @requires_auth("delete:dishes") def delete_dish(token, dish_id): delete_dish_id = dish_id dish = Dish.query.get(dish_id) if dish is None: abort(404) try: dish.delete() return jsonify({"success": True, "deleted dish": delete_dish_id}) except Exception: abort(422) @app.route('/dishes/search', methods=['POST']) def search_dish(): dishes = [] body = request.get_json() if body is None: abort(400) try: search = body.get('search_term') if search is None: abort(404) search_results = Dish.query.filter( Dish.name.ilike("%" + search + "%")) for result in search_results: dish = get_formatted_dish(result.id) dishes.append(dish) return jsonify({"success": True, "dishes": dishes}) except Exception: abort(422) @app.route('/categories/<int:category_id>/dishes', methods=['GET']) def dishes_by_categories(category_id): dishes_by_categories = [] dishes = Dish.query.filter_by(category_id=category_id).all() if dishes is None: abort(404) try: for d in dishes: dish = get_formatted_dish(d.id) dishes_by_categories.append(dish) return jsonify({ "success": True, "dishes_by_categories": dishes_by_categories }) except Exception: abort(422) @app.route('/dishes/try', methods=['POST']) def try_new_dishes(): dishes_to_try = [] body = request.get_json() if body is None: abort(400) previous_dishes = body.get('previous_dishes') if len(previous_dishes) == 0: abort(400) new_category = body.get('new_category') if len(new_category) == 0: abort(400) try: if new_category == 0: dishes = Dish.query.filter( Dish.id.notin_(previous_dishes), Dish.rating >= 3 ).order_by(func.random()).limit( 1 ) # if not specify a category, then recommend a dish that is not from previous dishes and ratings is greater than or equal to 3. for d in dishes: dish = get_formatted_dish(d.id) dishes_to_try.append(dish) else: dishes = Dish.query.filter( Dish.category_id == new_category, Dish.id.notin_(previous_dishes), Dish.rating >= 3 ).order_by(func.random()).limit( 1 ) # If specify a category, then recommend a dish that is from this category, not from previous dishes and ratings is greater than or equal to 3. for d in dishes: dish = get_formatted_dish(d.id) dishes_to_try.append(dish) return jsonify({"success": True, "dish to try": dishes_to_try[0]}) except Exception: abort(422) @app.route('/categories', methods=['GET']) def get_categories(): all_categories = Category.query.order_by('id').all() if all_categories is None: abort(404) try: formatted_all_categories = [ category.format() for category in all_categories ] return jsonify({ "success": True, "categories": formatted_all_categories }) except Exception: abort(422) @app.route('/categories', methods=['POST']) @requires_auth("post:categories") def create_category(token): body = request.get_json() if body is None: abort(400) new_category = body.get('category') if new_category is None: abort(404) try: category = Category(category=new_category) category.insert() categories = Category.query.order_by('id').all() if categories is None: abort(404) formatted_all_categories = [ category.format() for category in categories ] return jsonify({ "success": True, "categories": formatted_all_categories }) except Exception: abort(422) @app.route('/categories/<int:category_id>', methods=['PATCH']) @requires_auth("patch:categories") def update_category(token, category_id): body = request.get_json() if body is None: abort(400) new_category = body.get('category') if new_category is None: abort(404) category_item = Category.query.get(category_id) if category_item is None: abort(404) try: category_item.category = new_category category_item.update() return jsonify({ "success": True, "updated category": category_item.format() }) except Exception: abort(422) @app.route('/categories/<int:category_id>', methods=['GET']) def get_category_by_id(category_id): category = Category.query.get(category_id) if category is None: abort(404) return jsonify({"success": True, "category": category.format()}) @app.route('/categories/<int:category_id>', methods=['DELETE']) @requires_auth("delete:categories") def delete_category(token, category_id): category = Category.query.get(category_id) if category is None: abort(404) delete_id = category_id try: category.delete() categories = Category.query.order_by('id').all() formatted_all_categories = [ category.format() for category in categories ] return jsonify({ "success": True, "delete_id": delete_id, "categories": formatted_all_categories }) except Exception: abort(422) @app.route('/restaurants', methods=['GET']) def get_restaurants(): all_restaurants = Restaurant.query.order_by('id').all() if all_restaurants is None: abort(404) try: formatted_all_restaurants = [ restaurant.format() for restaurant in all_restaurants ] return jsonify({ "success": True, "restaurants": formatted_all_restaurants }) except Exception: abort(422) @app.route('/restaurants/<int:restaurant_id>/dishes', methods=['GET']) def dishes_by_restaurants(restaurant_id): dishes_by_restaurants = [] dishes = Dish.query.filter_by(restaurant_id=restaurant_id).all() if dishes is None: abort(404) try: for d in dishes: dish = get_formatted_dish(d.id) dishes_by_restaurants.append(dish) return jsonify({ "success": True, "dishes_by_restaurants": dishes_by_restaurants }) except Exception: abort(422) @app.route('/restaurants', methods=['POST']) @requires_auth("post:restaurants") def create_restaurant(token): body = request.get_json() if body is None: abort(400) new_name = body.get('name') new_city = body.get('city') new_addr = body.get('address') new_state = body.get('state') new_r_image_link = body.get('r_image_link') new_website = body.get('website') # new_r_image_link, new_website can be null if any(arg is None for arg in [ new_name, new_city, new_addr, new_state, new_r_image_link, new_website ]) or '' in [new_name, new_city, new_addr, new_state]: abort(400) try: new_restaurant = Restaurant(name=new_name, city=new_city, state=new_state, address=new_addr, r_image_link=new_r_image_link, website=new_website) new_restaurant.insert() return jsonify({ "success": True, "new_restaurant": new_restaurant.format() }) except Exception: abort(422) @app.route('/restaurants/<int:restaurant_id>', methods=['PATCH']) @requires_auth("patch:restaurants") def update_restaurant(token, restaurant_id): body = request.get_json() if body is None: abort(400) new_name = body.get('name') new_city = body.get('city') new_addr = body.get('address') new_state = body.get('state') new_r_image_link = body.get('r_image_link') new_website = body.get('website') restaurant = Restaurant.query.get(restaurant_id) if restaurant is None: abort(404) try: if new_name is not None and not '': restaurant.name = new_name if new_city is not None: restaurant.city = new_city if new_state is not None: restaurant.state = new_state if new_addr is not None: restaurant.address = new_addr if new_r_image_link is not None: restaurant.r_image_link = new_r_image_link if new_website is not None: restaurant.website = new_website restaurant.update() return jsonify({ "success": True, "updated_restaurant": restaurant.format() }) except Exception as e: abort(404) @app.route('/restaurants/<int:restaurant_id>', methods=['GET']) def get_restaurant_by_id(restaurant_id): restaurant = Restaurant.query.get(restaurant_id) if restaurant is None: abort(404) return jsonify({ "success": True, "restaurant_by_id": restaurant.format() }) @app.route('/restaurants/<int:restaurant_id>', methods=['DELETE']) @requires_auth("delete:restaurants") def delete_restaurant_by_id(token, restaurant_id): restaurant = Restaurant.query.get(restaurant_id) if restaurant is None: abort(404) try: deleted_id = restaurant_id restaurant.delete() all_restaurants = Restaurant.query.order_by('id').all() if all_restaurants is None: abort(404) formatted_all_restaurants = [ restaurant.format() for restaurant in all_restaurants ] return jsonify({ "success": True, "deleted_restaurant_id": deleted_id, "formatted_all_restaurants": formatted_all_restaurants }) except Exception: abort(422) # Error Handling @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "unprocessable" }), 422 @app.errorhandler(404) def resource_not_found(error): return jsonify({ "success": False, "error": 404, "message": "Resource not found" }), 404 @app.errorhandler(500) def internal_server_error(error): return jsonify({ "success": False, "error": 500, "message": "Internal server error" }), 500 @app.errorhandler(405) def method_not_allowed(error): return jsonify({ "success": False, "error": 405, "message": "Method not allowed" }), 405 @app.errorhandler(401) def unauthorized_error(error): return jsonify({ "success": False, "error": 401, "message": "Unauthorized error" }), 401 @app.errorhandler(400) def unauthorized_error(error): return jsonify({ "success": False, "error": 400, "message": "Bad request" }), 400 return app app = create_app() if __name__ == '__main__': #port = int(os.environ.get('PORT', 5000)) app.run()
def create_app(test_config=None): app = Flask(__name__) moment = Moment(app) setup_db(app) db = SQLAlchemy(app) migrate = Migrate(app, db) # Create and configure the app CORS(app) # Added CORS and after_request decorator to set Access-Control-Allow CORS(app, resources={r"/*": { "origins": "*" }}) # Serve the Angular app - routes for browser refresh issues @app.route('/') def index(): return render_template('index.html') @app.route('/movies') def showMovies(): return render_template('index.html') @app.route('/actors') def showActors(): return render_template('index.html') @app.route('/movies/<path:u_path>') def showOneMovie(u_path): return render_template('index.html') @app.route('/actors/<path:u_path>') def showOneActor(u_path): return render_template('index.html') @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type, Authorization, true') response.headers.add('Access-Control-Allow-Methods', 'GET, PATCH, PUT, POST, DELETE, OPTIONS') return response # Test cors is working @app.route('/test_cors') @cross_origin() def get_messages(): return 'CORS is working...' # Uncomment if want to drop and create database # db_drop_and_create_all() # ''' # MOVIE APIs # # ''' # ''' # GET: /api/movies # Authorized: Public user access # Endpoint: Gets all movies data representation # Returns: Status code 200 for successful get # where movies is the list of movies # or appropriate status code indicating reason for failure # ''' @app.route('/api/movies', methods=['GET']) def getAllMovies(): movies_all = Movie.query.order_by(Movie.title).all() if len(movies_all) == 0: abort(404) try: results = [] for i, movieObj in enumerate(movies_all): results.append(json.loads(movieObj.to_json())) return jsonify(results), 200 except Exception as e: print('\n'+'Error getting all movie records: ', e) abort(404) # ''' # GET: /api/movies/<int:id> # Authorized: Director or Assistant access # Endpoint: GET2 a specific movie # Returns: Status code 200 and json {"success": True, "movie": data} # where movie is a single movie data # or appropriate status code indicating reason for failure # ''' @app.route('/api/movies/<int:id>', methods=['GET']) @requires_role(['director', 'assistant']) def getMovie(id): if id is None: abort(404) movie_query = Movie.query.filter(Movie.id == id).one_or_none() if movie_query is None: abort(404) data = { "id": movie_query.id, "title": movie_query.title, "release_date": str(movie_query.release_date), "movie_img": movie_query.movie_img, "movie_publish": movie_query.movie_publish } try: return jsonify({ 'success': True, 'movie': data }), 200 except Exception as e: print('\n'+'Error getting movie detail record: ', e) abort(404) # ''' # POST: /api/movies # Authorized: Director access # Endpoint: Create a new movie # Returns: Status code 200 and json {"success": True} # where movie is a single new movie # or appropriate status code indicating reason for failure # ''' @app.route('/api/movies', methods=['POST']) @requires_role('director') def createMovie(): try: data = { 'id': None, 'title': request.get_json()['title'], 'release_date': request.get_json()['release_date'], 'movie_img': request.get_json()['movie_img'], 'movie_publish': False } if not ('title' in data and 'release_date' in data and 'movie_img' in data and 'movie_publish' in data): abort(422) movie = Movie(**data) movie.insert() return jsonify({ 'success': True }), 200 except Exception as e: print('\n'+'Error creating movie record: ', e) abort(422) # ''' # PATCH: /api/movies/<int:id> # Authorized: Assistant access # Endpoint: Update movie data fields # Returns: Status code 200 and json {"success": True} # where movie published is a single movie # or appropriate status code indicating reason for failure # ''' @app.route('/api/movies/<int:id>', methods=['PATCH']) @requires_role('assistant') def updateMovie(id): if id is None: abort(404) data = Movie.query.filter(Movie.id == id).one_or_none() if data is None: abort(404) request_json = request.get_json() data.title = request.json.get('title') data.release_date = request.json.get('release_date') data.movie_img = request.json.get('movie_img') try: data.update() return jsonify({ "success": True }), 200 except Exception as e: print('\n'+'Error updating movie record: ', e) abort(422) # ''' # PATCH: /api/movies/<int:id>/publish # Authorized: Assistant access # Endpoint: Publish movie data fields - boolen # Returns: Status code 200 and json {"success": True} # where movie updated is a single movie # or appropriate status code indicating reason for failure # ''' @app.route('/api/movies/<int:id>/publish', methods=['PATCH']) @requires_role('assistant') def publishMovie(id): if id is None: abort(404) data = Movie.query.filter(Movie.id == id).one_or_none() if data is None: abort(404) data.movie_publish = True try: data.update() return jsonify({ "success": True }), 200 except Exception as e: print('\n'+'Error publishing movie record: ', e) abort(422) # ''' # PATCH: /api/movies/<int:id>/publish # Authorized: Assistant access # Endpoint: Unpublish movie data fields - boolen # Returns: Status code 200 and json {"success": True} # where movie updated is a single movie # or appropriate status code indicating reason for failure # ''' @app.route('/api/movies/<int:id>/unpublish', methods=['PATCH']) @requires_role('assistant') def unpublishMovie(id): if id is None: abort(404) data = Movie.query.filter(Movie.id == id).one_or_none() if data is None: abort(404) data.movie_publish = False try: data.update() return jsonify({ "success": True }), 200 except Exception as e: print('\n'+'Error unpublishing movie record: ', e) abort(422) # ''' # DELETE: /api/movies/<int:id> # Authorized: Director access # Endpoint: Deletes specific movie data fields # Returns: Status code 200 and json {"success": True} # where movie deleted is a single movie # or appropriate status code indicating reason for failure # ''' @app.route('/api/movies/<int:id>', methods=['DELETE']) @requires_role('director') def deleteMovie(id): try: movie = Movie.query.filter(Movie.id == id).one_or_none() if movie is None: abort(404) movie.delete() return jsonify({ 'success': True }), 200 except Exception as e: print('\n'+'Error deleting movie record: ', e) abort(404) # ''' # SEARCH: /api/movies/search # Authorized: Public user access # Endpoint: Provide a like search for movie title # Returns: Status code 200 if search is successful # where movie(s) are searched for by title # or appropriate status code indicating reason for failure # ''' @app.route('/api/movies/search', methods=['GET']) def findMovieByTitle(): search_title = request.args.get('title') data = Movie.query.filter( Movie.title.ilike(f'%{search_title}%')).all() results = [] try: for i, movieObj in enumerate(data): results.append(json.loads(movieObj.to_json())) return jsonify(results), 200 except Exception as e: print('\n'+'Error searching by movie titles: ', e) abort(404) # ''' # ACTOR ENDPOINTS # # ''' # ''' # GET: /api/actors # Authorized: Public user access # Endpoint: Gets all actors data representation # Returns: Status code 200 for successful get # where actors is the list of actors # or appropriate status code indicating reason for failure # ''' @app.route('/api/actors', methods=['GET']) def getAllActors(): actors_all = Actor.query.order_by(Actor.first_name).all() if len(actors_all) == 0: abort(404) try: results = [] for i, actorObj in enumerate(actors_all): results.append(json.loads(actorObj.to_json())) return jsonify(results), 200 except Exception as e: print('\n'+'Error getting all actor records: ', e) abort(404) # ''' # GET: /api/actors/<int:id> # Authorized: Director or Assistant access # Endpoint: GET a specific actor # Returns: Status code 200 and json {"success": True, "actor": data} # where actor is a single actor data # or appropriate status code indicating reason for failure # ''' @app.route('/api/actors/<int:id>', methods=['GET']) @requires_role(['director', 'assistant']) def getActor(id): if id is None: abort(404) actor_query = Actor.query.filter(Actor.id == id).one_or_none() if actor_query is None: abort(404) data = { "id": actor_query.id, "first_name": actor_query.first_name, "last_name": actor_query.last_name, "birth_date": str(actor_query.birth_date), "gender": actor_query.gender, "actor_img": actor_query.actor_img, "actor_publish": actor_query.actor_publish } try: return jsonify({ 'success': True, 'actor': data }), 200 except Exception as e: print('\n'+'Error getting actor detail record: ', e) abort(404) # ''' # POST: /api/actors # Authorized: Director access # Endpoint: Create a new actor # Returns: Status code 200 and json {"success": True} # where actor is a single new actor # or appropriate status code indicating reason for failure # ''' @app.route('/api/actors', methods=['POST']) @requires_role('director') def createActor(): try: data = { 'id': None, 'first_name': request.get_json()['first_name'], 'last_name': request.get_json()['last_name'], 'birth_date': request.get_json()['birth_date'], 'gender': request.get_json()['gender'], 'actor_img': request.get_json()['actor_img'], 'actor_publish': False } if not ('first_name' in data and 'last_name' in data and 'birth_date' in data and 'gender' in data and 'actor_img' in data and 'actor_publish' in data): abort(422) actor = Actor(**data) actor.insert() return jsonify({ 'success': True }), 200 except Exception as e: print('\n'+'Error creating actor record: ', e) abort(422) # ''' # PATCH: /api/actors/<int:id> # Authorized: Assistant access # Endpoint: Update actor data fields # Returns: Status code 200 and json {"success": True} # where actor published is a single actor # or appropriate status code indicating reason for failure # ''' @app.route('/api/actors/<int:id>', methods=['PATCH']) @requires_role('assistant') def updateActor(id): if id is None: abort(404) data = Actor.query.filter(Actor.id == id).one_or_none() if data is None: abort(404) request_json = request.get_json() data.first_name = request.json.get('first_name') data.last_name = request.json.get('last_name') data.birth_date = request.json.get('birth_date') data.gender = request.json.get('gender') data.actor_img = request.json.get('actor_img') try: data.update() return jsonify({ "success": True }), 200 except Exception as e: print('\n'+'Error updating actor record: ', e) abort(422) # ''' # PATCH: /api/actors/<int:id>/publish # Authorized: Assistant access # Endpoint: Publish actor data fields - boolen # Returns: Status code 200 and json {"success": True} # where actor updated is a single actor # or appropriate status code indicating reason for failure # ''' @app.route('/api/actors/<int:id>/publish', methods=['PATCH']) @requires_role('assistant') def publishActor(id): if id is None: abort(404) data = Actor.query.filter(Actor.id == id).one_or_none() if data is None: abort(404) data.actor_publish = True try: data.update() return jsonify({ "success": True }), 200 except Exception as e: print('\n'+'Error publishing actor record: ', e) abort(422) # ''' # PATCH: /api/actors/<int:id>/unpublish # Authorized: Assistant access # Endpoint: Unpublish actor data fields - boolean # Returns: Status code 200 and json {"success": True} # where actor unpublish is a single actor # or appropriate status code indicating reason for failure # ''' @app.route('/api/actors/<int:id>/unpublish', methods=['PATCH']) @requires_role('assistant') def unpublishActor(id): if id is None: abort(404) data = Actor.query.filter(Actor.id == id).one_or_none() if data is None: abort(404) data.actor_publish = False try: data.update() return jsonify({ "success": True }), 200 except Exception as e: print('\n'+'Error unpublishing actor record: ', e) abort(422) # ''' # DELETE: /api/actors/<int:id> # Authorized: Director access # Endpoint: Deletes specific actor data fields # Returns: Status code 200 and json {"success": True} # where actor deleted is a single actor # or appropriate status code indicating reason for failure # ''' @app.route('/api/actors/<int:id>', methods=['DELETE']) @requires_role('director') def deleteActor(id): try: actor = Actor.query.filter(Actor.id == id).one_or_none() if actor is None: abort(404) actor.delete() return jsonify({ 'success': True }), 200 except Exception as e: print('\n'+'Error deleting actor record: ', e) abort(404) # ''' # SEARCH: /api/actors/search # Authorized: Public user access # Endpoint: Provide a like search for actor first-name # Returns: Status code 200 if search is successful # where actor(s) are searched for by first-name # or appropriate status code indicating reason for failure # ''' @app.route('/api/actors/search', methods=['GET']) def findActorByFirstName(): search_firstname = request.args.get('first_name') data = Actor.query.filter( Actor.first_name.ilike(f'%{search_firstname}%')).all() results = [] try: for i, actorObj in enumerate(data): results.append(json.loads(actorObj.to_json())) return jsonify(results), 200 except Exception as e: print('\n'+'Error searching by actor first name: ', e) abort(404) ''' Error handlers for all expected errors including 404 and 422. ''' @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "Resource Not Found" }), 404 @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "Unprocessable" }), 422 @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "Bad Request" }), 400 @app.errorhandler(405) def not_allowed(error): return jsonify({ "success": False, "error": 405, "message": "Method Not Allowed" }), 405 @app.errorhandler(AuthError) def unauthorized(error): return jsonify({ "error": 401 }), 401 return app