# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['service_repository',
 'service_repository.filters',
 'service_repository.interfaces',
 'service_repository.repositories']

package_data = \
{'': ['*']}

install_requires = \
['six>=1.16.0,<2.0.0']

setup_kwargs = {
    'name': 'service-repository',
    'version': '0.2.0',
    'description': 'Asynchronous abstract methods for service layer built into the repository pattern for SQLAlchemy or MongoDB with the same service layer.',
    'long_description': '# Abstract service layer built into the repository pattern for SQLAlchemy or MongoDB (asynchronous)\n\nAsynchronous abstract methods for service layer built into the repository pattern for SQLAlchemy or MongoDB with the same service layer.\n\nInherit the usual service methods:\n`pagination`, `create`, `get`, `update`, `delete`, `count`\n\n## Installation\n\n<div class="termy">\n\n```console\n$ pip install service-repository\n\n---> 100%\n```\n\n</div>\n\n## With SQLAlchemy\n\nCreate the model, as in the example [song/models.py](tests/song/models.py)\n\nCreate and extend of `BaseRepositorySqlalchemy` in you repository, as in the example [song/repositories.py](tests/song/repositories.py)\n\nCreate and extend you service, as in the example [song/services.py](tests/song/services.py)\n\nThe following is an example with the connection session, but pass the session as needed.\n\n```python\nimport pytest\nfrom sqlalchemy.ext.asyncio import AsyncSession, create_async_engine\nfrom sqlalchemy.orm import sessionmaker\nfrom sqlmodel import SQLModel\n\n\n# Get you connection session of SQLAlchemy, example next, but make as you\n@pytest.fixture()\nasync def sqlalchemy():\n    SQLALCHEMY_DATABASE_URI = "sqlite+aiosqlite:///./tests/testing.db"\n    engine = create_async_engine(\n        SQLALCHEMY_DATABASE_URI, future=True, echo=True\n    )\n    async_session = sessionmaker(\n        bind=engine, expire_on_commit=False, class_=AsyncSession\n    )\n    async with engine.begin() as conn:\n        await conn.run_sync(SQLModel.metadata.create_all)\n        yield async_session\n        await conn.run_sync(SQLModel.metadata.drop_all)\n```\n\nThe following is an example with register created.\n\n```python\nimport pytest\nfrom tests.song.services import SongService\nfrom tests.song.models import SongCreate\n\n\n@pytest.fixture\nasync def song_one(sqlalchemy):\n    song_create = {\n        "title": "Song title 1",\n        "is_active": True,\n    }\n    \n    async with sqlalchemy() as session:\n        song = await SongService(db=session).create(\n            schema_in=SongCreate(**song_create)\n        )\n    return song\n```\n\n#### Pagination with dynamic filter and sorting in the service.\n\n```python\nimport pytest\nfrom tests.song.services import SongService\n\n\n@pytest.mark.asyncio\nasync def test_song_service_with_or_and_asc_filter_paginate_song(sqlalchemy):\n    criteria = [\n        {"field": "foo", "op": "ilike", "value": "%bar%"},\n    ]\n    # Or with criteria in format or\n    criteria = [\n        {\n            "or": [\n                {"field": "title", "op": "==", "value": "Song title 2"},\n                {"field": "title", "op": "==", "value": "Song title 3"},\n            ]\n        }\n    ]\n    sort = [\n        {"field": "title", "direction": "asc"},\n    ]\n    async with sqlalchemy() as session:\n        pagination = await SongService(db=session).paginate(\n            page=1, per_page=5, criteria=criteria, sort=sort\n        )\n```\n\nOperator options for filtering are:\n\n`is_null, is_not_null, eq, ne, gt, lt, ge, le, like, ilike, not_ilike, in, not_in, any, not_any`\n\n#### Use the create extended method on service\n\n```python\nimport pytest\nfrom tests.song.services import SongService\nfrom tests.song.models import SongCreate\n\n\n@pytest.mark.asyncio\nasync def test_song_service_create_song(sqlalchemy):\n    song_data_one = {\n        "title": "Song title 1",\n        "is_active": True,\n    }\n    async with sqlalchemy() as session:\n        song = await SongService(db=session).create(\n            schema_in=SongCreate(**song_data_one)\n        )\n```\n\n#### Update method on service\n\n```python\nimport pytest\nfrom tests.song.services import SongService\nfrom tests.song.models import SongUpdate\n\n\n@pytest.mark.asyncio\nasync def test_song_service_update_song(sqlalchemy, song_one):\n    song_data = {\n        "title": "Song title 2",\n        "is_active": False,\n    }\n    async with sqlalchemy() as session:\n        song = await SongService(db=session).update(\n            instance=song_one, schema_in=SongUpdate(**song_data)\n        )\n```\n\n#### Get method on service to get one register\n\n```python\nimport pytest\nfrom tests.song.services import SongService\n\n\n@pytest.mark.asyncio\nasync def test_song_service_get_song(sqlalchemy, song_one):\n    async with sqlalchemy() as session:\n        song = await SongService(db=session).get(id=song_one.id)\n```\n\n#### Delete method on service\n\n```python\nimport pytest\nfrom tests.song.services import SongService\n\n\n@pytest.mark.asyncio\nasync def test_song_service_delete_song(sqlalchemy, song_one):\n    async with sqlalchemy() as session:\n        await SongService(db=session).delete(id=song_one.id)\n```\n\n#### Count method on service\n\n```python\nimport pytest\nfrom tests.song.services import SongService\n\n\n@pytest.mark.asyncio\nasync def test_song_service_count_song(sqlalchemy):\n    async with sqlalchemy() as session:\n        total = await SongService(db=session).count()\n```\n\n## With MongoDB\n\nCreate the model, as in the example [product/models.py](tests/product/models.py)\n\nCreate and extend of `BaseRepositoryMotor` in you repository, as in the example [product/repositories.py](tests/product/repositories.py)\n\nCreate and extend you service, as in the example [product/services.py](tests/product/services.py)\n\nThe following is an example with the connection session, but pass the session as needed.\n\n```python\nimport pytest\nfrom motor.motor_asyncio import AsyncIOMotorClient\n\n\n# Get you connection session of Motor, example next, but make as you\n@pytest.fixture()\nasync def motor():\n    client = AsyncIOMotorClient(\n        "localhost",\n        maxPoolSize=10,\n        minPoolSize=10,\n        tz_aware=True,\n    )\n    await client.drop_database("testing")\n    session = client.get_database("testing")\n    yield session\n```\n\nThe following is an example with register created.\n\n```python\nimport pytest\nfrom tests.product.models import ProductCreate\nfrom tests.product.services import ProductService\n\n\n@pytest.fixture\nasync def product_one(motor, product_data_one):\n    product_data = {\n        "title": "Product title 1",\n        "is_active": True,\n    }\n    product = await ProductService(db=motor).create(\n        schema_in=ProductCreate(**product_data)\n    )\n    return product\n```\n\n#### Pagination with dynamic filter and sorting in the service.\n\n```python\nimport pytest\nfrom tests.product.services import ProductService\n\n\n@pytest.mark.asyncio\nasync def test_product_service_with_or_and_asc_filter_paginate_product(\n    app, motor, product_data_one\n):\n    criteria = {"title": "Product title 1"}\n    sort = [("title", -1)]\n\n    pagination = await ProductService(db=motor).paginate(\n        page=1, per_page=5, criteria=criteria, sort=sort\n    )\n```\n\n#### Use the create extended method on service\n\n```python\nimport pytest\nfrom tests.product.models import ProductCreate\nfrom tests.product.services import ProductService\n\n\n@pytest.mark.asyncio\nasync def test_product_service_create_product(motor):\n    product_data = {\n        "title": "Product title 1",\n        "is_active": True,\n    }\n    product = await ProductService(db=motor).create(\n        schema_in=ProductCreate(**product_data)\n    )\n```\n\n#### Update method on service\n\n```python\nimport pytest\nfrom tests.product.models import ProductUpdate\nfrom tests.product.services import ProductService\n\n\n@pytest.mark.asyncio\nasync def test_product_service_update_product(motor, product_one):\n    product_data = {\n        "title": "Product title 2",\n        "is_active": False,\n    }\n    product = await ProductService(db=motor).update(\n        instance=product_one, schema_in=ProductUpdate(**product_data)\n    )\n```\n\n#### Get method on service to get one register\n\n```python\nimport pytest\nfrom tests.product.services import ProductService\n\n\n@pytest.mark.asyncio\nasync def test_product_service_get_product(motor, product_one):\n    product = await ProductService(db=motor).get(_id=product_one.id)\n```\n\n#### Delete method on service\n\n```python\nimport pytest\nfrom tests.product.services import ProductService\n\n\n@pytest.mark.asyncio\nasync def test_product_service_delete_product(motor, product_one):\n    await ProductService(db=motor).delete(_id=product_one.id)\n```\n\n#### Count method on service\n\n```python\nimport pytest\nfrom tests.product.services import ProductService\n\n\n@pytest.mark.asyncio\nasync def test_product_service_count_product(motor):\n    total = await ProductService(db=motor).count()\n```\n\n### Inherited base service layer with FastApi\n\nYou can create an inherited base service layer and add its methods, \nas in the example in FastApi with convenience methods for 404:\n\n```python\nfrom fastapi import HTTPException, status\nfrom pydantic import BaseModel\nfrom service_repository.services import BaseService as Base\n\n\nclass BaseService(Base):\n    """Class representing the base service."""\n    def __init__(self, db) -> None:\n        self.db = db\n\n    async def get_or_404(self, **kwargs):\n        """Get one instance by filter or 404 if not exists."""\n        instance = await self.get(**kwargs)\n        if not instance:\n            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)\n        return instance\n\n    async def update_or_404(self, schema_in: BaseModel, **kwargs):\n        """Update instance or 404 if not exists."""\n        instance = await self.get_or_404(**kwargs)\n        instance = await self.update(instance=instance, schema_in=schema_in)\n        return instance\n\n    async def delete_or_404(self, **kwargs):\n        """Delete one instance by filter or 404 if not exists."""\n        await self.get_or_404(**kwargs)\n        await self.delete(**kwargs)\n```\n\n### Inherited base service layer with Flask\n\nYou can create an inherited base service layer and add its methods, \nas in the example in Flask with convenience methods for 404:\n\n```python\nfrom flask import abort\nfrom pydantic import BaseModel\nfrom service_repository.services import BaseService as Base\n\n\nclass BaseService(Base):\n    """Class representing the base service."""\n    def __init__(self, db) -> None:\n        self.db = db\n\n    async def get_or_404(self, **kwargs):\n        """Get one instance by filter or 404 if not exists."""\n        instance = await self.get(**kwargs)\n        if not instance:\n            raise abort(404)\n        return instance\n\n    async def update_or_404(self, schema_in: BaseModel, **kwargs):\n        """Update instance or 404 if not exists."""\n        instance = await self.get_or_404(**kwargs)\n        instance = await self.update(instance=instance, schema_in=schema_in)\n        return instance\n\n    async def delete_or_404(self, **kwargs):\n        """Delete one instance by filter or 404 if not exists."""\n        await self.get_or_404(**kwargs)\n        await self.delete(**kwargs)\n```\n\n## Security\n\nIf you discover any security related issues, please email fndmiranda@gmail.com instead of using the issue tracker.\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.',
    'author': 'Fernando Miranda',
    'author_email': 'fndmiranda@gmail.com',
    'maintainer': 'None',
    'maintainer_email': 'None',
    'url': 'https://github.com/fndmiranda/service-repository',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.8.1,<4.0.0',
}


setup(**setup_kwargs)
