{"id":20533047,"url":"https://github.com/anatoliybr/data-modifier","last_synced_at":"2026-04-21T13:33:32.665Z","repository":{"id":210041028,"uuid":"720522404","full_name":"AnatoliyBr/data-modifier","owner":"AnatoliyBr","description":"gRPC server for expanding/modifying user data based on a third-party system.","archived":false,"fork":false,"pushed_at":"2023-11-30T11:23:21.000Z","size":86,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-16T13:18:09.282Z","etag":null,"topics":["clean-architecture","go","grpc","mock","proto"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AnatoliyBr.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}},"created_at":"2023-11-18T18:39:36.000Z","updated_at":"2023-11-30T11:28:59.000Z","dependencies_parsed_at":"2023-11-30T13:46:24.412Z","dependency_job_id":null,"html_url":"https://github.com/AnatoliyBr/data-modifier","commit_stats":null,"previous_names":["anatoliybr/data-modifier"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnatoliyBr%2Fdata-modifier","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnatoliyBr%2Fdata-modifier/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnatoliyBr%2Fdata-modifier/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AnatoliyBr%2Fdata-modifier/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AnatoliyBr","download_url":"https://codeload.github.com/AnatoliyBr/data-modifier/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242138805,"owners_count":20078007,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["clean-architecture","go","grpc","mock","proto"],"created_at":"2024-11-16T00:18:25.719Z","updated_at":"2026-04-21T13:33:27.496Z","avatar_url":"https://github.com/AnatoliyBr.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Data Modifier\nData Modifier - gRPC-сервер для расширения/модификации данных о пользователе на основе сторонней системы.\n\nИспользуемые технологии:\n* toml, godotenv (конфигурация)\n* ozzo-validation (валидация)\n* go.uber.org/zap (логирование)\n* google.golang.org/grpc (gRPC сервер)\n* grpc-ecosystem/go-grpc-middleware/v2/interceptors (gRPC интерсепторы)\n* testify, go.uber.org/mock/gomock (unit-тесты)\n\nКод организован согласно Clean Architecture, инъекция зависимостей обеспечивает низкую связанность слоев и упрощает тестирование.\n\nТакже реализован Graceful Shutdown для корректного завершения работы сервера. \n\n# Getting Started\nПеред запуском приложения необходимо добавить в директорию проекта `.env` файл с логином и паролем от сторонней системы, как в примере [.env.example](.env.example).\n\nА также настроить конфигурацию приложения через `.toml` файл, как в примере [config.toml](configs/config.toml). Путь к этому файлу можно передать с флагом `-config-path` при запуске приложения.\n\nЕсли требуется записать логи и ошибки в файлы, необходимо создать эти файлы и передавать путь к ним в опциях `output_path`/`error_output_path` (подробнее см. ниже).\n\n\u003cdetails\u003e\n    \u003csummary\u003e Описание всех опций с примерами: \u003c/summary\u003e\n\n* `tcp_ip`/`tcp_port` - IP-адрес и порт, на который gRPC сервер будет принимать запросы . Заметьте, что порт указывается с `:`.\n* `max_clients` - максимальное количество подключений (размер очереди запросов). \n* `num_pool_workers` - количество горутин для обработки запросов (размер пула обработчиков).\n\nПример конфигурации gRPC сервера:\n```toml\ntcp_ip=\"127.0.0.1\"\ntcp_port=\":8081\"\nmax_clients=10\nnum_pool_workers=5\n```\n\n* `web_api_ip`/`web_api_port` - IP-адрес и порт сторонней системы. Заметьте, что порт указывается с `:`.\n* `protocol_type` - HTTP протокол (\"http\", \"https\").\n* `employee_path` - путь для формирования запроса на получение данных о пользователе со сторонней системы. Заметьте, что путь указывается **без** `/`.\n* `absence_path` - путь для формирования запроса на получение данных о статусе отсутствия пользователя со сторонней системы. Заметьте, что путь указывается **без** `/`.\n\nПример конфигурации для работы со сторонней системой:\n```toml\nweb_api_ip=\"127.0.0.1\"\nweb_api_port=\":8082\"\nprotocol_type=\"http\"\nemployee_path=\"employees\"\nabsence_path=\"absences\"\n```\n\n* `log_format` - формат кодировки логов (\"json\", \"console\").\n\nПример лога в формате \"json\" и \"console\":\n```bash\n{\"L\":\"INFO\",\"T\":\"2023-11-29T15:24:59.674+0300\",\"C\":\"app/app.go:61\",\"M\":\"Initializing UserWebAPI...\"}\n\n2023-11-29T13:22:41.524+0300    INFO    app/app.go:61   Initializing UserWebAPI...\n```\n\n* `log_level` - уровень логирования (\"debug\", \"info\", \"warn\", \"error\"). Заметьте, что если поднять `log_level` до уровня \"error\", то логер пропустит сообщение уровня \"info\", так как все сообщения ниже установленного уровня игнорируются.\n* `encoder_type` - определяет формат ключей у полей логов (\"dev\", \"prod\"), если установлен `log_format`=\"json\". Описание для [dev](https://pkg.go.dev/go.uber.org/zap#NewDevelopmentEncoderConfig) и [prod](https://pkg.go.dev/go.uber.org/zap#NewProductionEncoderConfig).\n\nПример лога типа \"dev\" и \"prod\":\n```bash\n{\"L\":\"INFO\",\"T\":\"2023-11-29T16:36:59.832+0300\",\"C\":\"app/app.go:62\",\"M\":\"Initializing UserWebAPI...\"}\n\n{\"level\":\"info\",\"ts\":1701264983.1611516,\"caller\":\"app/app.go:62\",\"msg\":\"Initializing UserWebAPI...\"}\n```\n\n* `output_path`/`error_output_path` - **списки** путей к файлам для записи выходных логов и ошибок.\n\nПример конфигурации логера:\n```toml\nlog_format=\"console\"\nlog_level=\"debug\"\nencoder_type=\"dev\"\noutput_path=[ \"stdout\", \"./tmp/logs/rpc_traffic.txt\" ]\nerror_output_path=[ \"stderr\", \"./tmp/logs/rpc_traffic_errors.txt\" ]\n```\n\n\u003c/details\u003e\n\n# Usage\nДля сборки проекта необходимо выполнить команду `make`.\n\nДля запуска сервиса необходимо выполнить команду `make up`. Для запуска сервиса с тестовым сервером - `make test-up`. \n\nДля запуска unit-тестов необходимо выполнить команду `make test`.\n\nДля запуска линтера необходимо выполнить команду `make linter`.\n\n## Examples\nДля тестирования сервиса использовался **Postman** и **тестовый сервер** для имитации сторонней системы, который запускается с помощью флага `-test-server` (именно это делается при выполнении команды `make test-up`).\n\nПо заданию требовалось:\n\n\u003e ... по входящему gRPC запросу с информацией о пользователе по email найти того же пользователя на внешнем HTTP сервере, и обогатить входящее имя пользователя (ФИО) статусом отсутствия, если оно есть (в конец ФИО дописать emoji с соответствующим статусом).\n\nБыл описан интерфейс сервиса `DataModifier` с Unary RPC методом `AddAbsenceStatus`.\n\n### Добавление emoji, соответствующего статусу отсутствия, в поле display_name\nДля добавления **emoji**, соответствующего статусу отсутствия, в поле `display_name` необходимо передать информацию о пользователе (все поля обязательные и валидируются системой) и интервал времени (формат времени по умолчанию \"2006-01-02T15:04:05\"):\n\n```json\n{\n    \"user_data\": {\n        \"display_name\": \"Иванов Семен Петрович\",\n        \"email\": \"petrovich@mail.ru\",\n        \"mobile_phone\": \"+71234567890\",\n        \"work_phone\": \"1234\"\n        \n    },\n    \"time_period\": {\n        \"date_from\": \"2022-07-01T00:00:00\",\n        \"date_to\": \"2022-09-01T23:59:59\"\n    }\n}\n```\n\nПример ответа:\n\n```json\n{\n    \"modified_user_data\": {\n        \"display_name\": \"Иванов Семен Петрович 🏠\",\n        \"email\": \"petrovich@mail.ru\",\n        \"mobile_phone\": \"+71234567890\",\n        \"work_phone\": \"1234\"\n    }\n}\n```\n\n**Правила валидации** для каждой сущности определены в соответствующих методах (метод [Validate](/internal/entity/user.go) для структуры пользователя).\n\n* `display_name` - обязательное поле, должно состоять только из букв unicode, может содержать составные имена/фамилии (например, Иванов-Сидоров).\n* `email` - обязательное поле, должно быть валидной электронной почтой.\n* `mobile_phone` - обязательное поле, должно состоять только из символов [0-9], может начинаться на \"+\", длина от 10 до 12 символов.\n* `work_phone` - обязательное поле, должно состоять только из символов [0-9], может начинаться на \"+\", длина от 1 до 12 символов.\n\n# Decisions\nЧтобы организовать очередь запросов, можно использовать буферизированный канал, как показано [здесь](https://eli.thegreenplace.net/2019/on-concurrency-in-go-http-servers/). Либо можно ограничить число подключений на уровне слушателя с помощью [netutil.LimitListener](https://pkg.go.dev/golang.org/x/net/netutil#LimitListener).\n\nЧтобы организовать воркер-пул, можно использовать цикл горутин, считывающих задачи из общего канала, как показано [здесь](https://gobyexample.com/worker-pools). Либо можно задать количество воркеров для gRPC сервера из пакета [grpc](https://pkg.go.dev/google.golang.org/grpc) с помощью экспериментальной функции [NumStreamWorkers](https://pkg.go.dev/google.golang.org/grpc#NumStreamWorkers).\n\n\u003cdetails\u003e\n\u003csummary\u003e Подходит для Unary RPC запросов (под капотом в исходном коде) \u003c/summary\u003e\n\nЗаходим в [grpc-go/server.go](https://github.com/grpc/grpc-go/blob/v1.59.0/server.go). Находим функцию `Serve`, которая на каждое новое соединение запускает горутину `handleRowConn`. `handleRowConn` запускает горутину, в которой последовательно вызываются методы `serveStreams` и `removeConn`. В `serveStreams` вызывается `HandleStreams`, в которой в канал `s.serverWorkerChannel` передается функция с методом `handleStream`.\n\nДалее в функции `serverWorker` эта функция считывается из канала и вызывается. Если сервис и метод существуют, `handleStream` вызывает метод `processUnaryRPC` или `processStreamingRPC`.\n\n\u003c/details\u003e\n\n# Commands\n\u003cdetails\u003e\n\u003csummary\u003e Использованные команды\u003c/summary\u003e\n\n```bash\ngo get github.com/BurntSushi/toml\n\ngo get google.golang.org/grpc\ngo get google.golang.org/protobuf\n\ngo get golang.org/x/net\n\ncurl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2\n\nprotoc --proto_path=api/proto --go_out=pkg --go-grpc_out=pkg api/proto/datamodifier.proto\n\ngo get go.uber.org/zap\ngo get github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery\ngo get github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging\n\ngo get github.com/go-ozzo/ozzo-validation\ngo get github.com/go-ozzo/ozzo-validation/is\n\ngo get github.com/stretchr/testify\n\ngo install go.uber.org/mock/mockgen\ngo get go.uber.org/mock/mockgen\n\nmockgen -source=./internal/webapi/interfaces.go -destination=./internal/webapi/webapi_mocks.go -package=webapi\n\ngo get github.com/gorilla/mux\n```\n\n\u003c/details\u003e\n\n## Полезные ссылки\n* [Документация gRPC](https://grpc.io/) - фреймворк для удаленного вызова процедур, разработанный компанией Google.\n    * [Инструкция Go Quick Start](https://grpc.io/docs/languages/go/quickstart/).\n* [Protocol Buffers](https://protobuf.dev/) - инструмент сериализации (перевода в поток байтов) и язык описания gRPC, для начала работы необходимо установить компилятор.\n    * [Компилятор](https://github.com/protocolbuffers/protobuf/releases/latest) Protocol Buffers, чтобы компилировать из proto-файлов, которые описывают интерфейс сервиса, код на каком-то языке.\n    * [Инструкция Protocol Buffer Basics: Go](https://protobuf.dev/getting-started/gotutorial/)\n* [Пишем gRPC сервис на Go — сервис авторизации](https://habr.com/ru/articles/774796/)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanatoliybr%2Fdata-modifier","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanatoliybr%2Fdata-modifier","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanatoliybr%2Fdata-modifier/lists"}