api-foundry-query-engine 0.8.1__tar.gz → 0.8.2__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.1 → api_foundry_query_engine-0.8.2}/.gitignore +2 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/PKG-INFO +1 -1
- api_foundry_query_engine-0.8.2/api_foundry_query_engine/.pre-commit-config.yaml +22 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/adapters/adapter.py +6 -3
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/connectors/connection_factory.py +0 -1
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/dao/operation_dao.py +5 -4
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/dao/sql_insert_query_handler.py +12 -3
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/dao/sql_select_query_handler.py +2 -2
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/dao/sql_update_query_handler.py +13 -6
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/lambda_handler.py +1 -4
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/services/service.py +1 -4
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/services/transactional_service.py +2 -2
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/utils/api_model.py +25 -9
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/pyproject.toml +1 -1
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/LICENSE +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/__init__.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/adapters/case_change_adapter.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/adapters/gateway_adapter.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/adapters/security_adapter.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/connectors/athena_connector.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/connectors/connection.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/connectors/oracle_connector.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/connectors/postgres_connection.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/dao/dao.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/dao/sql_custom_query_handler.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/dao/sql_delete_query_handler.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/dao/sql_query_handler.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/dao/sql_subselect_query_handler.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/operation.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/services/security_service.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/utils/app_exception.py +0 -0
- {api_foundry_query_engine-0.8.1 → api_foundry_query_engine-0.8.2}/api_foundry_query_engine/utils/logger.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.2
|
|
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,22 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v3.4.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-added-large-files
|
|
9
|
+
|
|
10
|
+
- repo: https://github.com/psf/black
|
|
11
|
+
rev: 23.12.1
|
|
12
|
+
hooks:
|
|
13
|
+
- id: black
|
|
14
|
+
language_version: python3.11
|
|
15
|
+
|
|
16
|
+
- repo: https://github.com/PyCQA/flake8
|
|
17
|
+
rev: 7.0.0
|
|
18
|
+
hooks:
|
|
19
|
+
- id: flake8
|
|
20
|
+
args: [--max-line-length=120]
|
|
21
|
+
|
|
22
|
+
exclude: '^$'
|
|
@@ -10,7 +10,6 @@ log = logger(__name__)
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class Adapter(metaclass=abc.ABCMeta):
|
|
13
|
-
|
|
14
13
|
service: Service
|
|
15
14
|
config: Mapping[str, str]
|
|
16
15
|
|
|
@@ -23,9 +22,13 @@ class Adapter(metaclass=abc.ABCMeta):
|
|
|
23
22
|
and callable(__subclass.unmarshal)
|
|
24
23
|
)
|
|
25
24
|
|
|
26
|
-
def __init__(
|
|
25
|
+
def __init__(
|
|
26
|
+
self, config: Mapping[str, str] = {}, service: Optional[Service] = None
|
|
27
|
+
) -> None:
|
|
27
28
|
self.config = config
|
|
28
|
-
self.service =
|
|
29
|
+
self.service = (
|
|
30
|
+
service if service is not None else TransactionalService(config=self.config)
|
|
31
|
+
)
|
|
29
32
|
|
|
30
33
|
def unmarshal(self, event) -> Operation:
|
|
31
34
|
"""
|
|
@@ -20,7 +20,10 @@ from api_foundry_query_engine.utils.app_exception import ApplicationException
|
|
|
20
20
|
from api_foundry_query_engine.dao.dao import DAO
|
|
21
21
|
from api_foundry_query_engine.connectors.connection import Cursor
|
|
22
22
|
from api_foundry_query_engine.operation import Operation
|
|
23
|
-
from api_foundry_query_engine.utils.api_model import
|
|
23
|
+
from api_foundry_query_engine.utils.api_model import (
|
|
24
|
+
get_path_operation,
|
|
25
|
+
get_schema_object,
|
|
26
|
+
)
|
|
24
27
|
from api_foundry_query_engine.dao.sql_query_handler import SQLQueryHandler
|
|
25
28
|
|
|
26
29
|
|
|
@@ -83,9 +86,7 @@ class OperationDAO(DAO):
|
|
|
83
86
|
)
|
|
84
87
|
return self._query_handler
|
|
85
88
|
|
|
86
|
-
def execute(
|
|
87
|
-
self, connector, operation=None
|
|
88
|
-
) -> Union[list[dict], dict]:
|
|
89
|
+
def execute(self, connector, operation=None) -> Union[list[dict], dict]:
|
|
89
90
|
"""
|
|
90
91
|
Execute the database operation based on the provided connector.
|
|
91
92
|
|
|
@@ -73,10 +73,15 @@ class SQLInsertSchemaQueryHandler(SQLSchemaQueryHandler):
|
|
|
73
73
|
allowed_property_names = self.check_permissions(
|
|
74
74
|
"write", self.schema_object.permissions, self.schema_object.properties
|
|
75
75
|
)
|
|
76
|
-
allowed_properties = {
|
|
76
|
+
allowed_properties = {
|
|
77
|
+
k: v
|
|
78
|
+
for k, v in self.schema_object.properties.items()
|
|
79
|
+
if k in allowed_property_names
|
|
80
|
+
}
|
|
77
81
|
log.info(f"allowed properties: {allowed_properties}")
|
|
78
82
|
|
|
79
83
|
import json
|
|
84
|
+
|
|
80
85
|
for name, value in self.operation.store_params.items():
|
|
81
86
|
parts = name.split(".")
|
|
82
87
|
|
|
@@ -98,13 +103,17 @@ class SQLInsertSchemaQueryHandler(SQLSchemaQueryHandler):
|
|
|
98
103
|
|
|
99
104
|
columns.append(property.column_name)
|
|
100
105
|
if property.api_name is None:
|
|
101
|
-
raise ApplicationException(
|
|
106
|
+
raise ApplicationException(
|
|
107
|
+
400, f"Property '{name}' does not have a valid api_name."
|
|
108
|
+
)
|
|
102
109
|
placeholders.append(self.placeholder(property, property.api_name))
|
|
103
110
|
# Serialize embedded objects to JSON
|
|
104
111
|
if property.api_type == "object":
|
|
105
112
|
self.store_placeholders[property.api_name] = json.dumps(value)
|
|
106
113
|
else:
|
|
107
|
-
self.store_placeholders[
|
|
114
|
+
self.store_placeholders[
|
|
115
|
+
property.api_name
|
|
116
|
+
] = property.convert_to_db_value(value)
|
|
108
117
|
|
|
109
118
|
if self.key_property:
|
|
110
119
|
if self.key_property.key_type == "sequence":
|
|
@@ -70,7 +70,7 @@ class SQLSelectSchemaQueryHandler(SQLSchemaQueryHandler):
|
|
|
70
70
|
if self.schema_object.api_name is None:
|
|
71
71
|
raise ApplicationException(
|
|
72
72
|
500,
|
|
73
|
-
"schema_object.api_name is None, cannot use as key in prefix_map"
|
|
73
|
+
"schema_object.api_name is None, cannot use as key in prefix_map",
|
|
74
74
|
)
|
|
75
75
|
prefix = self.prefix_map[self.schema_object.api_name]
|
|
76
76
|
except KeyError:
|
|
@@ -80,7 +80,7 @@ class SQLSelectSchemaQueryHandler(SQLSchemaQueryHandler):
|
|
|
80
80
|
+ "schema object: "
|
|
81
81
|
+ str(self.schema_object.api_name)
|
|
82
82
|
+ ", property: "
|
|
83
|
-
+ name
|
|
83
|
+
+ name,
|
|
84
84
|
)
|
|
85
85
|
|
|
86
86
|
assignment, holders = self.search_value_assignment(property, value, prefix)
|
|
@@ -42,29 +42,36 @@ class SQLUpdateSchemaQueryHandler(SQLSchemaQueryHandler):
|
|
|
42
42
|
allowed_property_names = self.check_permissions(
|
|
43
43
|
"write", self.schema_object.permissions, self.schema_object.properties
|
|
44
44
|
)
|
|
45
|
-
allowed_properties = {
|
|
45
|
+
allowed_properties = {
|
|
46
|
+
k: v
|
|
47
|
+
for k, v in self.schema_object.properties.items()
|
|
48
|
+
if k in allowed_property_names
|
|
49
|
+
}
|
|
46
50
|
self.store_placeholders = {}
|
|
47
51
|
columns = []
|
|
48
52
|
invalid_columns = []
|
|
49
53
|
|
|
50
54
|
import json
|
|
55
|
+
|
|
51
56
|
for name, value in self.operation.store_params.items():
|
|
52
57
|
property = allowed_properties.get(name, None)
|
|
53
58
|
if property is None:
|
|
54
59
|
invalid_columns.append(name)
|
|
55
60
|
continue
|
|
56
61
|
|
|
57
|
-
placeholder =
|
|
62
|
+
placeholder = (
|
|
63
|
+
str(property.api_name) if property.api_name is not None else name
|
|
64
|
+
)
|
|
58
65
|
column_name = property.column_name
|
|
59
66
|
|
|
60
|
-
columns.append(
|
|
61
|
-
f"{column_name} = {self.placeholder(property, placeholder)}"
|
|
62
|
-
)
|
|
67
|
+
columns.append(f"{column_name} = {self.placeholder(property, placeholder)}")
|
|
63
68
|
# Serialize embedded objects to JSON
|
|
64
69
|
if property.api_type == "object":
|
|
65
70
|
self.store_placeholders[placeholder] = json.dumps(value)
|
|
66
71
|
else:
|
|
67
|
-
self.store_placeholders[placeholder] = property.convert_to_db_value(
|
|
72
|
+
self.store_placeholders[placeholder] = property.convert_to_db_value(
|
|
73
|
+
value
|
|
74
|
+
)
|
|
68
75
|
|
|
69
76
|
if invalid_columns:
|
|
70
77
|
raise ApplicationException(
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
from fileinput import filename
|
|
2
1
|
import json
|
|
3
2
|
import logging
|
|
4
3
|
import os
|
|
5
4
|
from typing import Optional, Mapping
|
|
6
|
-
import yaml
|
|
7
5
|
|
|
8
6
|
from api_foundry_query_engine.utils.app_exception import ApplicationException
|
|
9
7
|
from api_foundry_query_engine.adapters.gateway_adapter import GatewayAdapter
|
|
10
|
-
from api_foundry_query_engine.utils.api_model import APIModel
|
|
11
8
|
|
|
12
9
|
log = logging.getLogger(__name__)
|
|
13
10
|
|
|
14
11
|
engine_config: Optional[Mapping[str, str]] = None
|
|
15
12
|
adapter: Optional[GatewayAdapter] = None
|
|
16
13
|
|
|
17
|
-
class QueryEngine:
|
|
18
14
|
|
|
15
|
+
class QueryEngine:
|
|
19
16
|
def __init__(self, config: Mapping[str, str]):
|
|
20
17
|
self.adapter = GatewayAdapter(config)
|
|
21
18
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
import json
|
|
3
|
-
import os
|
|
4
3
|
|
|
5
4
|
from api_foundry_query_engine.utils.logger import logger
|
|
6
5
|
from api_foundry_query_engine.operation import Operation
|
|
@@ -8,7 +7,6 @@ from api_foundry_query_engine.operation import Operation
|
|
|
8
7
|
log = logger(__name__)
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
|
|
12
10
|
class Service:
|
|
13
11
|
def __init__(self, config: dict = {}):
|
|
14
12
|
self.config = config
|
|
@@ -17,7 +15,6 @@ class Service:
|
|
|
17
15
|
raise NotImplementedError
|
|
18
16
|
|
|
19
17
|
|
|
20
|
-
|
|
21
18
|
class ServiceAdapter(Service):
|
|
22
19
|
def __init__(self, config: dict = {}):
|
|
23
20
|
super().__init__(config)
|
|
@@ -26,7 +23,6 @@ class ServiceAdapter(Service):
|
|
|
26
23
|
return super().execute(operation)
|
|
27
24
|
|
|
28
25
|
|
|
29
|
-
|
|
30
26
|
class MutationPublisher(ServiceAdapter):
|
|
31
27
|
def __init__(self, config: dict = {}):
|
|
32
28
|
super().__init__(config)
|
|
@@ -65,6 +61,7 @@ class MutationPublisher(ServiceAdapter):
|
|
|
65
61
|
|
|
66
62
|
def __client(self, client_type):
|
|
67
63
|
import boto3
|
|
64
|
+
|
|
68
65
|
region = self.config.get("AWS_REGION", "us-east-1")
|
|
69
66
|
session = boto3.Session()
|
|
70
67
|
if session:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import traceback
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Mapping
|
|
3
3
|
|
|
4
4
|
from api_foundry_query_engine.utils.logger import logger
|
|
5
5
|
from api_foundry_query_engine.utils.app_exception import ApplicationException
|
|
@@ -19,7 +19,7 @@ class TransactionalService(ServiceAdapter):
|
|
|
19
19
|
def __init__(self, config: Mapping[str, str]):
|
|
20
20
|
super().__init__()
|
|
21
21
|
self.config = config
|
|
22
|
-
self.connection_factory = ConnectionFactory(config)
|
|
22
|
+
self.connection_factory = ConnectionFactory(config)
|
|
23
23
|
|
|
24
24
|
def execute(self, operation: Operation) -> list[dict]:
|
|
25
25
|
path_operation = get_path_operation(operation.entity, operation.action)
|
|
@@ -4,25 +4,27 @@ import yaml
|
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from typing import Any, Dict, Mapping, Optional
|
|
6
6
|
|
|
7
|
-
from api_foundry_query_engine.operation import Operation
|
|
8
7
|
from api_foundry_query_engine.utils.logger import logger
|
|
9
8
|
|
|
10
9
|
log = logger(__name__)
|
|
11
10
|
|
|
12
11
|
api_model = None
|
|
13
12
|
|
|
13
|
+
|
|
14
14
|
def get_schema_object(name: str) -> Optional["SchemaObject"]:
|
|
15
15
|
global api_model
|
|
16
16
|
if api_model is None:
|
|
17
17
|
return None
|
|
18
18
|
return api_model.schema_objects.get(name)
|
|
19
19
|
|
|
20
|
+
|
|
20
21
|
def get_path_operation(path: str, method: str) -> Optional["PathOperation"]:
|
|
21
22
|
global api_model
|
|
22
23
|
if api_model is None:
|
|
23
24
|
return None
|
|
24
25
|
return api_model.path_operations.get(f"{path}_{method}")
|
|
25
26
|
|
|
27
|
+
|
|
26
28
|
class SchemaObjectProperty:
|
|
27
29
|
"""Represents a property of a schema object."""
|
|
28
30
|
|
|
@@ -57,7 +59,9 @@ class SchemaObjectProperty:
|
|
|
57
59
|
"date-time": lambda x: datetime.fromisoformat(x) if x else None,
|
|
58
60
|
"time": lambda x: datetime.strptime(x, "%H:%M:%S").time() if x else None,
|
|
59
61
|
}
|
|
60
|
-
conversion_func = conversion_mapping.get(
|
|
62
|
+
conversion_func = conversion_mapping.get(
|
|
63
|
+
self.column_type if self.column_type is not None else "string", lambda x: x
|
|
64
|
+
)
|
|
61
65
|
return conversion_func(value)
|
|
62
66
|
|
|
63
67
|
def convert_to_api_value(self, value) -> Optional[Any]:
|
|
@@ -73,7 +77,9 @@ class SchemaObjectProperty:
|
|
|
73
77
|
"date-time": lambda x: x.isoformat() if x else None,
|
|
74
78
|
"time": lambda x: x.time().isoformat() if x else None,
|
|
75
79
|
}
|
|
76
|
-
conversion_func = conversion_mapping.get(
|
|
80
|
+
conversion_func = conversion_mapping.get(
|
|
81
|
+
self.api_type if self.api_type is not None else "string", lambda x: x
|
|
82
|
+
)
|
|
77
83
|
return conversion_func(value)
|
|
78
84
|
|
|
79
85
|
|
|
@@ -101,7 +107,9 @@ class SchemaObjectAssociation:
|
|
|
101
107
|
raise ValueError(f"Primary key not defined for schema '{self.schema_name}'")
|
|
102
108
|
column_name = getattr(child_schema.primary_key, "column_name", None)
|
|
103
109
|
if column_name is None:
|
|
104
|
-
raise ValueError(
|
|
110
|
+
raise ValueError(
|
|
111
|
+
f"Primary key property does not have 'column_name' for schema '{self.schema_name}'"
|
|
112
|
+
)
|
|
105
113
|
return column_name
|
|
106
114
|
|
|
107
115
|
@property
|
|
@@ -112,10 +120,14 @@ class SchemaObjectAssociation:
|
|
|
112
120
|
if not parent_schema_obj:
|
|
113
121
|
raise ValueError(f"SchemaObject '{self.parent_schema}' not found")
|
|
114
122
|
if not parent_schema_obj.primary_key:
|
|
115
|
-
raise ValueError(
|
|
123
|
+
raise ValueError(
|
|
124
|
+
f"Primary key not defined for schema '{self.parent_schema}'"
|
|
125
|
+
)
|
|
116
126
|
column_name = getattr(parent_schema_obj.primary_key, "column_name", None)
|
|
117
127
|
if column_name is None:
|
|
118
|
-
raise ValueError(
|
|
128
|
+
raise ValueError(
|
|
129
|
+
f"Primary key property does not have 'column_name' for schema '{self.parent_schema}'"
|
|
130
|
+
)
|
|
119
131
|
return column_name
|
|
120
132
|
|
|
121
133
|
def __repr__(self):
|
|
@@ -147,7 +159,9 @@ class SchemaObject:
|
|
|
147
159
|
for name, prop_data in data.get("properties", {}).items()
|
|
148
160
|
}
|
|
149
161
|
self.relations = {
|
|
150
|
-
name: SchemaObjectAssociation(
|
|
162
|
+
name: SchemaObjectAssociation(
|
|
163
|
+
self.api_name if self.api_name is not None else "", assoc_data
|
|
164
|
+
)
|
|
151
165
|
for name, assoc_data in data.get("relations", {}).items()
|
|
152
166
|
}
|
|
153
167
|
self.concurrency_property = (
|
|
@@ -208,18 +222,20 @@ class APIModel:
|
|
|
208
222
|
return None
|
|
209
223
|
return self.path_operations.get(f"{path}_{method}")
|
|
210
224
|
|
|
211
|
-
|
|
212
225
|
def __repr__(self):
|
|
213
226
|
return (
|
|
214
227
|
f"APIModel(schema_objects={list(self.schema_objects.keys())}, "
|
|
215
228
|
+ f"path_operations={list(self.path_operations.keys())})"
|
|
216
229
|
)
|
|
217
230
|
|
|
231
|
+
|
|
218
232
|
def set_api_model(engine_config: Mapping[str, str]):
|
|
219
233
|
global api_model
|
|
220
234
|
if api_model is None:
|
|
221
235
|
if engine_config.get("API_SPEC"):
|
|
222
236
|
api_model = APIModel(yaml.safe_load(engine_config["API_SPEC"]))
|
|
223
237
|
else:
|
|
224
|
-
with open(
|
|
238
|
+
with open(
|
|
239
|
+
os.environ.get("API_SPEC", "/var/task/api_spec.yaml"), "r"
|
|
240
|
+
) as file:
|
|
225
241
|
api_model = APIModel(yaml.safe_load(file))
|
|
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
|