Skip to content

Commit 0a38abf

Browse files
committed
Remove the need to pass logger to APIs
The API handling code now retrieves a logger from `flask.current_app` only when needed. This exposed a small problem with the test environment where the `Flask` app context was not setup for the unit tests.
1 parent 822ec1f commit 0a38abf

29 files changed

+220
-243
lines changed

lib/pbench/server/api/__init__.py

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,134 +49,132 @@ def register_endpoints(api, app, config):
4949
to make the APIs active."""
5050

5151
base_uri = config.rest_uri
52-
logger = app.logger
5352

54-
logger.info("Registering service endpoints with base URI {}", base_uri)
53+
app.logger.info("Registering service endpoints with base URI {}", base_uri)
5554

5655
api.add_resource(
5756
DatasetsContents,
5857
f"{base_uri}/datasets/contents/<string:dataset>/",
5958
f"{base_uri}/datasets/contents/<string:dataset>/<path:target>",
6059
endpoint="datasets_contents",
61-
resource_class_args=(config, logger),
60+
resource_class_args=(config,),
6261
)
6362
api.add_resource(
6463
DatasetsDateRange,
6564
f"{base_uri}/datasets/daterange",
6665
endpoint="datasets_daterange",
67-
resource_class_args=(config, logger),
66+
resource_class_args=(config,),
6867
)
6968
api.add_resource(
7069
DatasetsDelete,
7170
f"{base_uri}/datasets/delete/<string:dataset>",
7271
endpoint="datasets_delete",
73-
resource_class_args=(config, logger),
72+
resource_class_args=(config,),
7473
)
7574
api.add_resource(
7675
DatasetsDetail,
7776
f"{base_uri}/datasets/detail/<string:dataset>",
7877
endpoint="datasets_detail",
79-
resource_class_args=(config, logger),
78+
resource_class_args=(config,),
8079
)
8180
api.add_resource(
8281
DatasetsList,
8382
f"{base_uri}/datasets/list",
8483
endpoint="datasets_list",
85-
resource_class_args=(config, logger),
84+
resource_class_args=(config,),
8685
)
8786
api.add_resource(
8887
DatasetsMappings,
8988
f"{base_uri}/datasets/mappings/<string:dataset_view>",
9089
endpoint="datasets_mappings",
91-
resource_class_args=(config, logger),
90+
resource_class_args=(config,),
9291
)
9392
api.add_resource(
9493
DatasetsMetadata,
9594
f"{base_uri}/datasets/metadata/<string:dataset>",
9695
endpoint="datasets_metadata",
97-
resource_class_args=(config, logger),
96+
resource_class_args=(config,),
9897
)
9998
api.add_resource(
10099
DatasetsInventory,
101100
f"{base_uri}/datasets/inventory/<string:dataset>",
102101
f"{base_uri}/datasets/inventory/<string:dataset>/",
103102
f"{base_uri}/datasets/inventory/<string:dataset>/<path:target>",
104103
endpoint="datasets_inventory",
105-
resource_class_args=(config, logger),
104+
resource_class_args=(config,),
106105
)
107106
api.add_resource(
108107
SampleNamespace,
109108
f"{base_uri}/datasets/namespace/<string:dataset>/<string:dataset_view>",
110109
endpoint="datasets_namespace",
111-
resource_class_args=(config, logger),
110+
resource_class_args=(config,),
112111
)
113112
api.add_resource(
114113
SampleValues,
115114
f"{base_uri}/datasets/values/<string:dataset>/<string:dataset_view>",
116115
endpoint="datasets_values",
117-
resource_class_args=(config, logger),
116+
resource_class_args=(config,),
118117
)
119118
api.add_resource(
120119
DatasetsPublish,
121120
f"{base_uri}/datasets/publish/<string:dataset>",
122121
endpoint="datasets_publish",
123-
resource_class_args=(config, logger),
122+
resource_class_args=(config,),
124123
)
125124
api.add_resource(
126125
DatasetsSearch,
127126
f"{base_uri}/datasets/search",
128127
endpoint="datasets_search",
129-
resource_class_args=(config, logger),
128+
resource_class_args=(config,),
130129
)
131130
api.add_resource(
132131
EndpointConfig,
133132
f"{base_uri}/endpoints",
134133
endpoint="endpoints",
135-
resource_class_args=(config, logger),
134+
resource_class_args=(config,),
136135
)
137136
api.add_resource(
138137
Login,
139138
f"{base_uri}/login",
140139
endpoint="login",
141-
resource_class_args=(config, logger),
140+
resource_class_args=(config,),
142141
)
143142
api.add_resource(
144143
Logout,
145144
f"{base_uri}/logout",
146145
endpoint="logout",
147-
resource_class_args=(config, logger),
146+
resource_class_args=(config,),
148147
)
149148
api.add_resource(
150149
RegisterUser,
151150
f"{base_uri}/register",
152151
endpoint="register",
153-
resource_class_args=(config, logger),
152+
resource_class_args=(config,),
154153
)
155154
api.add_resource(
156155
ServerAudit,
157156
f"{base_uri}/server/audit",
158157
endpoint="server_audit",
159-
resource_class_args=(config, logger),
158+
resource_class_args=(config,),
160159
)
161160
api.add_resource(
162161
ServerConfiguration,
163162
f"{base_uri}/server/configuration",
164163
f"{base_uri}/server/configuration/",
165164
f"{base_uri}/server/configuration/<string:key>",
166165
endpoint="server_configuration",
167-
resource_class_args=(config, logger),
166+
resource_class_args=(config,),
168167
)
169168
api.add_resource(
170169
UserAPI,
171170
f"{base_uri}/user/<string:target_username>",
172171
endpoint="user",
173-
resource_class_args=(logger,),
174172
)
175173
api.add_resource(
176174
Upload,
177175
f"{base_uri}/upload/<string:filename>",
178176
endpoint="upload",
179-
resource_class_args=(config, logger),
177+
resource_class_args=(config,),
180178
)
181179

182180

lib/pbench/server/api/resources/__init__.py

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
from http import HTTPStatus
44
import json
55
from json.decoder import JSONDecodeError
6-
from logging import Logger
76
from typing import Any, Callable, Dict, List, NamedTuple, Optional, Union
87
import uuid
98

109
from dateutil import parser as date_parser
11-
from flask import request
10+
from flask import current_app, request
1211
from flask.wrappers import Request, Response
1312
from flask_restful import abort, Resource
1413
from sqlalchemy.orm.query import Query
@@ -1111,7 +1110,6 @@ class ApiBase(Resource):
11111110
def __init__(
11121111
self,
11131112
config: PbenchServerConfig,
1114-
logger: Logger,
11151113
*schemas: ApiSchema,
11161114
always_enabled: bool = False,
11171115
):
@@ -1130,7 +1128,6 @@ def __init__(
11301128
"""
11311129
super().__init__()
11321130
self.config = config
1133-
self.logger = logger
11341131
self.schemas = ApiSchemaSet(*schemas)
11351132
self.always_enabled = always_enabled
11361133

@@ -1222,14 +1219,14 @@ def _check_authorization(self, mode: ApiAuthorization):
12221219
if user:
12231220
username = user.username
12241221
else:
1225-
self.logger.error("User ID {} not found", user_id)
1222+
current_app.logger.error("User ID {} not found", user_id)
12261223

12271224
# The ADMIN authorization doesn't involve a target resource owner or
12281225
# access, so take care of that first as a special case. If there is
12291226
# an authenticated user, and that user holds ADMIN access rights, the
12301227
# check passes. Otherwise raise an "admin access" failure.
12311228
if mode.type == ApiAuthorizationType.ADMIN:
1232-
self.logger.debug(
1229+
current_app.logger.debug(
12331230
"Authorizing {} access for {} to an administrative resource",
12341231
role,
12351232
authorized_user,
@@ -1244,7 +1241,7 @@ def _check_authorization(self, mode: ApiAuthorization):
12441241

12451242
access = mode.access
12461243

1247-
self.logger.debug(
1244+
current_app.logger.debug(
12481245
"Authorizing {} access for {} to user {} ({}) with access {} using {}",
12491246
role,
12501247
authorized_user,
@@ -1274,7 +1271,7 @@ def _check_authorization(self, mode: ApiAuthorization):
12741271
if authorized_user is None:
12751272
# An unauthenticated user is never allowed to access private
12761273
# data nor to perform an potential mutation of data: REJECT
1277-
self.logger.warning(
1274+
current_app.logger.warning(
12781275
"Attempt to {} user {} data without login", role, username
12791276
)
12801277
raise UnauthorizedAccess(
@@ -1287,7 +1284,7 @@ def _check_authorization(self, mode: ApiAuthorization):
12871284
elif role != OperationCode.READ and user_id is None:
12881285
# No target user is specified, so we won't allow mutation of
12891286
# data: REJECT
1290-
self.logger.warning(
1287+
current_app.logger.warning(
12911288
"Unauthorized attempt by {} to {} data with defaulted user",
12921289
authorized_user,
12931290
role,
@@ -1303,7 +1300,7 @@ def _check_authorization(self, mode: ApiAuthorization):
13031300
# We are mutating data, or reading private data, so the
13041301
# authenticated user must either be the owner of the data or
13051302
# must have ADMIN role: REJECT
1306-
self.logger.warning(
1303+
current_app.logger.warning(
13071304
"Unauthorized attempt by {} to {} user {} data",
13081305
authorized_user,
13091306
role,
@@ -1391,7 +1388,7 @@ def _build_sql_query(
13911388
is_admin = authorized_user.is_admin() if authorized_user else False
13921389
query = base_query
13931390

1394-
self.logger.debug(
1391+
current_app.logger.debug(
13951392
"QUERY auth ID {}, user {!r}, access {!r}, admin {}",
13961393
authorized_id,
13971394
owner_id,
@@ -1422,24 +1419,28 @@ def _build_sql_query(
14221419
owner_id and owner_id != authorized_id and not is_admin
14231420
):
14241421
query = query.filter(Dataset.access == Dataset.PUBLIC_ACCESS)
1425-
self.logger.debug("QUERY: not self public")
1422+
current_app.logger.debug("QUERY: not self public")
14261423
elif access:
14271424
query = query.filter(Dataset.access == access)
14281425
if not owner_id and access == Dataset.PRIVATE_ACCESS and not is_admin:
14291426
query = query.filter(Dataset.owner_id == authorized_id)
14301427
user_term = True
1431-
self.logger.debug("QUERY: user: {}, access: {}", authorized_id, access)
1428+
current_app.logger.debug(
1429+
"QUERY: user: {}, access: {}", authorized_id, access
1430+
)
14321431
elif not owner_id and not is_admin:
14331432
query = query.filter(
14341433
(Dataset.owner_id == authorized_id)
14351434
| (Dataset.access == Dataset.PUBLIC_ACCESS)
14361435
)
14371436
user_term = True
1438-
self.logger.debug("QUERY: self ({}) + public", authorized_user.username)
1437+
current_app.logger.debug(
1438+
"QUERY: self ({}) + public", authorized_user.username
1439+
)
14391440
else:
14401441
# Either "user" was specified and will be added to the filter,
14411442
# or client is ADMIN and no access restrictions are required.
1442-
self.logger.debug(
1443+
current_app.logger.debug(
14431444
"QUERY: default, user: {}", owner_id if owner_id else authorized_user
14441445
)
14451446

@@ -1513,7 +1514,7 @@ def _dispatch(
15131514

15141515
api_name = self.__class__.__name__
15151516

1516-
self.logger.info("In {} {}: mime {}", method, api_name, request.mimetype)
1517+
current_app.logger.info("In {} {}: mime {}", method, api_name, request.mimetype)
15171518

15181519
if method is ApiMethod.GET:
15191520
execute = self._get
@@ -1576,15 +1577,15 @@ def _dispatch(
15761577
ApiParams(body=body_params, query=query_params, uri=uri_params),
15771578
)
15781579
except APIInternalError as e:
1579-
self.logger.exception("{} {}", api_name, e.details)
1580+
current_app.logger.exception("{} {}", api_name, e.details)
15801581
abort(e.http_status, message=str(e))
15811582
except APIAbort as e:
15821583
abort(e.http_status, message=str(e))
15831584
except Exception:
15841585
# Construct an APIInternalError to get the UUID and standard return
15851586
# message.
15861587
x = APIInternalError("Unexpected validation exception")
1587-
self.logger.exception("{} {}", api_name, x.details)
1588+
current_app.logger.exception("{} {}", api_name, x.details)
15881589
abort(x.http_status, message=str(x))
15891590

15901591
# Automatically authorize the operation only if the API schema for the
@@ -1598,16 +1599,16 @@ def _dispatch(
15981599
try:
15991600
self._check_authorization(auth_params)
16001601
except UnauthorizedAccess as e:
1601-
self.logger.warning("{}: {}", api_name, e)
1602+
current_app.logger.warning("{}: {}", api_name, e)
16021603
abort(e.http_status, message=str(e))
16031604
except APIInternalError as e:
1604-
self.logger.exception("{} {}", api_name, e.details)
1605+
current_app.logger.exception("{} {}", api_name, e.details)
16051606
abort(e.http_status, message=str(e))
16061607
except Exception:
16071608
# Construct an APIInternalError to get the UUID and standard return
16081609
# message.
16091610
x = APIInternalError("Unexpected authorize exception")
1610-
self.logger.exception("{} {}", api_name, x.details)
1611+
current_app.logger.exception("{} {}", api_name, x.details)
16111612
abort(x.http_status, message=str(x))
16121613

16131614
audit = None
@@ -1653,7 +1654,7 @@ def _dispatch(
16531654
)
16541655
return response
16551656
except APIInternalError as e:
1656-
self.logger.exception("{} {}", api_name, e.details)
1657+
current_app.logger.exception("{} {}", api_name, e.details)
16571658
abort(e.http_status, message=str(e))
16581659
except APIAbort as e:
16591660
if auditing["finalize"]:
@@ -1666,12 +1667,12 @@ def _dispatch(
16661667
attributes=attr,
16671668
)
16681669
except Exception:
1669-
self.logger.error(
1670+
current_app.logger.error(
16701671
"Unexpected exception on audit: {}", json.dumps(auditing)
16711672
)
16721673
abort(e.http_status, message=str(e))
16731674
except Exception as e:
1674-
self.logger.exception(
1675+
current_app.logger.exception(
16751676
"Exception {} API error: {}: {!r}", api_name, e, json.dumps(auditing)
16761677
)
16771678
if auditing["finalize"]:

lib/pbench/server/api/resources/datasets_daterange.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from logging import Logger
2-
31
from flask.json import jsonify
42
from flask.wrappers import Request, Response
53
from sqlalchemy import func
@@ -25,10 +23,9 @@ class DatasetsDateRange(ApiBase):
2523
API class to retrieve the available date range of accessible datasets.
2624
"""
2725

28-
def __init__(self, config: PbenchServerConfig, logger: Logger):
26+
def __init__(self, config: PbenchServerConfig):
2927
super().__init__(
3028
config,
31-
logger,
3229
ApiSchema(
3330
ApiMethod.GET,
3431
OperationCode.READ,

lib/pbench/server/api/resources/datasets_inventory.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
from http import HTTPStatus
2-
from logging import Logger
32
from urllib.request import Request
43

5-
from flask import send_file
4+
from flask import current_app, send_file
65
from flask.wrappers import Response
76

87
from pbench.server import OperationCode, PbenchServerConfig
@@ -26,10 +25,9 @@ class DatasetsInventory(ApiBase):
2625
API class to retrieve inventory files from a dataset
2726
"""
2827

29-
def __init__(self, config: PbenchServerConfig, logger: Logger):
28+
def __init__(self, config: PbenchServerConfig):
3029
super().__init__(
3130
config,
32-
logger,
3331
ApiSchema(
3432
ApiMethod.GET,
3533
OperationCode.READ,
@@ -62,7 +60,7 @@ def _get(
6260
dataset = params.uri["dataset"]
6361
target = params.uri.get("target")
6462

65-
cache_m = CacheManager(self.config, self.logger)
63+
cache_m = CacheManager(self.config, current_app.logger)
6664
try:
6765
tarball = cache_m.find_dataset(dataset.resource_id)
6866
except TarballNotFound as e:

0 commit comments

Comments
 (0)