-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathapp.py
More file actions
248 lines (215 loc) · 8.92 KB
/
app.py
File metadata and controls
248 lines (215 loc) · 8.92 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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
import os
from datetime import timedelta, date, datetime
import uvicorn
from apscheduler.triggers.interval import IntervalTrigger
from environs import Env
from fastapi import FastAPI
from fastapi.middleware.wsgi import WSGIMiddleware as WSGIMiddlewareFastAPI
from flask import Flask
from flask.json.provider import DefaultJSONProvider
from flask_cors import CORS
from flask_restx import Api
from starlette.applications import Starlette
from starlette.middleware.cors import CORSMiddleware
from config import config, oauth, limiter, jwt
from middleware.access_log_filter import (
SuppressTestAccessLogMiddleware,
install_test_request_log_filter,
)
from db.helpers_.psycopg import initialize_psycopg_connection
from endpoints.instantiations.admin_.routes import namespace_admin
from endpoints.instantiations.agencies_.routes import namespace_agencies
from endpoints.instantiations.auth_.callback import namespace_callback
from endpoints.instantiations.auth_.login import namespace_login
from endpoints.instantiations.auth_.refresh_session import namespace_refresh_session
from endpoints.instantiations.auth_.request_reset_password import (
namespace_request_reset_password,
)
from endpoints.instantiations.auth_.resend_validation_email.route import (
namespace_resend_validation_email,
)
from endpoints.instantiations.auth_.reset_password import namespace_reset_password
from endpoints.instantiations.auth_.reset_token_validation import (
namespace_reset_token_validation,
)
from endpoints.instantiations.auth_.routes import namespace_auth
from endpoints.instantiations.auth_.signup.route import namespace_signup
from endpoints.instantiations.auth_.validate_email.route import namespace_validate_email
from endpoints.instantiations.check_.route import namespace_url_checker
from endpoints.instantiations.contact_.route import namespace_contact
from endpoints.instantiations.data_requests_.data_requests import (
namespace_data_requests,
)
from endpoints.instantiations.data_sources_.routes import namespace_data_source
from endpoints.instantiations.dev_.route import (
namespace_create_test_user,
)
from endpoints.instantiations.github_.route import namespace_github
from endpoints.instantiations.locations_.locations import namespace_locations
from endpoints.instantiations.map.routes import namespace_map
from endpoints.instantiations.match_.route import namespace_match
from endpoints.instantiations.metadata_.route import namespace_metadata
from endpoints.instantiations.metrics_.metrics import namespace_metrics
from endpoints.instantiations.notifications_.route import namespace_notifications
from endpoints.instantiations.oauth_.link_to_github import namespace_link_to_github
from endpoints.instantiations.oauth_.login_with_github import (
namespace_login_with_github,
)
from endpoints.instantiations.oauth_.oauth import namespace_oauth
from endpoints.instantiations.search.routes import namespace_search
from endpoints.instantiations.source_collector.routes import (
namespace_source_collector,
)
from endpoints.instantiations.typeahead_.routes import (
namespace_typeahead_suggestions,
)
from endpoints.instantiations.user.routes import namespace_user
from endpoints.v3.permissions.routes import permission_router
from endpoints.v3.source_manager.sync.routes import sm_router
from endpoints.v3.user.routes import user_router
from middleware.scheduled_tasks.check_database_health import check_database_health
from middleware.scheduled_tasks.manager import SchedulerManager
from middleware.util.env import get_env_variable
env = Env()
env.read_env()
NAMESPACES = [
namespace_callback,
namespace_request_reset_password,
namespace_oauth,
namespace_reset_token_validation,
namespace_agencies,
namespace_data_source,
namespace_login,
namespace_refresh_session,
namespace_reset_password,
namespace_typeahead_suggestions,
namespace_search,
namespace_auth,
namespace_link_to_github,
namespace_login_with_github,
namespace_create_test_user,
namespace_data_requests,
namespace_url_checker,
namespace_user,
namespace_github,
namespace_notifications,
namespace_map,
namespace_signup,
namespace_match,
namespace_locations,
namespace_metrics,
namespace_admin,
namespace_contact,
namespace_metadata,
namespace_source_collector,
namespace_validate_email,
namespace_resend_validation_email,
]
def get_flask_app_cookie_encryption_key() -> str:
return get_env_variable("FLASK_APP_COOKIE_ENCRYPTION_KEY")
class UpdatedJSONProvider(DefaultJSONProvider):
def default(self, o):
if isinstance(o, date) or isinstance(o, datetime):
return o.isoformat()
return super().default(o)
def create_flask_app() -> Flask:
psycopg2_connection = initialize_psycopg_connection()
config.connection = psycopg2_connection
api = get_api_with_namespaces()
app = Flask(__name__)
app.json = UpdatedJSONProvider(app)
# JWT settings
app.config["JWT_SECRET_KEY"] = os.getenv("JWT_SECRET_KEY")
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(minutes=15)
app.config["JWT_REFRESH_TOKEN_EXPIRES"] = timedelta(days=1)
# Other configuration settings
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024
app.secret_key = get_flask_app_cookie_encryption_key()
# app.wsgi_app = WSGIMiddleware(app.wsgi_app)
CORS(app)
api.init_app(app)
oauth.init_app(app)
limiter.init_app(app)
jwt.init_app(app)
current_time = datetime.now()
# Initialize and start the scheduler
scheduler = SchedulerManager(app)
flag_run_scheduled_jobs: bool = env.bool("FLAG_RUN_SCHEDULED_JOBS", False)
if flag_run_scheduled_jobs:
scheduler.add_job(
job_id="database_health_check",
func=check_database_health,
interval=IntervalTrigger(
start_date=current_time + timedelta(minutes=3), minutes=60
),
)
scheduler.add_materialized_view_scheduled_job("typeahead_locations", 1)
scheduler.add_materialized_view_scheduled_job("typeahead_agencies", 2)
scheduler.add_materialized_view_scheduled_job("map_states", 4)
scheduler.add_materialized_view_scheduled_job("map_counties", 5)
scheduler.add_materialized_view_scheduled_job("map_localities", 6)
scheduler.start()
else:
print("Scheduled jobs are disabled.")
# Store scheduler in the app context to manage it later
app.scheduler = scheduler # pyright: ignore[reportAttributeAccessIssue]
return app
def get_api_with_namespaces():
api = Api(
version="2.0",
title="PDAP Data Sources API",
description="The following is the API documentation for the PDAP Data Sources API."
"\n\nBy accessing our API, you are agreeing to our [Terms of Service](https://docs.pdap.io/meta/operations/legal/terms-of-service). Please read them before you start."
"\n\nFor API help, consult [our getting started guide.](https://docs.pdap.io/api/introduction)"
"\n\nTo search the database, go to [pdap.io](https://pdap.io)."
"\n\nThe new FastAPI API is available at {this_address}/docs",
)
for namespace in NAMESPACES:
api.add_namespace(namespace)
return api
def create_asgi_app() -> Starlette:
flask_app = create_flask_app()
fast_api_app: FastAPI = create_fast_api_app()
app = Starlette()
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=[
"https://pdap.io",
"https://www.pdap.io",
"https://data-sources.pdap.dev",
"https://pdap.dev",
"https://data-sources.pdap.io",
# Dev origins
"http://localhost:8888",
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.add_middleware(SuppressTestAccessLogMiddleware)
app.mount("/api/v3", fast_api_app)
app.mount("/api/v2", WSGIMiddlewareFastAPI(flask_app))
install_test_request_log_filter()
return app
def create_fast_api_app() -> FastAPI:
fast_api_app = FastAPI(
title="PDAP Data Sources API",
version="3.0",
description="The following is the API documentation for the PDAP Data Sources API."
"\n\nBy accessing our API, you are agreeing to our [Terms of Service](https://docs.pdap.io/meta/operations/legal/terms-of-service). Please read them before you start."
"\n\nFor API help, consult [our getting started guide.](https://docs.pdap.io/api/introduction)"
"\n\nTo search the database, go to [pdap.io](https://pdap.io)."
"\n\nThe old Flask API is available at {this_address}/"
"",
)
for router in [sm_router, user_router, permission_router]:
fast_api_app.include_router(router)
return fast_api_app
if __name__ == "__main__":
app = create_asgi_app()
uvicorn.run(
app,
host=os.getenv("FLASK_RUN_HOST", "127.0.0.1"),
port=int(os.getenv("FLASK_RUN_PORT", 8000)),
)