Skip to content

Commit

Permalink
Merge pull request #49 from Gray-Advantage/sergey
Browse files Browse the repository at this point in the history
Datanar 2.5.0 (dev)
  • Loading branch information
Gray-Advantage authored Aug 21, 2024
2 parents 6f892b4 + 68e89bf commit 6c5274e
Show file tree
Hide file tree
Showing 90 changed files with 7,871 additions and 1,878 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,5 @@ jobs:
cd ~/datanar
git reset --hard origin/main
git pull origin main
docker compose down -v
docker compose down
docker compose up --build -d
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ RUN rm -rf requirements
COPY ./datanar /datanar/
WORKDIR /datanar

CMD python manage.py migrate \
CMD python manage.py makemigrations \
&& python manage.py migrate \
&& python manage.py init_superuser \
&& python manage.py compilemessages \
&& python manage.py collectstatic --no-input \
Expand Down
29 changes: 10 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# [Datanar](https://datanar.ru) - сайт по сокращению ссылок

---

## Содержание
- [Введение](#введение)
- [Полезность](#для-чего-может-быть-полезно-сокращать-ссылки)
Expand All @@ -16,8 +14,6 @@
- [Контейнеризация docker](#контейнеризация-docker)
- [Авторы](#авторы---эти-прекрасные-люди)

---

## Введение
[Datanar](https://datanar.ru) представляет собой сервис для сокращения ссылок,
который позволяет пользователям преобразовывать длинные URL-адреса в короткие
Expand Down Expand Up @@ -59,8 +55,6 @@
- Указывать сколько переходов можно совершить по ссылке
- Связать свой сервис с нашим по API

---

## Структура проекта
### Используемые фреймворки / библиотеки
- [Bootstrap](https://getbootstrap.com/) - популярная (html / css / js)
Expand All @@ -79,11 +73,11 @@
![scheme](docs/for_readme/scheme.png)

Основных моделей три - `Redirect`, `Click` и `BlockedDomain`
- `Redirect` - хранит в себе связь между длинной и короткой ссылкой
- `Redirect` - хранит в себе связь между длинным URL-адресом и короткой ссылкой
- `Click` - хранит информацию о переходе по `Redirect`
(Для статистики и для ограничения по количеству переходов)
- `BlockedDomain` - регулярное выражение, описывающие url-адреса, которые
запрещены для сокращения
(для введения статистики)
- `BlockedDomain` - регулярное выражение, описывающие URL-адреса, которые
запрещены для сокращения

### Контейнеры docker
Проект развёрнут с помощью технологии контейнеризации docker compose и имеет
Expand All @@ -101,9 +95,8 @@
- [Redis](https://hub.docker.com/_/redis) - быстрый сервер баз данных типа
ключ-значение. Требуется для celery
- [Celery](Dockerfile) - контейнер, созданный на базе `Django`, но с
изменённой командой запуска

---
изменённой командой запуска, является очередью задач для массового сокращения
ссылок и динамического отслеживания за сроком годности перенаправлений

## Развёртывание
Данная документация предлагает два основных способа запуска (развёртывания)
Expand All @@ -114,12 +107,10 @@
Долгий и трудный путь, зато вы имеете полный контроль над всем происходящим

### [Контейнеризация docker](docs/docker-install.md)
- подразумевает загрузку одной единственной программы и её настройку, а дальше
запуск всего приложения в полной комплектации с помощью одной команды.
Быстрый и простой путь, большая часть вопросов уже решена, остальное дело
техники

---
- подразумевает загрузку одной единственной программы Docker и её настройку,
а дальше запуск всего приложения в полной комплектации с помощью одной
команды. Быстрый и простой путь, большая часть вопросов уже решена, остальное
дело техники

## Авторы - эти прекрасные люди:
<div style="display: flex; align-items: center;">
Expand Down
108 changes: 103 additions & 5 deletions datanar/api/tests/test_correct_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from django.urls import reverse
from rest_framework import status

from redirects.models import Redirect


class ApiCorrectTest(TestCase):
fixtures = ["fixtures/for_test_data.json"]
Expand All @@ -19,8 +21,28 @@ def setUp(self):
"short_EEE",
"short_FFF",
]

def test_new_token_context(self):
self.full_redirect_fields = {
"id",
"long_link",
"short_link",
"password",
"validity_days",
"validity_clicks",
"created_at",
"create_method",
"is_active",
"deactivated_at",
}
self.simple_redirect_fields = {
"long_link",
"short_link",
"password",
"validity_days",
"validity_clicks",
"created_at",
}

def test_create_new_token_context(self):
client = Client()

response1 = client.post(
Expand All @@ -40,7 +62,7 @@ def test_new_token_context(self):
response2.json()["token"],
)

def test_token_context(self):
def test_get_token_context(self):
response1 = Client().post(
reverse("api:get_token"),
data={"username": self.username, "password": self.password},
Expand All @@ -55,7 +77,7 @@ def test_token_context(self):

self.assertEqual(response1.json()["token"], response2.json()["token"])

def test_redirects_list(self):
def test_get_redirects_list(self):
response = self.client.get(reverse("api:redirect-list"))
self.assertEqual(response.status_code, status.HTTP_200_OK)

Expand All @@ -64,8 +86,19 @@ def test_redirects_list(self):
self.short_links,
)

def test_get_redirects_list_context(self):
response = self.client.get(reverse("api:redirect-list"))
self.assertEqual(response.status_code, status.HTTP_200_OK)

for redirect in response.json():
self.assertEqual(
redirect.keys(),
self.full_redirect_fields,
"Отсутствует обязательное для авторизированного клиента поле",
)

def test_redirect_id(self):
for i in range(1, 7):
for i in range(1, len(self.short_links) + 1):
response = self.client.get(
reverse("api:redirect-detail", args=[i]),
)
Expand All @@ -75,14 +108,40 @@ def test_redirect_id(self):
self.short_links[i - 1],
)

def test_redirect_id_context(self):
for i in range(1, len(self.short_links) + 1):
response = self.client.get(
reverse("api:redirect-detail", args=[i]),
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(
response.json().keys(),
self.full_redirect_fields,
"Отсутствует обязательное для авторизированного клиента поле",
)

def test_create_redirect(self):
redirect_count = Redirect.objects.count()

response = self.client.post(
reverse("api:redirect-list"),
data={"long_link": "https://python.org/"},
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.json()["long_link"], "https://python.org/")

self.assertEqual(
Redirect.objects.count(),
redirect_count + 1,
"Redirect не создан",
)

self.assertEqual(
Redirect.objects.last().create_method,
Redirect.CreateMethod.API,
"Метод создания для `redirect` неправильный",
)

response = self.client.get(
reverse(
"redirects:redirect",
Expand All @@ -92,6 +151,37 @@ def test_create_redirect(self):
)
self.assertRedirects(response, "https://python.org/")

def test_create_redirect_context(self):
response = Client().post(
reverse("api:redirect-list"),
data={"long_link": "https://python.org/"},
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(
response.json().keys(),
self.simple_redirect_fields,
"Присутствуют лишние поля для не авторизированного пользователя",
)

token = (
Client()
.post(
reverse("api:get_token"),
data={"username": self.username, "password": self.password},
)
.json()["token"]
)
response = self.client.post(
reverse("api:redirect-list"),
data={"token": token, "long_link": "https://python.org/"},
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(
response.json().keys(),
self.full_redirect_fields,
"Отсутствует обязательное для авторизированного пользователя поле",
)

def test_create_redirect_with_short_link(self):
response = self.client.post(
reverse("api:redirect-list"),
Expand All @@ -105,8 +195,16 @@ def test_create_redirect_with_short_link(self):
self.assertEqual(response.json()["short_link"], "test_short")

def test_delete_redirect(self):
redirect_count = Redirect.objects.count()

response = self.client.delete(reverse("api:redirect-detail", args=[1]))
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

self.assertEqual(
Redirect.objects.count(),
redirect_count - 1,
"Redirect не удалён",
)


__all__ = []
12 changes: 12 additions & 0 deletions datanar/api/tests/test_wrong_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ def test_redirect_without_auth(self):
response = Client().get(reverse("api:redirect-detail", args=[1]))
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

def test_redirect_with_wrong_token(self):
token = self.client.post(
reverse("api:get_token"),
data={"username": self.username, "password": self.password},
).json()["token"]

response = Client().get(
reverse("api:redirect-detail", args=[1]),
data={"token": token + "_w"},
)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

def test_redirect_with_wrong_id(self):
response = self.client.get(reverse("api:redirect-detail", args=[42]))
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
Expand Down
32 changes: 31 additions & 1 deletion datanar/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,43 @@ def __init__(self):
router.register("redirects", views.RedirectViewSet, basename="redirect")

urlpatterns = [
path("docs/", views.APIDocumentationView.as_view(), name="api_docs"),
path("docs/", views.APIDocsPreambleView.as_view(), name="docs_preamble"),
path("api-token-auth/", rest_views.obtain_auth_token, name="get_token"),
path(
"api-token-auth-update/",
views.CreateNewTokenView.as_view(),
name="create_new_token",
),
path(
"docs/qr_code_get",
views.APIDocsQRCodeGetView.as_view(),
name="docs_qr_code_get",
),
path(
"docs/redirect_get",
views.APIDocsRedirectGetView.as_view(),
name="docs_redirect_get",
),
path(
"docs/redirect_create",
views.APIDocsRedirectCreateView.as_view(),
name="docs_redirect_create",
),
path(
"docs/redirect_delete",
views.APIDocsRedirectDeleteView.as_view(),
name="docs_redirect_delete",
),
path(
"docs/token_get",
views.APIDocsTokenGetView.as_view(),
name="docs_token_get",
),
path(
"docs/token_create",
views.APIDocsTokenCreateView.as_view(),
name="docs_token_create",
),
*router.urls,
]

Expand Down
Loading

0 comments on commit 6c5274e

Please sign in to comment.