{"id":34610207,"url":"https://github.com/vmariiechko/internship-assignment","last_synced_at":"2026-05-27T17:31:43.230Z","repository":{"id":144121590,"uuid":"368558167","full_name":"vmariiechko/internship-assignment","owner":"vmariiechko","description":null,"archived":false,"fork":false,"pushed_at":"2021-05-22T06:44:31.000Z","size":46,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-26T02:48:07.672Z","etag":null,"topics":["api","django-rest-framework","internship","internship-task","jwt"],"latest_commit_sha":null,"homepage":"https://wiadomosci-api.herokuapp.com/api/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vmariiechko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-05-18T14:21:32.000Z","updated_at":"2021-05-22T06:55:15.000Z","dependencies_parsed_at":null,"dependency_job_id":"981e7c1f-ba39-40bf-88d1-7665f8904e7e","html_url":"https://github.com/vmariiechko/internship-assignment","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vmariiechko/internship-assignment","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vmariiechko%2Finternship-assignment","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vmariiechko%2Finternship-assignment/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vmariiechko%2Finternship-assignment/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vmariiechko%2Finternship-assignment/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vmariiechko","download_url":"https://codeload.github.com/vmariiechko/internship-assignment/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vmariiechko%2Finternship-assignment/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33577632,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-27T02:00:06.184Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","django-rest-framework","internship","internship-task","jwt"],"created_at":"2025-12-24T14:06:32.232Z","updated_at":"2026-05-27T17:31:43.217Z","avatar_url":"https://github.com/vmariiechko.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Dokumentacja do rozwiązania zadania\n\n![python]\n![django]\n![JWT]\n[![license]][license-url]\n\n## Spis Treści\n\n- [Treść zadania](#treść-zadania)\n- [Podjęte decyzje](#podjęte-decyzje)\n- [Opis API](#opis-api)\n    * [Tablica wszystkich URL](#tablica-wszystkich-url)\n    * [Uwierzytelnianie](#uwierzytelnianie)\n    * [Widok wiadomości](#widok-wiadomości)\n    * [Widok tworzenia wiadomości](#widok-tworzenia-wiadomości)\n    * [Widok edycji wiadomości](#widok-edycji-wiadomości)\n    * [Widok kasowania wiadomości](#widok-kasowania-wiadomości)\n- [Testy jednostkowe](#testy-jednostkowe)\n- [Rozmieszczenie aplikacji](#rozmieszczenie-aplikacji)\n- [Podziękowanie](#dziękuję-bardzo-za-uwagę-i-czekam-na-zwrotną-informację)\n\n---\n\n## Treść zadania\n\nZadaniem jest zaprojektowanie API i wykonanie aplikacji do zapisywania, zwracania\nzapisanych oraz edycji krótkich tekstów (do 160 znaków).\n\n---\n\n## Podjęte decyzje\n\n* Wybrany framework: [Django Rest Framework][drf-url] (dalej DRF)\n* Zapisywanie wiadomości odbywa się w bazie danych SQLite (dalej BD) za pomocą Django ORM\n* Odczytywanie wiadomości odbywa się za pomocą tzw. serializatorów, \n   które konwertują dane z BD do Python'owych typów i odwrotnie\n* Licznik wyświetleń wiadomości jest zaimplementowany jako atrybut w BD.\n\n---\n\n## Opis API\n\n\u003e Uwaga: wszystkie punkty końcowe są wyświetlone w skróconym wariancie, \n\u003e w razie ręcznego wpisana linku, jako przedrostek musi być [wiadomosci-api.herokuapp.com][deploy-url] \u003cbr/\u003e\n\u003e Aby się nie pomylić ze wpisywaniem linku, zachęcam do korzystania z hiperłączy poniżej,\n\u003e które domyślnie prowadzą na zapytanie GET w postaci graficznego interfejsu,\n\u003e który udostępnia DRF.\n\n#### Struktura opisu API\n\n* Na początku jest podana tablica z ogólnym opisem URL.\n* Następnie jest opis każdego widoku.\n\n---\n\n### Tablica wszystkich URL\n\n| Punkt końcowy            |             GET             |            POST           |            PUT            |       DELETE       |\n|--------------------------|:---------------------------:|:-------------------------:|:-------------------------:|:------------------:|\n| [/api/][api-url]         |    Dostać punkty końcowe    |            N/A            |            N/A            |         N/A        |\n| [/api/smses/][smses-url] | Dostać wszystkie wiadomości |  Utworzyć nową wiadomość  |            N/A            |         N/A        |\n| [/api/smses/\\\u003cid\u003e/][sms] |   Znaleźć wiadomość po ID   |            N/A            | Nadpisać treść wiadomości | Skasować wiadomość |\n| [/api/token/][token]     |             N/A             |    Uwierzytelnianie JWT   |            N/A            |         N/A        |\n| [/api/token/refresh/][r] |             N/A             | Dostać nowy token dostępu |            N/A            |         N/A        |\n| [/api-auth/login][in]    |             N/A             |   Uwierzytelnianie sesji  |            N/A            |         N/A        |\n\n---\n\n### Uwierzytelnianie\n\n\u003e Do przetestowania aplikacji podaję do dyspozycji dwa konta: \u003cbr/\u003e\n\u003e username: ```Jan``` password: ```nowakhaslo``` \u003cbr/\u003e\n\u003e username: ```Zofia``` password: ```nowakhaslo```\n\nZaimplementowane rodzaje uwierzytelniania:\n\n1. Oparte na sesji:\n    * Dodane jako gotowe rozwiązanie w DRF\n    * Pozwala na szybkie i wygodne logowanie się w interfejsie (zachęcana metoda do skorzystania z aplikacji)\n    * URL logowania: [/api-auth/login][in]\n    \n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://imgur.com/9i5UKAH.png\" /\u003e\n\u003c/p\u003e\n\n2. Oparte na tokenach (JWT)\n    * Dodane jako dodatkowa funkcjonalność, \n      ponieważ dodanie uwierzytelniania przez sesję nie zajęło dużego wysiłku :)\n    * Dla logowania się, aby dostać token dostępu i odświeżania należy wysłać zapytanie\n      POST na [/api/token/][token] z JSON-em:\n        ```json\n        {\n            \"username\": \"*username*\",\n            \"password\": \"*password*\"\n        }\n        ```\n      zatem jako odpowiedź dostaniemy parę tokenów (kluczy):\n        ```json\n        {\n            \"refresh\": \"*długi napis, token odświeżania*\",\n            \"access\": \"*długi napis, token dostępu*\"\n        }\n        ```\n      \n      ![jwt-token1] ![jwt-token2]\n      \n      Należy zachować te klucze, zatem przy kolejnym zapytaniu, które wymaga uwierzytelniania,\n      koniecznie trzeba umieścić token dostępu w nagłówku HTTP jako:\n        ```\n        Authorization: \"Bearer \u003ctoken dostępu\u003e\"\n        ```\n      Token dostępu ma dość krótki czas istnienia, zatem, aby dostać nowy token dostępu,\n      należy wykorzystać token odświeżania, który ma znacznie dłuższy czas życia. \n    * Dla ponowienia tokena dostępu należy wysłać zapytanie POST na [/api/token/refresh/][r]\n      z JSON-em:\n        ```json\n        {\n            \"refresh\": \"*długi napis, otrzymany wcześniej token odświeżania*\"\n        }\n        ```\n      w wyniku dostaniemy nowy token dostępu w postaci tego samego JSON-a:\n        ```json\n        {\n            \"access\": \"*długi napis, nowy token dostępu*\"\n        }\n        ```\n      ![refresh1] ![refresh2]\n    * W wypadku wygaśnięcia tokena odświeżania należy ponownie zalogować się tak jak opisane\n      powyżej, aby dostać nową parę tokenów.\n\n---\n\n### Widok wiadomości\n\nSą dwa widoki dla wiadomości:\n1. Widok dla przeglądu wszystkich wiadomości, zaimplementowano jako dodatkowa\n   funkcjonalność dla kompletności rozwiązania. Należy wysłać zapytanie GET na [/api/smses/][smses-url] \u003cbr/\u003e\n   Odpowiedzią będzie JSON ze wszystkimi wiadomościami wraz z ich ID i autorem:\n```json\n[\n    {\n        \"id\": 1,\n        \"author\": \"Vadym\",\n        \"message\": \"Nowa wiadomość\",\n        \"views_count\": 1\n    },\n    \"...\"\n]\n```\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://imgur.com/VPqAq0c.png\" /\u003e\n\u003c/p\u003e\n\n2. Widok dla przeglądu specyficznej wiadomości. Należy wysłać zapytanie GET na [/api/smses/\\\u003cid\u003e/][sms],\ngdzie '\\\u003cid\u003e' jest wartością ID dla specyficznej wiadomości. \u003cbr/\u003e\nOdpowiedzią będzie JSON z konkretną wiadomością razem z licznikiem wyświetleń:\n```json\n{\n    \"message\": \"Nowa wiadomość\",\n    \"views_count\": 2\n}\n```\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://imgur.com/wnf1UkY.png\" /\u003e\n\u003c/p\u003e\n\n---\n\n### Widok tworzenia wiadomości\n\nAby utworzyć nową wiadomość, należy być zalogowanym, aby zatem wysłać uwierzytelnione zapytanie POST na [/api/smses/][smses-url] z JSON-em:\n```json\n{\n    \"message\": \"Najnowsza wiadomość\"\n}\n```\nJako odpowiedź dostaniemy status HTTP 201 CREATED.\n![create1] ![create2]\n\n---\n\n### Widok edycji wiadomości\n\nAby zmienić treść wiadomości, tylko autor może wysłać uwierzytelnione zapytanie PUT na [/api/smses/\\\u003cid\u003e/][sms] z JSON-em:\n```json\n{\n    \"message\": \"Edytowana wiadomość\"\n}\n```\nJako odpowiedź dostaniemy JSON z nową wiadomością:\n```json\n{\n    \"message\": \"Edytowana wiadomość\",\n    \"views_count\": 0\n}\n```\n\n![edit1] ![edit2]\n\n---\n\n### Widok kasowania wiadomości\n\nAby skasować wiadomość, tylko autor może wysłać uwierzytelnione zapytanie DELETE na [/api/smses/\\\u003cid\u003e/][sms] \u003cbr/\u003e\nJako odpowiedź dostaniemy status HTTP 200 OK\n\n![delete]\n\n---\n\n### Testy jednostkowe\n\nNapisałem 23 testy jednostkowe, pokrywające model wiadomości oraz wszystkie\npunkty końcowe (przy użyciu pakietu coverage). Uwzględniłem testowanie uwierzytelniania oraz wyzerowanie licznika wyświetleń \npo nadpisaniu treści wiadomości.\n\n[kliknij, aby przejść do folderu z testami][tests-url]\n\n---\n\n### Rozmieszczenie aplikacji\n\nJak można zrozumieć z URL, aplikacja była rozmieszczona na heroku. \u003cbr/\u003e\nNiestety czasami długo się ładuje strona, ale mam nadzieje, że to nie wpłynęło\nna ogólne wrażenie rozwiązania zadania.\n\n---\n\n### Dziękuję bardzo za uwagę i czekam na zwrotną informację!\n\n---\n\n\u003e Gmail [vmariiechko@gmail.com](mailto:vmariiechko@gmail.com) \u0026nbsp;\u0026middot;\u0026nbsp;\n\u003e GitHub [@vmariiechko](https://github.com/vmariiechko) \u0026nbsp;\u0026middot;\u0026nbsp;\n\u003e LinkedIn [@mariiechko](https://www.linkedin.com/in/mariiechko/)\n\n\u003c!-- Markdown links and images --\u003e\n[python]: https://img.shields.io/badge/Python-14354C?style=for-the-badge\u0026logo=python\u0026logoColor=white\n[django]: https://img.shields.io/badge/Django-092E20?style=for-the-badge\u0026logo=django\u0026logoColor=white\n[JWT]: https://img.shields.io/badge/JWT-%23d6d6d6?logo=JSON-Web-Tokens\u0026logoColor=black\u0026style=for-the-badge\n[license]: https://img.shields.io/badge/license-MIT-%2341CD52.svg?\u0026style=for-the-badge\n\n[drf-url]: https://www.django-rest-framework.org/\n[deploy-url]: https://wiadomosci-api.herokuapp.com/\n[api-url]: https://wiadomosci-api.herokuapp.com/api/\n[smses-url]: https://wiadomosci-api.herokuapp.com/api/smses/\n[sms]: https://wiadomosci-api.herokuapp.com/api/smses/1/\n[token]: https://wiadomosci-api.herokuapp.com/api/token/\n[r]: https://wiadomosci-api.herokuapp.com/api/token/refresh/\n[in]: https://wiadomosci-api.herokuapp.com/api-auth/login/\n[tests-url]: https://github.com/vmariiechko/internship-assignment/tree/main/api/tests\n[license-url]: https://github.com/vmariiechko/internship-assignment/blob/main/LICENSE\n\n[drf-login]: https://imgur.com/9i5UKAH.png\n[jwt-token1]: https://imgur.com/ft1xtM7.png\n[jwt-token2]: https://imgur.com/eTOyIdx.png\n[refresh1]: https://imgur.com/GtyTAk2.png\n[refresh2]: https://imgur.com/BqyhdFN.png\n[smses]: https://imgur.com/VPqAq0c.png\n[sms]: https://imgur.com/wnf1UkY.png\n[create1]: https://imgur.com/EeMs5XJ.png\n[create2]: https://imgur.com/28X5lUV.png\n[edit1]: https://imgur.com/DqJ8UGV.png\n[edit2]: https://imgur.com/UvhVM6i.png\n[delete]: https://imgur.com/sCREpre.png","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvmariiechko%2Finternship-assignment","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvmariiechko%2Finternship-assignment","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvmariiechko%2Finternship-assignment/lists"}