-
Notifications
You must be signed in to change notification settings - Fork 4
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
Showing
23 changed files
with
996 additions
and
88 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
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
Large diffs are not rendered by default.
Oops, something went wrong.
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
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,2 @@ | ||
db.sqlite3 | ||
__pycache__ |
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,13 @@ | ||
FROM python:3.8 | ||
|
||
MAINTAINER Codium <[email protected]> | ||
|
||
WORKDIR /opt/project | ||
|
||
COPY requirements.txt /tmp | ||
RUN pip install -r /tmp/requirements.txt | ||
|
||
VOLUME ["/opt/project"] | ||
EXPOSE 8000 | ||
|
||
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] |
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,20 @@ | ||
.PHONY: default docker-tests | ||
|
||
default: | ||
@printf "$$HELP" | ||
|
||
docker-build: | ||
docker build -t django-docker-bootstrap . | ||
|
||
docker-tests: | ||
docker run --rm -v "${PWD}:/opt/project" django-docker-bootstrap python manage.py test test | ||
|
||
define HELP | ||
# Docker commands | ||
- make docker-build\tGenerate the docker image with Django installed | ||
- make docker-tests\t\tRun the tests | ||
Please execute "make <command>". Example make help | ||
|
||
endef | ||
|
||
export HELP |
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,45 @@ | ||
## Description | ||
|
||
This all about an API that registers the users of a web application. | ||
|
||
It is designed to practice how to identify the different responsibilities in the code and decouple them. | ||
|
||
## Goal | ||
There are two main objectives: | ||
1. Part 1: decouple the code from the Framework used. | ||
2. Part 2: decouple the code from Database and Libraries. | ||
|
||
Start on the [views.py](src/framework/views.py) | ||
|
||
## Part 1: decouple the code from the Framework used. | ||
1. Run the tests. | ||
2. Do not write Business logic on the Controllers → Instead move ALL the logic to a Use Case class. | ||
3. Do not use the Inputs of the Framework as arguments of your Use Case class. | ||
4. Create your own exceptions to handle errors. | ||
5. Do not use the Framework response as the response of your Use Case class. | ||
|
||
## Part 2: decouple the code from Database and Libraries. | ||
For the second part of the exercise you need to repeat this 4 steps for each coupling: | ||
1. Define an Interface using Dependency Inversion Principle. | ||
2. Evolve your legacy code to match the Interface. | ||
3. Create an adapter that implements the Interface and uses the Library. | ||
4. Remove the coupling with the infrastructure (Database and Libraries) injecting the collaborator. | ||
|
||
|
||
## Install the dependencies | ||
|
||
within docker | ||
|
||
make docker-build | ||
|
||
## Run the tests | ||
|
||
within docker | ||
|
||
make docker-tests | ||
|
||
## Authors | ||
Luis Rovirosa [@luisrovirosa](https://www.twitter.com/luisrovirosa) | ||
|
||
Jordi Anguela [@jordianguela](https://www.twitter.com/jordianguela) |
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,14 @@ | ||
#!/usr/bin/env python | ||
import os | ||
import sys | ||
|
||
if __name__ == '__main__': | ||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.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) |
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,119 @@ | ||
""" | ||
Django settings for ur project. | ||
Generated by 'django-admin startproject' using Django 2.1.7. | ||
For more information on this file, see | ||
https://docs.djangoproject.com/en/2.1/topics/settings/ | ||
For the full list of settings and their values, see | ||
https://docs.djangoproject.com/en/2.1/ref/settings/ | ||
""" | ||
|
||
import os | ||
|
||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) | ||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||
|
||
|
||
# Quick-start development settings - unsuitable for production | ||
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ | ||
|
||
# SECURITY WARNING: keep the secret key used in production secret! | ||
SECRET_KEY = 'wdra&6411lo0jxx^-!51wuax9kd&27w1i+y(kg6cw$@_j*(nw4' | ||
|
||
# SECURITY WARNING: don't run with debug turned on in production! | ||
DEBUG = True | ||
|
||
ALLOWED_HOSTS: list[str] = [] | ||
|
||
# Application definition | ||
|
||
INSTALLED_APPS = [ | ||
'django.contrib.admin', | ||
'django.contrib.auth', | ||
'django.contrib.contenttypes', | ||
'django.contrib.sessions', | ||
'django.contrib.messages', | ||
'django.contrib.staticfiles', | ||
] | ||
|
||
MIDDLEWARE = [ | ||
'django.middleware.security.SecurityMiddleware', | ||
'django.contrib.sessions.middleware.SessionMiddleware', | ||
'django.middleware.common.CommonMiddleware', | ||
'django.middleware.csrf.CsrfViewMiddleware', | ||
'django.contrib.auth.middleware.AuthenticationMiddleware', | ||
'django.contrib.messages.middleware.MessageMiddleware', | ||
'django.middleware.clickjacking.XFrameOptionsMiddleware', | ||
] | ||
|
||
ROOT_URLCONF = 'project.urls' | ||
|
||
TEMPLATES = [ | ||
{ | ||
'BACKEND': 'django.template.backends.django.DjangoTemplates', | ||
'DIRS': [], | ||
'APP_DIRS': True, | ||
'OPTIONS': { | ||
'context_processors': [ | ||
'django.template.context_processors.debug', | ||
'django.template.context_processors.request', | ||
'django.contrib.auth.context_processors.auth', | ||
'django.contrib.messages.context_processors.messages', | ||
], | ||
}, | ||
}, | ||
] | ||
|
||
WSGI_APPLICATION = 'project.wsgi.application' | ||
|
||
|
||
# Database | ||
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases | ||
|
||
DATABASES = { | ||
'default': { | ||
'ENGINE': 'django.db.backends.sqlite3', | ||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), | ||
} | ||
} | ||
|
||
|
||
# Password validation | ||
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators | ||
|
||
AUTH_PASSWORD_VALIDATORS = [ | ||
{ | ||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', | ||
}, | ||
{ | ||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', | ||
}, | ||
{ | ||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', | ||
}, | ||
{ | ||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', | ||
}, | ||
] | ||
|
||
|
||
# Internationalization | ||
# https://docs.djangoproject.com/en/2.1/topics/i18n/ | ||
|
||
LANGUAGE_CODE = 'en-us' | ||
|
||
TIME_ZONE = 'UTC' | ||
|
||
USE_I18N = True | ||
|
||
USE_L10N = True | ||
|
||
USE_TZ = True | ||
|
||
|
||
# Static files (CSS, JavaScript, Images) | ||
# https://docs.djangoproject.com/en/2.1/howto/static-files/ | ||
|
||
STATIC_URL = '/static/' |
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,21 @@ | ||
"""ur URL Configuration | ||
The `urlpatterns` list routes URLs to views. For more information please see: | ||
https://docs.djangoproject.com/en/2.1/topics/http/urls/ | ||
Examples: | ||
Function views | ||
1. Add an import: from my_app import views | ||
2. Add a URL to urlpatterns: path('', views.home, name='home') | ||
Class-based views | ||
1. Add an import: from other_app.views import Home | ||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') | ||
Including another URLconf | ||
1. Import the include() function: from django.urls import include, path | ||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) | ||
""" | ||
from django.urls import path | ||
from src.framework.views import UserController | ||
|
||
urlpatterns = [ | ||
path('', UserController.as_view()), | ||
] |
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,16 @@ | ||
""" | ||
WSGI config for ur project. | ||
It exposes the WSGI callable as a module-level variable named ``application``. | ||
For more information on this file, see | ||
https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ | ||
""" | ||
|
||
import os | ||
|
||
from django.core.wsgi import get_wsgi_application | ||
|
||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings') | ||
|
||
application = get_wsgi_application() |
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 @@ | ||
django |
Empty file.
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,8 @@ | ||
from dataclasses import dataclass | ||
|
||
|
||
@dataclass | ||
class User: | ||
user_id: int | ||
name: str | ||
email: str |
Empty file.
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,31 @@ | ||
import smtplib | ||
import ssl | ||
from random import randint | ||
from django.http import JsonResponse, HttpResponseBadRequest, HttpRequest, HttpResponse | ||
from django.views import View | ||
from src.domain.user import User | ||
from src.infrastructure.user_framework_repository import UserFrameworkRepository | ||
|
||
class UserController(View): | ||
# Create your views here. | ||
def get(self) -> HttpResponse: | ||
return JsonResponse("Hello, world. You're at the polls index.") | ||
|
||
def post(self, request: HttpRequest) -> HttpResponse: | ||
if len(request.POST['password']) <= 8: | ||
return HttpResponseBadRequest('Password is not valid') | ||
if "_" not in request.POST['password']: | ||
return HttpResponseBadRequest('Password is not valid') | ||
if UserFrameworkRepository.get_instance().find_by_email(request.POST['email']) is not None: | ||
return HttpResponseBadRequest('The email is already in use') | ||
user = User(randint(1, 999999), request.POST['name'], request.POST['email']) | ||
UserFrameworkRepository.get_instance().save(user) | ||
|
||
# Send a confirmation email | ||
context = ssl.create_default_context() | ||
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server: | ||
# Uncomment this lines with a valid username and password | ||
server.login("[email protected]", "myPassword") | ||
server.sendmail('[email protected]', request.POST['email'], 'Confirmation link') | ||
|
||
return JsonResponse({'name': user.name, 'email': user.email, 'id': user.user_id}) |
Empty file.
24 changes: 24 additions & 0 deletions
24
user-registration-refactoring/src/infrastructure/user_framework_repository.py
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,24 @@ | ||
from src.domain.user import User | ||
|
||
|
||
class UserFrameworkRepository: | ||
repository: "UserFrameworkRepository" | None = None | ||
|
||
def __init__(self) -> None: | ||
self.users: dict[str, User] = {} | ||
|
||
def save(self, user: User) -> None: | ||
self.users[user.email] = user | ||
|
||
def find_by_email(self, email_address: str) -> User | None: | ||
return self.users.get(email_address) | ||
|
||
@staticmethod | ||
def get_instance() -> "UserFrameworkRepository": | ||
if UserFrameworkRepository.repository is None: | ||
UserFrameworkRepository.repository = UserFrameworkRepository() | ||
return UserFrameworkRepository.repository | ||
|
||
@staticmethod | ||
def set_instance(the_instance: "UserFrameworkRepository") -> None: | ||
UserFrameworkRepository.repository = the_instance |
Oops, something went wrong.