Skip to content

Commit

Permalink
Merge pull request #5 from GSA-TTS/cli-scaffolding
Browse files Browse the repository at this point in the history
Cli scaffolding
  • Loading branch information
akuny authored Oct 6, 2023
2 parents abe448f + 0efdfb3 commit c34ae2f
Show file tree
Hide file tree
Showing 13 changed files with 187 additions and 10 deletions.
22 changes: 22 additions & 0 deletions nad_ch/application_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
from .gateways.storage_mock import StorageGatewayMock


class ApplicationContext:
def __init__(self):
self._storage = StorageGatewayMock()

@property
def storage(self):
return self._storage


class TestApplicationContext(ApplicationContext):
def __init__(self):
self._storage = StorageGatewayMock()


def create_app_context():
if os.environ.get('APP_ENV') == 'test':
return TestApplicationContext()
return ApplicationContext()
Empty file added nad_ch/controllers/__init__.py
Empty file.
38 changes: 38 additions & 0 deletions nad_ch/controllers/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import click
from ..entities import File
from ..use_cases import upload_file, list_files, get_file_metadata


@click.group()
@click.pass_context
def cli(ctx):
pass


@cli.command()
@click.pass_context
@click.argument('filename')
@click.argument('content')
def upload(ctx, filename, content):
context = ctx.obj
file = File(name=filename, content=content)
upload_file(context, file)
click.echo(f"Uploaded {filename}")


@cli.command()
@click.pass_context
def listall(ctx):
context = ctx.obj
files = list_files(context)
for file in files:
click.echo(file.name)


@cli.command()
@click.pass_context
@click.argument('filename')
def metadata(ctx, filename):
context = ctx.obj
metadata = get_file_metadata(context, filename)
click.echo(f"File: {metadata.name}, Size: {metadata.size} bytes")
13 changes: 13 additions & 0 deletions nad_ch/entities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from dataclasses import dataclass


@dataclass
class File:
name: str
content: str


@dataclass
class FileMetadata:
name: str
size: int
Empty file added nad_ch/gateways/__init__.py
Empty file.
25 changes: 25 additions & 0 deletions nad_ch/gateways/storage_mock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from ..entities import File, FileMetadata
from ..interfaces.storage import StorageGateway


class StorageGatewayMock(StorageGateway):
def __init__(self):
self.files = []

def save(self, file: File) -> None:
self.files.append(file)

def list_all(self) -> list[File]:
return self.files

def get_file(self, file_name: str) -> File:
file = next((f for f in self.files if f.name == file_name), None)
if not file:
raise ValueError(f"No file named {file_name} found!")
return file

def get_metadata(self, file_name: str) -> FileMetadata:
file = next((f for f in self.files if f.name == file_name), None)
if not file:
raise ValueError(f"No file named {file_name} found!")
return FileMetadata(name=file.name, size=len(file.content))
Empty file added nad_ch/interfaces/__init__.py
Empty file.
13 changes: 13 additions & 0 deletions nad_ch/interfaces/storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from typing import Protocol
from ..entities import File, FileMetadata


class StorageGateway(Protocol):
def save(self, file: File) -> None:
...

def list_all(self) -> list[File]:
...

def get_metadata(self, file_name: str) -> FileMetadata:
...
6 changes: 4 additions & 2 deletions nad_ch/main.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from .use_cases import get_greeting
from .controllers.cli import cli
from .application_context import create_app_context


def main():
print(get_greeting())
context = create_app_context()
cli(obj=context)


if __name__ == '__main__':
Expand Down
20 changes: 18 additions & 2 deletions nad_ch/use_cases.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,18 @@
def get_greeting() -> str:
return "Hello!"
from .application_context import ApplicationContext
from .entities import File, FileMetadata
from .interfaces.storage import StorageGateway


def upload_file(ctx: ApplicationContext, file: File) -> None:
storage: StorageGateway = ctx.storage
storage.save(file)


def list_files(ctx: ApplicationContext) -> list[File]:
storage: StorageGateway = ctx.storage
return storage.list_all()


def get_file_metadata(ctx: ApplicationContext, file_name: str) -> FileMetadata:
storage: StorageGateway = ctx.storage
return storage.get_metadata(file_name)
16 changes: 15 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ authors = []

[tool.poetry.dependencies]
python = "^3.11"
click = "^8.1.7"

[tool.poetry.group.dev.dependencies]
pytest = "^7.4.2"
Expand Down
43 changes: 38 additions & 5 deletions tests/test_use_cases.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,42 @@
from nad_ch.use_cases import get_greeting
import pytest
from nad_ch.application_context import create_app_context
from nad_ch.entities import File, FileMetadata
from nad_ch.use_cases import upload_file, list_files, get_file_metadata


def test_get_greeting():
expected_result = "Hello!"
@pytest.fixture(scope="function")
def app_context():
context = create_app_context()
yield context

actual_result = get_greeting()

assert actual_result == expected_result
def test_upload_file(app_context):
file = File(name="test.txt", content="Sample content")

upload_file(app_context, file)

stored_file = app_context.storage.get_file("test.txt")
assert stored_file.name == "test.txt"
assert stored_file.content == "Sample content"


def test_list_files(app_context):
file1 = File(name="test1.txt", content="Content 1")
file2 = File(name="test2.txt", content="Content 2")
upload_file(app_context, file1)
upload_file(app_context, file2)

files = list_files(app_context)

assert len(files) == 2
assert any(f.name == "test1.txt" for f in files)
assert any(f.name == "test2.txt" for f in files)


def test_get_file_metadata(app_context):
file = File(name="test.txt", content="Sample content for metadata")
upload_file(app_context, file)

metadata = get_file_metadata(app_context, "test.txt")

assert isinstance(metadata, FileMetadata)

0 comments on commit c34ae2f

Please sign in to comment.