├── images ├── grpc.png ├── kafka.png ├── schema.png ├── postgres.png ├── retranslator.png └── observability.png ├── task-6.md ├── task-7.md ├── LICENSE ├── task-5.md ├── task-2.md ├── README.md ├── task-1.md ├── task-4.md └── task-3.md /images/grpc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ozonmp/omp-docs/HEAD/images/grpc.png -------------------------------------------------------------------------------- /images/kafka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ozonmp/omp-docs/HEAD/images/kafka.png -------------------------------------------------------------------------------- /images/schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ozonmp/omp-docs/HEAD/images/schema.png -------------------------------------------------------------------------------- /images/postgres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ozonmp/omp-docs/HEAD/images/postgres.png -------------------------------------------------------------------------------- /images/retranslator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ozonmp/omp-docs/HEAD/images/retranslator.png -------------------------------------------------------------------------------- /images/observability.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ozonmp/omp-docs/HEAD/images/observability.png -------------------------------------------------------------------------------- /task-6.md: -------------------------------------------------------------------------------- 1 | # Ozon Marketplace Project 2 | 3 | ![schema](images/kafka.png) 4 | 5 | Дальше везде используются **placeholder**-ы: 6 | 7 | - `{domain}`,`{Domain}` 8 | - `{subdomain}`,`{Subdomain}` 9 | 10 | Например, для поддомена `package` из домена `logistic` значение **placeholder**-ов будет: 11 | 12 | - `{domain}`,`{Domain}` = `logistic`,`Logistic` 13 | - `{subdomain}`,`{Subdomain}` = `package`,`Package` 14 | - `{domain}`/`{subdomain}` = `logistic`/`package` 15 | - `{subdomains}`,`{Subdomains}` = `packages`,`Packages` 16 | 17 | --- 18 | 19 | **Задание VI** 20 | 21 | 1. Имплементировать отправку событий в **kafka** топик 22 | 23 | 2. Имплементировать для сервиса `{domain-kw}-{subdomain}-facade` чтение данных из **kafka** топика в стандартный поток вывода 24 | 25 | 3. Имплементировать **update** операцию 26 | 27 | 4. Имплементировать сохранение прочтенных данных в базу 💎 28 | 29 | 5. Добавить поддержку работы с множеством брокеров 💎 30 | -------------------------------------------------------------------------------- /task-7.md: -------------------------------------------------------------------------------- 1 | # Ozon Marketplace Project 2 | 3 | ![schema](images/schema.png) 4 | 5 | Дальше везде используются **placeholder**-ы: 6 | 7 | - `{domain}`,`{Domain}` 8 | - `{subdomain}`,`{Subdomain}` 9 | 10 | Например, для поддомена `package` из домена `logistic` значение **placeholder**-ов будет: 11 | 12 | - `{domain}`,`{Domain}` = `logistic`,`Logistic` 13 | - `{subdomain}`,`{Subdomain}` = `package`,`Package` 14 | - `{domain}`/`{subdomain}` = `logistic`/`package` 15 | - `{subdomains}`,`{Subdomains}` = `packages`,`Packages` 16 | 17 | --- 18 | 19 | **Задание VII** 20 | 21 | 1. Вынести код ретранслятора в отдельный сервис 💎 22 | 2. Интегрировать свой форк от **omp-bot** и `{domain-kw}-{subdomain}-api` сервис 💎 23 | 3. Интегрировать свой форк от **omp-bot** и `{domain-kw}-{subdomain}-facade` сервис 💎 24 | 4. Добавить поддержку множественного количества попыток 💎 25 | - при подключение к базе 26 | - при подключение к сервису `{domain-kw}-{subdomain}-api` 27 | - при отправке событий в **kafka** топик 28 | 5. Добавить поддержку кэширования сущности через **redis** 💎 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2022 rusdevops 2 | 3 | All rights reserved. 4 | 5 | MIT License 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /task-5.md: -------------------------------------------------------------------------------- 1 | # Ozon Marketplace Project 2 | 3 | ![schema](images/observability.png) 4 | 5 | Дальше везде используются **placeholder**-ы: 6 | 7 | - `{domain}`,`{Domain}` 8 | - `{subdomain}`,`{Subdomain}` 9 | 10 | Например, для поддомена `package` из домена `logistic` значение **placeholder**-ов будет: 11 | 12 | - `{domain}`,`{Domain}` = `logistic`,`Logistic` 13 | - `{subdomain}`,`{Subdomain}` = `package`,`Package` 14 | - `{domain}`/`{subdomain}` = `logistic`/`package` 15 | - `{subdomains}`,`{Subdomains}` = `packages`,`Packages` 16 | 17 | --- 18 | 19 | **Задание V** 20 | 21 | _Логирование_ 22 | 23 | 1. Покрыть логированием код обработки ручек со всем уровнем вложенности 24 | 2. Добавить поддержку изменения уровня логирования через заголовок запроса 25 | 3. Добавить поддержку детализированного вывода запроса и ответа через **middleware** :gem: 26 | 4. Добавить отображение в **swagger** отладочных заголовков :gem: 27 | 28 | _Трассировка_ 29 | 30 | 1. Добавить создание спанов для ручек 31 | 2. Добавить заполнение необходимых полей и ошибок 32 | 3. Пострелять с помощью **hey** 33 | 34 | _Метрики_ 35 | 36 | 1. Добавить метрики на 37 | 1. Кол-во **NotFound** событий 🔍 38 | 2. Кол-во создаваемых **CUD** событий 🔍 39 | 3. Кол-во обрабатываемых событий в ретрансляторе 🔍 40 | 2. Добавить **dashboard** на отображение метрик :gem: 41 | -------------------------------------------------------------------------------- /task-2.md: -------------------------------------------------------------------------------- 1 | # Ozon Marketplace Project 2 | 3 | ![schema](images/retranslator.png) 4 | 5 | Дальше везде используются **placeholder**-ы: 6 | - `{domain}`,`{Domain}` 7 | - `{subdomain}`,`{Subdomain}` 8 | 9 | Например, для поддомена `package` из домена `logistic` значение **placeholder**-ов будет: 10 | - `{domain}`,`{Domain}` = `logistic`,`Logistic` 11 | - `{subdomain}`,`{Subdomain}` = `package`,`Package` 12 | - `{domain}`/`{subdomain}` = `logistic`/`package` 13 | --- 14 | 15 | ### Задание 2 16 | 17 | 1. Создать репозиторий в формате `{domain-kw}-{subdomain}-api` 18 | 19 | 2. Описать сущность `{domain}.{Subdomain}` и `{domain}.{Subdomain}Event` в **internal/model/{subdomain}.go** 20 | 21 | 3. Реализовать паттерн consumer-producer из **db** в **kafka** на основе интерфейсов [EventRepo](https://github.com/ozonmp/omp-demo-api/blob/b847b3ae4a3c9e1d25e31e077c847a22f8b7aa99/internal/app/repo/event.go#L7) и [EventSender](https://github.com/ozonmp/omp-demo-api/blob/b847b3ae4a3c9e1d25e31e077c847a22f8b7aa99/internal/app/sender/event.go#L7) для одного типа события **Created** 22 | 23 | 4. Написать тесты 24 | 25 | 5. Синхронизацию работы потоков сделать через `context` 💎 26 | 27 | 6. Создавать задачи у **workerpool** по обработке батчевых идентификаторов записей событий 💎 28 | 29 | 7. Поддержать несколько типов событий учитывая корректный порядок 💎 30 | 31 | 8. Реализовать гарантию доставки **At-least-once** 💎 32 | 33 | 9. Найти скрытые ошибки в коде 💎 34 | 35 | **Рецепт** 36 | 37 | [omp-demo-api](https://github.com/ozonmp/omp-demo-api) 38 | 39 | P.S. Обратите внимание используется зеркальная (внешняя) точка зрения на вопрос, кто является потребителем, а кто является производителем. 40 | Поэтому паттерн назвали **consumer-producer** и классы переименовали. 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ozon Marketplace Project 2 | 3 | ![schema](images/schema.png) 4 | 5 | Дальше везде используются **placeholder**-ы: 6 | - `{domain}`,`{Domain}` 7 | - `{subdomain}`,`{Subdomain}` 8 | 9 | Например, для поддомена `package` из домена `logistic` значение **placeholder**-ов будет: 10 | - `{domain}`,`{Domain}` = `logistic`,`Logistic` 11 | - `{subdomain}`,`{Subdomain}` = `package`,`Package` 12 | - `{domain}`/`{subdomain}` = `logistic`/`package` 13 | --- 14 | 15 | 16 | ### Задание 1 17 | 18 | 1. Сделать форк **ozonmp/omp-bot** репозитория в свой профиль 19 | 2. Запросить у своего тьютора свой домен/поддомен: **{domain}/{subdomain}** 20 | 3. Добавить в ветку `feature/task-1` своего форка поддержку следующих команд: 21 | ``` 22 | /help__{domain}__{subdomain} — print list of commands 23 | /get__{domain}__{subdomain} — get a entity 24 | /list__{domain}__{subdomain} — get a list of your entity (💎: with pagination via telegram keyboard) 25 | /delete__{domain}__{subdomain} — delete an existing entity 26 | 27 | /new__{domain}__{subdomain} — create a new entity // not implemented (💎: implement list fields via arguments) 28 | /edit__{domain}__{subdomain} — edit a entity // not implemented 29 | ``` 30 | 4. Сделать PR из ветки `feature/task-1` своего форка в ветку `master` своего форка 31 | 5. Отправить ссылку на PR личным сообщением своему тьютору до конда дедлайна сдачи (см. таблицу прогресса) 32 | 33 | #### Рецепт 34 | 35 | Для добавления поддержки команд в рамках своего поддомена: 36 | 37 | 1. Написать структуру `{Subdomain}` с методом `String()` 38 | 2. Написать интерфейс `{Subdomain}Service` и **dummy** имплементацию 39 | 3. Написать интерфейс `{Subdomain}Commander` по обработке команд 40 | 41 | --- 42 | 43 | 2. Реализовать `{Subdomain}Service` в **internal/service/{domain}/{subdomain}/** 44 | 45 | ```go 46 | package {subdomain} 47 | 48 | import "github.com/ozonmp/omp-bot/internal/model/{domain}" 49 | 50 | type {Subdomain}Service interface { 51 | Describe({subdomain}ID uint64) (*{domain}.{Subdomain}, error) 52 | List(cursor uint64, limit uint64) ([]{domain}.{Subdomain}, error) 53 | Create({domain}.{Subdomain}) (uint64, error) 54 | Update({subdomain}ID uint64, {subdomain} {domain}.{Subdomain}) error 55 | Remove({subdomain}ID uint64) (bool, error) 56 | } 57 | 58 | type Dummy{Subdomain}Service struct {} 59 | 60 | func NewDummy{Subdomain}Service() *Dummy{Subdomain}Service { 61 | return &Dummy{Subdomain}Service{} 62 | } 63 | 64 | // ... 65 | ``` 66 | 67 | --- 68 | 69 | 3. Реализовать `{Subdomain}Commander` по обработке команд в **internal/app/commands/{domain}/{subdomain}/** 70 | 71 | ```go 72 | package {subdomain} 73 | 74 | import ( 75 | model "github.com/ozonmp/omp-bot/internal/model/{domain}" 76 | service "github.com/ozonmp/omp-bot/internal/service/{domain}/{subdomain}" 77 | ) 78 | 79 | type {Subdomain}Commander interface { 80 | Help(inputMsg *tgbotapi.Message) 81 | Get(inputMsg *tgbotapi.Message) 82 | List(inputMsg *tgbotapi.Message) 83 | Delete(inputMsg *tgbotapi.Message) 84 | 85 | New(inputMsg *tgbotapi.Message) // return error not implemented 86 | Edit(inputMsg *tgbotapi.Message) // return error not implemented 87 | } 88 | 89 | func New{Subdomain}Commander(bot *tgbotapi.BotAPI, service service.{Subdomain}Service) {Subdomain}Commander { 90 | // ... 91 | } 92 | ``` 93 | -------------------------------------------------------------------------------- /task-1.md: -------------------------------------------------------------------------------- 1 | # Ozon Marketplace Project 2 | 3 | ![schema](images/schema.png) 4 | 5 | Дальше везде используются **placeholder**-ы: 6 | - `{domain}`,`{Domain}` 7 | - `{subdomain}`,`{Subdomain}` 8 | 9 | Например, для поддомена `package` из домена `logistic` значение **placeholder**-ов будет: 10 | - `{domain}`,`{Domain}` = `logistic`,`Logistic` 11 | - `{subdomain}`,`{Subdomain}` = `package`,`Package` 12 | - `{domain}`/`{subdomain}` = `logistic`/`package` 13 | --- 14 | 15 | 16 | ### Задание 1 17 | 18 | 1. Сделать форк **ozonmp/omp-bot** репозитория в свой профиль 19 | 2. Запросить у своего тьютора свой домен/поддомен: **{domain}/{subdomain}** 20 | 3. Добавить в ветку `feature/task-1` своего форка поддержку следующих команд: 21 | ``` 22 | /help__{domain}__{subdomain} — print list of commands 23 | /get__{domain}__{subdomain} — get a entity 24 | /list__{domain}__{subdomain} — get a list of your entity (💎: with pagination via telegram keyboard) 25 | /delete__{domain}__{subdomain} — delete an existing entity 26 | 27 | /new__{domain}__{subdomain} — create a new entity // not implemented (💎: implement list fields via arguments) 28 | /edit__{domain}__{subdomain} — edit a entity // not implemented 29 | ``` 30 | 4. Сделать PR из ветки `feature/task-1` своего форка в ветку `master` своего форка 31 | 5. Отправить ссылку на PR личным сообщением своему тьютору до конда дедлайна сдачи (см. таблицу прогресса) 32 | 33 | #### Рецепт 34 | 35 | Для добавления поддержки команд в рамках своего поддомена: 36 | 37 | 1. Написать структуру `{Subdomain}` с методом `String()` 38 | 2. Написать интерфейс `{Subdomain}Service` и **dummy** имплементацию 39 | 3. Написать интерфейс `{Subdomain}Commander` по обработке команд 40 | 41 | --- 42 | 43 | 2. Реализовать `{Subdomain}Service` в **internal/service/{domain}/{subdomain}/** 44 | 45 | ```go 46 | package {subdomain} 47 | 48 | import "github.com/ozonmp/omp-bot/internal/model/{domain}" 49 | 50 | type {Subdomain}Service interface { 51 | Describe({subdomain}ID uint64) (*{domain}.{Subdomain}, error) 52 | List(cursor uint64, limit uint64) ([]{domain}.{Subdomain}, error) 53 | Create({domain}.{Subdomain}) (uint64, error) 54 | Update({subdomain}ID uint64, {subdomain} {domain}.{Subdomain}) error 55 | Remove({subdomain}ID uint64) (bool, error) 56 | } 57 | 58 | type Dummy{Subdomain}Service struct {} 59 | 60 | func NewDummy{Subdomain}Service() *Dummy{Subdomain}Service { 61 | return &Dummy{Subdomain}Service{} 62 | } 63 | 64 | // ... 65 | ``` 66 | 67 | --- 68 | 69 | 3. Реализовать `{Subdomain}Commander` по обработке команд в **internal/app/commands/{domain}/{subdomain}/** 70 | 71 | ```go 72 | package {subdomain} 73 | 74 | import ( 75 | model "github.com/ozonmp/omp-bot/internal/model/{domain}" 76 | service "github.com/ozonmp/omp-bot/internal/service/{domain}/{subdomain}" 77 | ) 78 | 79 | type {Subdomain}Commander interface { 80 | Help(inputMsg *tgbotapi.Message) 81 | Get(inputMsg *tgbotapi.Message) 82 | List(inputMsg *tgbotapi.Message) 83 | Delete(inputMsg *tgbotapi.Message) 84 | 85 | New(inputMsg *tgbotapi.Message) // return error not implemented 86 | Edit(inputMsg *tgbotapi.Message) // return error not implemented 87 | } 88 | 89 | func New{Subdomain}Commander(bot *tgbotapi.BotAPI, service service.{Subdomain}Service) {Subdomain}Commander { 90 | // ... 91 | } 92 | ``` 93 | -------------------------------------------------------------------------------- /task-4.md: -------------------------------------------------------------------------------- 1 | # Ozon Marketplace Project 2 | 3 | ![schema](images/postgres.png) 4 | 5 | Дальше везде используются **placeholder**-ы: 6 | 7 | - `{domain}`,`{Domain}` 8 | - `{subdomain}`,`{Subdomain}` 9 | 10 | Например, для поддомена `package` из домена `logistic` значение **placeholder**-ов будет: 11 | 12 | - `{domain}`,`{Domain}` = `logistic`,`Logistic` 13 | - `{subdomain}`,`{Subdomain}` = `package`,`Package` 14 | - `{domain}`/`{subdomain}` = `logistic`/`package` 15 | - `{subdomains}`,`{Subdomains}` = `packages`,`Packages` 16 | 17 | --- 18 | 19 | **Задание IV** 20 | 21 | 1. Реализовать методы для интерфейса `Repo` 22 | 2. Написать миграции для создания таблиц и создания индексов 23 | 3. Реализовать методы для интерфейса `RepoEvent` (сообщения в **proto**) 24 | 4. Подготовить **dataset** для таблиц `subdomains` и `subdomains_events` :gem: 25 | 5. Реализовать поддержку вариаций типов событий на обновление сущности `subdomain` :gem: 26 | 6. Обеспечить защиту от **sql**-инъекции :gem: 27 | 7. Настроить партиципирование таблицы на **N** частей :gem: 28 | 8. Написать тесты :gem: 29 | 30 | --- 31 | 32 | **Рецепт** 33 | 34 | Используя паттерн [Transactional Outbox Pattern](https://microservices.io/patterns/data/transactional-outbox.html) 35 | 36 | 1. Создать таблицы следующих форматов: 37 | 38 | `{subdomains}` таблица 39 | 40 | | id **bigint** | ... | removed **bool** | created **timestamp** | updated **timestamp** | 41 | | :-----------: | :--: | :--------------: | :-------------------: | :-------------------: | 42 | | | | | | | 43 | 44 | 45 | 46 | `{subdomains}_events` 📤 таблица 47 | 48 | | id **bigint** | {subdomain}_id **bigint** | type **text** | status | payload **jsonb** | updated **timestamp** | 49 | | :-----------: | :-----------------------: | :-----------: | ------ | :----------------: | ------- | 50 | | | | Created | lock | `SubdomainCreated` | | 51 | | | | Updated | lock | `SubdomainUpdated` | | 52 | | | | Removed | | `SubdomainRemoved` | | 53 | 54 | 55 | 2. Составить список sql запросов для таблицы `{subdomains}`, потом для `{subdomains}_events` 56 | 57 | ```sql 58 | -- Lock n events 🐘 🏆 59 | ``` 60 | 61 | 3. Имплементировать методы интерфейсов с помощью [squirell](https://github.com/Masterminds/squirrel) 62 | 63 | ```go 64 | type Repo interface { 65 | Add(*model.Subdomain) (uint64, error) 66 | Get(subdomainID uint64) (*model.Subdomain, error) 67 | List(limit uint64, cursor uint64) ([]model.Subdomain, error) 68 | Remove(subdomainID uint64) (bool, error) 69 | } 70 | ``` 71 | 72 | 73 | ```go 74 | type EventRepo interface { 75 | Lock(n uint64) ([]model.SubdomainEvent, error) 76 | Unlock(eventIDs []uint64) error 77 | Remove(eventIDs []uint64) (bool, error) 78 | } 79 | ``` 80 | 81 | 4. Написать и накатить миграции 82 | ```sh 83 | $ cd migrations 84 | $ cat .env 85 | PGPASSWORD=docker 86 | PGUSER=docker 87 | $ set -o allexport; source .env; set +o allexport 88 | $ goose postgres "host=localhost sslmode=disable dbname={domain-kw}_{subdomain}_api port=5432" up 89 | $ goose postgres "host=localhost sslmode=disable dbname={domain-kw}_{subdomain}_api port=5432" status 90 | ``` 91 | 5. Поднять сервис и пострелять в него разными запросами [пример](https://github.com/ozonmp/omp-template-api/blob/main/DOCS.md#gateway) 92 | -------------------------------------------------------------------------------- /task-3.md: -------------------------------------------------------------------------------- 1 | # Ozon Marketplace Project 2 | 3 | ![schema](images/grpc.png) 4 | 5 | Дальше везде используются **placeholder**-ы: 6 | 7 | - `{domain}`,`{Domain}` 8 | - `{subdomain}`,`{Subdomain}` 9 | 10 | Например, для поддомена `package` из домена `logistic` значение **placeholder**-ов будет: 11 | 12 | - `{domain}`,`{Domain}` = `logistic`,`Logistic` 13 | - `{subdomain}`,`{Subdomain}` = `package`,`Package` 14 | - `{domain}`/`{subdomain}` = `logistic`/`package` 15 | - `{subdomains}`,`{Subdomains}` = `packages`,`Packages` 16 | 17 | --- 18 | 19 | ### Задание 3 20 | 21 | 1. Сделать **rebase** своего репозитория `{kw-domain}-{subdomain}-api` на [omp-template-api](https://github.com/ozonmp/omp-template-api) 22 | 2. Добавить в **proto** следующие **handler**-ы (пример [template](https://github.com/ozonmp/omp-template-api/blob/be1223fb1d1c9751b0d9db1d6e2dfff6ba4c9316/protos/ozonmp/omp_template_api/v1/omp_template_api.proto)): 23 | 1. `Create{Subdomain}` 24 | 2. `Describe{Subdomain}` 25 | 3. `List{Subdomains}` 26 | 4. `Remove{Subdomain}` 27 | 3. Добавить теги валидации в поля сообщений (пример [template](https://github.com/ozonmp/omp-template-api/blob/be1223fb1d1c9751b0d9db1d6e2dfff6ba4c9316/protos/ozonmp/omp_template_api/v1/omp_template_api.proto#L28)) 28 | 4. Сделать рефакторинг: заменить `template` на `{subomain}` (см. рецепт) 29 | 5. Сгенерировать **gRPC** код клиента и сервера (make generate) 30 | 6. Имплементировать код новых ручек в **internal/api/api.go** (пример [template](https://github.com/ozonmp/omp-template-api/blob/be1223fb1d1c9751b0d9db1d6e2dfff6ba4c9316/internal/api/api.go#L34)) 31 | 1. Код ручек должен просто логгировать вызовы (с уровнем `debug`) 32 | 2. Возвращать пустой ответ или внутреннюю ошибку (`not implemented`) 33 | 3. При желание разделить по разным файлам имплементацию ручек 34 | 7. Протестировать через **grpc_cli** (или **grpcurl**) написанные ручки (пример [template](https://github.com/ozonmp/omp-template-api/blob/main/DOCS.md#grpc)) 35 | 8. Написать тесты по обработке не валидных запросов :gem: 36 | 9. Настроить маршрутизацию при запуске контейнеров: :gem: (можно сделать через [dist](https://github.com/ozonmp/omp-grpc-template/tree/master/swagger/dist) директорию) 37 | - с `0.0.0.0:8080/swagger` на контейнер **swagger** 38 | - c `0.0.0.0:8080/api` на контейнер сервиса на порт **gateway**-a 39 | 10. Сгенерировать **Python** код клиента и задеплоить его в **PyPi** :gem: (пример [template](https://github.com/ozonmp/omp-template-api/blob/main/DOCS.md#python-client)) 40 | 41 | 42 | **Рецепт** 43 | 44 | Переезд проекта на рельсы шаблона 45 | ```sh 46 | export domain_kw=omp 47 | export subdomain=demo 48 | 49 | git remote add template https://github.com/ozonmp/omp-template-api 50 | git fetch template main 51 | git rebase template/main 52 | git checkout template/main -- Makefile go.mod go.sum 53 | git rebase --continue 54 | rm -rf pkg/omp-template-api 55 | mkdir pkg/${domain_kw}-${subdomain}-api 56 | mv api/ozonmp/omp_template_api/v1/omp_template_api.proto \ 57 | api/ozonmp/omp_template_api/v1/${domain_kw}_${subdomain}_api.proto 58 | mv api/ozonmp/omp_template_api api/ozonmp/${domain_kw}_${subdomain}_api 59 | mv pypkg/omp-template-api pypkg/${domain_kw}-${subdomain}-api 60 | // grep (exclude 'api/google' dir) 61 | // - template -> ${subdomain} 62 | // - grep omp -> ${domain_kw} 63 | make generate 64 | go mod tidy 65 | make build 66 | # перенесли в шаблонном репозитории README.md в DOCS.md, чтобы было меньше коонфликтов при rebase 67 | mv DOCS.md README.md 68 | git add . 69 | git commit -m"refactored" 70 | ``` 71 | 72 | Описание сообщений 73 | ```proto 74 | // ... 75 | 76 | message {Subdomain} { 77 | uint64 id = 1; 78 | string foo = 2; 79 | } 80 | 81 | message Create{Subdomain}V1Request { 82 | string foo = 1; 83 | } 84 | 85 | message Create{Subdomain}V1Response { 86 | uint64 {subdomain}_id = 1; 87 | } 88 | 89 | message Describe{Subdomain}V1Request { 90 | uint64 {subdomain}_id = 1; 91 | } 92 | 93 | message List{Subdomains}V1Request { 94 | } 95 | 96 | message List{Subdomains}V1Response { 97 | repeated {Subdomain} items = 1; 98 | } 99 | 100 | message Remove{Subdomain}V1Request { 101 | uint64 {subdomain}_id = 1; 102 | } 103 | 104 | message Remove{Subdomain}V1Response { 105 | bool found = 1; 106 | } 107 | ``` 108 | --------------------------------------------------------------------------------