-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 310ef83
Showing
38 changed files
with
669 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[flake8] | ||
max-line-length = 120 | ||
exclude = .env/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
|
||
# Created by https://www.toptal.com/developers/gitignore/api/django | ||
# Edit at https://www.toptal.com/developers/gitignore?templates=django | ||
|
||
### Django ### | ||
*.log | ||
*.pot | ||
*.pyc | ||
__pycache__/ | ||
local_settings.py | ||
db.sqlite3 | ||
db.sqlite3-journal | ||
media | ||
|
||
# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ | ||
# in your Git repository. Update and uncomment the following line accordingly. | ||
# <django-project-name>/staticfiles/ | ||
|
||
### Django.Python Stack ### | ||
# Byte-compiled / optimized / DLL files | ||
*.py[cod] | ||
*$py.class | ||
|
||
# C extensions | ||
*.so | ||
|
||
# Distribution / packaging | ||
.Python | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
wheels/ | ||
share/python-wheels/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
MANIFEST | ||
|
||
# PyInstaller | ||
# Usually these files are written by a python script from a template | ||
# before PyInstaller builds the exe, so as to inject date/other infos into it. | ||
*.manifest | ||
*.spec | ||
|
||
# Installer logs | ||
pip-log.txt | ||
pip-delete-this-directory.txt | ||
|
||
# Unit test / coverage reports | ||
htmlcov/ | ||
.tox/ | ||
.nox/ | ||
.coverage | ||
.coverage.* | ||
.cache | ||
nosetests.xml | ||
coverage.xml | ||
*.cover | ||
*.py,cover | ||
.hypothesis/ | ||
.pytest_cache/ | ||
cover/ | ||
|
||
# Translations | ||
*.mo | ||
|
||
# Django stuff: | ||
|
||
# Flask stuff: | ||
instance/ | ||
.webassets-cache | ||
|
||
# Scrapy stuff: | ||
.scrapy | ||
|
||
# Sphinx documentation | ||
docs/_build/ | ||
|
||
# PyBuilder | ||
.pybuilder/ | ||
target/ | ||
|
||
# Jupyter Notebook | ||
.ipynb_checkpoints | ||
|
||
# IPython | ||
profile_default/ | ||
ipython_config.py | ||
|
||
# pyenv | ||
# For a library or package, you might want to ignore these files since the code is | ||
# intended to run in multiple environments; otherwise, check them in: | ||
# .python-version | ||
|
||
# pipenv | ||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. | ||
# However, in case of collaboration, if having platform-specific dependencies or dependencies | ||
# having no cross-platform support, pipenv may install dependencies that don't work, or not | ||
# install all needed dependencies. | ||
#Pipfile.lock | ||
|
||
# poetry | ||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. | ||
# This is especially recommended for binary packages to ensure reproducibility, and is more | ||
# commonly ignored for libraries. | ||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control | ||
#poetry.lock | ||
|
||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow | ||
__pypackages__/ | ||
|
||
# Celery stuff | ||
celerybeat-schedule | ||
celerybeat.pid | ||
|
||
# SageMath parsed files | ||
*.sage.py | ||
|
||
# Environments | ||
.env | ||
.venv | ||
env/ | ||
venv/ | ||
ENV/ | ||
env.bak/ | ||
venv.bak/ | ||
|
||
# Spyder project settings | ||
.spyderproject | ||
.spyproject | ||
|
||
# Rope project settings | ||
.ropeproject | ||
|
||
# mkdocs documentation | ||
/site | ||
|
||
# mypy | ||
.mypy_cache/ | ||
.dmypy.json | ||
dmypy.json | ||
|
||
# Pyre type checker | ||
.pyre/ | ||
|
||
# pytype static type analyzer | ||
.pytype/ | ||
|
||
# Cython debug symbols | ||
cython_debug/ | ||
|
||
# PyCharm | ||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can | ||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore | ||
# and can be added to the global gitignore or merged into this file. For a more nuclear | ||
# option (not recommended) you can uncomment the following to ignore the entire idea folder. | ||
#.idea/ | ||
|
||
# End of https://www.toptal.com/developers/gitignore/api/django |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"python.linting.flake8Enabled": true, | ||
"python.linting.enabled": true | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<mxfile host="app.diagrams.net" modified="2022-03-13T03:46:27.746Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36" etag="FOYSFn7iCrOLh9TuYcWU" version="17.1.2" type="device"><diagram name="Page-1" id="c4acf3e9-155e-7222-9cf6-157b1a14988f">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</diagram></mxfile> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
open the diagram using draw.io |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#!/usr/bin/env python | ||
"""Django's command-line utility for administrative tasks.""" | ||
import os | ||
import sys | ||
|
||
|
||
def main(): | ||
"""Run administrative tasks.""" | ||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'urlshortner.settings') | ||
try: | ||
from django.core.management import execute_from_command_line | ||
except ImportError as exc: | ||
raise ImportError( | ||
"Couldn't import Django. Are you sure it's installed and " | ||
"available on your PYTHONPATH environment variable? Did you " | ||
"forget to activate a virtual environment?" | ||
) from exc | ||
execute_from_command_line(sys.argv) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
asgiref==3.5.0 | ||
decorator==5.1.1 | ||
Django==4.0.3 | ||
flake8==4.0.1 | ||
mccabe==0.6.1 | ||
punq==0.6.2 | ||
pycodestyle==2.8.0 | ||
pyflakes==2.4.0 | ||
qrcode==7.3.1 | ||
six==1.16.0 | ||
sqlparse==0.4.2 | ||
validators==0.18.2 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# from django.contrib import admin | ||
|
||
# Register your models here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class ShortnerConfig(AppConfig): | ||
default_auto_field = 'django.db.models.BigAutoField' | ||
name = 'shortner' |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from typing import Protocol | ||
|
||
|
||
class TokenBroker(Protocol): | ||
def get_token(self, url: str) -> str: ... | ||
def get_url(self, token: str) -> str: ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from typing import Protocol | ||
|
||
|
||
class RandomGenerator(Protocol): | ||
def get_next_int(self, stop: int) -> int: ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from typing import Protocol | ||
|
||
|
||
class TokenGeneratorConfig(Protocol): | ||
def get_token_length(self) -> int: ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from typing import Protocol | ||
|
||
|
||
class UrlValidator(Protocol): | ||
def is_url_valid(self, url: str) -> bool: ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from typing import Protocol | ||
|
||
from shortner.domain.entities.url_token import UrlToken | ||
|
||
|
||
class UrlTokenRepository(Protocol): | ||
def get_by_token(self, token: str) -> UrlToken: ... | ||
def insert(self, data: UrlToken): ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
class NotFoundException(Exception): | ||
pass | ||
|
||
|
||
class BusinessException(Exception): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from dataclasses import dataclass | ||
|
||
|
||
@dataclass | ||
class UrlToken: | ||
url: str | ||
token: str |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from shortner.domain.boundaries.input.token_broker import TokenBroker | ||
from shortner.domain.boundaries.output.url_validator import UrlValidator | ||
from shortner.domain.boundaries.output.urltoken_repository import UrlTokenRepository | ||
from shortner.domain.services.token_generator import TokenGenerator | ||
from shortner.domain.entities.exceptions import BusinessException, NotFoundException | ||
from shortner.domain.entities.url_token import UrlToken | ||
|
||
|
||
class TokenBrokerService(TokenBroker): | ||
def __init__(self, | ||
repository: UrlTokenRepository, | ||
url_validator: UrlValidator, | ||
token_generator: TokenGenerator): | ||
self.repository = repository | ||
self.url_validator = url_validator | ||
self.token_generator = token_generator | ||
|
||
def get_token(self, url: str) -> str: | ||
if not self.url_validator.is_url_valid(url): | ||
raise BusinessException('url is not valid') | ||
|
||
token = self.token_generator.get_token() | ||
|
||
self.repository.insert(UrlToken(url, token)) | ||
|
||
return token | ||
|
||
def get_url(self, token: str) -> str: | ||
url_token = self.repository.get_by_token(token) | ||
if url_token is None: | ||
raise NotFoundException('token not found') | ||
|
||
return url_token.url |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from shortner.domain.services.token_generator import TokenGenerator | ||
from shortner.domain.boundaries.output.random_generator import RandomGenerator | ||
from shortner.domain.boundaries.output.token_generator_config import TokenGeneratorConfig | ||
|
||
|
||
class TokenGenerationService(TokenGenerator): | ||
def __init__(self, | ||
token_generation_config: TokenGeneratorConfig, | ||
random_generator: RandomGenerator): | ||
self.token_generation_config = token_generation_config | ||
self.random_generator = random_generator | ||
|
||
CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' | ||
|
||
def get_token(self) -> str: | ||
chars = [self.CHARS[self.random_generator.get_next_int(len(self.CHARS))] | ||
for i in range(self.token_generation_config.get_token_length())] | ||
result = ''.join(chars) | ||
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from typing import Protocol | ||
|
||
|
||
class TokenGenerator(Protocol): | ||
def get_token(self) -> str: ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import punq | ||
|
||
from shortner.domain.boundaries.input.token_broker import TokenBroker | ||
from shortner.domain.boundaries.output.random_generator import RandomGenerator | ||
from shortner.domain.boundaries.output.token_generator_config import TokenGeneratorConfig | ||
from shortner.domain.boundaries.output.url_validator import UrlValidator | ||
from shortner.domain.boundaries.output.urltoken_repository import UrlTokenRepository | ||
from shortner.domain.services.token_broker_service import TokenBrokerService | ||
from shortner.domain.services.token_generation_service import TokenGenerationService | ||
from shortner.domain.services.token_generator import TokenGenerator | ||
from shortner.services.default_random_generator_service import DefaultRandomGeneratorService | ||
from shortner.services.env_token_generator_config import EnvTokenGeneratorConfig | ||
from shortner.services.validators_url_validator_service import ValidatorsUrlValidatorService | ||
from shortner.services.model_url_token_repository import ModelUrlTokenRepository | ||
|
||
container = punq.Container() | ||
|
||
container.register(TokenBroker, TokenBrokerService) | ||
container.register(RandomGenerator, DefaultRandomGeneratorService) | ||
container.register(UrlValidator, ValidatorsUrlValidatorService) | ||
container.register(UrlTokenRepository, ModelUrlTokenRepository) | ||
container.register(TokenGenerator, TokenGenerationService) | ||
container.register(TokenGeneratorConfig, EnvTokenGeneratorConfig) | ||
|
||
|
||
# based on https://github.com/django/django/blob/main/django/views/generic/base.py | ||
def get_ioc_view(cls, **initkwargs): | ||
def view(request, *args, **kwargs): | ||
self = container.instantiate(cls, **initkwargs) | ||
self.setup(request, *args, **kwargs) | ||
return self.dispatch(request, *args, **kwargs) | ||
|
||
view.view_class = cls | ||
view.view_initkwargs = initkwargs | ||
view.__doc__ = cls.__doc__ | ||
view.__module__ = cls.__module__ | ||
view.__annotations__ = cls.dispatch.__annotations__ | ||
view.__dict__.update(cls.dispatch.__dict__) | ||
|
||
return view |
Oops, something went wrong.