# Домашнее задание по модулю "Межсервисное взаимодействие и основы эксплуатации"

Добавить сервис LOMS и организовать взаимодействие между cart и loms с использованием gRPC.

## Основное задание

Необходимо:
- Имплементировать сервис, отвечающий за учет заказов и стоки по товарам. Логика работы методов и их контракты описаны ниже.
- Реализовать взаимодействие сервисов cart и loms через gRPC.

Требование к решению:
- Создать protobuf контракт сервиса loms.
- В каждом проекте нужно добавить в Makefile команды для генерации .go файлов из proto файлов и установки нужных зависимостей (используем protoc).
- Состояние храним в in-memory, персистентное хранилище на данный момент не требуется. 2 репозитория - Stock и Order. 
- Код должен быть покрыт тестами (тесты на методы репозитория - не требуются). 
- Добавить gRPC интерцептор, который будет валидировать запросы через proto-gen-validator (правила валидации указываются в *.proto).
- Добавить HTTP-gateway. HTTP-запросы также должны проходить валидацию через добавленный выше gRPC интерцептор. 

## Дополнительное задание
- Добавить swagger-ui и возможность совершать запросы из swagger к сервису. 
- Написать end-to-end тесты на все новые методы . 

## Спецификация LOMS (Logistics and Order Management System)

Сервис отвечает за учет заказов и стоки по товарам.

### OrderCreate

Создает новый заказ для пользователя из списка переданных товаров с резервированием нужного количества стоков:
+ заказ получает статус "new"
+ резервирует нужное количество единиц товара
+ если удалось зарезервировать стоки, заказ получает статус "awaiting payment"
+ если не удалось зарезервировать стоки, заказ получает статус "failed", изменение стоков не происходит

**Параметры ошибочных ответов:**

| Сценарий                                                                     | gRPC код ошибки (HTTP) | Описание                                                                        |
|------------------------------------------------------------------------------|------------------------|---------------------------------------------------------------------------------|
| Вызов с нулевым или отрицательным значением userId                           | 3 (400)                | Идентификатор пользователя должен быть натуральным числом (больше нуля)         |
| Вызов c пустым списком товаров                                               | 3 (400)                | Идентификатор товара должен быть натуральным числом (больше нуля)               |
| Вызов с нулевыми или отрицательными значениями sku в списке                  | 3 (400)                | Количество должно быть натуральным числом (больше нуля)                         |
| Вызов с нулевыми или отрицательными значениями Count для любого sku в списке | 3 (400)                | Count должен быть натуральным числом (больше нуля)                              |
| Превышение стоков хотя бы у одного товара                                    | 9 (400)                | Для всех товаров сток должен быть больше или равен запрашиваемому               |
| Отсутствие информации по стокам в системе                                    | 9 (400)                | Невозможно создать заказ, если по хотя бы одному товару нет информации о стоках |
| Все остальные случаи                                                         | 13 или 2 (500)         | Проблемы из-за неисправностей в системе                                         |

![loms-order-create](img/loms-order-create.png)

Request
```
{
    userId int64
    items []{
        sku int64
        count uint32
    }
}
```

Response
```
{
    orderId int64
}
```

### OrderInfo

Показывает информацию по заказу. Товары в ответе должны быть отсортированы по SKU в порядке возрастания. 

**Параметры ошибочных ответов:**

| Сценарий                                            | gRPC код ошибки (HTTP) | Описание                                                            |
|-----------------------------------------------------|------------------------|---------------------------------------------------------------------|
| Вызов с нулевым или отрицательным значением orderId | 3 (400)                | Идентификатор заказа должен быть натуральным числом (больше нуля)   |
| Заказ с указанным orderId отсутствует в системе     | 5 (404)                | Можно получить информацию только для существующего в системе заказа |
| Все остальные случаи                                | 13 или 2 (500)         | Проблемы из-за неисправностей в системе                             |


![loms-order-info](img/loms-order-info.png)

Request
```
{
    orderId int64
}
```

Response
```
{
    status string // (new | awaiting payment | failed | payed | cancelled)
    userId int64
    items []{
        sku int64
        count uint32
    }
}
```

### OrderPay

Помечает заказ оплаченным. Зарезервированные товары должны перейти в статус купленных.
+ удаляем зарезервированные стоки на товаре
+ заказ получает статус "payed"

**Параметры ошибочных ответов:**

| Сценарий                                            | gRPC код ошибки (HTTP) | Описание                                                          |
|-----------------------------------------------------|------------------------|-------------------------------------------------------------------|
| Вызов с нулевым или отрицательным значением orderId | 3 (400)                | Идентификатор заказа должен быть натуральным числом (больше нуля) |
| Оплата несуществующего заказа                       | 5 (404)                | Можно оплачивать только существующий заказ                        |
| Оплата оплаченного заказа                           | 0 (200)                | Оплата оплаченного заказа разрешается                             |
| Оплата заказа в статусе != "awaiting payment"       | 9 (400)                | Оплата заказа в невалидном статусе невозможна                     |
| Все остальные случаи                                | 13 или 2 (500)         | Проблемы из-за неисправностей в системе                           |

![loms-order-pay](img/loms-order-pay.png)

Request
```
{
    orderId int64
}
```

Response
```
{}
```

### OrderCancel

Отменяет заказ, снимает резерв со всех товаров в заказе.
+ зарезервированные стоки на товаре становятся свободными стоками
+ заказ получает статус "cancelled"

**Параметры ошибочных ответов:**

| Сценарий                                            | gRPC код ошибки (HTTP) | Описание                                                          |
|-----------------------------------------------------|------------------------|-------------------------------------------------------------------|
| Вызов с нулевым или отрицательным значением orderId | 3 (400)                | Идентификатор заказа должен быть натуральным числом (больше нуля) |
| Отмена несуществующего заказа                       | 5 (404)                | Можно отменять только существующий заказ                          |
| Отмена отмененного заказа                           | 0 (200)                | Отмена отмененного заказа разрешается (идемпотентность)           |
| Отмена заказа в статусе == "payed" или "failed"     | 9 (400)                | Невозможность отменить неудавшийся заказ, а также оплаченный      |
| Все остальные случаи                                | 13 или 2 (500)         | Проблемы из-за неисправностей в системе                           |

![loms-order-cancel](img/loms-order-cancel.png)

Request
```
{
    orderId int64
}
```

Response
```
{}
```

### StocksInfo

Возвращает количество товаров, которые можно купить. Если товар был зарезервирован у кого-то в заказе и ждет оплаты, его купить нельзя.
- данные по товарам берутся из stock-data.json (embed)
- структура stock:
  - sku - товар
  - total_count - всего товаров
  - reserved - количество зарезервированных

**Параметры ошибочных ответов:**

| Сценарий                                         | gRPC код ошибки (HTTP) | Описание                                                           |
|--------------------------------------------------|------------------------|--------------------------------------------------------------------|
| Вызов с нулевым или отрицательным значением sku  | 3 (400)                | Идентификатор товара должен быть натуральным числом (больше нуля)  |
| Товара в запросе нет в базе стоков               | 5 (404)                | Можно получить информацию по стокам, если она есть в бд            |
| Все остальные случаи                             | 13 или 2 (500)         | Проблемы из-за неисправностей в системе                            |

![loms-stok-info](img/loms-stok-info.png)

Request
```
{
    sku int64
}
```

Response
```
{
    count uint32
}
```

## Доработки сервиса cart

### POST /checkout/<user_id>

Требуется добавить метод checkout - оформить заказ по всем товарам корзины. Вызывает loms.OrderCreate.
Сервис cart имеет HTTP-интерфейс. Взаимодействие с LOMS - через gRPC.

**Параметры ошибочных ответов:**

| Сценарий                                            | HTTP код ошибки | Описание                                                                |
|-----------------------------------------------------|-----------------|-------------------------------------------------------------------------|
| Вызов с нулевым или отрицательным значением user_id | 400             | Идентификатор пользователя должен быть натуральным числом (больше нуля) |
| Вызов для пустой корзины                            | 404             | Невозможно оформить заказ для пустой корзины                            |
| Все остальные случаи                                | 500             | Проблемы из-за неисправностей в системе                                 |

![cart-cart-checkout](img/cart-cart-checkout.png)

Request
```
POST /checkout/<user_id> (user_id - int64)
```

Response
```
{
    order_id int64
}
```

### POST /user/<user_id>/cart/<sku_id>

Требуется добавить запрос в метод добавления товаров в корзину на проверку наличия стоков с помощью вызова gRPC метода loms.StocksInfo.

**Параметры ошибочных ответов:**
Сценарии из прошлых домашних заданий без изменений. 

| Сценарий                                | HTTP код ошибки | Описание                                                           |
|-----------------------------------------|-----------------|--------------------------------------------------------------------|
| Превышение стоков при добавлении товара | 412             | Невозможно добавить товара по количеству больше, чем есть в стоках |
| Все остальные случаи                    | 500             | Проблемы из-за неисправностей в системе                            |

![cart-cart-item-add](img/cart-cart-item-add.png)
    
# Путь покупки товаров:

- <cart_host>/user/{user_id}/cart/{sku_id} - Добавляем товар в корзину с проверкой на наличие стоков.
- <cart_host>/user/{user_id}/cart || <cart_host>/user/{user_id}/cart/{sku_id} - Можем удалять товары из корзины. 
- <cart_host>/user/{user_id}/cart - Можем получить состав корзины.
- <cart_host>/checkout/{user_id} - Создаем заказ по товарам из корзины. 
- <order_host>/order/pay with body { "orderId": {order_id} } - Оплачиваем заказ.
- <order_host>/order/cancel with body { "orderId": {order_id} } - Можем отменить заказ до оплаты.

### Примечания
* e2e тесты проверяют HTTP коды ошибок, однако gRPC коды должны быть те, что указаны в требованиях. Например, могут быть проблемы с codes.FailedPrecondition, подробнее [тут](https://github.com/grpc-ecosystem/grpc-gateway/blob/main/runtime/errors.go).
* Запросы из cart.http, loms.http & loms.grpc основаны на данных, что лежат в stock-data.json

## Автоматические проверки

Ваше решение должно проходить автоматические проверки:

- Компиляция
- Линтер
- Unit-тесты (если есть)
- Автотесты

Прохождение автоматических проверок влияет на итоговую оценку за домашнюю работу.

### Дедлайны сдачи и проверки задания: 
- 7 июня 23:59 (сдача) / 10 июня, 23:59 (проверка)
