# braincube-aws-core
Microframework for Python AWS lambdas.
[![Language](https://img.shields.io/badge/language-Python-blue.svg)](https://www.python.org/)
[![pypi](https://img.shields.io/pypi/v/braincube-aws-core.svg)](https://pypi.org/project/braincube-aws-core/)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
## Installation
```bash
pip install braincube-aws-core
```
## Built With
- [asyncpg](https://github.com/MagicStack/asyncpg) - A fast PostgreSQL Database Client Library for Python/asyncio.
- [pydantic](https://github.com/pydantic/pydantic) - Data validation using Python type hints.
- [pypika](https://github.com/kayak/pypika) - Python Query Builder.
### Application Controllers Example
```python
import asyncio
from uuid import uuid4
from http import HTTPStatus
from core.app.data import HTTPRequest, HTTPResponse
from core.app.app_module import AppModule
from core.app.app_controller import AppController
from pydantic import BaseModel
class AccountDto(BaseModel):
iban: str
bban: str
data = {
"a0a412d9-87ef-474b-9ac8-b682ec5e0fb3": AccountDto(iban="EUR27100777770209299700", bban="EURC12345612345678"),
"5ebc25bd-e152-4a70-b251-d68e43be581e": AccountDto(iban="GR27100777770209299700", bban="GRC12345612345678"),
}
app = AppController("/accounts")
@app.get("/{id}")
async def get_account(request: HTTPRequest) -> HTTPResponse:
account = data.get(request.path_params["id"])
return HTTPResponse(HTTPStatus.OK if account else HTTPStatus.NO_CONTENT, account)
@app.post()
async def create_account(request: HTTPRequest[AccountDto]) -> HTTPResponse:
data[uuid4()] = request.body
return HTTPResponse(HTTPStatus.CREATED)
loop = asyncio.get_event_loop()
module = AppModule([app])
def main(event, context):
return loop.run_until_complete(module.serve(event, context))
```
### Dependency Injection Example
```python
from core.di.injector import inject
from core.dal.postgres_connection import get_pool, Pool
@inject("data_warehouse_pool")
async def provide_warehouse_pool() -> Pool:
return await get_pool()
@inject(qualifier="pool:data_warehouse_pool")
class BankService:
def __init__(self, pool: Pool):
self._pool = pool
```
### Postgres Repository Example
```python
from core.app.data import HTTPRequest
from core.utils.data import Order, OrderType
from core.dal.data import Key, Schema, Column, Relation, SimpleColumn, JoinType, JoinThrough, StatementField
from core.dal.postgres_connection import get_pool, Pool
from core.dal.postgres_repository import PostgresRepository
# schema definition
equities = Schema(
table="equities",
alias="e",
primary_key=["id"],
columns=[
Column("id", updatable=False, insertable=False),
Column("name"),
Column("type"),
Column("issuer_id", alias="issuerId"),
Column("industry_sector", alias="industrySector"),
Column("isin"),
Column("reference"),
Column("bloomberg_code", alias="bloombergCode"),
Column("market_symbol", alias="marketSymbol"),
Column("currency"),
Column("country", ),
Column("min_amount", alias="minAmount"),
],
statement_fields=[
StatementField("isTypeOne", statement="CASE WHEN e.type = 1 then True else False END")
],
order=[
Order(type=OrderType.asc, alias="name")
],
relations=[
Relation(
table="parties",
alias="p",
columns=[
SimpleColumn("name"),
SimpleColumn("short_name", alias="shortName"),
],
join_forced=False,
join_type=JoinType.left,
join_through=JoinThrough(from_column_name="issuer_id", to_column_name="id")
)
]
)
# repository definition
class EquitiesRepo(PostgresRepository):
def __init__(self, pool: Pool):
super().__init__(pool, equities)
# repository usage
request = HTTPRequest()
repo = EquitiesRepo(await get_pool())
await repo.find_by_pk(Key(request.path_params["id"]), request.query_params.fields)
await repo.exists_by_pk(Key("9448a57b-f686-4935-b152-566baab712db"))
await repo.find_one(
request.query_params.fields,
conditions=request.query_params.conditions,
order=request.query_params.order)
await repo.find_all(
request.query_params.fields,
conditions=request.query_params.conditions,
order=request.query_params.order)
await repo.find_all_by_pk(
[
Key("9448a57b-f686-4935-b152-566baab712db"),
Key("43c8ec37-9a59-44eb-be90-def391ba2f02")
],
aliases=request.query_params.fields,
order=request.query_params.order)
await repo.find_many(
request.query_params.fields,
conditions=request.query_params.conditions,
page=request.query_params.page,
order=request.query_params.order)
await repo.insert({
"name": "Bursa de Valori Bucuresti SA",
"type": 1,
"industrySector": 40,
"isin": "ROBVBAACNOR0",
"bloombergCode": "BBG000BBWMC5",
"marketSymbol": "BVB RO Equity",
"currency": "RON",
"country": "RO",
})
await repo.insert_bulk(
aliases=["name", "type", "industrySector", "isin", "bloombergCode", "marketSymbol", "currency", "country"],
data=[
["Bursa de Valori Bucuresti SA", 1, 40, "ROBVBAACNOR0", "BBG000BBWMC5", "BVB RO Equity", "RON", "RO"],
["Citigroup Inc", 1, 40, "US1729674242", "BBG000FY4S11", "C US Equity", "USD", "US"],
["Coca-Cola HBC AG", 1, 49, "CH0198251305", "BBG004HJV2T1", "EEE GA Equity", "EUR", "GR"],
]
)
await repo.update({
"type": 1,
"isin": 40,
}, request.query_params.conditions, request.query_params.fields)
await repo.update_by_pk(Key("9448a57b-f686-4935-b152-566baab712db"), {
"type": 1,
"isin": 40
})
await repo.delete(request.query_params.conditions, ["id", "name", "type"])
await repo.delete_by_pk(Key("9448a57b-f686-4935-b152-566baab712db"), ["id", "name", "type"])
await repo.fetch("SELECT * FROM equities WHERE type = $1 and isin = $2", [1, "TREEGYO00017"])
await repo.fetch_one("SELECT * FROM equities WHERE id = $1", ["2b67122a-f47e-41b1-b7f7-53be5ca381a0"])
await repo.execute("DELETE FROM equities WHERE id = $1", ["2b67122a-f47e-41b1-b7f7-53be5ca381a0"])
```
### Query params format
```
fields=name, type, industrySector, isin, bloombergCode, parties_name, parties_shortName
type=1
isin=range(40, 49)
id=any(9448a57b-f686-4935-b152-566baab712db, 43c8ec37-9a59-44eb-be90-def391ba2f02)
page_no=1
page_size=50
top_size=50
order=name, id DESC
```
### Local Development Requirements
To use the SAM CLI, you need the following tools.
* [SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)
* [Python 3](https://www.python.org/downloads/)
* [Docker](https://hub.docker.com/search/?type=edition&offering=community)
### Run server locally
```bash
# open ssh tunel
sudo sh ssh_tunnel_Analog_JBox.sh
# apply code changes to docker image
sam-api$ sam build
# start server locally on http://127.0.0.1:3000
sam-api$ sam local start-api --warm-containers EAGER
# or run function locally using event.json as parameter
sam-api$ sam local invoke ApiFunction --event events/event.json
```
### Deploy to AWS
```bash
sam build --use-container
sam deploy --capabilities CAPABILITY_NAMED_IAM --guided --profile analog_user --region eu-west-1
```
### Build and deploy new package version using twine
```bash
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade build
python3 -m pip install --upgrade twine
```
```bash
python3 -m build
twine upload --skip-existing dist/*
```
### Resources
* [SAM template.yml](https://github.com/aws/serverless-application-model/blob/master/docs/internals/generated_resources.rst)
* [asyncpg driver](https://magicstack.github.io/asyncpg/current/)
* [PyPika query builder](https://pypika.readthedocs.io/en/latest/)
* [Pydantic](https://docs.pydantic.dev/)
Raw data
{
"_id": null,
"home_page": "https://bitbucket.org/braincube-common/core-aws.git",
"name": "braincube-aws-core",
"maintainer": "",
"docs_url": null,
"requires_python": "<4,>=3.10",
"maintainer_email": "",
"keywords": "python,amazon,aws,lambda,routing,dal,injection",
"author": "Braincube",
"author_email": "v.boudis@braincube.gr",
"download_url": "https://files.pythonhosted.org/packages/6b/56/c874cd2f6f0f739d1a6b3abf5444759e47481915f3a8d3be6cffa26b75a5/braincube-aws-core-0.0.29.tar.gz",
"platform": null,
"description": "# braincube-aws-core\n\nMicroframework for Python AWS lambdas.\n\n[![Language](https://img.shields.io/badge/language-Python-blue.svg)](https://www.python.org/)\n[![pypi](https://img.shields.io/pypi/v/braincube-aws-core.svg)](https://pypi.org/project/braincube-aws-core/)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n\n## Installation\n\n```bash\npip install braincube-aws-core\n```\n\n## Built With\n\n- [asyncpg](https://github.com/MagicStack/asyncpg) - A fast PostgreSQL Database Client Library for Python/asyncio.\n- [pydantic](https://github.com/pydantic/pydantic) - Data validation using Python type hints.\n- [pypika](https://github.com/kayak/pypika) - Python Query Builder.\n\n### Application Controllers Example\n\n```python\nimport asyncio\n\nfrom uuid import uuid4\nfrom http import HTTPStatus\n\nfrom core.app.data import HTTPRequest, HTTPResponse\nfrom core.app.app_module import AppModule\nfrom core.app.app_controller import AppController\n\nfrom pydantic import BaseModel\n\n\nclass AccountDto(BaseModel):\n iban: str\n bban: str\n\n\ndata = {\n \"a0a412d9-87ef-474b-9ac8-b682ec5e0fb3\": AccountDto(iban=\"EUR27100777770209299700\", bban=\"EURC12345612345678\"),\n \"5ebc25bd-e152-4a70-b251-d68e43be581e\": AccountDto(iban=\"GR27100777770209299700\", bban=\"GRC12345612345678\"),\n}\n\napp = AppController(\"/accounts\")\n\n\n@app.get(\"/{id}\")\nasync def get_account(request: HTTPRequest) -> HTTPResponse:\n account = data.get(request.path_params[\"id\"])\n return HTTPResponse(HTTPStatus.OK if account else HTTPStatus.NO_CONTENT, account)\n\n\n@app.post()\nasync def create_account(request: HTTPRequest[AccountDto]) -> HTTPResponse:\n data[uuid4()] = request.body\n return HTTPResponse(HTTPStatus.CREATED)\n\n\nloop = asyncio.get_event_loop()\n\nmodule = AppModule([app])\n\n\ndef main(event, context):\n return loop.run_until_complete(module.serve(event, context))\n```\n\n### Dependency Injection Example\n\n```python\nfrom core.di.injector import inject\nfrom core.dal.postgres_connection import get_pool, Pool\n\n\n@inject(\"data_warehouse_pool\")\nasync def provide_warehouse_pool() -> Pool:\n return await get_pool()\n\n\n@inject(qualifier=\"pool:data_warehouse_pool\")\nclass BankService:\n\n def __init__(self, pool: Pool):\n self._pool = pool\n```\n\n### Postgres Repository Example\n\n```python\nfrom core.app.data import HTTPRequest\nfrom core.utils.data import Order, OrderType\nfrom core.dal.data import Key, Schema, Column, Relation, SimpleColumn, JoinType, JoinThrough, StatementField\nfrom core.dal.postgres_connection import get_pool, Pool\nfrom core.dal.postgres_repository import PostgresRepository\n\n# schema definition\nequities = Schema(\n table=\"equities\",\n alias=\"e\",\n primary_key=[\"id\"],\n columns=[\n Column(\"id\", updatable=False, insertable=False),\n Column(\"name\"),\n Column(\"type\"),\n Column(\"issuer_id\", alias=\"issuerId\"),\n Column(\"industry_sector\", alias=\"industrySector\"),\n Column(\"isin\"),\n Column(\"reference\"),\n Column(\"bloomberg_code\", alias=\"bloombergCode\"),\n Column(\"market_symbol\", alias=\"marketSymbol\"),\n Column(\"currency\"),\n Column(\"country\", ),\n Column(\"min_amount\", alias=\"minAmount\"),\n ],\n statement_fields=[\n StatementField(\"isTypeOne\", statement=\"CASE WHEN e.type = 1 then True else False END\")\n ],\n order=[\n Order(type=OrderType.asc, alias=\"name\")\n ],\n relations=[\n Relation(\n table=\"parties\",\n alias=\"p\",\n columns=[\n SimpleColumn(\"name\"),\n SimpleColumn(\"short_name\", alias=\"shortName\"),\n ],\n join_forced=False,\n join_type=JoinType.left,\n join_through=JoinThrough(from_column_name=\"issuer_id\", to_column_name=\"id\")\n )\n ]\n)\n\n\n# repository definition\nclass EquitiesRepo(PostgresRepository):\n\n def __init__(self, pool: Pool):\n super().__init__(pool, equities)\n\n\n# repository usage\n\nrequest = HTTPRequest()\n\nrepo = EquitiesRepo(await get_pool())\n\nawait repo.find_by_pk(Key(request.path_params[\"id\"]), request.query_params.fields)\n\nawait repo.exists_by_pk(Key(\"9448a57b-f686-4935-b152-566baab712db\"))\n\nawait repo.find_one(\n request.query_params.fields,\n conditions=request.query_params.conditions,\n order=request.query_params.order)\n\nawait repo.find_all(\n request.query_params.fields,\n conditions=request.query_params.conditions,\n order=request.query_params.order)\n\nawait repo.find_all_by_pk(\n [\n Key(\"9448a57b-f686-4935-b152-566baab712db\"),\n Key(\"43c8ec37-9a59-44eb-be90-def391ba2f02\")\n ],\n aliases=request.query_params.fields,\n order=request.query_params.order)\n\nawait repo.find_many(\n request.query_params.fields,\n conditions=request.query_params.conditions,\n page=request.query_params.page,\n order=request.query_params.order)\n\nawait repo.insert({\n \"name\": \"Bursa de Valori Bucuresti SA\",\n \"type\": 1,\n \"industrySector\": 40,\n \"isin\": \"ROBVBAACNOR0\",\n \"bloombergCode\": \"BBG000BBWMC5\",\n \"marketSymbol\": \"BVB RO Equity\",\n \"currency\": \"RON\",\n \"country\": \"RO\",\n})\n\nawait repo.insert_bulk(\n aliases=[\"name\", \"type\", \"industrySector\", \"isin\", \"bloombergCode\", \"marketSymbol\", \"currency\", \"country\"],\n data=[\n [\"Bursa de Valori Bucuresti SA\", 1, 40, \"ROBVBAACNOR0\", \"BBG000BBWMC5\", \"BVB RO Equity\", \"RON\", \"RO\"],\n [\"Citigroup Inc\", 1, 40, \"US1729674242\", \"BBG000FY4S11\", \"C US Equity\", \"USD\", \"US\"],\n [\"Coca-Cola HBC AG\", 1, 49, \"CH0198251305\", \"BBG004HJV2T1\", \"EEE GA Equity\", \"EUR\", \"GR\"],\n ]\n)\n\nawait repo.update({\n \"type\": 1,\n \"isin\": 40,\n}, request.query_params.conditions, request.query_params.fields)\n\nawait repo.update_by_pk(Key(\"9448a57b-f686-4935-b152-566baab712db\"), {\n \"type\": 1,\n \"isin\": 40\n})\n\nawait repo.delete(request.query_params.conditions, [\"id\", \"name\", \"type\"])\n\nawait repo.delete_by_pk(Key(\"9448a57b-f686-4935-b152-566baab712db\"), [\"id\", \"name\", \"type\"])\n\nawait repo.fetch(\"SELECT * FROM equities WHERE type = $1 and isin = $2\", [1, \"TREEGYO00017\"])\n\nawait repo.fetch_one(\"SELECT * FROM equities WHERE id = $1\", [\"2b67122a-f47e-41b1-b7f7-53be5ca381a0\"])\n\nawait repo.execute(\"DELETE FROM equities WHERE id = $1\", [\"2b67122a-f47e-41b1-b7f7-53be5ca381a0\"])\n```\n\n### Query params format\n\n```\nfields=name, type, industrySector, isin, bloombergCode, parties_name, parties_shortName\ntype=1\nisin=range(40, 49)\nid=any(9448a57b-f686-4935-b152-566baab712db, 43c8ec37-9a59-44eb-be90-def391ba2f02)\npage_no=1\npage_size=50\ntop_size=50\norder=name, id DESC\n```\n\n### Local Development Requirements\n\nTo use the SAM CLI, you need the following tools.\n\n* [SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)\n* [Python 3](https://www.python.org/downloads/)\n* [Docker](https://hub.docker.com/search/?type=edition&offering=community)\n\n### Run server locally\n\n```bash\n# open ssh tunel\nsudo sh ssh_tunnel_Analog_JBox.sh\n# apply code changes to docker image\nsam-api$ sam build\n# start server locally on http://127.0.0.1:3000\nsam-api$ sam local start-api --warm-containers EAGER\n# or run function locally using event.json as parameter\nsam-api$ sam local invoke ApiFunction --event events/event.json\n```\n\n### Deploy to AWS\n\n```bash\nsam build --use-container\nsam deploy --capabilities CAPABILITY_NAMED_IAM --guided --profile analog_user --region eu-west-1\n```\n\n### Build and deploy new package version using twine\n\n```bash\npython3 -m pip install --upgrade pip\npython3 -m pip install --upgrade build\npython3 -m pip install --upgrade twine\n```\n\n```bash\npython3 -m build\ntwine upload --skip-existing dist/*\n```\n\n### Resources\n\n* [SAM template.yml](https://github.com/aws/serverless-application-model/blob/master/docs/internals/generated_resources.rst)\n* [asyncpg driver](https://magicstack.github.io/asyncpg/current/)\n* [PyPika query builder](https://pypika.readthedocs.io/en/latest/)\n* [Pydantic](https://docs.pydantic.dev/)\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Microframework for python aws lambdas",
"version": "0.0.29",
"project_urls": {
"Homepage": "https://bitbucket.org/braincube-common/core-aws.git"
},
"split_keywords": [
"python",
"amazon",
"aws",
"lambda",
"routing",
"dal",
"injection"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "ed4c5fe01a423d415b5ede138335defecc078669b6a9c8915e42f24620dae162",
"md5": "a58ebae72dec34d94c3405be4cf6c5cf",
"sha256": "dbc3515a97e759d0c2cb6624c137a9d761751dcd2ebed7f2baf4b069129caf39"
},
"downloads": -1,
"filename": "braincube_aws_core-0.0.29-py3-none-any.whl",
"has_sig": false,
"md5_digest": "a58ebae72dec34d94c3405be4cf6c5cf",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4,>=3.10",
"size": 27008,
"upload_time": "2023-10-02T20:45:25",
"upload_time_iso_8601": "2023-10-02T20:45:25.981719Z",
"url": "https://files.pythonhosted.org/packages/ed/4c/5fe01a423d415b5ede138335defecc078669b6a9c8915e42f24620dae162/braincube_aws_core-0.0.29-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "6b56c874cd2f6f0f739d1a6b3abf5444759e47481915f3a8d3be6cffa26b75a5",
"md5": "9870260fb0e5fc98efd853afcd12aa0f",
"sha256": "5afebe656980e1a4893430e2b03faaac2879f6336d38f3f13f78332cd53161c9"
},
"downloads": -1,
"filename": "braincube-aws-core-0.0.29.tar.gz",
"has_sig": false,
"md5_digest": "9870260fb0e5fc98efd853afcd12aa0f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4,>=3.10",
"size": 21903,
"upload_time": "2023-10-02T20:45:28",
"upload_time_iso_8601": "2023-10-02T20:45:28.299914Z",
"url": "https://files.pythonhosted.org/packages/6b/56/c874cd2f6f0f739d1a6b3abf5444759e47481915f3a8d3be6cffa26b75a5/braincube-aws-core-0.0.29.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-10-02 20:45:28",
"github": false,
"gitlab": false,
"bitbucket": true,
"codeberg": false,
"bitbucket_user": "braincube-common",
"bitbucket_project": "core-aws",
"lcname": "braincube-aws-core"
}