├── week05-k8s ├── slides.pdf └── README.md ├── week01-intro ├── system_design_intro.pdf └── README.md ├── week02-inmemory-url-shortener └── README.md ├── README.md ├── week04-mongodb └── README.md └── tasks ├── 02-hw1-milestone2 ├── README.md └── microblog.yaml ├── 01-hw1-milestone1 ├── README.md └── microblog.yaml ├── 03-hw2-milestone1 ├── README.md └── microblog.yaml ├── 00-golang-task └── README.md └── 04-hw3-milestone1 ├── README.md └── microblog.yaml /week05-k8s/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hse-system-design/public/HEAD/week05-k8s/slides.pdf -------------------------------------------------------------------------------- /week01-intro/system_design_intro.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hse-system-design/public/HEAD/week01-intro/system_design_intro.pdf -------------------------------------------------------------------------------- /week01-intro/README.md: -------------------------------------------------------------------------------- 1 | # Введение и основы Go 2 | 3 | * Презентация про организацию курса и основные характеристики языка Go [тут](./system_design_intro.pdf) 4 | * Ссылка на Go Tour, который нужно пройти к следующему занятию -- https://go.dev/tour 5 | * Утиллита Jetbrains Toolbox для для установки/обновления IDE от Jetbains -- https://www.jetbrains.com/toolbox-app/ 6 | -------------------------------------------------------------------------------- /week02-inmemory-url-shortener/README.md: -------------------------------------------------------------------------------- 1 | # Содержание 2 | 3 | На занятии мы 4 | 5 | - обсудили, как концептуально делать сокращатель ссылок 6 | - написали сокращатель ссылок на Go с in-memory хранилищем 7 | - покрыли написанный сервер тестами 8 | 9 | # Ссылки 10 | 11 | - Рассмотренный на занятии код можно найти [здесь](https://github.com/hse-system-design/live-coding/tree/7a12fe387c4da2818b14679b6d82978f6d7b0e68/url-shortener) 12 | - (optional) [table-driven tests in Go](https://dave.cheney.net/2019/05/07/prefer-table-driven-tests) -------------------------------------------------------------------------------- /week05-k8s/README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes 2 | 3 | На этом занятии мы обсудили часть проблем оркестрации и посмотрели, 4 | как эти проблемы решает самая популярная система оркестрации --- Kubernetes. 5 | 6 | ## Откуда взялось сокращение k8s? 7 | 8 | `k8s` --- это [нумероним](https://en.wikipedia.org/wiki/Numeronym) от слова kubernetes, 9 | по сути означающий "слово начинается на k, заканчивается на s и имеет 8 букв между ними". 10 | 11 | Другой часто встречающийся в software engineering нумероним --- `i18n` (internationalization). 12 | 13 | ## Ссылки 14 | 15 | - **мотивация использования Kubernetes --- https://youtu.be/LeVULLqWwcg** 16 | - слайды, которые я показывал на первой половине занятия [slides](slides.pdf) 17 | - команды и yaml-файлы, на которые мы смотрели во время семинара --- в репозитории [live coding](https://github.com/hse-system-design/live-coding/tree/master/k8s-101) 18 | - интерактивный туториал по Kubernetes на [офицальном сайте](https://kubernetes.io/docs/tutorials/kubernetes-basics/) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # System Design 2022 2 | 3 | Это репозиторий курса ["Дизайн Систем"](https://www.hse.ru/edu/courses/452715820). В этой ветке содержатся материалы 2022го года. 4 | Материалы других лет см. в других ветках. 5 | 6 | # Полезные ссылки 7 | 8 | * [Telegram-канал с анонсами](https://t.me/+qKTB_0W2N4swNmEy) 9 | * [Telegram-чат с преподавателями](https://t.me/+cH3c1m--rxY0ZWIy) 10 | * [Ссылка на Zoom](https://us06web.zoom.us/j/88575619011?pwd=c3doQml3bWUzRXRPNW93eDFhZ2xoUT09) -- занятия проходят по средам с 18:10 МСК 11 | * [Ведомость с оценками](https://docs.google.com/spreadsheets/d/1-qqi7KYG-0pjWgNicBqh-J6EsJeGCffzDsII-_DNTNY?usp=sharing) 12 | 13 | # Формула оценивания 14 | 15 | Вся активность, подлежащая оцениванию на курсе, состоит из следующих компонентов: 16 | 17 | * `O1` --- оценка за большие домашние задания (их будет 3 за весь курс) 18 | * `O2` --- оценка за маленькие домашние задания (их будет 5-8 за весь курс) 19 | * `O3` --- оценка за *конструктивную* активность на семинарах в последней части курса (где мы будем проектировать системы). 20 | 21 | Оценка по каждому из этих компонентов вычисляется как доля выполненных активностей, умноженная на 10. 22 | 23 | Итоговая оценка за курс рассчитывается по формуле ``round(0.7 * O1 + 0.3 * min(10; O2+O3))``, 24 | где `round` --- обычное математическое округление, `round(3.5) = 4`. -------------------------------------------------------------------------------- /week04-mongodb/README.md: -------------------------------------------------------------------------------- 1 | # Содержание Занятия 2 | 3 | На занятии мы 4 | 5 | - поверхностно познакомились с MongoDB и обсудили её основные отличия от популярных реляционных баз данных 6 | - обсудили ключевые моменты API сервиа, который необходимо реализовать в [hw1-milestone1](../tasks/01-hw1-milestone1/microblog.yaml) 7 | - обсудили плюсы и минусы двух разных подходов к реализации пагинации с точки зрения эффективности работы с базой данных 8 | - посмотрели, как работать с MongoDB в Go 9 | 10 | # Полезные ссылки 11 | 12 | - **[самое важное, что вы должны знать про MongoDB](https://youtu.be/b2F-DItXtZs)** 13 | - сокращатель ссылок, хранящий данные в MongoDB в репозитории [live coding](https://github.com/hse-system-design/live-coding/tree/ab10d180b2f4a4b2468d612828bc74741f2d6993) 14 | (рекоммендуется знакомиться с кодом в этом репозитории по-коммитно, см. раздел History / git log на вашем компьютере) 15 | - [Go Mongo Tutorial](https://www.mongodb.com/blog/post/mongodb-go-driver-tutorial) 16 | - [don't use "offset"](https://www.youtube.com/watch?v=WDJRRNCGIRs&ab_channel=HusseinNasser) --- 10-минутное видео, 17 | с объяснением проблемы использования offset в SQL (то же, что мы обсуждали на занятии про пагинацию) 18 | - [jsonb in postgres](https://dzone.com/articles/using-jsonb-in-postgresql-how-to-effectively-store) --- как 19 | получить в постгресе что-то типа mongo experience. -------------------------------------------------------------------------------- /tasks/02-hw1-milestone2/README.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание 1. Часть 2. 2 | 3 | **Решения принимаются до 9 октября 23:59:59.(9) MSK включительно**. 4 | 5 | ## Задача 6 | 7 | Добавить в сервис, реализованный в первой части ДЗ, поддержку MongoDB для хранения данных. 8 | 9 | Также необходимо добавить дополнительный эндпоинт `/maintenance/ping` 10 | для определения готовности сервиса к работе (см. обновленный [microblog.yaml](./microblog.yaml)) 11 | Гарантируется, что тесты не стартуют до тех пор, пока сервис не вернёт `200 OK` 12 | с этого эндпоинта. 13 | 14 | ## Рекоммендации 15 | 16 | - если вы не делали [первую часть](../01-hw1-milestone1) домашки, начните с неё. 17 | Баллов вы за это уже не получите, но это будет хорошим стартом. 18 | Jenkins-пайплайн для проверки первой домашки всё еще доступен. 19 | 20 | - во время старта сервиса в режиме `mongo` создайте индекс для оптимального поиска постов автора. 21 | 22 | ## Формат сдачи 23 | 24 | - Сервис должен быть оформлен в виде **приватного** git-репозитория в организации [hse-system-design](https://github.com/hse-system-design) 25 | с реализацией на Go. В корне репозитория должен находиться ``Dockerfile``, описывающий образ сервиса. 26 | 27 | - При запуске Docker-образа сервис должен автоматически стартовать в соответствии с установленными переменными 28 | окружения: 29 | 30 | - `SERVER_PORT` --- номер порта, на котором должен быть доступен API 31 | - `STORAGE_MODE` --- режим хранения постов, возможно одно из двух значений: 32 | - `inmemory` --- хранение в памяти (в этом режиме сервис должен себя вести так же, как в первом задании) 33 | - `mongo` --- хранение в MongoDB. Адрес и название базы данных передаются 34 | в отдельных переменных окружения (см. ниже) 35 | - `MONGO_URL` --- адрес для подключения к MongoDB 36 | - `MONGO_DBNAME` --- название базы данных, которую можно использовать для хранения. 37 | 38 | - Критерий сдачи --- успешная сборка Jenkins-джобы http://51.250.71.53/job/hw1-milestone2/ 39 | (логин `student` пароль `student1234!`) 40 | 41 | - Успешную сборку нужно сохранить, нажав кнопку "Keep this build forever", 42 | и загрузить в форму https://forms.gle/Yg8SPRTnkJ41MKmN6. 43 | 44 | - Убедиться, что вы отправили решение, можно в [этой таблице](https://docs.google.com/spreadsheets/d/1Ls98pppmQjWXE0p-4isKWD0YRFfMtFdQanOEJk_5wfA?usp=sharing) 45 | -------------------------------------------------------------------------------- /tasks/01-hw1-milestone1/README.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание №1. Часть 1. 2 | 3 | **Решения до 2 октября 23:59:59.(9) МСК включительно**. 4 | 5 | Моментом сдачи считается timestamp успешного решения в Jenkins. 6 | 7 | ## Задача 8 | 9 | Разработать web-сервис, реализующий HTTP API с функциональностью минималистичного микроблога типа Twitter. 10 | Сервис должен предоставлять следующий функционал: 11 | - создание нового поста 12 | - получение поста по уникальному идентификатору 13 | - получение всех постов пользователя в обратном хронологическом порядке с пагинацией 14 | 15 | Формальное описание API можно найти в [microblog.yaml](./microblog.yaml). 16 | 17 | ## Рекоммендации 18 | 19 | - Для хранения постов можно использовать встроенные в Go структуры данных map и slice, 20 | а также примитивы синхронизации из пакета ``sync``, например, ``sync.RWMutex``. 21 | Если у вас есть сложности в реализацией хранилища, рекомендуется сначала сделать задание [golang-task](../00-golang-task). 22 | 23 | - Обратите внимание, что последующие домашние задания в этом курсе будут являться доработками данного сервиса, 24 | поэтому рекомендуем позаботиться о том, чтобы вы сами смогли разобраться в своем коде через 2 недели. 25 | 26 | - Не поленитесь написать тесты к своему решению, чтобы не тратить время на ожидание сборки в Jenkins. 27 | В этом вам помогут библиотечки [stretchr/testify](https://github.com/stretchr/testify) и [getkin/kin-openapi](https://github.com/getkin/kin-openapi#validating-http-requestsresponses). 28 | При выполнении следующих заданий скажете себе спасибо! 29 | 30 | - Сразу выделите интерфейс хранилища постов, 31 | поскольку в одной из следующих частей этой домашки вам придется перейти с in-memory хранилища 32 | на использование персистентной базы данных. 33 | 34 | ## Формат сдачи 35 | 36 | - Сервис должен быть оформлен в виде git-репозитория с реализацией на Go. 37 | В **корне** репозитория должен находиться ``Dockerfile``, описывающий образ сервиса. 38 | 39 | - При запуске Docker-образа сервис должен автоматически стартовать на порту, 40 | указанном в переменной окружения `SERVER_PORT`. 41 | 42 | - Критерий сдачи --- успешная сборка Jenkins-джобы http://51.250.71.53/job/hw1-milestone1/ 43 | (логин `student` пароль `student1234!`) 44 | 45 | - Успешную сборку нужно сохранить, нажав кнопку "Keep the build forever", 46 | и загрузить в форму https://forms.gle/Xkgsj9tEu2cncq1D8 47 | 48 | - Проверить, что вы успешно отправили посылку, можно по [этой таблице](https://docs.google.com/spreadsheets/d/1549wsZqTzb3p4Vw6c4-cCWeI1T5eR-kx4MUNNKqsECU/edit?usp=sharing) 49 | -------------------------------------------------------------------------------- /tasks/03-hw2-milestone1/README.md: -------------------------------------------------------------------------------- 1 | # Домашнее Задание 2. 2 | 3 | ## Сроки сдачи 4 | 5 | * Срок сдачи в качестве маленькой домашки `hw2-m1` --- до **6 ноября 23:59:59.(9) МСК** включительно 6 | * Срок сдачи в качестве большой домашки `hw2` --- до **20 ноября 23:59:59.(9) МСК** включительно 7 | 8 | ## Задача 9 | 10 | Добавить в сервис, реализованный в первом ДЗ, возможность модификации документов 11 | с кешированием на основе Redis. Модификацию документов также необходимо поддержать в in-memory режиме. 12 | 13 | 14 | К решению предъявляются все те же требования, что в [hw1-milestone2](../02-hw1-milestone2), 15 | а также несколько дополнительных: 16 | 17 | - необходимо реализовать эндпоинт ``PATCH /api/v1/posts/{postId}``. 18 | Этот эндпоинт должен поддерживаться во всех режимах работы приложения. 19 | - необходимо поддержать значение ``cached`` переменной ``STORAGE_MODE``. В этом режиме приложение должно 20 | кешировать посты в Redis, адрес которого задан в переменной окружения ``REDIS_URL``. 21 | 22 | Обновленную спецификацию OpenAPI можно найти в [microblog.yaml](./microblog.yaml). 23 | 24 | ## Рекоммендации 25 | 26 | - если у вас в коде всё еще нет интерфейса хранилища постов, рекомендуем его создать, 27 | иначе поддержать все 3 интерфейса будет непросто. 28 | 29 | - озаботьтесь грамотной инвалидацией кэша. Если не знаете, как это сделать, 30 | посмотрите записи наших семинаров, посвященных этой теме. 31 | 32 | ## Формат сдачи 33 | 34 | - Сервис должен быть оформлен в виде **приватного** git-репозитория в организации [hse-system-design](https://github.com/hse-system-design) 35 | с реализацией на Go. В корне репозитория должен находиться ``Dockerfile``, описывающий образ сервиса. 36 | 37 | - При запуске Docker-образа сервис должен автоматически стартовать в соответствии с установленными переменными 38 | окружения: 39 | 40 | - `SERVER_PORT` --- номер порта, на котором должен быть доступен API 41 | - `STORAGE_MODE` --- режим хранения постов, возможно одно из двух значений: 42 | - `inmemory` --- хранение в памяти (в этом режиме сервис должен себя вести так же, как в первом задании) 43 | - `mongo` --- хранение в MongoDB. Адрес и название базы данных передаются 44 | в отдельных переменных окружения (см. ниже) 45 | - `cached` --- хранение в MongoDB с кешированием к Redis. 46 | Адреса MongoDB и Redis передается в отдельной переменной окружения (см. ниже) 47 | - `MONGO_URL` --- адрес для подключения к MongoDB 48 | - `MONGO_DBNAME` --- название базы данных, которую можно использовать для хранения. 49 | - `REDIS_URL` --- адрес для подключения к Redis 50 | 51 | - Критерий сдачи --- успешная сборка Jenkins-джобы http://51.250.71.53/job/hw2-milestone1/ 52 | (логин `student` пароль `student1234!`) 53 | 54 | - Успешную сборку нужно сохранить, нажав кнопку "Keep this build forever" 55 | 56 | - Опционально можно сохранить хеш коммита в форму https://forms.gle/W2gzpVqfyMM8aZGe7 -------------------------------------------------------------------------------- /tasks/00-golang-task/README.md: -------------------------------------------------------------------------------- 1 | # Go 2 | 3 | **Решение принимается вплоть до 18 сентября 23:59:59.(9) МСК** 4 | 5 | Время сдачи решения определяется по времени запуска сборки в Jenkins. 6 | 7 | Таблицу отправленных в форму решений можно посмотреть [по ссылке](https://docs.google.com/spreadsheets/d/1MZx53-OHb5OiDx2vBAUeDAxwIqippkILAbM1KvkE3q8/edit?usp=drivesdk) 8 | 9 | ## Постановка задачи 10 | 11 | В этом задании нужно будет потренироваться писать код на Go. 12 | Для этого вам предлагается реализовать интерфейс ``Collection``, реализующий коллекцию со следующим функционалом: 13 | 14 | - добавление пары ключ/значение 15 | - получение значения по ключу 16 | - итерация по парам ключ/значение в хронологическом порядке их добавления в коллекцию 17 | - удаление из коллекции наименьшего ключа (вместе с ассоциированным с ним значением) 18 | 19 | Подробное описание интерфейса, который необходимо реализовать, можно найти в файле [interfaces.go](https://github.com/hse-system-design/checker/blob/master/golang-task/interfaces.go) 20 | в коде проверяющей системы. Также рядом в файле [impl_test.go](https://github.com/hse-system-design/checker/blob/master/golang-task/impl_test.go) 21 | содержится код тестов, которые ваш интерфейс должен пройти. 22 | Чтобы запустить эти тесты, добавьте реализацию интерфейса и функцию `NewCollection`, которая создает пустую коллекцию, 23 | в файл `impl.go` и запустите команду 24 | `go mod tidy && go test -v -timeout 10s` в директории с этими файлами. 25 | 26 | ## Как сдавать эту задачу 27 | 28 | У нас в облаке поднят Jenkins, который выполняет роль проверяющей системы. 29 | 30 | Найти его можно по адресу - http://51.250.71.53/ (про доступ до Jenkins - смотри ниже) 31 | 32 | В нем нужно найти и перейти в пайплайн `golang-task` и нажать кнопку в левом меню `Build with Parameters`. 33 | 34 | * STUDENT_NAME - Это ваше имя (пожалуйста, заполняйте его на кириллице, аналогично тому, как вы заполняли его в ведомости) 35 | * GITHUB_CLONE_URL - Это ссылка на ваш персональный **приватный** github-репозиторий в организации [hse-system-design](https://github.com/hse-system-design) 36 | **NOTE:** убедительная просьба называть репозиторий в стиле `2022-name-surname`, например `2022-lev-khotov`. 37 | Вы можете использовать один и тот же репозиторий для всех домашек, храня решения в разных ветках, а можете 38 | создавать отдельный репозиторий на каждую домашку. В этом случае, придерживайтесь пожалуйста нейминга `2022-name-surname-hwname`, 39 | например `2022-lev-khotov-00-golang-task` (название домашки идентично названию папки, в которой лежит его условие). 40 | * GIT_COMMIT_HASH - Это хеш коммита, в котором содержится ваше решение (i.e. в данном коммите **в корне репозитория** должен быть файл `impl.go`). 41 | Обратите внимание, что необходимо указать полный хеш конкретного коммита -- ветки/теги приняты не будут. 42 | 43 | После нажатия `Build`, Дженкинс начнет собирать ваш код и прогонять тесты. 44 | Откройте вашу сборку (она будет последней в списке сборок, очевидно) и если она прошла успешно, 45 | нажмите `Keep this build forever` в правом верхнем углу. 46 | 47 | После этого можно скопировать ссылку на эту сборку (полную ссылку до этой странички) и прислать в эту гугл-форму - 48 | https://forms.gle/im73wYY8xdzsidta7 49 | 50 | **Важно:** обратите внимание, что указанные в Jenkins репозиторий и коммит должны быть доступны вплоть до завершения 51 | курса и проставления оценок в ведомость. Поэтому не переименовывайте и не удаляйте репозиторий после того, 52 | как вы сдали задание. 53 | 54 | ## Hints 55 | 56 | * В тестах вы можете увидеть один стресс-тест, который должен уложиться в 10 секунд на Jenkins. 57 | Так что все операции в вашем решении должны работать за O(1) или O(log N). 58 | Для справки: референсное решение проходит тесты за ~4.5 секунды на маке с M1 Pro и 7.5-8 секунд на Jenkins. 59 | * Для решения вам могут быть полезны стандартные пакеты [container/list](https://pkg.go.dev/container/list) 60 | и [container/heap](https://pkg.go.dev/container/heap). 61 | 62 | 63 | ### Как открыть Jenkins 64 | 65 | 1) Открываете http://51.250.71.53/ 66 | 2) Login: `student` 67 | 3) Password: `student1234!` 68 | -------------------------------------------------------------------------------- /tasks/04-hw3-milestone1/README.md: -------------------------------------------------------------------------------- 1 | # Домашнее Задание 3 2 | 3 | ## Сроки сдачи 4 | 5 | * Срок сдачи в качестве маленькой домашки `hw3-m1` --- до **27 ноября 23:59:59.(9) МСК** включительно 6 | * Срок сдачи в качестве большой домашки `hw3` --- до **11 декабря 23:59:59.(9) МСК** включительно 7 | 8 | ## Задача 9 | 10 | Добавить новую функциональность - подписки на других пользователей и лента постов. 11 | Любой пользователь может подписаться на другого пользователя. Подписаться на самого себя нельзя. 12 | 13 | Лента - это упорядоченный по времени публикации список всех постов от других пользователей, на которые подписался текущий авторизованный. 14 | Отписываться в нашей системе пока нельзя. 15 | 16 | Более формально: 17 | 18 | - Необходимо реализовать эндпоинт ``POST /api/v1/users/{userId}/subscribe`` 19 | Он добавляет текущему авторизованному пользователю новую подписку. 20 | - Необходимо реализовать эндпоинт ``GET /api/v1/subscriptions`` 21 | Он возвращает идентификаторы всех пользователей, на который подписался текущий. 22 | - Необходимо реализовать эндпоинт ``GET /api/v1/subscribers`` 23 | Он возвращает идентификаторы всех пользователей, которые подписались на текущего. 24 | - Необходимо реализовать эндпоинт ``GET /api/v1/feed`` 25 | Возвращает список постов из ленты пользователя. Интерфейс коммуникации с этим эндпоинтом точно такой же как и у ``/api/v1/users/{userId}/posts`` 26 | (То есть такой же формат выдачи и такой же способ организации страниц) 27 | - Не забудьте, что система все еще поддерживает два эндпоинта для добавления и редактирования постов. Их вызовы должны оказывать влияние на соответствующие ленты пользователей. 28 | 29 | Обновленную спецификацию OpenAPI можно найти в [microblog.yaml](./microblog.yaml). 30 | 31 | ВАЖНО: 32 | 33 | Отображение ленты - это не самая тривиальная задача и наивный алгоритм при большом количестве подписчиков 34 | перестанет эффективно работать. На наших масштабах такую проблему сложно будет заметить, 35 | но мы с вами разрабатываем сервис сразу на вырост, 36 | поэтому решение должно включать в себя фоновую обработку перестроения ленты. 37 | 38 | Для этих целей вам будет выдан Redis как самый простой и удобный для использования брокер сообщений. 39 | При запуске тестов ваш код будет запущено в виде двух сервисов: 40 | 41 | - веб-сервер, который будет принимать http запросы 42 | - воркер, который будет обрабатывать фоновые задачи. 43 | 44 | Различить, какой сервис сейчас запускается, можно будет по переменной окружения `APP_MODE`. 45 | 46 | В связи с тем, что обработка в фоне означает eventual consistency, в тестах этот момент будет учтен --- 47 | тесты сделают небольшую паузу в критические моменты, чтобы процессы в системе имели время сойтись. 48 | 49 | ## Рекоммендации 50 | 51 | - Заранее продумайте схемы в базе данных - какие коллекции вам нужны, какие индексы нужно построить и тд. 52 | 53 | - Используйте готовые инструменты для реализации обработки очередей. Например, рекомендуется ознакомиться с 54 | примером реализации очереди на Redis & Go с прошлого кода: [код](https://github.com/hse-system-design/public/tree/year-2021/15-queues-more/task-queue) 55 | и [видео-разбор](https://youtu.be/0oNnkb8X744?t=55). 56 | 57 | ## Формат сдачи 58 | 59 | 60 | - Сервис должен быть оформлен в виде **приватного** git-репозитория в организации [hse-system-design](https://github.com/hse-system-design) 61 | с реализацией на Go. В корне репозитория должен находиться ``Dockerfile``, описывающий образ сервиса. 62 | 63 | - При запуске Docker-образа сервис должен автоматически стартовать в соответствии с установленными переменными 64 | окружения: 65 | 66 | - `APP_MODE` --- режим запуска сервиса. Возможные значения: 67 | - `SERVER` --- сервис должен запустить http сервер 68 | - `WORKER` --- сервис должен запустить воркер 69 | - `SERVER_PORT` --- номер порта, на котором должен быть доступен API 70 | - `MONGO_URL` --- адрес для подключения к MongoDB 71 | - `MONGO_DBNAME` --- название базы данных, которую можно использовать для хранения. 72 | - `REDIS_URL` --- адрес для подключения к Redis 73 | 74 | - Критерий сдачи --- успешная сборка Jenkins-джобы http://51.250.71.53/job/hw3-milestone1/ 75 | (логин `student` пароль `student1234!`) 76 | 77 | - Успешную сборку нужно сохранить, нажав кнопку "Хранить эту сборку вечно", 78 | и (опционально) загрузить в форму https://forms.gle/ZW79KQb9fT8XcZ596 79 | (проверить, что вы заполнили форму, можно по этой [таблице](https://docs.google.com/spreadsheets/d/11BQNTqvKwFumB0ae7IUSYwFcQ0E43Z5npOZkQ_WTEAA/edit?usp=sharing)) -------------------------------------------------------------------------------- /tasks/01-hw1-milestone1/microblog.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.3 2 | info: 3 | title: Microblog API 4 | description: Microblog API 5 | version: 1.0.0 6 | components: 7 | schemas: 8 | PostId: 9 | description: Уникальный идентификатор поста в формате Base64URL. 10 | type: string 11 | pattern: '[A-Za-z0-9_\-]+' 12 | UserId: 13 | description: Уникальный идентификатор пользователя 14 | type: string 15 | pattern: '[0-9a-f]+' 16 | ISOTimestamp: 17 | description: Момент времени в формате ISO 8601 в часовом поясе UTC+0. 18 | type: string 19 | pattern: '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z' 20 | Post: 21 | type: object 22 | nullable: false 23 | properties: 24 | id: 25 | allOf: 26 | - $ref: '#/components/schemas/PostId' 27 | - nullable: false 28 | - readOnly: true 29 | text: 30 | type: string 31 | nullable: false 32 | authorId: 33 | allOf: 34 | - $ref: '#/components/schemas/UserId' 35 | - nullable: false 36 | - readOnly: true 37 | createdAt: 38 | allOf: 39 | - $ref: '#/components/schemas/ISOTimestamp' 40 | - nullable: false 41 | - readOnly: true 42 | PageToken: 43 | type: string 44 | pattern: '[A-Za-z0-9_\-]+' 45 | paths: 46 | '/api/v1/posts': 47 | post: 48 | summary: Публикация поста 49 | parameters: 50 | - in: header 51 | name: System-Design-User-Id 52 | required: true 53 | description: > 54 | Идентификатор ползователя, который аутентифицирован в данном запросе. 55 | schema: 56 | $ref: '#/components/schemas/UserId' 57 | requestBody: 58 | content: 59 | application/json: 60 | schema: 61 | $ref: '#/components/schemas/Post' 62 | responses: 63 | 200: 64 | description: Пост был успешно создан. Тело ответа содержит созданный пост. 65 | content: 66 | application/json: 67 | schema: 68 | $ref: '#/components/schemas/Post' 69 | 401: 70 | description: > 71 | Токен пользователя отсутствует в запросе, или передан в неверном формате. 72 | '/api/v1/posts/{postId}': 73 | get: 74 | summary: Получение поста по идентификатору 75 | parameters: 76 | - in: path 77 | name: postId 78 | required: true 79 | schema: 80 | $ref: '#/components/schemas/PostId' 81 | responses: 82 | 200: 83 | description: Пост найден 84 | content: 85 | application/json: 86 | schema: 87 | $ref: '#/components/schemas/Post' 88 | 404: 89 | description: Поста с указанным идентификатором не существует 90 | '/api/v1/users/{userId}/posts': 91 | get: 92 | summary: Получение страницы последних постов пользователя 93 | description: > 94 | Получение страницы с постами пользователя. 95 | 96 | Для получения первой страницы (с самыми последними постами), необходимо выполнить запрос 97 | без параметра `page`. 98 | Для получения следующей странцы, необходимо в параметр `page` передать токен следующей страницы, 99 | полученный в теле ответа с предыдущей страницей. 100 | parameters: 101 | - in: path 102 | name: userId 103 | required: true 104 | schema: 105 | $ref: '#/components/schemas/UserId' 106 | - in: query 107 | name: page 108 | description: Токен страницы 109 | required: false 110 | schema: 111 | $ref: '#/components/schemas/PageToken' 112 | - in: query 113 | name: size 114 | description: Количество постов на странице 115 | required: false 116 | schema: 117 | type: integer 118 | minimum: 1 119 | maximum: 100 120 | default: 10 121 | responses: 122 | 200: 123 | description: Страница с постами. 124 | content: 125 | application/json: 126 | schema: 127 | type: object 128 | properties: 129 | posts: 130 | type: array 131 | description: > 132 | Посты в обратном хронологическом порядке. 133 | Отсутствие данного поля эквивалентно пустому массиву. 134 | items: 135 | $ref: '#/components/schemas/Post' 136 | nextPage: 137 | allOf: 138 | - $ref: '#/components/schemas/PageToken' 139 | - nullable: false 140 | - description: > 141 | Токен следующей страницы при её наличии. 142 | Поле отсутствует, если текущая страница содержит самый ранний пост пользователя. 143 | 400: 144 | description: Некорректный запрос, например, из-за некорректного токена страницы. 145 | -------------------------------------------------------------------------------- /tasks/02-hw1-milestone2/microblog.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.3 2 | info: 3 | title: Microblog API 4 | description: Microblog API 5 | version: 1.0.0 6 | components: 7 | schemas: 8 | PostId: 9 | description: Уникальный идентификатор поста в формате Base64URL. 10 | type: string 11 | pattern: '[A-Za-z0-9_\-]+' 12 | UserId: 13 | description: Уникальный идентификатор пользователя 14 | type: string 15 | pattern: '[0-9a-f]+' 16 | ISOTimestamp: 17 | description: Момент времени в формате ISO 8601 в часовом поясе UTC+0. 18 | type: string 19 | pattern: '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z' 20 | Post: 21 | type: object 22 | nullable: false 23 | properties: 24 | id: 25 | allOf: 26 | - $ref: '#/components/schemas/PostId' 27 | - nullable: false 28 | - readOnly: true 29 | text: 30 | type: string 31 | nullable: false 32 | authorId: 33 | allOf: 34 | - $ref: '#/components/schemas/UserId' 35 | - nullable: false 36 | - readOnly: true 37 | createdAt: 38 | allOf: 39 | - $ref: '#/components/schemas/ISOTimestamp' 40 | - nullable: false 41 | - readOnly: true 42 | PageToken: 43 | type: string 44 | pattern: '[A-Za-z0-9_\-]+' 45 | paths: 46 | '/api/v1/posts': 47 | post: 48 | summary: Публикация поста 49 | parameters: 50 | - in: header 51 | name: System-Design-User-Id 52 | required: true 53 | description: > 54 | Идентификатор ползователя, который аутентифицирован в данном запросе. 55 | schema: 56 | $ref: '#/components/schemas/UserId' 57 | requestBody: 58 | content: 59 | application/json: 60 | schema: 61 | $ref: '#/components/schemas/Post' 62 | responses: 63 | 200: 64 | description: Пост был успешно создан. Тело ответа содержит созданный пост. 65 | content: 66 | application/json: 67 | schema: 68 | $ref: '#/components/schemas/Post' 69 | 401: 70 | description: > 71 | Токен пользователя отсутствует в запросе, или передан в неверном формате, или его срок действия истёк. 72 | '/api/v1/posts/{postId}': 73 | get: 74 | summary: Получение поста по идентификатору 75 | parameters: 76 | - in: path 77 | name: postId 78 | required: true 79 | schema: 80 | $ref: '#/components/schemas/PostId' 81 | responses: 82 | 200: 83 | description: Пост найден 84 | content: 85 | application/json: 86 | schema: 87 | $ref: '#/components/schemas/Post' 88 | 404: 89 | description: Поста с указанным идентификатором не существует 90 | '/api/v1/users/{userId}/posts': 91 | get: 92 | summary: Получение страницы последних постов пользователя 93 | description: > 94 | Получение страницы с постами пользователя. 95 | 96 | Для получения первой страницы (с самыми последними постами), необходимо выполнить запрос 97 | без параметра `page`. 98 | Для получения следующей странцы, необходимо в параметр `page` передать токен следующей страницы, 99 | полученный в теле ответа с предыдущей страницей. 100 | parameters: 101 | - in: path 102 | name: userId 103 | required: true 104 | schema: 105 | $ref: '#/components/schemas/UserId' 106 | - in: query 107 | name: page 108 | description: Токен страницы 109 | required: false 110 | schema: 111 | $ref: '#/components/schemas/PageToken' 112 | - in: query 113 | name: size 114 | description: Количество постов на странице 115 | required: false 116 | schema: 117 | type: integer 118 | minimum: 1 119 | maximum: 100 120 | default: 10 121 | responses: 122 | 200: 123 | description: Страница с постами. 124 | content: 125 | application/json: 126 | schema: 127 | type: object 128 | properties: 129 | posts: 130 | type: array 131 | description: > 132 | Посты в обратном хронологическом порядке. 133 | Отсутствие данного поля эквивалентно пустому массиву. 134 | items: 135 | $ref: '#/components/schemas/Post' 136 | nextPage: 137 | allOf: 138 | - $ref: '#/components/schemas/PageToken' 139 | - nullable: false 140 | - description: > 141 | Токен следующей страницы при её наличии. 142 | Поле отсутствует, если текущая страница содержит самый ранний пост пользователя. 143 | 400: 144 | description: Некорректный запрос, например, из-за некорректного токена страницы. 145 | 146 | /maintenance/ping: 147 | get: 148 | summary: Служебный эндпоинт для определения готовности сервиса к работе 149 | responses: 150 | 200: 151 | description: Сервис готов к работе -------------------------------------------------------------------------------- /tasks/03-hw2-milestone1/microblog.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.3 2 | info: 3 | title: Microblog API 4 | description: Microblog API 5 | version: 1.0.0 6 | components: 7 | schemas: 8 | PostId: 9 | description: Уникальный идентификатор поста в формате Base64URL. 10 | type: string 11 | pattern: '[A-Za-z0-9_\-]+' 12 | UserId: 13 | description: Уникальный идентификатор пользователя 14 | type: string 15 | pattern: '[0-9a-f]+' 16 | ISOTimestamp: 17 | description: Момент времени в формате ISO 8601 в часовом поясе UTC+0. 18 | type: string 19 | pattern: '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z' 20 | Post: 21 | type: object 22 | nullable: false 23 | properties: 24 | id: 25 | allOf: 26 | - $ref: '#/components/schemas/PostId' 27 | - nullable: false 28 | - readOnly: true 29 | text: 30 | type: string 31 | nullable: false 32 | authorId: 33 | allOf: 34 | - $ref: '#/components/schemas/UserId' 35 | - nullable: false 36 | - readOnly: true 37 | createdAt: 38 | allOf: 39 | - $ref: '#/components/schemas/ISOTimestamp' 40 | - nullable: false 41 | - readOnly: true 42 | lastModifiedAt: 43 | allOf: 44 | - $ref: '#/components/schemas/ISOTimestamp' 45 | - nullable: false 46 | - readOnly: true 47 | PageToken: 48 | type: string 49 | pattern: '[A-Za-z0-9_\-]+' 50 | paths: 51 | '/api/v1/posts': 52 | post: 53 | summary: Публикация поста 54 | parameters: 55 | - in: header 56 | name: System-Design-User-Id 57 | required: true 58 | description: > 59 | Идентификатор ползователя, который аутентифицирован в данном запросе. 60 | schema: 61 | $ref: '#/components/schemas/UserId' 62 | requestBody: 63 | content: 64 | application/json: 65 | schema: 66 | $ref: '#/components/schemas/Post' 67 | responses: 68 | 200: 69 | description: Пост был успешно создан. Тело ответа содержит созданный пост. 70 | content: 71 | application/json: 72 | schema: 73 | $ref: '#/components/schemas/Post' 74 | 401: 75 | description: > 76 | Токен пользователя отсутствует в запросе, или передан в неверном формате, или его срок действия истёк. 77 | '/api/v1/posts/{postId}': 78 | get: 79 | summary: Получение поста по идентификатору 80 | parameters: 81 | - in: path 82 | name: postId 83 | required: true 84 | schema: 85 | $ref: '#/components/schemas/PostId' 86 | responses: 87 | 200: 88 | description: Пост найден 89 | content: 90 | application/json: 91 | schema: 92 | $ref: '#/components/schemas/Post' 93 | 404: 94 | description: Поста с указанным идентификатором не существует 95 | patch: 96 | summary: Модификация поста 97 | parameters: 98 | - in: path 99 | name: postId 100 | required: true 101 | schema: 102 | $ref: '#/components/schemas/PostId' 103 | - in: header 104 | name: System-Design-User-Id 105 | required: true 106 | description: > 107 | Идентификатор ползователя, который аутентифицирован в данном запросе. 108 | schema: 109 | $ref: '#/components/schemas/UserId' 110 | requestBody: 111 | content: 112 | application/json: 113 | schema: 114 | $ref: '#/components/schemas/Post' 115 | responses: 116 | 200: 117 | description: Пост был успешно обновлен. В теле содержится обновленный пост. 118 | content: 119 | application/json: 120 | schema: 121 | $ref: '#/components/schemas/Post' 122 | 401: 123 | description: Пользователь не аутентифирован 124 | 403: 125 | description: Пост не может быть отредактирован, т.к. опубликован другим пользователем. 126 | 404: 127 | description: Поста с указанным идентификатором не существует 128 | '/api/v1/users/{userId}/posts': 129 | get: 130 | summary: Получение страницы последних постов пользователя 131 | description: > 132 | Получение страницы с постами пользователя. 133 | 134 | Для получения первой страницы (с самыми последними постами), необходимо выполнить запрос 135 | без параметра `page`. 136 | Для получения следующей странцы, необходимо в параметр `page` передать токен следующей страницы, 137 | полученный в теле ответа с предыдущей страницей. 138 | parameters: 139 | - in: path 140 | name: userId 141 | required: true 142 | schema: 143 | $ref: '#/components/schemas/UserId' 144 | - in: query 145 | name: page 146 | description: Токен страницы 147 | required: false 148 | schema: 149 | $ref: '#/components/schemas/PageToken' 150 | - in: query 151 | name: size 152 | description: Количество постов на странице 153 | required: false 154 | schema: 155 | type: integer 156 | minimum: 1 157 | maximum: 100 158 | default: 10 159 | responses: 160 | 200: 161 | description: Страница с постами. 162 | content: 163 | application/json: 164 | schema: 165 | type: object 166 | properties: 167 | posts: 168 | type: array 169 | description: > 170 | Посты в обратном хронологическом порядке. 171 | Отсутствие данного поля эквивалентно пустому массиву. 172 | items: 173 | $ref: '#/components/schemas/Post' 174 | nextPage: 175 | allOf: 176 | - $ref: '#/components/schemas/PageToken' 177 | - nullable: false 178 | - description: > 179 | Токен следующей страницы при её наличии. 180 | Поле отсутствует, если текущая страница содержит самый ранний пост пользователя. 181 | 400: 182 | description: Некорректный запрос, например, из-за некорректного токена страницы. 183 | /maintenance/ping: 184 | get: 185 | summary: Служебный эндпоинт для определения готовности сервиса к работе 186 | responses: 187 | 200: 188 | description: Сервис готов к работе -------------------------------------------------------------------------------- /tasks/04-hw3-milestone1/microblog.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.3 2 | info: 3 | title: Microblog API 4 | description: Microblog API 5 | version: 1.0.0 6 | components: 7 | schemas: 8 | PostId: 9 | description: Уникальный идентификатор поста в формате Base64URL. 10 | type: string 11 | pattern: '[A-Za-z0-9_\-]+' 12 | UserId: 13 | description: Уникальный идентификатор пользователя 14 | type: string 15 | pattern: '[0-9a-f]+' 16 | ISOTimestamp: 17 | description: Момент времени в формате ISO 8601 в часовом поясе UTC+0. 18 | type: string 19 | pattern: '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z' 20 | Post: 21 | type: object 22 | nullable: false 23 | properties: 24 | id: 25 | allOf: 26 | - $ref: '#/components/schemas/PostId' 27 | - nullable: false 28 | - readOnly: true 29 | text: 30 | type: string 31 | nullable: false 32 | authorId: 33 | allOf: 34 | - $ref: '#/components/schemas/UserId' 35 | - nullable: false 36 | - readOnly: true 37 | createdAt: 38 | allOf: 39 | - $ref: '#/components/schemas/ISOTimestamp' 40 | - nullable: false 41 | - readOnly: true 42 | lastModifiedAt: 43 | allOf: 44 | - $ref: '#/components/schemas/ISOTimestamp' 45 | - nullable: false 46 | - readOnly: true 47 | PageToken: 48 | type: string 49 | pattern: '[A-Za-z0-9_\-]+' 50 | paths: 51 | '/api/v1/posts': 52 | post: 53 | summary: Публикация поста 54 | parameters: 55 | - in: header 56 | name: System-Design-User-Id 57 | required: true 58 | description: > 59 | Идентификатор ползователя, который аутентифицирован в данном запросе. 60 | schema: 61 | $ref: '#/components/schemas/UserId' 62 | requestBody: 63 | content: 64 | application/json: 65 | schema: 66 | $ref: '#/components/schemas/Post' 67 | responses: 68 | 200: 69 | description: Пост был успешно создан. Тело ответа содержит созданный пост. 70 | content: 71 | application/json: 72 | schema: 73 | $ref: '#/components/schemas/Post' 74 | 401: 75 | description: > 76 | Токен пользователя отсутствует в запросе, или передан в неверном формате, или его срок действия истёк. 77 | '/api/v1/posts/{postId}': 78 | get: 79 | summary: Получение поста по идентификатору 80 | parameters: 81 | - in: path 82 | name: postId 83 | required: true 84 | schema: 85 | $ref: '#/components/schemas/PostId' 86 | responses: 87 | 200: 88 | description: Пост найден 89 | content: 90 | application/json: 91 | schema: 92 | $ref: '#/components/schemas/Post' 93 | 404: 94 | description: Поста с указанным идентификатором не существует 95 | patch: 96 | summary: Модификация поста 97 | parameters: 98 | - in: path 99 | name: postId 100 | required: true 101 | schema: 102 | $ref: '#/components/schemas/PostId' 103 | - in: header 104 | name: System-Design-User-Id 105 | required: true 106 | description: > 107 | Идентификатор ползователя, который аутентифицирован в данном запросе. 108 | schema: 109 | $ref: '#/components/schemas/UserId' 110 | requestBody: 111 | content: 112 | application/json: 113 | schema: 114 | $ref: '#/components/schemas/Post' 115 | responses: 116 | 200: 117 | description: Пост был успешно обновлен. В теле содержится обновленный пост. 118 | content: 119 | application/json: 120 | schema: 121 | $ref: '#/components/schemas/Post' 122 | 401: 123 | description: Пользователь не аутентифирован 124 | 403: 125 | description: Пост не может быть отредактирован, т.к. опубликован другим пользователем. 126 | 404: 127 | description: Поста с указанным идентификатором не существует 128 | '/api/v1/users/{userId}/posts': 129 | get: 130 | summary: Получение страницы последних постов пользователя 131 | description: > 132 | Получение страницы с постами пользователя. 133 | 134 | Для получения первой страницы (с самыми последними постами), необходимо выполнить запрос 135 | без параметра `page`. 136 | Для получения следующей странцы, необходимо в параметр `page` передать токен следующей страницы, 137 | полученный в теле ответа с предыдущей страницей. 138 | parameters: 139 | - in: path 140 | name: userId 141 | required: true 142 | schema: 143 | $ref: '#/components/schemas/UserId' 144 | - in: query 145 | name: page 146 | description: Токен страницы 147 | required: false 148 | schema: 149 | $ref: '#/components/schemas/PageToken' 150 | - in: query 151 | name: size 152 | description: Количество постов на странице 153 | required: false 154 | schema: 155 | type: integer 156 | minimum: 1 157 | maximum: 100 158 | default: 10 159 | responses: 160 | 200: 161 | description: Страница с постами. 162 | content: 163 | application/json: 164 | schema: 165 | type: object 166 | properties: 167 | posts: 168 | type: array 169 | description: > 170 | Посты в обратном хронологическом порядке. 171 | Отсутствие данного поля эквивалентно пустому массиву. 172 | items: 173 | $ref: '#/components/schemas/Post' 174 | nextPage: 175 | allOf: 176 | - $ref: '#/components/schemas/PageToken' 177 | - nullable: false 178 | - description: > 179 | Токен следующей страницы при её наличии. 180 | Поле отсутствует, если текущая страница содержит самый ранний пост пользователя. 181 | 400: 182 | description: Некорректный запрос, например, из-за некорректного токена страницы. 183 | '/api/v1/users/{userId}/subscribe': 184 | post: 185 | summary: Подписка на пользователя 186 | description: > 187 | Текущий авторизированный пользователь подписывается на указанного пользователя 188 | 189 | Повторная подписка на пользователя считается успешым запросом. Однако мы не должны видеть его в подписчиках два раза. 190 | Подписка на самого себя - это ошибочный запрос, должен вернуться 400. 191 | parameters: 192 | - in: path 193 | name: userId 194 | required: true 195 | schema: 196 | $ref: '#/components/schemas/UserId' 197 | responses: 198 | 200: 199 | description: Подписка прошла успешно 200 | 400: 201 | description: Некорректный запрос 202 | '/api/v1/subscriptions': 203 | get: 204 | summary: Получение пользователей, на которых была произведена подписка 205 | description: > 206 | Получение списка идентификаторов пользователей, на которых была произведена подписка 207 | responses: 208 | 200: 209 | description: Массив идентификаторов пользователей 210 | content: 211 | application/json: 212 | schema: 213 | type: object 214 | properties: 215 | users: 216 | type: array 217 | description: > 218 | Массив строк, содержащих идентификаторы пользователей. Порядок не важен. 219 | items: 220 | type: string 221 | 400: 222 | description: Некорректный запрос 223 | '/api/v1/subscribers': 224 | get: 225 | summary: Получение пользователей, которые подписались на текущего пользователя 226 | description: > 227 | Получение списка идентификаторов пользователей, которые подписались на текущего пользователя 228 | responses: 229 | 200: 230 | description: Массив идентификаторов пользователей 231 | content: 232 | application/json: 233 | schema: 234 | type: object 235 | properties: 236 | users: 237 | type: array 238 | description: > 239 | Массив строк, содержащих идентификаторы пользователей. Порядок не важен. 240 | items: 241 | type: string 242 | 400: 243 | description: Некорректный запрос 244 | '/api/v1/feed': 245 | get: 246 | summary: Получение ленты постов для авторизированного пользователя 247 | description: > 248 | Лента пользователя - это набор постов пользователей, на которых он подписан, упорядоченный по времени. 249 | 250 | Для получения первой страницы (с самыми последними постами), необходимо выполнить запрос 251 | без параметра `page`. 252 | Для получения следующей странцы, необходимо в параметр `page` передать токен следующей страницы, 253 | полученный в теле ответа с предыдущей страницей. 254 | parameters: 255 | - in: query 256 | name: page 257 | description: Токен страницы 258 | required: false 259 | schema: 260 | $ref: '#/components/schemas/PageToken' 261 | - in: query 262 | name: size 263 | description: Количество постов на странице 264 | required: false 265 | schema: 266 | type: integer 267 | minimum: 1 268 | maximum: 100 269 | default: 10 270 | responses: 271 | 200: 272 | description: Страница с постам из ленты 273 | content: 274 | application/json: 275 | schema: 276 | type: object 277 | properties: 278 | posts: 279 | type: array 280 | description: > 281 | Посты в обратном хронологическом порядке. 282 | Отсутствие данного поля эквивалентно пустому массиву. 283 | items: 284 | $ref: '#/components/schemas/Post' 285 | nextPage: 286 | allOf: 287 | - $ref: '#/components/schemas/PageToken' 288 | - nullable: false 289 | - description: > 290 | Токен следующей страницы при её наличии. 291 | Поле отсутствует, если текущая страница содержит самый ранний пост пользователя. 292 | 400: 293 | description: Некорректный запрос 294 | /maintenance/ping: 295 | get: 296 | summary: Служебный эндпоинт для определения готовности сервиса к работе 297 | responses: 298 | 200: 299 | description: Сервис готов к работе --------------------------------------------------------------------------------