api-foundry-query-engine 0.0.1__py3-none-any.whl
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/adapters/adapter.py +67 -0
- api_foundry_query_engine/adapters/case_change_adapter.py +79 -0
- api_foundry_query_engine/adapters/gateway_adapter.py +112 -0
- api_foundry_query_engine/connectors/connection.py +32 -0
- api_foundry_query_engine/connectors/connection_factory.py +110 -0
- api_foundry_query_engine/connectors/oracle_connector.py +29 -0
- api_foundry_query_engine/connectors/postgres_connection.py +105 -0
- api_foundry_query_engine/dao/dao.py +23 -0
- api_foundry_query_engine/dao/operation_dao.py +141 -0
- api_foundry_query_engine/dao/sql_custom_query_handler.py +66 -0
- api_foundry_query_engine/dao/sql_delete_query_handler.py +36 -0
- api_foundry_query_engine/dao/sql_insert_query_handler.py +102 -0
- api_foundry_query_engine/dao/sql_query_handler.py +386 -0
- api_foundry_query_engine/dao/sql_select_query_handler.py +305 -0
- api_foundry_query_engine/dao/sql_subselect_query_handler.py +63 -0
- api_foundry_query_engine/dao/sql_update_query_handler.py +59 -0
- api_foundry_query_engine/handler.py +48 -0
- api_foundry_query_engine/operation.py +15 -0
- api_foundry_query_engine/services/service.py +60 -0
- api_foundry_query_engine/services/transactional_service.py +43 -0
- api_foundry_query_engine/utils/api_model.py +175 -0
- api_foundry_query_engine/utils/app_exception.py +22 -0
- api_foundry_query_engine/utils/logger.py +60 -0
- api_foundry_query_engine-0.0.1.dist-info/METADATA +14 -0
- api_foundry_query_engine-0.0.1.dist-info/RECORD +27 -0
- api_foundry_query_engine-0.0.1.dist-info/WHEEL +4 -0
- api_foundry_query_engine-0.0.1.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from api_foundry_query_engine.dao.sql_query_handler import SQLSchemaQueryHandler
|
|
4
|
+
from api_foundry_query_engine.dao.sql_select_query_handler import SQLSelectSchemaQueryHandler
|
|
5
|
+
from api_foundry_query_engine.operation import Operation
|
|
6
|
+
from api_foundry_query_engine.utils.api_model import SchemaObjectAssociation
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SQLSubselectSchemaQueryHandler(SQLSelectSchemaQueryHandler):
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
operation: Operation,
|
|
13
|
+
relation: SchemaObjectAssociation,
|
|
14
|
+
parent_generator: SQLSchemaQueryHandler,
|
|
15
|
+
) -> None:
|
|
16
|
+
super().__init__(
|
|
17
|
+
operation, relation.child_schema_object, parent_generator.engine
|
|
18
|
+
)
|
|
19
|
+
self.relation = relation
|
|
20
|
+
self.parent_generator = parent_generator
|
|
21
|
+
|
|
22
|
+
def selection_result_map(self) -> dict:
|
|
23
|
+
filter_str = self.operation.metadata_params.get("properties", "")
|
|
24
|
+
result = {self.relation.child_property: self.relation.child_property}
|
|
25
|
+
|
|
26
|
+
for relation_name, reg_exs in self.get_regex_map(filter_str).items():
|
|
27
|
+
if relation_name != self.relation.api_name:
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
schema_object = self.relation.child_schema_object
|
|
31
|
+
|
|
32
|
+
# Filter and prefix keys for the current entity and regular expressions
|
|
33
|
+
filtered_keys = self.filter_and_prefix_keys(
|
|
34
|
+
reg_exs, schema_object.properties
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Extend the result map with the filtered keys
|
|
38
|
+
result.update(filtered_keys)
|
|
39
|
+
|
|
40
|
+
return result
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def placeholders(self) -> dict:
|
|
44
|
+
return self.search_placeholders
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def sql(self) -> Optional[str]:
|
|
48
|
+
if len(self.select_list_columns) == 1: # then it only contains the key
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
sql = (
|
|
52
|
+
f"SELECT {self.select_list} "
|
|
53
|
+
+ f"FROM {self.relation.child_schema_object.table_name} "
|
|
54
|
+
+ f"WHERE {self.relation.child_property} "
|
|
55
|
+
+ f"IN ( SELECT {self.relation.parent_property} "
|
|
56
|
+
+ f"FROM {self.parent_generator.table_expression}"
|
|
57
|
+
+ f"{self.parent_generator.search_condition} "
|
|
58
|
+
# + f"{order_by} {limit} {offset})"
|
|
59
|
+
+ ")"
|
|
60
|
+
)
|
|
61
|
+
self.search_placeholders = self.parent_generator.search_placeholders
|
|
62
|
+
# self._execute_sql(args["cursor"], sql, query_parameters)
|
|
63
|
+
return sql
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from api_foundry_query_engine.dao.sql_query_handler import SQLSchemaQueryHandler
|
|
2
|
+
from api_foundry_query_engine.operation import Operation
|
|
3
|
+
from api_foundry_query_engine.utils.app_exception import ApplicationException
|
|
4
|
+
from api_foundry_query_engine.utils.api_model import SchemaObject
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SQLUpdateSchemaQueryHandler(SQLSchemaQueryHandler):
|
|
8
|
+
def __init__(
|
|
9
|
+
self, operation: Operation, schema_object: SchemaObject, engine: str
|
|
10
|
+
) -> None:
|
|
11
|
+
super().__init__(operation, schema_object, engine)
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def sql(self) -> str:
|
|
15
|
+
concurrency_property = self.schema_object.concurrency_property
|
|
16
|
+
if not concurrency_property:
|
|
17
|
+
return (
|
|
18
|
+
f"UPDATE {self.table_expression}{self.update_values}"
|
|
19
|
+
+ f"{self.search_condition} RETURNING {self.select_list}"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
if not self.operation.query_params.get(concurrency_property.api_name):
|
|
23
|
+
raise ApplicationException(
|
|
24
|
+
400,
|
|
25
|
+
"Missing required concurrency management property. "
|
|
26
|
+
+ f"schema_object: {self.schema_object.api_name}, "
|
|
27
|
+
+ f"property: {concurrency_property.api_name}",
|
|
28
|
+
)
|
|
29
|
+
if self.operation.store_params.get(concurrency_property.api_name):
|
|
30
|
+
raise ApplicationException(
|
|
31
|
+
400,
|
|
32
|
+
"For updating concurrency managed schema objects the current version "
|
|
33
|
+
+ " may not be supplied as a storage parameter. "
|
|
34
|
+
+ f"schema_object: {self.schema_object.api_name}, "
|
|
35
|
+
+ f"property: {concurrency_property.api_name}",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return f"UPDATE {self.table_expression}{self.update_values}, {concurrency_property.column_name} = {self.concurrency_generator(concurrency_property)} {self.search_condition} RETURNING {self.select_list}" # noqa E501
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def update_values(self) -> str:
|
|
42
|
+
self.store_placeholders = {}
|
|
43
|
+
columns = []
|
|
44
|
+
|
|
45
|
+
for name, value in self.operation.store_params.items():
|
|
46
|
+
try:
|
|
47
|
+
property = self.schema_object.properties[name]
|
|
48
|
+
except KeyError:
|
|
49
|
+
raise ApplicationException(
|
|
50
|
+
400, f"Search condition column not found {name}"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
placeholder = property.api_name
|
|
54
|
+
column_name = property.column_name
|
|
55
|
+
|
|
56
|
+
columns.append(f"{column_name} = {self.placeholder(property, placeholder)}")
|
|
57
|
+
self.store_placeholders[placeholder] = property.convert_to_db_value(value)
|
|
58
|
+
|
|
59
|
+
return f" SET {', '.join(columns)}"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
from api_foundry_query_engine.utils.app_exception import ApplicationException
|
|
7
|
+
from api_foundry_query_engine.utils.api_model import APIModel
|
|
8
|
+
|
|
9
|
+
from api_foundry_query_engine.adapters.gateway_adapter import GatewayAdapter
|
|
10
|
+
|
|
11
|
+
log = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
api_model = None
|
|
14
|
+
adapter = GatewayAdapter()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def lambda_handler(event, _):
|
|
18
|
+
log.debug(f"event: {event}")
|
|
19
|
+
try:
|
|
20
|
+
if not api_model:
|
|
21
|
+
with open(os.environ.get("API_SPEC", "/var/task/api_spec.yaml"), "r") as file:
|
|
22
|
+
api_model = APIModel(yaml.safe_load(file))
|
|
23
|
+
|
|
24
|
+
response = adapter.process_event(event)
|
|
25
|
+
|
|
26
|
+
# Ensure the response conforms to API Gateway requirements
|
|
27
|
+
return {
|
|
28
|
+
"isBase64Encoded": False,
|
|
29
|
+
"statusCode": 200,
|
|
30
|
+
"headers": {"Content-Type": "application/json"},
|
|
31
|
+
"body": json.dumps(response),
|
|
32
|
+
}
|
|
33
|
+
except ApplicationException as e:
|
|
34
|
+
log.error(f"exception: {e}", exc_info=True)
|
|
35
|
+
return {
|
|
36
|
+
"isBase64Encoded": False,
|
|
37
|
+
"statusCode": e.status_code,
|
|
38
|
+
"headers": {"Content-Type": "application/json"},
|
|
39
|
+
"body": json.dumps({"message": f"exception: {e}"}),
|
|
40
|
+
}
|
|
41
|
+
except Exception as e:
|
|
42
|
+
log.error(f"exception: {e}", exc_info=True)
|
|
43
|
+
return {
|
|
44
|
+
"isBase64Encoded": False,
|
|
45
|
+
"statusCode": 500,
|
|
46
|
+
"headers": {"Content-Type": "application/json"},
|
|
47
|
+
"body": json.dumps({"message": f"exception: {e}"}),
|
|
48
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class Operation:
|
|
2
|
+
def __init__(
|
|
3
|
+
self,
|
|
4
|
+
*,
|
|
5
|
+
entity: str,
|
|
6
|
+
action: str,
|
|
7
|
+
query_params={},
|
|
8
|
+
store_params={},
|
|
9
|
+
metadata_params={},
|
|
10
|
+
):
|
|
11
|
+
self.entity = entity
|
|
12
|
+
self.action = action
|
|
13
|
+
self.query_params = query_params
|
|
14
|
+
self.store_params = store_params
|
|
15
|
+
self.metadata_params = metadata_params
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from api_foundry_query_engine.utils.logger import logger
|
|
6
|
+
from api_foundry_query_engine.operation import Operation
|
|
7
|
+
|
|
8
|
+
log = logger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Service:
|
|
12
|
+
def execute(self, operation: Operation) -> list[dict]:
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ServiceAdapter(Service):
|
|
17
|
+
def execute(self, operation):
|
|
18
|
+
super().execute(operation)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MutationPublisher(ServiceAdapter):
|
|
22
|
+
def execute(self, operation):
|
|
23
|
+
result = super().execute(operation)
|
|
24
|
+
self.publish_notification(operation)
|
|
25
|
+
return result
|
|
26
|
+
|
|
27
|
+
def publish_notification(self, operation):
|
|
28
|
+
topic_arn = os.environ.get("BROADCAST_TOPIC", None)
|
|
29
|
+
log.debug(f"Topic ARN: {topic_arn}")
|
|
30
|
+
|
|
31
|
+
if topic_arn is not None:
|
|
32
|
+
log.debug("Sending message")
|
|
33
|
+
message = {
|
|
34
|
+
"entity": operation.api_name,
|
|
35
|
+
"action": operation.action,
|
|
36
|
+
"store_params": operation.store_params,
|
|
37
|
+
"query_params": operation.query_params,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
message_str = json.dumps({"default": json.dumps(message)})
|
|
41
|
+
log.debug(f"message_str: {message_str}")
|
|
42
|
+
hash_object = hashlib.sha256(message_str.encode("utf-8"))
|
|
43
|
+
hex_dig = hash_object.hexdigest()
|
|
44
|
+
|
|
45
|
+
msg_id = self.__client("sns").publish(
|
|
46
|
+
TopicArn=topic_arn,
|
|
47
|
+
MessageStructure="json",
|
|
48
|
+
MessageDeduplicationId=hex_dig,
|
|
49
|
+
MessageGroupId=operation.api_name,
|
|
50
|
+
Message=message_str,
|
|
51
|
+
)
|
|
52
|
+
log.info(f"publish msg id {msg_id}")
|
|
53
|
+
|
|
54
|
+
def __client(client_type, region: str = os.environ.get("AWS_REGION", "us-east-1")):
|
|
55
|
+
import boto3
|
|
56
|
+
|
|
57
|
+
session = boto3.session.Session()
|
|
58
|
+
if session:
|
|
59
|
+
return session.client(client_type, region_name=region)
|
|
60
|
+
return boto3.client(client_type, region_name=region)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import traceback
|
|
2
|
+
|
|
3
|
+
from api_foundry_query_engine.utils.logger import logger
|
|
4
|
+
from api_foundry_query_engine.utils.app_exception import ApplicationException
|
|
5
|
+
from api_foundry_query_engine.operation import Operation
|
|
6
|
+
from api_foundry_query_engine.services.service import ServiceAdapter
|
|
7
|
+
from api_foundry_query_engine.connectors.connection_factory import connection_factory
|
|
8
|
+
from api_foundry_query_engine.dao.operation_dao import OperationDAO
|
|
9
|
+
from api_foundry_query_engine.utils.api_model import get_path_operation, get_schema_object
|
|
10
|
+
|
|
11
|
+
log = logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TransactionalService(ServiceAdapter):
|
|
15
|
+
def execute(self, operation: Operation):
|
|
16
|
+
path_operation = get_path_operation(operation.entity, operation.action)
|
|
17
|
+
if path_operation:
|
|
18
|
+
database = path_operation.database
|
|
19
|
+
else:
|
|
20
|
+
schema_object = get_schema_object(operation.entity)
|
|
21
|
+
if schema_object:
|
|
22
|
+
database = schema_object.database
|
|
23
|
+
else:
|
|
24
|
+
raise ApplicationException(500, f"Unknown operation: {operation.entity}")
|
|
25
|
+
|
|
26
|
+
connection = connection_factory.get_connection(database)
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
result = None
|
|
30
|
+
cursor = connection.cursor()
|
|
31
|
+
try:
|
|
32
|
+
result = OperationDAO(operation, connection.engine()).execute(cursor)
|
|
33
|
+
finally:
|
|
34
|
+
cursor.close()
|
|
35
|
+
if operation.action != "read":
|
|
36
|
+
connection.commit()
|
|
37
|
+
return result
|
|
38
|
+
except Exception as error:
|
|
39
|
+
log.error(f"transaction exception: {error}")
|
|
40
|
+
log.error(f"traceback: {traceback.format_exc()}")
|
|
41
|
+
raise error
|
|
42
|
+
finally:
|
|
43
|
+
connection.close()
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Any, Dict, Optional
|
|
3
|
+
|
|
4
|
+
from api_foundry_query_engine.utils.logger import logger
|
|
5
|
+
|
|
6
|
+
log = logger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SchemaObjectProperty:
|
|
10
|
+
"""Represents a property of a schema object."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, data: Dict[str, Any]):
|
|
13
|
+
self.api_name = data.get("api_name")
|
|
14
|
+
self.column_name = data.get("column_name")
|
|
15
|
+
self.type = data.get("type")
|
|
16
|
+
self.api_type = data.get("api_type")
|
|
17
|
+
self.column_type = data.get("column_type")
|
|
18
|
+
self.required = data.get("required", False)
|
|
19
|
+
self.min_length = data.get("min_length")
|
|
20
|
+
self.max_length = data.get("max_length")
|
|
21
|
+
self.pattern = data.get("pattern")
|
|
22
|
+
self.default = data.get("default")
|
|
23
|
+
self.key_type = data.get("key_type")
|
|
24
|
+
self.sequence_name = data.get("sequence_name")
|
|
25
|
+
self.concurrency_control = data.get("concurrency_control")
|
|
26
|
+
|
|
27
|
+
def __repr__(self):
|
|
28
|
+
return f"SchemaObjectProperty(api_name={self.api_name}, column_name={self.column_name}, type={self.type})"
|
|
29
|
+
|
|
30
|
+
def convert_to_db_value(self, value: str) -> Optional[Any]:
|
|
31
|
+
if value is None:
|
|
32
|
+
return None
|
|
33
|
+
conversion_mapping = {
|
|
34
|
+
"string": lambda x: x,
|
|
35
|
+
"number": float,
|
|
36
|
+
"float": float,
|
|
37
|
+
"integer": int,
|
|
38
|
+
"boolean": lambda x: x.lower() == "true",
|
|
39
|
+
"date": lambda x: datetime.strptime(x, "%Y-%m-%d").date() if x else None,
|
|
40
|
+
"date-time": lambda x: datetime.fromisoformat(x) if x else None,
|
|
41
|
+
"time": lambda x: datetime.strptime(x, "%H:%M:%S").time() if x else None,
|
|
42
|
+
}
|
|
43
|
+
conversion_func = conversion_mapping.get(self.column_type, lambda x: x)
|
|
44
|
+
return conversion_func(value)
|
|
45
|
+
|
|
46
|
+
def convert_to_api_value(self, value) -> Optional[Any]:
|
|
47
|
+
if value is None:
|
|
48
|
+
return None
|
|
49
|
+
conversion_mapping = {
|
|
50
|
+
"string": lambda x: x,
|
|
51
|
+
"number": float,
|
|
52
|
+
"float": float,
|
|
53
|
+
"integer": int,
|
|
54
|
+
"boolean": str,
|
|
55
|
+
"date": lambda x: x.date().isoformat() if x else None,
|
|
56
|
+
"date-time": lambda x: x.isoformat() if x else None,
|
|
57
|
+
"time": lambda x: x.time().isoformat() if x else None,
|
|
58
|
+
}
|
|
59
|
+
conversion_func = conversion_mapping.get(self.api_type, lambda x: x)
|
|
60
|
+
return conversion_func(value)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class SchemaObjectAssociation:
|
|
65
|
+
"""Represents an association (relationship) between schema objects."""
|
|
66
|
+
|
|
67
|
+
def __init__(self, parent_schema: str, data: Dict[str, Any]):
|
|
68
|
+
self.parent_schema = parent_schema
|
|
69
|
+
self.schema_name = data.get("schema_name")
|
|
70
|
+
self.api_name = data.get("api_name")
|
|
71
|
+
self.type = data.get("type")
|
|
72
|
+
self._child_property = data.get("child_property")
|
|
73
|
+
self._parent_property = data.get("parent_property")
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def child_property(self) -> str:
|
|
77
|
+
return self._child_property if self._child_property else get_schema_object(self.schema_name).primary_key.column_name
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def parent_property(self) -> str:
|
|
81
|
+
return self._parent_property if self._parent_property else get_schema_object(self.parent_schema).primary_key.column_name
|
|
82
|
+
|
|
83
|
+
def __repr__(self):
|
|
84
|
+
return f"SchemaObjectAssociation(name={self.api_name}, child_property={self._child_property}, parent_property={self.parent_property})"
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def child_schema_object(self) -> "SchemaObject":
|
|
88
|
+
return get_schema_object(self.schema_name)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class SchemaObject:
|
|
93
|
+
"""Represents a schema object in the API configuration."""
|
|
94
|
+
|
|
95
|
+
def __init__(self, data: Dict[str, Any]):
|
|
96
|
+
self.api_name = data.get("api_name")
|
|
97
|
+
self.database = data.get("database")
|
|
98
|
+
self.table_name = data.get("table_name")
|
|
99
|
+
self.properties = {
|
|
100
|
+
name: SchemaObjectProperty(prop_data)
|
|
101
|
+
for name, prop_data in data.get("properties", {}).items()
|
|
102
|
+
}
|
|
103
|
+
self.relations = {
|
|
104
|
+
name: SchemaObjectAssociation(self.api_name, assoc_data)
|
|
105
|
+
for name, assoc_data in data.get("relations", {}).items()
|
|
106
|
+
}
|
|
107
|
+
self.concurrency_property = (
|
|
108
|
+
self.properties[data.get("concurrency_property")]
|
|
109
|
+
if data.get("concurrency_property")
|
|
110
|
+
else None
|
|
111
|
+
)
|
|
112
|
+
self._primary_key = data.get("primary_key")
|
|
113
|
+
|
|
114
|
+
def __repr__(self):
|
|
115
|
+
return f"SchemaObject(table_name={self.table_name}, primary_key={self.primary_key})"
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def primary_key(self):
|
|
119
|
+
return self.properties.get(self._primary_key)
|
|
120
|
+
|
|
121
|
+
class PathOperation:
|
|
122
|
+
"""Represents a path operation in the API configuration."""
|
|
123
|
+
|
|
124
|
+
def __init__(self, data: Dict[str, Any]):
|
|
125
|
+
self.entity = data["entity"]
|
|
126
|
+
self.action = data["action"]
|
|
127
|
+
self.sql = data["sql"]
|
|
128
|
+
self.database = data["database"]
|
|
129
|
+
self.inputs = {
|
|
130
|
+
name: SchemaObjectProperty(input_data)
|
|
131
|
+
for name, input_data in data.get("inputs", {}).items()
|
|
132
|
+
}
|
|
133
|
+
self.outputs = {
|
|
134
|
+
name: SchemaObjectProperty(output_data)
|
|
135
|
+
for name, output_data in data.get("outputs", {}).items()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
def __repr__(self):
|
|
139
|
+
return f"PathOperation(entity={self.entity}, method={self.method})"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
schema_objects = None
|
|
143
|
+
path_operations = None
|
|
144
|
+
|
|
145
|
+
def get_schema_object(name: str) -> Optional[SchemaObject]:
|
|
146
|
+
"""Returns a schema object by name."""
|
|
147
|
+
global schema_objects
|
|
148
|
+
return schema_objects.get(name)
|
|
149
|
+
|
|
150
|
+
def get_path_operation(path: str, method:str) -> Optional[PathOperation]:
|
|
151
|
+
"""Returns a path operation by name."""
|
|
152
|
+
log.info(f"path: {path}, method: {method}")
|
|
153
|
+
global path_operations
|
|
154
|
+
return path_operations.get(f"{path}_{method}")
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class APIModel:
|
|
158
|
+
"""Class to load and expose the API configuration as objects."""
|
|
159
|
+
|
|
160
|
+
def __init__(self, config: Dict[str, Any]):
|
|
161
|
+
print("building api_model")
|
|
162
|
+
global schema_objects
|
|
163
|
+
schema_objects = {
|
|
164
|
+
name: SchemaObject(schema_data)
|
|
165
|
+
for name, schema_data in config.get("schema_objects", {}).items()
|
|
166
|
+
}
|
|
167
|
+
global path_operations
|
|
168
|
+
path_operations = {
|
|
169
|
+
name: PathOperation(path_data)
|
|
170
|
+
for name, path_data in config.get("path_operations", {}).items()
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
def __repr__(self):
|
|
174
|
+
return f"APIModel(schema_objects={list(self.schema_objects.keys())}, path_operations={list(self.path_operations.keys())})"
|
|
175
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class ApplicationException(Exception):
|
|
2
|
+
"""Custom exception class for application errors."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, status_code: int, message: str):
|
|
5
|
+
"""
|
|
6
|
+
Initialize the ApplicationException.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
- status_code (int): The HTTP status code associated with the
|
|
10
|
+
exception.
|
|
11
|
+
- message (str): The error message.
|
|
12
|
+
"""
|
|
13
|
+
super().__init__(message)
|
|
14
|
+
self.status_code = status_code
|
|
15
|
+
self.message = message
|
|
16
|
+
|
|
17
|
+
def __str__(self):
|
|
18
|
+
"""Return a string representation of the exception."""
|
|
19
|
+
return (
|
|
20
|
+
f"ApplicationException(status_code={self.status_code}, "
|
|
21
|
+
+ f"message='{self.message}')"
|
|
22
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
# Configuring the logging module with basic settings, including format and log level,
|
|
5
|
+
# where the log level is obtained from the environment variable LOGGING_LEVEL
|
|
6
|
+
# with a default of DEBUG, and force=True to ensure the configuration is applied immediately.
|
|
7
|
+
logging.basicConfig(
|
|
8
|
+
format="%(name)s:%(lineno)s - %(levelname)s - %(message)s",
|
|
9
|
+
level=os.getenv("LOGGING_LEVEL", "DEBUG").upper(),
|
|
10
|
+
force=True,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
WARN = logging.WARN
|
|
14
|
+
INFO = logging.INFO
|
|
15
|
+
DEBUG = logging.DEBUG
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def logger(name=None):
|
|
19
|
+
"""
|
|
20
|
+
Function to create a logger with a specified name or default name.
|
|
21
|
+
|
|
22
|
+
Parameters:
|
|
23
|
+
name (str): Name of the logger. If not provided, the root logger is returned.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
logging.Logger: Logger object with the specified name or the root logger.
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
# Retrieving the logging level from the environment variable LOGGING_LEVEL
|
|
30
|
+
# with a default of DEBUG, and converting it to uppercase
|
|
31
|
+
loggingLevel = os.getenv("LOGGING_LEVEL", "DEBUG").upper()
|
|
32
|
+
|
|
33
|
+
# Setting the logging level for the root logger to the obtained logging level
|
|
34
|
+
logging.getLogger().setLevel(loggingLevel)
|
|
35
|
+
|
|
36
|
+
# Returning a logger object with the specified name or the root logger
|
|
37
|
+
return logging.getLogger(name)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def write_logging_file(file_name, content):
|
|
41
|
+
"""
|
|
42
|
+
Function to write a given string to a file in the temp/logging folder.
|
|
43
|
+
|
|
44
|
+
Parameters:
|
|
45
|
+
file_name (str): Name of the file to write the content to.
|
|
46
|
+
content (str): The string content to write to the file.
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
# Define the directory path
|
|
50
|
+
dir_path = os.path.join("temp", "logging")
|
|
51
|
+
|
|
52
|
+
# Ensure the directory exists
|
|
53
|
+
os.makedirs(dir_path, exist_ok=True)
|
|
54
|
+
|
|
55
|
+
# Define the file path
|
|
56
|
+
file_path = os.path.join(dir_path, file_name)
|
|
57
|
+
|
|
58
|
+
# Write the content to the file
|
|
59
|
+
with open(file_path, "w") as file:
|
|
60
|
+
file.write(content + "\n")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: api-foundry-query-engine
|
|
3
|
+
Version: 0.0.1
|
|
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
|
+
Project-URL: Documentation, https://github.com/DanRepik/api-foundry
|
|
6
|
+
Project-URL: Source, https://github.com/DanRepik/api-foundry
|
|
7
|
+
Author-email: Daniel Repik <danrepik@icloud.com>
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Requires-Dist: boto3
|
|
13
|
+
Requires-Dist: pyhumps
|
|
14
|
+
Requires-Dist: pyyaml
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
api_foundry_query_engine/handler.py,sha256=f7mo67G4rFE-WZq8nldrWbt2qjGR_OXygbXz6_Oh5us,1535
|
|
2
|
+
api_foundry_query_engine/operation.py,sha256=otkRKUfyTW2mWpy5mt2tM7k1w_a-4S068sjYtOeNqRM,374
|
|
3
|
+
api_foundry_query_engine/adapters/adapter.py,sha256=c4X7U3zWh5feR4NbIAPL-oY7-hLd2tiHITGPSXR7ZMQ,1901
|
|
4
|
+
api_foundry_query_engine/adapters/case_change_adapter.py,sha256=JhVOl3b4Dp2n5-u_-4MaO9FgCwCOnDzQ0oyJJYcTAMc,2314
|
|
5
|
+
api_foundry_query_engine/adapters/gateway_adapter.py,sha256=bUZvDM28QEEVjFnI1SBrwyY9iazY-QD1hQgMyBc3wq4,3168
|
|
6
|
+
api_foundry_query_engine/connectors/connection.py,sha256=aGm6R5wt1MW8_5wsrPYW6DI3jnaDhFc9a6CYzLuNVlk,702
|
|
7
|
+
api_foundry_query_engine/connectors/connection_factory.py,sha256=3Dj5qYT_bwzfl8XxQzqoe32qkGioXYo65BfQT3fBckM,3958
|
|
8
|
+
api_foundry_query_engine/connectors/oracle_connector.py,sha256=EtRoOqM8puUS62hoKyd2Gu2ghwPHXbXCtdlnOP50UcU,1020
|
|
9
|
+
api_foundry_query_engine/connectors/postgres_connection.py,sha256=0yM5Q6jaIT2hrYuiQxMftZkMYihrefMQVFKpn6XC4OE,3349
|
|
10
|
+
api_foundry_query_engine/dao/dao.py,sha256=aBFJ0WT0PhMitCJa5_PRBjDeBNj9hLDePRa-awnLtRg,684
|
|
11
|
+
api_foundry_query_engine/dao/operation_dao.py,sha256=LZN22vZ0WVjLIQk0SjSFw4aiU1_Ej2k7luPITwVS9eo,5376
|
|
12
|
+
api_foundry_query_engine/dao/sql_custom_query_handler.py,sha256=C7lXAzOkN4TdVByoOXZ2qLrc8vznilkbHh7mOiYNl5M,2423
|
|
13
|
+
api_foundry_query_engine/dao/sql_delete_query_handler.py,sha256=WByOIUQ83qlzcSFgppxxOWTodvV1cj7t3JZo2u3Oqbs,1656
|
|
14
|
+
api_foundry_query_engine/dao/sql_insert_query_handler.py,sha256=VF1TRsodNUoTR6MzOaujHTslLjEttg2poBj_z5e-WP8,4423
|
|
15
|
+
api_foundry_query_engine/dao/sql_query_handler.py,sha256=r7Car7KznJ3gI2E5bzjgw2TH4Sew12rWG3pAb4nAfno,12566
|
|
16
|
+
api_foundry_query_engine/dao/sql_select_query_handler.py,sha256=ZwqBE0cibsOT-AKrY1DMBwreqRIhytTe0oVcxGJk3vE,11841
|
|
17
|
+
api_foundry_query_engine/dao/sql_subselect_query_handler.py,sha256=URDe-p4k2csP5ncGnJkPQCxwITpWGXRqzXJH5st4Wgw,2392
|
|
18
|
+
api_foundry_query_engine/dao/sql_update_query_handler.py,sha256=1gz44r-PhpqGamC_subZ0zSSd5Wr9EnZQLkQoEj0-jg,2585
|
|
19
|
+
api_foundry_query_engine/services/service.py,sha256=0imMvYgNVgNPlyiZ70pC1ZhIvFCCZIcj8igtGum07Mg,1915
|
|
20
|
+
api_foundry_query_engine/services/transactional_service.py,sha256=Pf08l7uhlvZ2JqFkBb89FDMXnwj_OjWhQDFLUAu3PxM,1692
|
|
21
|
+
api_foundry_query_engine/utils/api_model.py,sha256=xB8IY-y2FgE08llWTkm6EDDc-qyAD_mKh54FByfaGjI,6388
|
|
22
|
+
api_foundry_query_engine/utils/app_exception.py,sha256=8F5DEL8ovY0vJBXE3RERTA26u7kthcSX0VqViplZhdE,704
|
|
23
|
+
api_foundry_query_engine/utils/logger.py,sha256=24u7AAVyzaPuS2nHSCq1SDemerJ8pCF5lde2NtIbwuo,1892
|
|
24
|
+
api_foundry_query_engine-0.0.1.dist-info/METADATA,sha256=dqfJMtvB1Ruoy-3djxzfTcsfyMqADpubZpIcSwgJ3yw,761
|
|
25
|
+
api_foundry_query_engine-0.0.1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
26
|
+
api_foundry_query_engine-0.0.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
27
|
+
api_foundry_query_engine-0.0.1.dist-info/RECORD,,
|