Skip to content

Latest commit

 

History

History
236 lines (174 loc) · 15.3 KB

server-task.md

File metadata and controls

236 lines (174 loc) · 15.3 KB

Четвертое задание: веб-сервер

Нужно создать новостной веб-сервер с REST API, который принимает HTTP-запросы и отдает ответы в формате JSON. Можно представить, что это сервер для мобильного приложения.

Функциональные требования

Сущности

  1. Пользователи

    • Имя
    • Логин
    • Пароль
    • Дата создания
    • Админ (булевая переменная)
    • Может ли создавать новости (булева переменная)
  2. Категории (могут быть вложенными, то есть одна категория является подкатегорией для другой).

    Допустим, есть категория "Языки программирования", и у нее может быть подкатегория "Динамически Типизированные ЯП", и далее, соответственно, подкатегория подкатегории "Python" — и таких уровней вложенности может быть произвольное количество.

  3. Новости

    • Краткое название
    • Дата создания
    • Один пользователь (создатель)
    • Одна категория
    • Текстовый контент
    • Любое количество фотографий, от нуля
    • Опубликована ли новость (булева переменная).

API

  1. Когда по API запрашиваем новости, в каждой новости должны быть вложены все сущности (автор, категория (рекурсивно) и т.д.), за исключением картинок: в ответе нужно вернуть их URI.

  2. API новостей должно поддерживать фильтрацию. Фильтрация определяется параметрами в URI query (см. примеры ниже). Клиент может использовать сразу несколько фильтров по разным полям, и тогда они объединяются при помощи логического "И". Например, фильтр /news?created_at=2011-01-01&author=Ivan выдает новости, созданные 1 января 2011 г. пользователем по имени Ivan. Поддерживаются фильтры для полей:

    • День создания (созданные до даты, созданные с даты, созданные в указанный день)

      Примеры (можно выбрать другие названия):

      • /news?created_at=2018-05-21
      • /news?created_until=2018-05-21
      • /news?created_since=2018-05-21
    • Имя пользователя-автора

    • Категория по айди

    • Название (вхождение подстроки)

    • Контент (вхождение подстроки)

  3. API новостей должно поддерживать поиск по строке, которая может быть найдена либо в текстовом контенте, либо в имени автора, либо в названии категории.

  4. API новостей должно поддерживать сортировку отдельным параметром URI query. Клиент может использовать сортировку вместе с любым набором фильтров. Критерии сортировки:

    • Дата,
    • Автор (имя по алфавиту),
    • Категория (название по алфавиту),
    • Количество фотографий

    Пример: /news?sort_by=category.

  5. Должны быть отдельные эндпойнты для сущностей:

    • Категории:
      • создание только для админов
      • получение списка для всех
      • редактирование только для админов (редактирование названия и смена родительской категории)
    • Новости:
      • создание для юзеров, имеющих право создания новостей
      • получение списка всем. Опубликованные новости видят все, а неопубликованные - только их автор.
      • редактирование только автору новости. Редактироваться могут и опубликованные, и неопубликованные новости.
    • Пользователи:
      • создание только для админов
      • получение списка всем
    • Картинки:
      • получение одной картинки.
  6. Запросы на редактирование должны поддерживать редактирование любого количества полей сущности, т.е. одного поля, двух полей и т.д., вплоть до сразу всех. Например, при редактировании новости у нас должна быть возможность изменить только текст, не трогая остальные поля, или категорию вместе с заголовком, или любые другие поля в любых сочетаниях.

  7. Что доступно только админам, при запросе не-админам возвращать 404, а не 403, чтобы не показать само наличие такого API.

  8. Запросы по всем остальным URL должны возвращать 404.

  9. Для аутентификации рекомендуем использовать basic auth как самый простой вариант. Аутентификацию не надо проверять в эндпойнтах, которые доступны всем.

  10. Все API, возвращающие список данных, должны быть пагинированы, т.е. выдавать ограниченное количество элементов. Пагинация нужна для сокращения нагрузки на сервер: представьте, как она вырастет, если юзеры начнут массово запрашивать полный список из тысяч новостей, накопленных за годы работы сервера.

    • сервер никогда не выдает в ответе больше N элементов (т.е. новостей в запросе новостей, юзеров в запросе юзеров и т.п.). Значение N должно быть задано в одном месте (в коде или в конфиге), а не скопировано в разные места кода, чтобы ее можно было изменить правкой единственной строчки.

    • клиент может указать, сколько элементов он хочет получить и начиная с какого по счету элемента. Пример для новостей: /news?offset=15&limit=20 - вернуть 20 новостей, пропустив 15 штук от начала списка (названия параметров даны только для примера). Клиент может не указывать любой параметр или даже оба:

      • если limit опущен, подразумевается N
      • а если offset опущен, подразумевается 0.
    • если клиент запросил значение limit большее, чем N, он все равно получает не более N элементов.

    • пагинацию реализуйте ключевыми словами LIMIT и OFFSET в соответствующих SQL-запросах.

  11. Сервер никогда не возвращает пароль пользователя.

  12. В БД нужно хранить хеши паролей, а не сами пароли. Можно (но совсем необязательно) сделать сложнее и правильнее: использовать хеширование c солью, например, при помощи случайно сгенерированной соли и PBKDF2 (см. модуль Crypto.KDF.PBKDF2 в библиотеке cryptonite).

  13. В ответе на запрос картинки должен быть правильный заголовок Content-Type, например, Content-Type: image/png. Как его узнать? Можно его передавать в запросе, который создает картинку.

  14. Для создания картинки достаточно передать ее содержимое, закодированное в base64, в запросе на создание или редактирование новости. Но при желании вы можете передать картинку в запросе типа multipart/form-data.

Технические требования

  1. В качестве веб-сервера под капотом использовать Warp

    Обязательно прочесть описание, как он работает: http://www.aosabook.org/en/posa/warp.html.

    В целом эту книгу (AOSA Book) очень рекомендуем, там огромное количество инсайтов по многим крутым опенсорсным проектам.

    Чтобы использовать Warp, необходимо понимать WAI, так что еще по нему прочитать — https://www.yesodweb.com/book/web-application-interface.

  2. В качестве базы использовать PostgreSQL

  3. Рекомендуемые библиотеки:

    1. cryptonite для хеширования паролей
    2. warp, wai и servant как основа для веб-сервера
    3. библиотека для работы с PostgreSQL, любая, на выбор:
      • попроще: postgresql-simple, hasql
      • дольше изучать, но более безопасные в плане типов и чаще применяемые на практике: beam, pesistent + esqueleto
    4. библиотека для чтения конфига: любая, например, aeson, yaml, configurator, dhall
    5. вероятно, вам понадобится text, bytestring, http-types, network-uri, containers, unordeded-containers, base64, base64-bytestring, vector, array и другие библиотеки из Haskell Platform. В целом можно использовать почти все подобные простые библиотеки. Библиотека должна решать одну задачу, а не вести себя как фреймворк, т.е. не работать одновременно с базой и вебом. Если у вас есть сомнения, можно ли использовать библиотеку, спросите в чате.
  4. Для работы с базой можно использовать любые библиотеки, главное — самим создавать и поддерживать миграции. В идеале, в готовом проекте по миграциям должна прослеживаться история изменения таблиц в процессе разработки - то есть дожна быть не только инициализирующая миграция (разве что вы сразу всё красиво спроектировали:)), но и миграции для добавления новых таблиц, изменения названий и типов полей и т.д.

    Миграции — код, который задает структуру базе (то есть создание таблиц, изменение, переименование, удаление полей и тд).

    Обязательно держите в голове, что после того, как проект выпускается в продакшен, база на проде заполняется данными, которые ни в коем случае нельзя потерять. Но требование менять структуру базы регулярно появляется за время развития проекта, и надо уметь развивать базу без потери данных. Для этого нужно как можно тщательней формализовать все изменения, которые вы проводите над базой от версии к версии.

  5. Должна быть предоставлена отдельная команда, которая позволит применить все миграции к локальной базе данных и создать всю нужную структуру для бд.

  6. В отдельной папке проекта сделать sh-скрипты со всеми возможными CURL-запросами, чтобы мы могли быстро склонировать проект и потестить его. Сделать по sh-файлу на каждый CURL-запрос.

  7. Также следовать общим правилам, описанным в прошлом задании.

Источники

Lessons learned while writing a Haskell application — не всем рекомендациям отсюда мы бы советовали следовать, но почитать про релевантный опыт автора статьи будет однозначно полезно.