eaasy 0.1.0__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.
@@ -0,0 +1,7 @@
1
+
2
+ global-exclude __pycache__
3
+ global-exclude *.py[cod]
4
+ global-exclude *.so
5
+ global-exclude *.dylib
6
+ global-exclude *.pyd
7
+ app.py
eaasy-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,35 @@
1
+ Metadata-Version: 2.1
2
+ Name: eaasy
3
+ Version: 0.1.0
4
+ Summary: Build your e-commerce ea(a)sily
5
+ Home-page: https://github.com/ciulene/eaas
6
+ Author: Giuliano Errico
7
+ Author-email: errgioul2@gmail.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.6
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: aniso8601==9.0.1
14
+ Requires-Dist: attrs==24.2.0
15
+ Requires-Dist: blinker==1.9.0
16
+ Requires-Dist: click==8.1.7
17
+ Requires-Dist: Flask==3.1.0
18
+ Requires-Dist: Flask-Cors==5.0.0
19
+ Requires-Dist: flask-restx==1.3.0
20
+ Requires-Dist: gunicorn==23.0.0
21
+ Requires-Dist: importlib_resources==6.4.5
22
+ Requires-Dist: itsdangerous==2.2.0
23
+ Requires-Dist: Jinja2==3.1.4
24
+ Requires-Dist: jsonschema==4.23.0
25
+ Requires-Dist: jsonschema-specifications==2024.10.1
26
+ Requires-Dist: MarkupSafe==3.0.2
27
+ Requires-Dist: packaging==24.2
28
+ Requires-Dist: pytz==2024.2
29
+ Requires-Dist: referencing==0.35.1
30
+ Requires-Dist: rpds-py==0.22.3
31
+ Requires-Dist: setuptools==75.6.0
32
+ Requires-Dist: Werkzeug==3.1.3
33
+ Requires-Dist: wheel==0.45.1
34
+
35
+ # E-commerce As A Software
eaasy-0.1.0/README.md ADDED
@@ -0,0 +1 @@
1
+ # E-commerce As A Software
@@ -0,0 +1,13 @@
1
+ __author__ = 'Giuliano Errico'
2
+
3
+ from .eaas import Eaas, GunEaas
4
+ from .domain.db import Base, BaseEntity, PrimaryKey, Audit
5
+
6
+ __all__ = [
7
+ 'Eaas',
8
+ 'GunEaas',
9
+ 'Base',
10
+ 'BaseEntity',
11
+ 'PrimaryKey',
12
+ 'Audit'
13
+ ]
@@ -0,0 +1,5 @@
1
+ from .liveness import liveness_ns
2
+
3
+ __all__ = [
4
+ 'liveness_ns'
5
+ ]
@@ -0,0 +1,10 @@
1
+ from flask_restx import Namespace, Resource
2
+
3
+ liveness_ns = Namespace("Liveness", description="Liveness", path="/liveness")
4
+
5
+ @liveness_ns.route('')
6
+ class Liveness(Resource):
7
+ def get(self):
8
+ '''Liveness'''
9
+
10
+ return 'Ok', 200
@@ -0,0 +1,68 @@
1
+ from flask import Flask, request
2
+ from flask_restx import Api, Namespace
3
+ from flask_cors import CORS
4
+ from eaas.api import liveness_ns
5
+ from gunicorn.app.base import BaseApplication
6
+ import logging
7
+
8
+
9
+ class Eaas:
10
+ def __init__(self, logger:logging.Logger | bool | None = None, *args, **kwargs):
11
+ self.logger = self.__get_logger(logger)
12
+ name = kwargs.get('name', __name__)
13
+ version = kwargs.get('version', '1.0')
14
+ title = kwargs.get('title', 'API')
15
+ description = kwargs.get('description', 'A simple API')
16
+ doc=kwargs.get('doc', '/swagger')
17
+ self.app = Flask(name)
18
+ CORS(self.app, resources={r"/*": {"origins": "*"}}, supports_credentials=True)
19
+ self.api = Api( self.app, version=version, title=title, description=description, doc=doc)
20
+ self.app.before_request(self.__log_request)
21
+
22
+ def __get_logger(self, logger:logging.Logger | bool | None) -> logging.Logger | None:
23
+ if logger is None:
24
+ return None
25
+ if isinstance(logger, bool):
26
+ if logger:
27
+ logging.basicConfig(
28
+ level=logging.INFO,
29
+ format='%(asctime)s %(levelname)s %(message)s'
30
+ )
31
+ return logging.getLogger(__name__)
32
+ return None
33
+ return logger
34
+
35
+ def __log_request(self):
36
+ if self.logger: self.logger.info(
37
+ f'{request.method} {request.endpoint} {request.remote_addr} {request.user_agent}'
38
+ )
39
+
40
+ def run(self):
41
+ self.app.run()
42
+
43
+ def add_default_namespaces(self):
44
+ self.api.add_namespace(liveness_ns)
45
+
46
+ def add_namespace(self, namespace: Namespace):
47
+ self.api.add_namespace(namespace)
48
+
49
+ def create_app(self):
50
+ return self.app
51
+
52
+
53
+ class GunEaas(BaseApplication):
54
+ def __init__(self, app: Flask, options: dict | None = None):
55
+ self.options = options or {}
56
+ self.application = app
57
+ super().__init__()
58
+
59
+ def load_config(self):
60
+ config = {
61
+ key: value for key, value in self.options.items()
62
+ if self.cfg is not None and key in self.cfg.settings and value is not None}
63
+ if self.cfg is not None:
64
+ for key, value in config.items():
65
+ self.cfg.set(key.lower(), value)
66
+
67
+ def load(self):
68
+ return self.application
@@ -0,0 +1,3 @@
1
+ from .builder import buil_model
2
+
3
+ __all__ = ['buil_model']
@@ -0,0 +1,41 @@
1
+ from eaas import BaseEntity
2
+ from flask_restx import Namespace, Model, OrderedModel, fields
3
+ from datetime import datetime, timezone
4
+
5
+ def buil_model(entity: BaseEntity) -> tuple[Namespace, Model | OrderedModel]:
6
+ namespace = Namespace(entity.__name__, description=f"{entity.__name__} operations", path=f"/{entity.__name__.lower()}")
7
+ model_name = entity.__name__
8
+ model_properties = {}
9
+ column_list = entity.column_list()
10
+ for prop in column_list:
11
+ if not prop.name.startswith('_'):
12
+ if f'{prop.type}' == 'INTEGER':
13
+ model_properties[prop.name] = fields.Integer(
14
+ required=not prop.nullable,
15
+ description=prop.name,
16
+ default=0 if not prop.nullable else None,
17
+ example=0)
18
+ elif f'{prop.type}' == 'VARCHAR':
19
+ model_properties[prop.name] = fields.String(
20
+ required=not prop.nullable,
21
+ description=prop.name,
22
+ default='' if not prop.nullable else None,
23
+ example='')
24
+ elif f'{prop.type}' == 'DATETIME':
25
+ model_properties[prop.name] = fields.DateTime(
26
+ required=not prop.nullable,
27
+ description=prop.name,
28
+ default=datetime.now(timezone.utc) if not prop.nullable else None,
29
+ example=datetime.now(timezone.utc))
30
+ elif f'{prop.type}' == 'BOOLEAN':
31
+ model_properties[prop.name] = fields.Boolean(
32
+ required=not prop.nullable,
33
+ description=prop.name,
34
+ default=False,
35
+ example=False)
36
+ else:
37
+ print(f"\033[33mType {prop.type} not supported\033[0m")
38
+
39
+ model = namespace.model(model_name, model_properties)
40
+
41
+ return namespace, model
@@ -0,0 +1,35 @@
1
+ Metadata-Version: 2.1
2
+ Name: eaasy
3
+ Version: 0.1.0
4
+ Summary: Build your e-commerce ea(a)sily
5
+ Home-page: https://github.com/ciulene/eaas
6
+ Author: Giuliano Errico
7
+ Author-email: errgioul2@gmail.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.6
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: aniso8601==9.0.1
14
+ Requires-Dist: attrs==24.2.0
15
+ Requires-Dist: blinker==1.9.0
16
+ Requires-Dist: click==8.1.7
17
+ Requires-Dist: Flask==3.1.0
18
+ Requires-Dist: Flask-Cors==5.0.0
19
+ Requires-Dist: flask-restx==1.3.0
20
+ Requires-Dist: gunicorn==23.0.0
21
+ Requires-Dist: importlib_resources==6.4.5
22
+ Requires-Dist: itsdangerous==2.2.0
23
+ Requires-Dist: Jinja2==3.1.4
24
+ Requires-Dist: jsonschema==4.23.0
25
+ Requires-Dist: jsonschema-specifications==2024.10.1
26
+ Requires-Dist: MarkupSafe==3.0.2
27
+ Requires-Dist: packaging==24.2
28
+ Requires-Dist: pytz==2024.2
29
+ Requires-Dist: referencing==0.35.1
30
+ Requires-Dist: rpds-py==0.22.3
31
+ Requires-Dist: setuptools==75.6.0
32
+ Requires-Dist: Werkzeug==3.1.3
33
+ Requires-Dist: wheel==0.45.1
34
+
35
+ # E-commerce As A Software
@@ -0,0 +1,15 @@
1
+ MANIFEST.in
2
+ README.md
3
+ setup.py
4
+ eaas/__init__.py
5
+ eaas/eaas.py
6
+ eaas/api/__init__.py
7
+ eaas/api/liveness.py
8
+ eaas/extensions/__init__.py
9
+ eaas/extensions/builder.py
10
+ eaasy.egg-info/PKG-INFO
11
+ eaasy.egg-info/SOURCES.txt
12
+ eaasy.egg-info/dependency_links.txt
13
+ eaasy.egg-info/requires.txt
14
+ eaasy.egg-info/top_level.txt
15
+ tests/test_builder.py
@@ -0,0 +1,21 @@
1
+ aniso8601==9.0.1
2
+ attrs==24.2.0
3
+ blinker==1.9.0
4
+ click==8.1.7
5
+ Flask==3.1.0
6
+ Flask-Cors==5.0.0
7
+ flask-restx==1.3.0
8
+ gunicorn==23.0.0
9
+ importlib_resources==6.4.5
10
+ itsdangerous==2.2.0
11
+ Jinja2==3.1.4
12
+ jsonschema==4.23.0
13
+ jsonschema-specifications==2024.10.1
14
+ MarkupSafe==3.0.2
15
+ packaging==24.2
16
+ pytz==2024.2
17
+ referencing==0.35.1
18
+ rpds-py==0.22.3
19
+ setuptools==75.6.0
20
+ Werkzeug==3.1.3
21
+ wheel==0.45.1
@@ -0,0 +1 @@
1
+ eaas
eaasy-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
eaasy-0.1.0/setup.py ADDED
@@ -0,0 +1,24 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ def parse_requirements(filename):
4
+ with open(filename, 'r') as file:
5
+ return [line.strip() for line in file if line.strip() and not line.startswith('#')]
6
+
7
+ setup(
8
+ name='eaasy',
9
+ version='0.1.0',
10
+ packages=find_packages(),
11
+ install_requires=parse_requirements('requirements.txt'),
12
+ author='Giuliano Errico',
13
+ author_email='errgioul2@gmail.com',
14
+ description='Build your e-commerce ea(a)sily',
15
+ long_description=open('README.md').read(),
16
+ long_description_content_type='text/markdown',
17
+ url='https://github.com/ciulene/eaas',
18
+ classifiers=[
19
+ 'Programming Language :: Python :: 3',
20
+ 'License :: OSI Approved :: MIT License',
21
+ 'Operating System :: OS Independent',
22
+ ],
23
+ python_requires='>=3.6',
24
+ )
@@ -0,0 +1,115 @@
1
+ from unittest import TestCase
2
+ from unittest.mock import MagicMock, patch
3
+ from eaas.extensions import buil_model
4
+ from flask_restx import fields, Namespace, Model
5
+
6
+ class MockProperty:
7
+ def __init__(self, name, type, nullable):
8
+ self.name = name
9
+ self.type = type
10
+ self.nullable = nullable
11
+
12
+ @patch("builtins.print")
13
+ class TestBuilder(TestCase):
14
+ def setUp(self, *_):
15
+ # Create a mock BaseEntity
16
+ self.mock_entity = MagicMock()
17
+ self.mock_entity.__name__ = "TestEntity"
18
+ self.mock_entity.column_list.return_value = [
19
+ MockProperty("id", "INTEGER", False),
20
+ MockProperty("name", "VARCHAR", True),
21
+ MockProperty("email", "VARCHAR", False),
22
+ MockProperty("created_at", "DATETIME", False),
23
+ MockProperty("deleted_at", "DATETIME", True),
24
+ MockProperty("is_active", "BOOLEAN", True),
25
+ MockProperty("unsupported_field", "UNSUPPORTED", True)
26
+ ]
27
+
28
+ def test_building_model_returns_expected_namespace(self, *_):
29
+ # Act
30
+ namespace, _ = buil_model(self.mock_entity)
31
+
32
+ # Assert
33
+ self.assertIsInstance(namespace, Namespace)
34
+ self.assertEqual(namespace.name, self.mock_entity.__name__)
35
+ self.assertEqual(namespace.description, "TestEntity operations")
36
+ self.assertEqual(namespace.path, "/testentity")
37
+
38
+ def test_building_model_returns_expected_properties(self, *_):
39
+ # Act
40
+ _, model = buil_model(self.mock_entity)
41
+
42
+ # Assert
43
+ self.assertIsInstance(model, Model)
44
+ self.assertIn("id", model)
45
+ self.assertIn("name", model)
46
+ self.assertIn("created_at", model)
47
+ self.assertIn("is_active", model)
48
+
49
+ def test_building_model_returns_expected_integer(self, *_):
50
+ # Act
51
+ _, model = buil_model(self.mock_entity)
52
+
53
+ # Assert
54
+ id_field = model["id"]
55
+ self.assertIsInstance(id_field, fields.Integer)
56
+ self.assertTrue(id_field.required)
57
+ self.assertEqual(id_field.default, 0)
58
+ self.assertEqual(id_field.example, 0)
59
+
60
+ def test_building_model_returns_expected_nullable_string(self, *_):
61
+ # Act
62
+ _, model = buil_model(self.mock_entity)
63
+
64
+ # Assert
65
+ name_field = model["name"]
66
+ self.assertIsInstance(name_field, fields.String)
67
+ self.assertFalse(name_field.required)
68
+ self.assertIsNone(name_field.default)
69
+
70
+ def test_building_model_returns_expected_required_string(self, *_):
71
+ # Act
72
+ _, model = buil_model(self.mock_entity)
73
+
74
+ # Assert
75
+ email_field = model["email"]
76
+ self.assertIsInstance(email_field, fields.String)
77
+ self.assertTrue(email_field.required)
78
+ self.assertEqual(email_field.default, "")
79
+
80
+ def test_building_model_returns_expected_nullable_datetime(self, *_):
81
+ # Act
82
+ _, model = buil_model(self.mock_entity)
83
+
84
+ # Assert
85
+ created_at_field = model["deleted_at"]
86
+ self.assertIsInstance(created_at_field, fields.DateTime)
87
+ self.assertFalse(created_at_field.required)
88
+ self.assertIsNone(created_at_field.default)
89
+
90
+ def test_building_model_returns_expected_required_datetime(self, *_):
91
+ # Act
92
+ _, model = buil_model(self.mock_entity)
93
+
94
+ # Assert
95
+ deleted_at_field = model["created_at"]
96
+ self.assertIsInstance(deleted_at_field, fields.DateTime)
97
+ self.assertTrue(deleted_at_field.required)
98
+ self.assertIsNotNone(deleted_at_field.default)
99
+
100
+ def test_building_model_returns_expected_boolean(self, *_):
101
+ # Act
102
+ _, model = buil_model(self.mock_entity)
103
+
104
+ # Assert
105
+ is_active_field = model["is_active"]
106
+ self.assertIsInstance(is_active_field, fields.Boolean)
107
+ self.assertFalse(is_active_field.required)
108
+ self.assertEqual(is_active_field.default, False)
109
+
110
+ def test_building_model_logs_unsupported_types(self, *_):
111
+ # Act
112
+ _, model = buil_model(self.mock_entity)
113
+
114
+ # Assert
115
+ self.assertNotIn("unsupported_field", model)