-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
173 lines (147 loc) · 6.62 KB
/
Copy pathapp.py
File metadata and controls
173 lines (147 loc) · 6.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import os
import sys
import pymongo
sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))
from flask import Flask, request
from flasgger import Swagger
from routes.router import main_blueprint
from dotenv import load_dotenv
from config.logger import get_logger
from config.security import configure_talisman, configure_rate_limiting, configure_cors
from config.mongo_engine import connect_to_database
from config.jwt import setup_jwt
from config.mongo_uri import setup_mongo_uri
from services.DataService import DataService
from services.MovieQueryService import MovieQueryService
from services.GenderStatisticsService import GenderStatisticsService
from services.AggregationPipelineService import AggregationPipelineService
from repositories.DBRepo import DBRepo
from controllers.api.AccountController import AccountController
from controllers.api.MovieController import MovieController
from controllers.api.RatingController import RatingController
from controllers.api.ActorController import ActorController
from controllers.api.UserController import UserController
from controllers.api.GenderDataController import GenderDataController
from models.UserModel import User
from models.MovieModel import MovieMetaData
from models.CreditsModel import Credit
from models.RatingsModel import Rating
from models.GenderDataModel import GenderStatistics
from routes.api.v1.account_router import create_account_blueprint
from routes.api.v1.movie_router import create_movie_blueprint
from routes.api.v1.credit_router import create_credit_blueprint
from routes.api.v1.rating_router import create_rating_blueprint
from routes.api.v1.gender_data_router import create_gender_statistics_blueprint
from routes.api.v1.health import health_blueprint
from utils.JsonWebToken import JsonWebToken
from services.GenerateHateoasLinks import GenerateHateoasLinks
from utils.JsonConvert import JsonConvert
from utils.CustomErrors import CustomError
from seed.seed_db import seed_database
from pymongo.errors import ConnectionFailure
# Load environment variables
load_dotenv()
# Initialize Flask app
app = Flask(__name__)
# Initialize Swagger for API documentation
Swagger(app, template={
"info": {
"title": "RESTful Movies API",
"description": "API documentation for the Movies API",
"version": "1.0.0"
},
"basePath": "/api/v1"
})
# Setup Loguru logger
logger = get_logger()
flask_env = os.getenv("FLASK_ENV")
app.config["FLASK_ENV"] = flask_env
# Setup rate limiter
configure_rate_limiting(app)
# Setup CORS
configure_cors(app)
# Set up the mongo URI
mongo_uri = setup_mongo_uri(app)
# Setup JWT authentication
setup_jwt(app)
# Connect MongoEngine to the database
connect_to_database(mongo_uri)
# Test MongoDB connection using PyMongo
def check_mongo_connection():
try:
mongo_uri
if not mongo_uri:
raise ValueError("Mongo uri is not valid or set")
client = pymongo.MongoClient(mongo_uri)
# Attempt to ping the MongoDB server
client.admin.command('ping')
logger.info("DB Connection: SUCCESS")
except ConnectionFailure as e:
logger.error(f"ERROR: Could not connect to database. {e}")
sys.exit(1)
except Exception as e:
logger.error(f"ERROR: {e}")
sys.exit(1)
# Perform the database connection check before starting the app
check_mongo_connection()
# Instantiate dependencies to adhere to IoC and DI principles
json_web_token = JsonWebToken(logger)
generate_hateoas_links = GenerateHateoasLinks(logger, app)
json_convert = JsonConvert(logger)
user_db_repo = DBRepo(User, logger)
movie_db_repo = DBRepo(MovieMetaData, logger)
credit_db_repo = DBRepo(Credit, logger)
rating_db_repo = DBRepo(Rating, logger)
gender_data_db_repo = DBRepo(GenderStatistics, logger)
data_service = DataService(logger, gender_data_db_repo)
movie_query_service = MovieQueryService(logger, movie_db_repo, credit_db_repo, rating_db_repo)
aggregation_pipeline_service = AggregationPipelineService(logger)
gender_statistics_service = GenderStatisticsService(logger, gender_data_db_repo, aggregation_pipeline_service)
account_controller = AccountController(logger, json_web_token, User, user_db_repo, generate_hateoas_links)
movie_controller = MovieController(logger, movie_db_repo, credit_db_repo, rating_db_repo, generate_hateoas_links, json_convert, data_service, movie_query_service)
user_controller = UserController(logger, user_db_repo)
rating_controller = RatingController(logger, rating_db_repo, movie_db_repo, json_convert, generate_hateoas_links, movie_query_service)
actor_controller = ActorController(logger, credit_db_repo, json_convert, generate_hateoas_links, movie_query_service, movie_db_repo)
gender_data_controller = GenderDataController(logger, gender_data_db_repo, gender_statistics_service, generate_hateoas_links)
# Register the main router blueprint
app.register_blueprint(main_blueprint)
# Create and register the remaining blueprints
account_blueprint = create_account_blueprint(account_controller, user_controller)
app.register_blueprint(account_blueprint, url_prefix='/api/v1')
movie_blueprint = create_movie_blueprint(movie_controller)
app.register_blueprint(movie_blueprint, url_prefix='/api/v1')
credit_blueprint = create_credit_blueprint(actor_controller)
app.register_blueprint(credit_blueprint, url_prefix='/api/v1')
rating_blueprint = create_rating_blueprint(rating_controller)
app.register_blueprint(rating_blueprint, url_prefix='/api/v1')
gender_statistics_blueprint = create_gender_statistics_blueprint(gender_data_controller)
app.register_blueprint(gender_statistics_blueprint, url_prefix='/api/v1')
# Health check route
app.register_blueprint(health_blueprint)
# Seed the database with extracted movie data, if neccessary
seed_database(data_service, logger)
# Log each request
@app.before_request
def log_request():
logger.info(f"{request.method} {request.path} from {request.remote_addr}")
# Handle custom errors
@app.errorhandler(CustomError)
def handle_custom_error(error):
logger.warning(f"Custom error occurred: {error.message}")
return error.to_response()
# Log unhandled errors
@app.errorhandler(Exception)
def handle_exception(e):
logger.error(f"Unhandled error: {e}")
return {"error": "Internal Server Error"}, 500
if flask_env == "production":
logger.info("🔒 Running in production mode, configuring security headers.")
# Configure Flask-Talisman for security headers in production
configure_talisman(app)
else:
logger.info("⚠️ Running in development mode, security headers are not applied.")
if __name__ == "__main__":
logger.info("🚀 Starting Flask API...")
port = int(os.getenv("PORT", 5000))
print(port)
app.run(host="0.0.0.0", port=port)