Exemplo n.º 1
0
    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()
Exemplo n.º 2
0
    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()
Exemplo n.º 3
0
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()
Exemplo n.º 4
0
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