Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.0.0 #17

Merged
merged 14 commits into from
Sep 20, 2023
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
run: pip install poetry

- name: Install Dependencies
run: poetry config virtualenvs.create false && poetry install
run: poetry config virtualenvs.create false && poetry install -E database -E redis

- name: Test
run: pytest
11 changes: 11 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

default:
@echo "=== Utils FastApi Core === \n"
@echo " make lint = Run linters"
@echo " make test = Run tests and generate coverage"

lint:
@./scripts/lint.sh

test:
@pytest
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<p align="center">
<a href=""><img src="docs/assets/logo-full.png" alt="FastAPI"></a>
</p>
<p align="center">
<em>FastAPI Core is <b>Reusable core</b>, <b>repositories</b> and <b>utilities</b> for FastAPI + SQLModel</em>
</p>
<p align="center">
<a href="https://pypi.org/project/fastapi_core" target="_blank">
<img src="https://img.shields.io/pypi/v/fastapi_core" alt="Package version">
</a>
<a href="https://pypi.org/project/fastapi_core" target="_blank">
<img src="https://img.shields.io/pypi/l/fastapi_core" alt="Package version">
</a>
<a href="https://pypi.org/project/fastapi_core" target="_blank">
<img src="https://img.shields.io/pypi/pyversions/fastapi_core.svg" alt="Supported Python versions">
</a>
</p>

---

[//]: # (**Documentation**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>)

**Source Code
**: <a href="https://github.com/yurihartmann/fastapi-core" target="_blank">https://github.com/yurihartmann/fastapi-core </a>

---

**Libs Used:**

**FastApi**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>

**SQLModel**: <a href="https://sqlmodel.tiangolo.com" target="_blank">https://sqlmodel.tiangolo.com</a>

---

FastApi Core has a reusable core with dependencies injections, database connection, repositories, talkers, cache,
buckets, queues, logger and more utils...

The key features are:

* **Database Connection**: Has a database connection simplify with master and read only
* **Fast to code**: With reusable, you do not need implement every feature
* **Repository**: Designed to focus in your rules and abstract all interactions with databases
* **Talkers**: Simplify integration with other APIs
* **Cache**: Abstract cache and implement for use
* **Buckets**: Abstract buckets and implement for use
* **Logger**: Logger simple by default

## How use

To see how use and example of service using FastApi Core go to this repository:

**Fast Food API
**: <a href="https://github.com/yurihartmann/fast-food-api" target="_blank">https://github.com/yurihartmann/fast-food-api </a>
14 changes: 6 additions & 8 deletions docs/NEXT_STEPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
- [x] Pagination
- [x] FastAPI runner
- [x] Ruff
- [ ] Unittests
- [ ] Talker (integrations with APIs) with Circuit Breaker
- [ ] Migration Router
- [X] Talker (integrations with APIs)
- [X] Migration Router
- [ ] HealthCheck Router
- [ ] Render Doc Router
- [ ] Cache Drivers
- [ ] Authentication
- [ ] Worker Manager
- [x] CSV Exporter
- [X] Cache Drivers
- [x] CSV Exporter
- [ ] trace open telemetri
- [ ] Unittests
Binary file added docs/assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/logo-full.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/logo-only-name.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 0 additions & 38 deletions fastapi_core/app_settings.py

This file was deleted.

2 changes: 2 additions & 0 deletions fastapi_core/bucket_driver/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .bucket_driver_abc import BucketDriverABC
from .s3_bucket_driver import S3BucketDriver
13 changes: 13 additions & 0 deletions fastapi_core/bucket_driver/bucket_driver_abc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from abc import ABC, abstractmethod
from tempfile import TemporaryFile


class BucketDriverABC(ABC):

@abstractmethod
async def download_file(self, file_path: str, *args, **kwargs) -> TemporaryFile:
"""Not Implemented"""

@abstractmethod
async def upload_file(self, file: bytes, file_path: str, *args, **kwargs):
"""Not Implemented"""
31 changes: 31 additions & 0 deletions fastapi_core/bucket_driver/s3_bucket_driver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from tempfile import TemporaryFile

from platform_core.bucket_driver.bucket_driver_abc import BucketDriverABC


class S3BucketDriver(BucketDriverABC):

def __init__(self, s3_client, bucket: str):
self.s3_client = s3_client
self.bucket = bucket

async def download_file(self, file_path: str, *args, **kwargs) -> TemporaryFile:
file = TemporaryFile()
self.s3_client.download_fileobj(
Bucket=self.bucket,
Key=file_path,
Fileobj=file,
*args,
**kwargs
)
file.seek(0)
return file

async def upload_file(self, file: bytes, file_path: str, *args, **kwargs):
self.s3_client.put_object(
Body=file,
Bucket=self.bucket,
Key=file_path,
*args,
**kwargs
)
Empty file.
62 changes: 62 additions & 0 deletions fastapi_core/cache_driver/cache_driver_abc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import json
from abc import ABC, abstractmethod
from typing import Union, List, Dict

from fastapi_core.utils.app_dependencies_abc import AppDependenciesABC


class CacheDriverABC(AppDependenciesABC, ABC):
def __init__(self, namespace_prefix: str = ""):
self.namespace_prefix = namespace_prefix

def get_key_for_namespace(self, key: str) -> str:
return self.namespace_prefix + ":" + key

def get_keys_for_namespace(self, keys: List[str]) -> List[str]:
keys_with_namespace = []
for key in keys:
keys_with_namespace.append(self.get_key_for_namespace(key))
return keys_with_namespace

@abstractmethod
async def keys(self) -> List[str]:
"""Not Implemented"""

@abstractmethod
async def get(self, key: str) -> Union[bytes, None]:
"""Not Implemented"""

async def get_dict(self, key: str) -> dict | None:
value = await self.get(key=key)

if value:
return json.loads(value)

return value

@abstractmethod
async def get_many(self, keys: List[str]) -> Dict[str, bytes]:
"""Not Implemented"""

@abstractmethod
async def set(self, key: str, value, seconds_for_expire: int = 600) -> None:
"""Not Implemented"""

async def set_dict(self, key: str, data: dict, seconds_for_expire: int = 600):
await self.set(key=key, value=json.dumps(data), seconds_for_expire=seconds_for_expire)

@abstractmethod
async def set_many(self, mapped_data: Dict[str, str], seconds_for_expire: int = 600) -> None:
"""Not Implemented"""

@abstractmethod
async def dump(self, key: str) -> None:
"""Not Implemented"""

@abstractmethod
async def dump_prefix(self, key_prefix: str) -> None:
"""Not Implemented"""

@abstractmethod
async def flush_for_namespace(self) -> None:
"""Not Implemented"""
80 changes: 80 additions & 0 deletions fastapi_core/cache_driver/in_memory_driver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import dataclasses
import datetime
from typing import Union, List, Dict

from fastapi_core.cache_driver.cache_driver_abc import CacheDriverABC
from fastapi_core.utils.app_dependencies_abc import AppDependenciesABC


@dataclasses.dataclass
class CacheData:
value: bytes
seconds_for_expire: int
datetime: datetime


class InMemoryCacheDriver(CacheDriverABC, AppDependenciesABC):
async def is_ready(self) -> bool:
return True

_memory: dict = {}

def keys(self) -> List[str]:
return list(self._memory.keys())

async def get(self, key: str) -> Union[bytes, None]:
key = self.get_key_for_namespace(key)
cache_data: CacheData = self._memory.get(key)

if not cache_data:
return None

if ((cache_data.datetime - datetime.datetime.now()).total_seconds() * -1) > cache_data.seconds_for_expire:
del self._memory[key]
return None

return cache_data.value

async def get_many(self, keys: List[str]) -> Dict[str, bytes]:
result_dict = {}
for key in keys:
result = await self.get(key)
if result is not None:
result_dict[key] = result
return result_dict

async def set(self, key: str, value, seconds_for_expire: int = 600) -> None:
key = self.get_key_for_namespace(key)
self._memory.update(
{
key: CacheData(
value=str(value).encode(), seconds_for_expire=seconds_for_expire, datetime=datetime.datetime.now()
)
}
)

async def set_many(self, mapped_data: Dict[str, str], seconds_for_expire: int = 600) -> None:
for key, value in mapped_data.items():
await self.set(key, value, seconds_for_expire)

async def dump(self, key: str) -> None:
key = self.get_key_for_namespace(key)
if self._memory.get(key):
del self._memory[key]

return None

async def dump_prefix(self, key_prefix: str) -> None:
key_prefix = self.get_key_for_namespace(key_prefix)

for key in list(self._memory.keys()):
if key_prefix in key:
del self._memory[key]

return None

async def flush_for_namespace(self) -> None:
self._memory = {}

def __str__(self):
return "InMemoryCacheDriver"
Loading
Loading