ci: add Dockerfile

This commit is contained in:
cătălin 2023-12-09 11:15:39 +01:00
commit 65a56f2658
Signed by: catalin
GPG key ID: 0178DF42F43E5FD2
15 changed files with 382 additions and 70 deletions

1
.gitignore vendored
View file

@ -6,6 +6,7 @@ apps/http/sls/.serverless/
apps/http/sls/.requirements.zip
__pycache__/
.pdm-python
.pdm-build/
.env
.coverage
htmlcov

41
Dockerfile Normal file
View file

@ -0,0 +1,41 @@
FROM python:3.11-slim AS base
ENV PYTHONDONTWRITEBYTECODE 1
ENV USERNAME "secretsanta"
ENV APP_HOME "/home/$USERNAME"
ENV APP_PATH "$APP_HOME/src"
ARG uid=1000
ARG gid=1000
# hadolint ignore=DL3001,DL3008
RUN apt-get -y update \
&& apt-get -y upgrade \
&& apt-get -y install git curl make python3-virtualenv --no-install-recommends \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd -g "$gid" "$USERNAME" \
&& useradd -lu "$uid" -g "$gid" "$USERNAME" \
&& mkhomedir_helper "$USERNAME" \
&& mkdir "$APP_PATH" \
&& chown "$uid:$gid" "$APP_PATH"
USER "$USERNAME"
ENV PATH="${PATH}:$APP_HOME/.local/bin"
ENV PYTHONPATH "$APP_PATH"
RUN pip install pipx==1.2.0 --no-cache-dir \
&& pipx install pdm==2.10
WORKDIR "$APP_PATH"
ENV POETRY_VIRTUALENVS_IN_PROJECT=1
COPY --chown="$USERNAME" pyproject.toml pdm.lock Makefile README.md LICENSE ./
COPY --chown="$USERNAME" apps/ apps/
COPY --chown="$USERNAME" secretsanta/ secretsanta/
RUN pdm install
FROM base AS run
RUN pdm install -G litestar
WORKDIR "$APP_PATH/apps/http/litestar"
CMD ["pdm", "run", "litestar", "run"]

View file

@ -1,7 +1,14 @@
build:
docker build --target run -t git.roboces.dev/catalin/secretsanta:latest .
run--sls:
@echo "Activating virtual environment and starting serverless offline"
@. .venv/bin/activate && cd apps/http/sls && npx sls offline
run--litestar:
@echo "Starting litestar"
@. cd apps/http/litestar/ && pdm run litestar run
deploy--sls:
@echo "Deploying via serverless framework"
@. cd apps/http/sls && pdm export -G sls -f requirements --prod > requirements.txt && npx sls deploy

View file

@ -1,8 +0,0 @@
{
"version": "2.0",
"app_name": "secretsanta",
"stages": {
"dev": {
}
}
}

View file

@ -4,8 +4,8 @@ from chalice import Chalice
from secretsanta.domain import Group
from secretsanta.repo import S3Repo
from secretsanta.service import (
GetGroupParticipantsService,
CreateGroupService,
GetGroupParticipantsService,
GetPairService,
)
from secretsanta.settings import get_config
@ -16,13 +16,6 @@ app = Chalice(app_name="secretsanta", debug=config.environment == "local")
repo = S3Repo()
def _participants2url(participants: list[str], group_uuid: str) -> list[str]:
return [
f"{config.server_url}/api/v1/groups/{group_uuid}/pair/{participant_name}"
for participant_name in participants
]
@app.route("/api/v1/groups/{group_uuid}")
def get_participants(group_uuid: str):
service = GetGroupParticipantsService(repo)
@ -34,8 +27,7 @@ def create_group(group_uuid: str):
request = app.current_request
body = request.json_body
service = CreateGroupService(repo)
participants = anyio.run(service.run, Group(uuid=group_uuid, **body)).participants
return _participants2url(participants, group_uuid)
return anyio.run(service.run, Group(uuid=group_uuid, **body))
@app.route("/api/v1/groups/{group_uuid}/pair/{participant}")

View file

@ -1,15 +1,15 @@
from dataclasses import dataclass, asdict
from typing import Literal, Annotated
from dataclasses import asdict, dataclass
from typing import Annotated, Literal
from litestar import Controller, get, Litestar, put, Request, Response, MediaType
from litestar import Controller, Litestar, MediaType, Request, Response, get, put
from litestar.params import Parameter
from pydantic import UUID4
from secretsanta.domain import Group
from secretsanta.repo import S3Repo
from secretsanta.service import (
GetGroupParticipantsService,
CreateGroupService,
GetGroupParticipantsService,
GetPairService,
)
from secretsanta.settings import get_config
@ -46,10 +46,13 @@ class GroupController(Controller):
@put("/{group_uuid:uuid}")
async def create_group(
self, request: Request, group_uuid: UUID4, data: CreateGroup
self,
request: Request,
group_uuid: UUID4,
data: CreateGroup,
) -> list[str]:
group = await self.create_group_service.run(
Group(uuid=group_uuid, **asdict(data))
Group(uuid=group_uuid, **asdict(data)),
)
return self._group2urls(group.participants, request.url)
@ -59,11 +62,13 @@ class GroupController(Controller):
group_uuid: UUID4,
participant_name: str,
response_type: Annotated[
Literal["json", "plain"], Parameter(query="type")
Literal["json", "plain"],
Parameter(query="type"),
] = "plain",
) -> Response[str] | PairResponse:
pair = await self.get_pair_service.run(
group_uuid=group_uuid, participant=participant_name
group_uuid=group_uuid,
participant=participant_name,
)
if response_type == "json":
return PairResponse(pair=pair)

View file

@ -5,9 +5,9 @@ import anyio
from secretsanta.domain import Group
from secretsanta.repo import S3Repo
from secretsanta.service import (
CreateGroupService,
GetGroupParticipantsService,
GetPairService,
CreateGroupService,
)
from secretsanta.settings import get_config

View file

@ -26,16 +26,16 @@ custom:
dockerizePip: true
zip: true
slim: true
layer: true
prune:
automatic: true
includeLayers: true
number: 1
apiGatewayCaching:
enabled: true
apiGatewayThrottling:
maxRequestsPerSecond: 100
maxConcurrentRequests: 5
layer: false
#prune:
# automatic: true
# includeLayers: true
# number: 1
#apiGatewayCaching:
# enabled: true
#apiGatewayThrottling:
# maxRequestsPerSecond: 100
# maxConcurrentRequests: 5
plugins:
- serverless-python-requirements
@ -47,27 +47,25 @@ plugins:
functions:
get_participants:
handler: app.get_participants
layers:
- Ref: PythonRequirementsLambdaLayer
events:
- httpApi:
path: /api/v1/groups/{group_uuid}
method: get
create_group:
handler: app.create_group
layers:
- Ref: PythonRequirementsLambdaLayer
events:
- httpApi:
path: /api/v1/groups/{group_uuid}
method: put
#create_group:
# handler: app.create_group
# layers:
# - Ref: PythonRequirementsLambdaLayer
# events:
# - httpApi:
# path: /api/v1/groups/{group_uuid}
# method: put
get_pair:
handler: app.get_pair
layers:
- Ref: PythonRequirementsLambdaLayer
events:
- httpApi:
path: /api/v1/groups/{group_uuid}/pair/{participant}
method: get
#get_pair:
# handler: app.get_pair
# layers:
# - Ref: PythonRequirementsLambdaLayer
# events:
# - httpApi:
# path: /api/v1/groups/{group_uuid}/pair/{participant}
# method: get

262
pdm.lock generated
View file

@ -5,7 +5,7 @@
groups = ["default", "litestar", "testing", "dev", "linting", "chalice", "sls"]
strategy = ["cross_platform"]
lock_version = "4.4"
content_hash = "sha256:3047d1e2fc131623e2ccfaab7af28560d897f225b61904fed245930b74f43c61"
content_hash = "sha256:ba9863870aa9f5f47435713bc9ae20992b801a13bced3f58aad28f2a5685614d"
[[package]]
name = "aioboto3"
@ -193,6 +193,36 @@ files = [
{file = "boto3-1.28.64.tar.gz", hash = "sha256:a5cf93b202568e9d378afdc84be55a6dedf11d30156289fe829e23e6d7dccabb"},
]
[[package]]
name = "boto3-stubs"
version = "1.33.10"
requires_python = ">=3.7"
summary = "Type annotations for boto3 1.33.10 generated with mypy-boto3-builder 7.21.0"
dependencies = [
"botocore-stubs",
"types-s3transfer",
"typing-extensions>=4.1.0; python_version < \"3.12\"",
]
files = [
{file = "boto3-stubs-1.33.10.tar.gz", hash = "sha256:9e29024bcb6ac12c220d8ea5bfe79c532c92e0c1d1170c14217165e1a8b09218"},
{file = "boto3_stubs-1.33.10-py3-none-any.whl", hash = "sha256:61d07f445f88d5cca0a66e23dfd83611ff947bac08e01a64908df3605c22aabc"},
]
[[package]]
name = "boto3-stubs"
version = "1.33.10"
extras = ["s3"]
requires_python = ">=3.7"
summary = "Type annotations for boto3 1.33.10 generated with mypy-boto3-builder 7.21.0"
dependencies = [
"boto3-stubs==1.33.10",
"mypy-boto3-s3<1.34.0,>=1.33.0",
]
files = [
{file = "boto3-stubs-1.33.10.tar.gz", hash = "sha256:9e29024bcb6ac12c220d8ea5bfe79c532c92e0c1d1170c14217165e1a8b09218"},
{file = "boto3_stubs-1.33.10-py3-none-any.whl", hash = "sha256:61d07f445f88d5cca0a66e23dfd83611ff947bac08e01a64908df3605c22aabc"},
]
[[package]]
name = "botocore"
version = "1.31.64"
@ -231,6 +261,39 @@ files = [
{file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
]
[[package]]
name = "cffi"
version = "1.16.0"
requires_python = ">=3.8"
summary = "Foreign Function Interface for Python calling C code."
dependencies = [
"pycparser",
]
files = [
{file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"},
{file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"},
{file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"},
{file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"},
{file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"},
{file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"},
{file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"},
{file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"},
{file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"},
{file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"},
{file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"},
{file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"},
]
[[package]]
name = "chalice"
version = "1.29.0"
@ -252,6 +315,46 @@ files = [
{file = "chalice-1.29.0.tar.gz", hash = "sha256:f07aad9a2d4e5b06ef279cf668a48ec455b131168000b91efe7d841425599035"},
]
[[package]]
name = "charset-normalizer"
version = "3.3.2"
requires_python = ">=3.7.0"
summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
files = [
{file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
{file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
{file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
{file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
{file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
{file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
{file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
{file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
{file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
]
[[package]]
name = "click"
version = "8.1.7"
@ -339,6 +442,40 @@ files = [
{file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"},
]
[[package]]
name = "cryptography"
version = "41.0.7"
requires_python = ">=3.7"
summary = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
dependencies = [
"cffi>=1.12",
]
files = [
{file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf"},
{file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d"},
{file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a"},
{file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15"},
{file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a"},
{file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1"},
{file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157"},
{file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406"},
{file = "cryptography-41.0.7-cp37-abi3-win32.whl", hash = "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d"},
{file = "cryptography-41.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2"},
{file = "cryptography-41.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960"},
{file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003"},
{file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7"},
{file = "cryptography-41.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec"},
{file = "cryptography-41.0.7-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be"},
{file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a"},
{file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c"},
{file = "cryptography-41.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a"},
{file = "cryptography-41.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39"},
{file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a"},
{file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248"},
{file = "cryptography-41.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309"},
{file = "cryptography-41.0.7.tar.gz", hash = "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc"},
]
[[package]]
name = "editorconfig"
version = "0.12.3"
@ -648,6 +785,43 @@ files = [
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
]
[[package]]
name = "moto"
version = "4.2.11"
requires_python = ">=3.7"
summary = ""
dependencies = [
"Jinja2>=2.10.1",
"boto3>=1.9.201",
"botocore>=1.12.201",
"cryptography>=3.3.1",
"python-dateutil<3.0.0,>=2.1",
"requests>=2.5",
"responses>=0.13.0",
"werkzeug!=2.2.0,!=2.2.1,>=0.5",
"xmltodict",
]
files = [
{file = "moto-4.2.11-py2.py3-none-any.whl", hash = "sha256:58c12ab9ee69b6a5d1cddf83611ba4071508f07894317c57844b3ae6dc5bcd38"},
{file = "moto-4.2.11.tar.gz", hash = "sha256:2da62d52eaa765dfe2762c920f0a88a58f3a09e04581c91db967d92faec848f1"},
]
[[package]]
name = "moto"
version = "4.2.11"
extras = ["s3"]
requires_python = ">=3.7"
summary = ""
dependencies = [
"PyYAML>=5.1",
"moto==4.2.11",
"py-partiql-parser==0.4.2",
]
files = [
{file = "moto-4.2.11-py2.py3-none-any.whl", hash = "sha256:58c12ab9ee69b6a5d1cddf83611ba4071508f07894317c57844b3ae6dc5bcd38"},
{file = "moto-4.2.11.tar.gz", hash = "sha256:2da62d52eaa765dfe2762c920f0a88a58f3a09e04581c91db967d92faec848f1"},
]
[[package]]
name = "msgspec"
version = "0.18.4"
@ -695,6 +869,19 @@ files = [
{file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"},
]
[[package]]
name = "mypy-boto3-s3"
version = "1.33.2"
requires_python = ">=3.7"
summary = "Type annotations for boto3.S3 1.33.2 service generated with mypy-boto3-builder 7.20.3"
dependencies = [
"typing-extensions>=4.1.0; python_version < \"3.12\"",
]
files = [
{file = "mypy-boto3-s3-1.33.2.tar.gz", hash = "sha256:f54a3ad3288f4e4719ebada3dde68c320507b0fc451d59bc68af7e6ab15cbdad"},
{file = "mypy_boto3_s3-1.33.2-py3-none-any.whl", hash = "sha256:9d463df6def30de31a467d49ab92ff7795d46709d56eff6f52216a08bac27918"},
]
[[package]]
name = "packaging"
version = "23.2"
@ -739,6 +926,25 @@ files = [
{file = "polyfactory-2.12.0.tar.gz", hash = "sha256:26dc3a52baae1ebd6386708d9a99f8ea4ef57c9d45e556815ee5e44a1cd27fc0"},
]
[[package]]
name = "py-partiql-parser"
version = "0.4.2"
summary = "Pure Python PartiQL Parser"
files = [
{file = "py-partiql-parser-0.4.2.tar.gz", hash = "sha256:9c99d545be7897c6bfa97a107f6cfbcd92e359d394e4f3b95430e6409e8dd1e1"},
{file = "py_partiql_parser-0.4.2-py3-none-any.whl", hash = "sha256:f3f34de8dddf65ed2d47b4263560bbf97be1ecc6bd5c61da039ede90f26a10ce"},
]
[[package]]
name = "pycparser"
version = "2.21"
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
summary = "C parser in Python"
files = [
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
]
[[package]]
name = "pydantic"
version = "2.5.2"
@ -944,6 +1150,37 @@ files = [
{file = "readchar-4.0.5.tar.gz", hash = "sha256:08a456c2d7c1888cde3f4688b542621b676eb38cd6cfed7eb6cb2e2905ddc826"},
]
[[package]]
name = "requests"
version = "2.31.0"
requires_python = ">=3.7"
summary = "Python HTTP for Humans."
dependencies = [
"certifi>=2017.4.17",
"charset-normalizer<4,>=2",
"idna<4,>=2.5",
"urllib3<3,>=1.21.1",
]
files = [
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
]
[[package]]
name = "responses"
version = "0.24.1"
requires_python = ">=3.8"
summary = "A utility library for mocking out the `requests` Python library."
dependencies = [
"pyyaml",
"requests<3.0,>=2.30.0",
"urllib3<3.0,>=1.25.10",
]
files = [
{file = "responses-0.24.1-py3-none-any.whl", hash = "sha256:a2b43f4c08bfb9c9bd242568328c65a34b318741d3fab884ac843c5ceeb543f9"},
{file = "responses-0.24.1.tar.gz", hash = "sha256:b127c6ca3f8df0eb9cc82fd93109a3007a86acb24871834c47b77765152ecf8c"},
]
[[package]]
name = "rich"
version = "13.7.0"
@ -1301,6 +1538,19 @@ files = [
{file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"},
]
[[package]]
name = "werkzeug"
version = "3.0.1"
requires_python = ">=3.8"
summary = "The comprehensive WSGI web application library."
dependencies = [
"MarkupSafe>=2.1.1",
]
files = [
{file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"},
{file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"},
]
[[package]]
name = "wheel"
version = "0.42.0"
@ -1341,6 +1591,16 @@ files = [
{file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
]
[[package]]
name = "xmltodict"
version = "0.13.0"
requires_python = ">=3.4"
summary = "Makes working with XML feel like you are working with JSON"
files = [
{file = "xmltodict-0.13.0-py2.py3-none-any.whl", hash = "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852"},
{file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"},
]
[[package]]
name = "yarl"
version = "1.9.4"

View file

@ -22,6 +22,7 @@ testing = [
"polyfactory>=2.12.0",
"pytest>=7.4.3",
"pytest-cov>=4.1.0",
"moto[s3]>=4.2.11",
]
linting = [
"ruff>=0.1.7",
@ -41,4 +42,9 @@ build-backend = "pdm.backend"
[tool.pdm.dev-dependencies]
dev = [
"types-aioboto3-lite[s3]>=12.0.0",
"boto3-stubs[s3]>=1.33.10",
]
[tool.ruff]
extend-select = ["W", "C90", "I", "N", "UP", "S", "BLE", "B", "A", "COM", "C4", "DTZ", "T10", "EM", "ISC", "T20", "PT", "RSE", "RET", "SIM", "PTH", "ERA", "PGH", "PL", "TRY", "RUF"]
extend-ignore = ["S101", "ISC002", "ISC002", "COM812"]

View file

@ -1,7 +1,8 @@
from abc import ABC, abstractmethod
import random
from abc import ABC, abstractmethod
from typing import Any
from pydantic import BaseModel, UUID4
from pydantic import UUID4, BaseModel
from secretsanta.settings import Config, get_config
@ -42,3 +43,7 @@ class IService(ABC):
def __init__(self, repo: IRepo, config: Config | None = None):
self.repo = repo
self.config = config or get_config()
@abstractmethod
async def run(self, *args, **kwargs) -> Any:
... # pragma: no cover

View file

@ -5,12 +5,12 @@ import aioboto3
from botocore.exceptions import ClientError
from secretsanta.domain import Group, IRepo
from secretsanta.settings import get_config
from secretsanta.settings import Config, get_config
class S3Repo(IRepo):
def __init__(self):
self.config = get_config()
def __init__(self, config: Config | None = None):
self.config = config or get_config()
self.session = aioboto3.Session(
aws_access_key_id=self.config.s3_access_key_id,
aws_secret_access_key=self.config.s3_secret_access_key,
@ -34,7 +34,7 @@ class S3Repo(IRepo):
Key=f"secretsanta/{group_uuid}.json",
)
except ClientError:
return
return None
else:
group_raw = await s3_obj["Body"].read()
return Group(**json.loads(group_raw))

View file

@ -32,7 +32,8 @@ class CreateGroupService(IService):
class GetGroupParticipantsService(IService):
async def run(self, group_uuid: str) -> list[str] | None:
group = await self.repo.get(group_uuid)
if group:
if not group:
return None
return participants2url(
participants=group.participants,
group_uuid=group.uuid,

View file

@ -55,7 +55,7 @@ def repo_with_data(repo, group):
@pytest.fixture()
def config():
return get_config(
s3_bucket_name="foo", s3_secret_access_key="foo", s3_access_key_id="foo"
s3_bucket_name="foo", s3_secret_access_key="testing", s3_access_key_id="testing", s3_region="us-east-1"
)

View file

@ -46,6 +46,10 @@ async def test_get_participants(get_participants_service, group, config):
)
async def test_get_participants_returns_none(get_participants_service, group):
assert not await get_participants_service.run(group.uuid)
@pytest.mark.usefixtures("repo_with_data")
async def test_get_pair(get_pair_service, group):
assert group.pairs