api-foundry-query-engine 0.8.49__tar.gz → 0.8.51__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/PKG-INFO +1 -1
- api_foundry_query_engine-0.8.51/api_foundry_query_engine/__init__.py +1 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/dao/sql_insert_query_handler.py +2 -4
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/dao/sql_restore_query_handler.py +1 -2
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/dao/sql_select_query_handler.py +1 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/dao/sql_subselect_query_handler.py +1 -1
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/lambda_handler.py +1 -1
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/utils/claims_check.py +9 -10
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/utils/token_decoder.py +16 -47
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/pyproject.toml +1 -1
- api_foundry_query_engine-0.8.49/api_foundry_query_engine/__init__.py +0 -1
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/.gitignore +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/LICENSE +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/.pre-commit-config.yaml +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/adapters/adapter.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/adapters/case_change_adapter.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/adapters/gateway_adapter.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/adapters/security_adapter.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/connectors/connection.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/connectors/connection_factory.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/connectors/oracle_connector.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/connectors/postgres_connection.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/dao/batch_operation_handler.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/dao/dao.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/dao/operation_dao.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/dao/sql_custom_query_handler.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/dao/sql_delete_query_handler.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/dao/sql_query_handler.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/dao/sql_update_query_handler.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/operation.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/services/service.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/services/transactional_service.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/utils/api_model.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/utils/app_exception.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/utils/dependency_resolver.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/utils/gateway_operation.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/utils/logger.py +0 -0
- {api_foundry_query_engine-0.8.49 → api_foundry_query_engine-0.8.51}/api_foundry_query_engine/utils/reference_resolver.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: api-foundry-query-engine
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.51
|
|
4
4
|
Summary: The AWS lambda service handler use by the `api_foundry` project is a powerful tool designed to automate the deployment of REST APIs on AWS using Lambda services to access and interact with relational databases (RDBMS). This project leverages the OpenAPI specification to define and manage the APIs
|
|
5
5
|
Project-URL: Documentation, https://github.com/DanRepik/api-foundry
|
|
6
6
|
Project-URL: Source, https://github.com/DanRepik/api-foundry
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.8.51"
|
|
@@ -70,7 +70,7 @@ class SQLInsertSchemaQueryHandler(SQLSchemaQueryHandler):
|
|
|
70
70
|
# Get columns to return - use read permissions if available,
|
|
71
71
|
# otherwise fall back to primary key
|
|
72
72
|
returning_clause = self._get_returning_clause()
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
if not self.concurrency_property:
|
|
75
75
|
return (
|
|
76
76
|
f"INSERT INTO {self.table_expression}{self.insert_values} "
|
|
@@ -100,9 +100,7 @@ class SQLInsertSchemaQueryHandler(SQLSchemaQueryHandler):
|
|
|
100
100
|
|
|
101
101
|
if allowed_properties:
|
|
102
102
|
# Return all accessible columns
|
|
103
|
-
columns = [
|
|
104
|
-
prop.column_name for prop in allowed_properties.values()
|
|
105
|
-
]
|
|
103
|
+
columns = [prop.column_name for prop in allowed_properties.values()]
|
|
106
104
|
return f"RETURNING {', '.join(columns)}"
|
|
107
105
|
|
|
108
106
|
# Fallback to primary key if no permissions
|
|
@@ -172,8 +172,7 @@ class SQLRestoreSchemaQueryHandler(SQLSchemaQueryHandler):
|
|
|
172
172
|
if not self.check_permission():
|
|
173
173
|
raise ApplicationException(
|
|
174
174
|
403,
|
|
175
|
-
f"Subject is not allowed to restore "
|
|
176
|
-
f"{self.schema_object.api_name}",
|
|
175
|
+
f"Subject is not allowed to restore " f"{self.schema_object.api_name}",
|
|
177
176
|
)
|
|
178
177
|
|
|
179
178
|
if not self.schema_object.has_soft_delete_support():
|
|
@@ -55,7 +55,7 @@ class SQLSubselectSchemaQueryHandler(SQLSelectSchemaQueryHandler):
|
|
|
55
55
|
parent_alias = self.parent_generator.prefix_map[
|
|
56
56
|
str(self.parent_generator.schema_object.api_name)
|
|
57
57
|
]
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
sql = (
|
|
60
60
|
f"SELECT {self.select_list} "
|
|
61
61
|
+ f"FROM {self.relation.child_schema_object.qualified_name} "
|
|
@@ -418,16 +418,15 @@ def _extract_entity_from_path(event: Dict[str, Any]) -> Optional[str]:
|
|
|
418
418
|
# Remove leading slash and split by slash
|
|
419
419
|
path_parts = path.lstrip("/").split("/")
|
|
420
420
|
|
|
421
|
-
#
|
|
422
|
-
# For paths like "/
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
return part
|
|
421
|
+
# Find the first non-parameter part (entity is typically first)
|
|
422
|
+
# For paths like "/comment/123" or "/comment/123/version/1"
|
|
423
|
+
# Skip common API prefixes like "api", "v1", etc.
|
|
424
|
+
api_prefixes = {"api", "v1", "v2", "v3"}
|
|
425
|
+
for part in path_parts:
|
|
426
|
+
if part and not part.startswith("{") and part not in api_prefixes:
|
|
427
|
+
# Skip numeric IDs and known path keywords
|
|
428
|
+
if not part.isdigit() and part not in {"version", "batch"}:
|
|
429
|
+
return part
|
|
431
430
|
|
|
432
431
|
return None
|
|
433
432
|
|
|
@@ -53,14 +53,10 @@ class LambdaTokenValidator:
|
|
|
53
53
|
import boto3
|
|
54
54
|
|
|
55
55
|
self.lambda_client = boto3.client("lambda")
|
|
56
|
-
log.debug(
|
|
57
|
-
"Lambda client initialized for validator: %s", lambda_arn
|
|
58
|
-
)
|
|
56
|
+
log.debug("Lambda client initialized for validator: %s", lambda_arn)
|
|
59
57
|
except ImportError:
|
|
60
58
|
log.error("boto3 not available for Lambda token validation")
|
|
61
|
-
raise ImportError(
|
|
62
|
-
"boto3 is required for Lambda token validation"
|
|
63
|
-
)
|
|
59
|
+
raise ImportError("boto3 is required for Lambda token validation")
|
|
64
60
|
|
|
65
61
|
def validate(
|
|
66
62
|
self, token: str, method_arn: str = "arn:aws:execute-api:*:*:*"
|
|
@@ -110,9 +106,7 @@ class LambdaTokenValidator:
|
|
|
110
106
|
log.warning("Authorizer returned no context/claims")
|
|
111
107
|
raise ValueError("Authorizer returned no context/claims")
|
|
112
108
|
|
|
113
|
-
log.debug(
|
|
114
|
-
"Token validated successfully, claims extracted from context"
|
|
115
|
-
)
|
|
109
|
+
log.debug("Token validated successfully, claims extracted from context")
|
|
116
110
|
return context
|
|
117
111
|
|
|
118
112
|
except Exception as e:
|
|
@@ -177,9 +171,7 @@ def token_decoder(
|
|
|
177
171
|
|
|
178
172
|
# Validate configuration at decorator time
|
|
179
173
|
config_anonymous_role = anonymous_role or os.getenv("ANONYMOUS_ROLE")
|
|
180
|
-
config_lambda_arn = lambda_validator_arn or os.getenv(
|
|
181
|
-
"TOKEN_VALIDATOR_LAMBDA_ARN"
|
|
182
|
-
)
|
|
174
|
+
config_lambda_arn = lambda_validator_arn or os.getenv("TOKEN_VALIDATOR_LAMBDA_ARN")
|
|
183
175
|
|
|
184
176
|
# If no config provided, allow for runtime configuration
|
|
185
177
|
if (
|
|
@@ -206,9 +198,7 @@ def token_decoder(
|
|
|
206
198
|
|
|
207
199
|
# Check if authorizer already exists (gateway validated)
|
|
208
200
|
if event.get("requestContext", {}).get("authorizer"):
|
|
209
|
-
log.debug(
|
|
210
|
-
"Authorizer already exists, skipping JWT processing"
|
|
211
|
-
)
|
|
201
|
+
log.debug("Authorizer already exists, skipping JWT processing")
|
|
212
202
|
return handler(event, context)
|
|
213
203
|
|
|
214
204
|
# Determine configuration sources
|
|
@@ -216,26 +206,20 @@ def token_decoder(
|
|
|
216
206
|
"TOKEN_VALIDATOR_LAMBDA_ARN"
|
|
217
207
|
)
|
|
218
208
|
config_jwks_url = (
|
|
219
|
-
jwks_url
|
|
220
|
-
or f"{os.getenv('JWKS_HOST')}/.well-known/jwks.json"
|
|
209
|
+
jwks_url or f"{os.getenv('JWKS_HOST')}/.well-known/jwks.json"
|
|
221
210
|
if os.getenv("JWKS_HOST")
|
|
222
211
|
else None
|
|
223
212
|
)
|
|
224
213
|
config_issuer = issuer or os.getenv("JWT_ISSUER")
|
|
225
214
|
config_audience = (
|
|
226
|
-
audience
|
|
227
|
-
or os.getenv("JWT_ALLOWED_AUDIENCES", "").split(",")
|
|
215
|
+
audience or os.getenv("JWT_ALLOWED_AUDIENCES", "").split(",")
|
|
228
216
|
if os.getenv("JWT_ALLOWED_AUDIENCES")
|
|
229
217
|
else []
|
|
230
218
|
)
|
|
231
219
|
config_algorithms = algorithms or ["RS256"]
|
|
232
|
-
config_anonymous_role = anonymous_role or os.getenv(
|
|
233
|
-
"ANONYMOUS_ROLE"
|
|
234
|
-
)
|
|
220
|
+
config_anonymous_role = anonymous_role or os.getenv("ANONYMOUS_ROLE")
|
|
235
221
|
log.debug("anonymous_role parameter: %s", anonymous_role)
|
|
236
|
-
log.debug(
|
|
237
|
-
"ANONYMOUS_ROLE env: %s", os.getenv("ANONYMOUS_ROLE", "")
|
|
238
|
-
)
|
|
222
|
+
log.debug("ANONYMOUS_ROLE env: %s", os.getenv("ANONYMOUS_ROLE", ""))
|
|
239
223
|
log.debug("config_anonymous_role: %s", config_anonymous_role)
|
|
240
224
|
log.debug("config_lambda_arn: %s", config_lambda_arn)
|
|
241
225
|
|
|
@@ -243,9 +227,7 @@ def token_decoder(
|
|
|
243
227
|
if not config_lambda_arn and not config_jwks_url:
|
|
244
228
|
log.debug("No validation method configured")
|
|
245
229
|
if config_anonymous_role:
|
|
246
|
-
log.debug(
|
|
247
|
-
"Using anonymous role: %s", config_anonymous_role
|
|
248
|
-
)
|
|
230
|
+
log.debug("Using anonymous role: %s", config_anonymous_role)
|
|
249
231
|
if "requestContext" not in event:
|
|
250
232
|
event["requestContext"] = {}
|
|
251
233
|
event["requestContext"]["authorizer"] = {
|
|
@@ -304,10 +286,7 @@ def token_decoder(
|
|
|
304
286
|
|
|
305
287
|
if auth_header:
|
|
306
288
|
auth_parts = auth_header.split(" ")
|
|
307
|
-
if (
|
|
308
|
-
len(auth_parts) == 2
|
|
309
|
-
and auth_parts[0].lower() == "bearer"
|
|
310
|
-
):
|
|
289
|
+
if len(auth_parts) == 2 and auth_parts[0].lower() == "bearer":
|
|
311
290
|
token = auth_parts[1]
|
|
312
291
|
log.debug("Token extracted, length: %d", len(token))
|
|
313
292
|
except Exception:
|
|
@@ -319,9 +298,7 @@ def token_decoder(
|
|
|
319
298
|
if token and wrapper._lambda_validator:
|
|
320
299
|
try:
|
|
321
300
|
log.debug("Attempting Lambda validation")
|
|
322
|
-
decoded_token = wrapper._lambda_validator.validate(
|
|
323
|
-
token
|
|
324
|
-
)
|
|
301
|
+
decoded_token = wrapper._lambda_validator.validate(token)
|
|
325
302
|
log.debug("Lambda validation successful")
|
|
326
303
|
except Exception as e:
|
|
327
304
|
log.warning("Lambda validation failed: %s", e)
|
|
@@ -331,9 +308,7 @@ def token_decoder(
|
|
|
331
308
|
if token and not decoded_token and wrapper._jwt_decoder:
|
|
332
309
|
try:
|
|
333
310
|
log.debug("Attempting JWKS validation")
|
|
334
|
-
decoded_token = wrapper._jwt_decoder.decode_token(
|
|
335
|
-
token
|
|
336
|
-
)
|
|
311
|
+
decoded_token = wrapper._jwt_decoder.decode_token(token)
|
|
337
312
|
log.debug("JWKS validation successful")
|
|
338
313
|
except Exception as e:
|
|
339
314
|
log.warning("JWKS validation failed: %s", e)
|
|
@@ -345,24 +320,18 @@ def token_decoder(
|
|
|
345
320
|
"Using anonymous role: %s",
|
|
346
321
|
wrapper._anonymous_role,
|
|
347
322
|
)
|
|
348
|
-
decoded_token = {
|
|
349
|
-
"roles": [wrapper._anonymous_role]
|
|
350
|
-
}
|
|
323
|
+
decoded_token = {"roles": [wrapper._anonymous_role]}
|
|
351
324
|
else:
|
|
352
325
|
log.error("No valid token and no anonymous access")
|
|
353
326
|
return {
|
|
354
327
|
"statusCode": 401,
|
|
355
328
|
"headers": {"Content-Type": "application/json"},
|
|
356
|
-
"body": json.dumps(
|
|
357
|
-
{"error": "Unauthorized"}
|
|
358
|
-
),
|
|
329
|
+
"body": json.dumps({"error": "Unauthorized"}),
|
|
359
330
|
}
|
|
360
331
|
|
|
361
332
|
log.debug(
|
|
362
333
|
"JWT token result: %s",
|
|
363
|
-
json.dumps(decoded_token, default=str)
|
|
364
|
-
if decoded_token
|
|
365
|
-
else "None",
|
|
334
|
+
json.dumps(decoded_token, default=str) if decoded_token else "None",
|
|
366
335
|
)
|
|
367
336
|
|
|
368
337
|
# Populate requestContext
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.8.49"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|