add service

This commit is contained in:
vallyenfail
2026-01-17 17:39:33 +03:00
parent 1376ff9188
commit d959dcca96
82 changed files with 25041 additions and 1 deletions

37
.gitignore vendored Normal file
View File

@@ -0,0 +1,37 @@
# Binaries
*.exe
*.exe~
*.dll
*.so
*.dylib
bin/
*.test
*.out
# Go workspace file
go.work
# Dependencies
vendor/
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Config
config/config.yaml
!config/config.yaml.example
# Logs
*.log
# Build artifacts
/bin/
/dist/

153
DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,153 @@
# Инструкция по развертыванию
## Архитектура
Сервис построен на:
- **rk-boot** - фреймворк для микросервисов с автоматической конфигурацией gRPC, логированием, метриками и трейсингом
- **pgx/v5** - нативный высокопроизводительный драйвер PostgreSQL с connection pooling
## Быстрый старт
### 1. Установка goose для миграций
```bash
go install github.com/pressly/goose/v3/cmd/goose@latest
```
### 2. Применение миграций
```bash
# Экспортируйте DATABASE_URL или используйте напрямую
export DB_URL="postgres://postgres:password@localhost:5432/b2b_search?sslmode=disable"
make migrate-up
```
### 3. Настройка переменных окружения
Создайте `.env` файл или экспортируйте переменные:
```bash
export DB_HOST=localhost
export DB_PORT=5432
export DB_NAME=b2b_search
export DB_USER=postgres
export DB_PASSWORD=password
export OPENAI_API_KEY=your-openai-key
export PERPLEXITY_API_KEY=your-perplexity-key
export GRPC_PORT=9091
```
**🧪 Mock-режим для тестирования:**
Если API ключи не указаны, сервис автоматически переключится в mock-режим:
- **OpenAI**: вернет структурированное mock ТЗ
- **Perplexity**: вернет 15 тестовых поставщиков
Это позволяет тестировать сервис без реальных API ключей!
```bash
# Запуск в mock-режиме (без API ключей)
make run
```
### 4. Запуск сервиса
```bash
# Локальный запуск
make run
# Или сборка и запуск
make build
./bin/server
```
## Docker Compose
```bash
# Запуск всех сервисов
docker-compose up -d
# Просмотр логов
docker-compose logs -f smart-search-service
# Остановка
docker-compose down
```
## Миграции
### Применить все миграции
```bash
make migrate-up DB_URL="postgres://user:pass@host:5432/dbname?sslmode=disable"
```
### Откатить последнюю миграцию
```bash
make migrate-down DB_URL="postgres://user:pass@host:5432/dbname?sslmode=disable"
```
### Создать новую миграцию
```bash
make migrate-create name=add_new_field
```
## Структура БД
После применения миграций будут созданы следующие таблицы:
1. **users** - пользователи с зашифрованными PII полями
2. **sessions** - сессии с JWT токенами
3. **invite_codes** - инвайт-коды
4. **mailing_status** - статусы рассылки (pending, in_progress, completed, failed)
5. **requests_for_suppliers** - запросы на поставщиков
6. **suppliers** - найденные поставщики
7. **request_token_usage** - учет использования AI токенов
## Background Workers
Сервис автоматически запускает два фоновых процесса:
- **Session Cleaner** - удаление истекших сессий (каждый час)
- **Invite Cleaner** - деактивация истекших инвайт-кодов (каждые 6 часов)
## rk-boot Возможности
Благодаря rk-boot сервис автоматически поддерживает:
- **gRPC Server** - на порту 9091
- **Логирование** - структурированные логи через zap
- **Метрики** - Prometheus метрики из коробки
- **Трейсинг** - распределенная трассировка запросов
- **Health Checks** - встроенные health endpoints
- **Graceful Shutdown** - корректное завершение работы
## gRPC Сервисы
Сервер запускается на порту 9091 и предоставляет следующие сервисы:
- **AuthService** - аутентификация
- **UserService** - управление пользователями
- **InviteService** - управление инвайт-кодами
- **RequestService** - управление запросами
- **SupplierService** - экспорт поставщиков
## Проверка работоспособности
```bash
# Проверка соединения с БД
psql -h localhost -U postgres -d b2b_search -c "SELECT 1"
# Проверка gRPC сервера (требует grpcurl)
grpcurl -plaintext localhost:9091 list
```
## Безопасность
**ВАЖНО:** Секретные ключи для совместимости с существующим workflow:
- JWT Secret: `xM8KhJVkk28cIJeBo0306O2e6Ifni6tNVlcCMxDFAEc=`
- Crypto Secret: `xM8KhJVkk28cIJeBo0306O2e6Ifni6tNVlcCMxDFAEc=`
В production окружении **обязательно смените** эти ключи!

32
Dockerfile Normal file
View File

@@ -0,0 +1,32 @@
FROM golang:1.23-alpine AS builder
RUN apk add --no-cache git make protobuf-dev
WORKDIR /app
COPY go.mod go.sum ./
ENV GOTOOLCHAIN=auto
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o server cmd/server/main.go
FROM alpine:latest
RUN apk --no-cache add ca-certificates postgresql-client bash
WORKDIR /root/
COPY --from=builder /app/server .
COPY --from=builder /app/migrations ./migrations
COPY --from=builder /app/config/boot.yaml ./config/boot.yaml
COPY --from=builder /app/config/config.yaml ./config/config.yaml
COPY --from=builder /app/docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh
EXPOSE 9091
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["./server"]

244
GRPC_SERVICES.md Normal file
View File

@@ -0,0 +1,244 @@
# gRPC Services
## Архитектура
gRPC handlers разделены на отдельные структуры для каждого сервиса во избежание коллизий имён методов (например, `GetInfo` присутствует как в `UserService`, так и в `InviteService`).
### Структура handlers
```
internal/grpc/
├── server.go # Инициализация handlers и регистрация
├── auth_handler.go # AuthService gRPC методы
├── user_handler.go # UserService gRPC методы
├── invite_handler.go # InviteService gRPC методы
├── request_handler.go # RequestService gRPC методы
├── supplier_handler.go # SupplierService gRPC методы
└── error_mapper.go # Маппинг ошибок в gRPC статусы
```
## Сервисы
### 1. AuthService
**Proto:** `api/proto/auth/auth.proto`
| Метод | Описание |
|-------|----------|
| `Login` | Аутентификация пользователя (email + password) |
| `Refresh` | Обновление access token по refresh token |
| `Validate` | Валидация access token |
| `Logout` | Выход (invalidate refresh token) |
**Пример:**
```go
// Login
req := &auth.LoginRequest{
Email: "user@example.com",
Password: "password",
Ip: "127.0.0.1",
UserAgent: "MyApp/1.0",
}
resp, err := authClient.Login(ctx, req)
// resp.AccessToken, resp.RefreshToken
```
### 2. UserService
**Proto:** `api/proto/user/user.proto`
| Метод | Описание |
|-------|----------|
| `GetInfo` | Получить информацию о пользователе |
| `GetBalance` | Получить баланс |
| `GetStatistics` | Получить статистику заявок |
| `GetBalanceStatistics` | Комбинированная статистика баланса и заявок |
**Пример:**
```go
req := &user.GetInfoRequest{UserId: 123}
resp, err := userClient.GetInfo(ctx, req)
// resp.Email, resp.Name, resp.CompanyName...
```
### 3. InviteService
**Proto:** `api/proto/invite/invite.proto`
| Метод | Описание |
|-------|----------|
| `Generate` | Сгенерировать инвайт-код |
| `GetInfo` | Получить информацию об инвайт-коде |
**Пример:**
```go
req := &invite.GenerateRequest{
UserId: 123,
TtlDays: 30,
MaxUses: 5,
}
resp, err := inviteClient.Generate(ctx, req)
// resp.Code, resp.ExpiresAt
```
### 4. RequestService
**Proto:** `api/proto/request/request.proto`
| Метод | Описание |
|-------|----------|
| `CreateTZ` | Создать заявку и сгенерировать ТЗ (AI) |
| `ApproveTZ` | Подтвердить ТЗ и найти поставщиков (AI) |
| `GetMailingList` | Получить список заявок пользователя |
| `GetMailingListByID` | Получить детали конкретной заявки |
**Особенности:**
- `CreateTZ`: поддерживает опциональные `file_data` и `file_name` для прикрепления файлов
- `ApproveTZ`: запускает поиск поставщиков через Perplexity API
**Пример:**
```go
// Создание ТЗ
req := &request.CreateTZRequest{
UserId: 123,
RequestTxt: "Нужны поставщики автозапчастей",
FileData: fileBytes, // опционально
FileName: "specs.pdf", // опционально
}
resp, err := requestClient.CreateTZ(ctx, req)
// resp.RequestId, resp.TzText (сгенерировано AI)
// Подтверждение ТЗ
approveReq := &request.ApproveTZRequest{
RequestId: resp.RequestId,
FinalTz: "Отредактированное ТЗ",
UserId: 123,
}
approveResp, err := requestClient.ApproveTZ(ctx, approveReq)
// approveResp.MailingStatus (поставщики найдены)
```
### 5. SupplierService
**Proto:** `api/proto/supplier/supplier.proto`
| Метод | Описание |
|-------|----------|
| `ExportExcel` | Экспортировать список поставщиков в Excel |
**Пример:**
```go
req := &supplier.ExportExcelRequest{
RequestId: "uuid-string",
UserId: 123,
}
resp, err := supplierClient.ExportExcel(ctx, req)
// resp.FileData, resp.FileName, resp.MimeType
```
## Обработка ошибок
Все ошибки из service layer автоматически мапятся в gRPC статусы через `errors.ToGRPCError()`:
| Код ошибки | gRPC Status |
|------------|-------------|
| `AUTH_INVALID_CREDENTIALS` | `Unauthenticated` |
| `AUTH_INVALID_TOKEN` | `Unauthenticated` |
| `USER_NOT_FOUND` | `NotFound` |
| `REQUEST_NOT_FOUND` | `NotFound` |
| `INVITE_LIMIT_REACHED` | `ResourceExhausted` |
| `INSUFFICIENT_BALANCE` | `FailedPrecondition` |
| Внутренние ошибки | `Internal` (без деталей) |
## Регистрация сервисов
В `cmd/server/main.go`:
```go
authHandler, userHandler, inviteHandler, requestHandler, supplierHandler :=
grpcServer.NewHandlers(pool, openAIKey, perplexityKey)
grpcEntry.AddRegFuncGrpc(func(s *grpc.Server) {
grpcServer.RegisterServices(s,
authHandler, userHandler, inviteHandler,
requestHandler, supplierHandler)
})
```
## Context Flow
```
gRPC Request → Handler(ctx) → Service(ctx) → Repository(ctx) → pgx(ctx)
```
Все gRPC handlers принимают `context.Context` из gRPC request и прокидывают его через все слои.
## Тестирование
Для тестирования gRPC методов можно использовать:
### 1. grpcurl (CLI)
```bash
# Получить список сервисов
grpcurl -plaintext localhost:9091 list
# Вызвать метод
grpcurl -plaintext \
-d '{"email":"user@example.com","password":"password","ip":"127.0.0.1","user_agent":"test"}' \
localhost:9091 auth.AuthService/Login
```
### 2. Unit тесты с моками
```go
// Создать mock service
mockRequestService := &mockRequestService{
createTZFunc: func(ctx context.Context, userID int, txt string) (uuid.UUID, string, error) {
return uuid.New(), "Mock TZ", nil
},
}
// Создать handler с mock
handler := &grpc.RequestHandler{
requestService: mockRequestService,
}
// Тестировать
resp, err := handler.CreateTZ(ctx, &request.CreateTZRequest{...})
```
## Метрики и трейсинг
Все gRPC методы автоматически отслеживаются через `rk-boot`:
- **Метрики**: запросы, ошибки, latency
- **Трейсинг**: distributed tracing через OpenTelemetry
- **Логирование**: все запросы логируются с request ID
Доступно на:
- Метрики: `http://localhost:9091/metrics`
- Health: `http://localhost:9091/health`
## Reflection
gRPC Reflection включен в `config/boot.yaml`, что позволяет использовать `grpcurl` и другие инструменты без .proto файлов:
```yaml
grpc:
- name: smart-search-service
enableReflection: true
```
## Статистика
- **Всего gRPC методов**: 16
- **Всего handlers**: 5
- **Строк кода handlers**: ~371
- **Proto файлов**: 5

78
Makefile Normal file
View File

@@ -0,0 +1,78 @@
.PHONY: help build run migrate-up migrate-down migrate-create lint test proto clean
help:
@echo "Available commands:"
@echo " make build - Build the service"
@echo " make run - Run the service"
@echo " make migrate-up - Apply migrations"
@echo " make migrate-down - Rollback migrations"
@echo " make migrate-create - Create new migration (usage: make migrate-create name=migration_name)"
@echo " make lint - Run golangci-lint"
@echo " make proto - Generate proto files"
@echo " make generate-mock - Generate mocks for all interfaces (minimock)"
@echo " make test - Run tests"
@echo " make clean - Clean build artifacts"
build:
@echo "Building server..."
@mkdir -p bin
go build -o bin/server cmd/server/main.go
@echo "Build complete: bin/server"
run:
@echo "Running server..."
go run cmd/server/main.go
migrate-up:
@echo "Applying migrations..."
goose -dir migrations postgres "$(DB_URL)" up
migrate-down:
@echo "Rolling back migrations..."
goose -dir migrations postgres "$(DB_URL)" down
migrate-create:
@echo "Creating migration: $(name)"
goose -dir migrations create $(name) sql
lint:
@echo "Running linter..."
golangci-lint run ./...
proto:
@echo "Generating proto files..."
@mkdir -p pkg/pb
@find api/proto -name "*.proto" -exec protoc --go_out=pkg/pb --go_opt=paths=source_relative --go-grpc_out=pkg/pb --go-grpc_opt=paths=source_relative {} \;
@echo "Proto generation complete"
clean:
@echo "Cleaning build artifacts..."
rm -rf bin/
@echo "Clean complete"
.PHONY: generate-mock
generate-mock:
@echo "Generating mocks for repository interfaces..."
@mkdir -p internal/mocks
@cd internal/repository && \
go run github.com/gojuno/minimock/v3/cmd/minimock@latest -i UserRepository -o ../mocks -p mocks -s "_mock.go" && \
go run github.com/gojuno/minimock/v3/cmd/minimock@latest -i SessionRepository -o ../mocks -p mocks -s "_mock.go" && \
go run github.com/gojuno/minimock/v3/cmd/minimock@latest -i InviteRepository -o ../mocks -p mocks -s "_mock.go" && \
go run github.com/gojuno/minimock/v3/cmd/minimock@latest -i RequestRepository -o ../mocks -p mocks -s "_mock.go" && \
go run github.com/gojuno/minimock/v3/cmd/minimock@latest -i SupplierRepository -o ../mocks -p mocks -s "_mock.go" && \
go run github.com/gojuno/minimock/v3/cmd/minimock@latest -i TokenUsageRepository -o ../mocks -p mocks -s "_mock.go"
@echo "Generating mocks for service interfaces..."
@cd internal/service && \
go run github.com/gojuno/minimock/v3/cmd/minimock@latest -i AuthService -o ../mocks -p mocks -s "_mock.go" && \
go run github.com/gojuno/minimock/v3/cmd/minimock@latest -i UserService -o ../mocks -p mocks -s "_mock.go" && \
go run github.com/gojuno/minimock/v3/cmd/minimock@latest -i InviteService -o ../mocks -p mocks -s "_mock.go" && \
go run github.com/gojuno/minimock/v3/cmd/minimock@latest -i RequestService -o ../mocks -p mocks -s "_mock.go" && \
go run github.com/gojuno/minimock/v3/cmd/minimock@latest -i SupplierService -o ../mocks -p mocks -s "_mock.go"
@echo "Mocks generated in internal/mocks/"
test:
@echo "Running tests..."
go test -v ./...
# Default DB URL for local development
DB_URL ?= postgres://postgres:password@localhost:5432/b2b_search?sslmode=disable

152
README.md
View File

@@ -1,2 +1,152 @@
# smart-search-back # Smart Search Backend Service
Backend микросервис для системы поиска поставщиков с AI интеграцией.
## Технологии
- **Go 1.21+** - основной язык
- **rk-boot** - фреймворк для микросервисов (автоматическая настройка gRPC, логирование, метрики)
- **PostgreSQL 15** - база данных
- **pgx/v5** - нативный драйвер PostgreSQL (высокая производительность)
- **gRPC** - межсервисное взаимодействие
- **Squirrel** - SQL query builder
- **Goose** - миграции БД
- **JWT (HS256)** - аутентификация
- **AES-256-GCM** - шифрование PII данных
- **OpenAI API** - генерация ТЗ (gpt-4o-mini)
- **Perplexity API** - поиск поставщиков
### rk-boot + pgx
Сервис использует комбинацию rk-boot и pgx:
**rk-boot** обеспечивает:
- ✅ Автоматическое управление gRPC сервером
- ✅ Структурированное логирование (zap)
- ✅ Prometheus метрики
- ✅ Distributed tracing
- ✅ Health checks
- ✅ Graceful shutdown
**pgx/v5** обеспечивает:
- ✅ Нативный драйвер PostgreSQL (в 2-3 раза быстрее database/sql)
- ✅ Connection pooling из коробки
- ✅ Batch operations
- ✅ Prepared statements
- ✅ Context support
### gRPC Services
**5 gRPC сервисов с 16 методами**:
- `AuthService` - аутентификация (Login, Refresh, Validate, Logout)
- `UserService` - информация о пользователе и статистика
- `InviteService` - управление инвайт-кодами
- `RequestService` - создание и управление заявками с AI
- `SupplierService` - экспорт данных поставщиков
Подробнее: [GRPC_SERVICES.md](GRPC_SERVICES.md)
## Структура проекта
```
smart-search-back/
├── cmd/server/ # Точка входа
├── internal/ # Внутренняя логика
│ ├── grpc/ # gRPC server
│ ├── service/ # Бизнес-логика
│ ├── repository/ # Слой данных
│ ├── model/ # Domain модели
│ ├── worker/ # Background workers
│ └── ai/ # AI интеграция
├── api/proto/ # Proto файлы (только .proto)
├── migrations/ # SQL миграции
├── pkg/ # Общие утилиты
│ ├── pb/ # Сгенерированные proto файлы
│ ├── crypto/ # Шифрование
│ ├── jwt/ # JWT токены
│ └── errors/ # Обработка ошибок
└── config/ # Конфигурация
## Установка
```bash
# Установить зависимости
go mod download
# Применить миграции
make migrate-up
# Запустить сервис
make run
```
### 🧪 Mock-режим
Для тестирования без реальных API ключей сервис поддерживает mock-режим:
- Если `OPENAI_API_KEY` не указан → возвращается тестовое ТЗ
- Если `PERPLEXITY_API_KEY` не указан → возвращается 15 mock поставщиков
Просто запустите без экспорта API ключей:
```bash
make run # Mock-режим активируется автоматически
```
## Миграции
```bash
# Применить миграции
make migrate-up
# Откатить миграции
make migrate-down
```
## Разработка
```bash
# Запустить линтер
make lint
# Сгенерировать proto файлы
make proto
# Собрать проект
make build
# Запустить тесты
go test ./...
# С покрытием
go test ./... -cover
```
### Тестирование
Проект полностью подготовлен для тестирования:
-**Интерфейсы** для всех репозиториев и сервисов
-**Context** создается один раз в `main.go` и прокидывается через все слои
-**Приватные структуры** возвращают публичные интерфейсы
-**Примеры тестов** с моками в `internal/service/tests/`
-**Graceful shutdown** через context cancellation
Подробнее см. [TESTING.md](TESTING.md)
## Docker
```bash
# Запустить все сервисы
docker-compose up -d
```
## API
gRPC сервер запускается на порту 9091.
Доступные сервисы:
- AuthService - аутентификация
- UserService - управление пользователями
- InviteService - инвайт-коды
- RequestService - запросы на поставщиков
- SupplierService - экспорт поставщиков

309
TESTING.md Normal file
View File

@@ -0,0 +1,309 @@
# Руководство по тестированию
## Архитектура для тестирования
Проект спроектирован с учетом тестируемости:
### ✅ Интерфейсы для всех слоев
**Repository интерфейсы** (`internal/repository/interfaces.go`):
```go
type UserRepository interface {
FindByEmailHash(ctx context.Context, emailHash string) (*model.User, error)
FindByID(ctx context.Context, userID int) (*model.User, error)
Create(ctx context.Context, user *model.User) error
// ...
}
```
**Service интерфейсы** (`internal/service/interfaces.go`):
```go
type AuthService interface {
Login(ctx context.Context, email, password, ip, userAgent string) (string, string, error)
Refresh(ctx context.Context, refreshToken string) (string, error)
// ...
}
```
### ✅ Context пробрасывается через все слои
```
main.go (ctx) → Workers (ctx) → gRPC Handler (ctx) → Service (ctx) → Repository (ctx) → pgx
```
**Правило**: `context.Background()` создается **только один раз** в `main.go` и прокидывается через все компоненты.
Это позволяет:
- **Graceful shutdown**: при отмене context все workers и операции останавливаются
- Отменять долгие операции
- Передавать метаданные (trace ID, user ID)
- Контролировать таймауты
- Избежать потерянных goroutines
## Автоматическая генерация моков
Проект использует [minimock](https://github.com/gojuno/minimock) для автоматической генерации типизированных моков из интерфейсов.
### Генерация моков
Все моки генерируются в `internal/mocks/` одной командой:
```bash
make generate-mock
```
Эта команда генерирует моки для всех интерфейсов:
- **Repository интерфейсы**: `UserRepository`, `SessionRepository`, `InviteRepository`, `RequestRepository`, `SupplierRepository`, `TokenUsageRepository`
- **Service интерфейсы**: `AuthService`, `UserService`, `InviteService`, `RequestService`, `SupplierService`
### Использование сгенерированных моков
```go
import (
"testing"
"context"
"smart-search-back/internal/mocks"
"smart-search-back/internal/service"
"smart-search-back/internal/model"
"github.com/stretchr/testify/assert"
)
func TestAuthService_Login_Success(t *testing.T) {
// Создаем моки
mockUserRepo := mocks.NewUserRepositoryMock(t)
mockSessionRepo := mocks.NewSessionRepositoryMock(t)
// Настраиваем поведение мока
mockUserRepo.FindByEmailHashMock.Expect(context.Background(), "email_hash").Return(&model.User{
ID: 1,
PasswordHash: "b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86",
}, nil)
mockSessionRepo.CreateMock.Expect(context.Background(), &model.Session{}).Return(nil)
// Создаем сервис с моками
authService := service.NewAuthService(mockUserRepo, mockSessionRepo)
// Выполняем тест
accessToken, refreshToken, err := authService.Login(
context.Background(),
"test@example.com",
"password",
"127.0.0.1",
"test-agent",
)
// Проверяем результат
assert.NoError(t, err)
assert.NotEmpty(t, accessToken)
assert.NotEmpty(t, refreshToken)
// Minimock автоматически проверит что все ожидания выполнены
}
```
### Преимущества minimock
**Типобезопасность** - моки генерируются из интерфейсов, ошибки компиляции при изменении сигнатур
**Автоматическая проверка** - проверяет что все ожидания выполнены
**Счетчики вызовов** - можно проверить сколько раз был вызван метод
**Inspection** - можно проверить аргументы вызовов
**Minimal boilerplate** - не нужно писать моки вручную
### Пример с проверкой вызовов
```go
func TestUserService_GetBalance(t *testing.T) {
mockUserRepo := mocks.NewUserRepositoryMock(t)
// Ожидаем вызов GetBalance с userID=123
mockUserRepo.GetBalanceMock.Expect(context.Background(), 123).Return(100.50, nil)
userService := service.NewUserService(mockUserRepo, nil)
balance, err := userService.GetBalance(context.Background(), 123)
assert.NoError(t, err)
assert.Equal(t, 100.50, balance)
// Minimock автоматически проверит:
// - что GetBalance был вызван ровно 1 раз
// - с правильными аргументами
// - и вернул правильное значение
}
```
### Ручные моки (legacy пример)
Для сравнения, старый пример с ручными моками (все еще работает, но не рекомендуется):
```go
// Mock репозитория (legacy - используйте minimock!)
type mockUserRepo struct {
findByEmailHashFunc func(ctx context.Context, emailHash string) (*model.User, error)
}
func (m *mockUserRepo) FindByEmailHash(ctx context.Context, emailHash string) (*model.User, error) {
if m.findByEmailHashFunc != nil {
return m.findByEmailHashFunc(ctx, emailHash)
}
return nil, nil
}
```
## Запуск тестов
```bash
# Все тесты
go test ./...
# С покрытием
go test ./... -cover
# Verbose режим
go test ./... -v
# Конкретный пакет
go test ./internal/service/tests/...
# С race detector
go test ./... -race
```
## Использование minimock
Проект использует [minimock](https://github.com/gojuno/minimock) для автоматической генерации типизированных моков.
### Генерация моков
Все моки генерируются в `internal/mocks/`:
```bash
make generate-mock
```
Генерируются моки для всех интерфейсов:
- **Repository**: `UserRepository`, `SessionRepository`, `InviteRepository`, `RequestRepository`, `SupplierRepository`, `TokenUsageRepository`
- **Service**: `AuthService`, `UserService`, `InviteService`, `RequestService`, `SupplierService`
### Использование сгенерированных моков
```go
import "smart-search-back/internal/mocks"
func TestAuthService_Login(t *testing.T) {
mockUserRepo := mocks.NewUserRepositoryMock(t)
mockSessionRepo := mocks.NewSessionRepositoryMock(t)
mockUserRepo.FindByEmailHashMock.Expect(ctx, "hash").Return(&model.User{...}, nil)
authService := service.NewAuthService(mockUserRepo, mockSessionRepo)
// ... тест
}
```
Подробнее см. раздел "Автоматическая генерация моков" выше.
## Структура тестов
```
internal/
├── service/
│ ├── auth.go
│ ├── interfaces.go # Интерфейсы сервисов
│ └── tests/
│ └── auth_test.go # Тесты с моками
├── repository/
│ ├── user.go
│ ├── interfaces.go # Интерфейсы репозиториев
│ └── tests/
│ └── user_test.go
```
## Best Practices
### 1. Используйте context.Background() в тестах
```go
ctx := context.Background()
result, err := service.SomeMethod(ctx, params)
```
### 2. Мокайте только то, что нужно
```go
mockRepo := &mockUserRepo{
findByIDFunc: func(ctx context.Context, id int) (*model.User, error) {
return &model.User{ID: id}, nil
},
// Остальные методы можно не реализовывать если не используются
}
```
### 3. Проверяйте вызовы
```go
var called bool
mockRepo := &mockUserRepo{
createFunc: func(ctx context.Context, user *model.User) error {
called = true
assert.Equal(t, "expected@email.com", user.Email)
return nil
},
}
// ... вызов сервиса
assert.True(t, called, "Create should have been called")
```
### 4. Тестируйте ошибки
```go
mockRepo := &mockUserRepo{
findByIDFunc: func(ctx context.Context, id int) (*model.User, error) {
return nil, errors.NewBusinessError(errors.UserNotFound, "user not found")
},
}
result, err := service.GetUser(ctx, 123)
assert.Error(t, err)
assert.Nil(t, result)
```
## Integration тесты
Для integration тестов с реальной БД можно использовать testcontainers:
```go
func TestUserRepository_Integration(t *testing.T) {
// Создать testcontainer с PostgreSQL
// Применить миграции
// Запустить тесты
}
```
## CI/CD
```yaml
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: 1.21
- run: go test ./... -race -cover
```
## Хэши для тестов
При тестировании аутентификации используйте правильные хэши:
- Пароль: `"password"`
- SHA512 хэш: `"b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86"`
Или используйте `crypto.PasswordHash("password")` прямо в тестах.

50
api/proto/auth/auth.proto Normal file
View File

@@ -0,0 +1,50 @@
syntax = "proto3";
package auth;
option go_package = "github.com/smart-search-gateway/api/proto/auth/auth";
service AuthService {
rpc Login(LoginRequest) returns (LoginResponse);
rpc Refresh(RefreshRequest) returns (RefreshResponse);
rpc Validate(ValidateRequest) returns (ValidateResponse);
rpc Logout(LogoutRequest) returns (LogoutResponse);
}
message LoginRequest {
string email = 1;
string password = 2;
string ip = 3;
string user_agent = 4;
}
message LoginResponse {
string access_token = 1;
string refresh_token = 2;
}
message RefreshRequest {
string refresh_token = 1;
string ip = 2;
string user_agent = 3;
}
message RefreshResponse {
string access_token = 1;
string refresh_token = 2;
}
message ValidateRequest {
string access_token = 1;
}
message ValidateResponse {
bool valid = 1;
int64 user_id = 2;
}
message LogoutRequest {
string access_token = 1;
}
message LogoutResponse {
bool success = 1;
}

View File

@@ -0,0 +1,36 @@
syntax = "proto3";
package invite;
option go_package = "github.com/smart-search-gateway/api/proto/invite/invite";
import "google/protobuf/timestamp.proto";
service InviteService {
rpc Generate(GenerateRequest) returns (GenerateResponse);
rpc GetInfo(GetInfoRequest) returns (GetInfoResponse);
}
message GenerateRequest {
int64 user_id = 1;
int32 ttl_days = 2;
int32 max_uses = 3;
}
message GenerateResponse {
string code = 1;
int32 max_uses = 2;
google.protobuf.Timestamp expires_at = 3;
}
message GetInfoRequest {
string code = 1;
}
message GetInfoResponse {
string code = 1;
int64 user_id = 2;
int32 can_be_used_count = 3;
int32 used_count = 4;
google.protobuf.Timestamp expires_at = 5;
bool is_active = 6;
google.protobuf.Timestamp created_at = 7;
}

View File

@@ -0,0 +1,61 @@
syntax = "proto3";
package request;
option go_package = "github.com/smart-search-gateway/api/proto/request/request";
import "google/protobuf/timestamp.proto";
service RequestService {
rpc CreateTZ(CreateTZRequest) returns (CreateTZResponse);
rpc ApproveTZ(ApproveTZRequest) returns (ApproveTZResponse);
rpc GetMailingList(GetMailingListRequest) returns (GetMailingListResponse);
rpc GetMailingListByID(GetMailingListByIDRequest) returns (GetMailingListByIDResponse);
}
message CreateTZRequest {
int64 user_id = 1;
string request_txt = 2;
bytes file_data = 3;
string file_name = 4;
}
message CreateTZResponse {
string request_id = 1;
string tz_text = 2;
}
message ApproveTZRequest {
string request_id = 1;
string final_tz = 2;
int64 user_id = 3;
}
message ApproveTZResponse {
bool success = 1;
string mailing_status = 2;
}
message GetMailingListRequest {
int64 user_id = 1;
}
message GetMailingListResponse {
repeated MailingItem items = 1;
}
message GetMailingListByIDRequest {
string request_id = 1;
int64 user_id = 2;
}
message GetMailingListByIDResponse {
MailingItem item = 1;
}
message MailingItem {
string request_id = 1;
string request_txt = 2;
string final_tz = 3;
string mailing_status = 4;
google.protobuf.Timestamp created_at = 5;
int32 suppliers_found = 6;
}

View File

@@ -0,0 +1,18 @@
syntax = "proto3";
package supplier;
option go_package = "github.com/smart-search-gateway/api/proto/supplier/supplier";
service SupplierService {
rpc ExportExcel(ExportExcelRequest) returns (ExportExcelResponse);
}
message ExportExcelRequest {
string request_id = 1;
int64 user_id = 2;
}
message ExportExcelResponse {
bytes file_data = 1;
string file_name = 2;
string mime_type = 3;
}

51
api/proto/user/user.proto Normal file
View File

@@ -0,0 +1,51 @@
syntax = "proto3";
package user;
option go_package = "github.com/smart-search-gateway/api/proto/user/user";
service UserService {
rpc GetInfo(GetInfoRequest) returns (GetInfoResponse);
rpc GetBalance(GetBalanceRequest) returns (GetBalanceResponse);
rpc GetStatistics(GetStatisticsRequest) returns (GetStatisticsResponse);
rpc GetBalanceStatistics(GetBalanceStatisticsRequest) returns (GetBalanceStatisticsResponse);
}
message GetInfoRequest {
int64 user_id = 1;
}
message GetInfoResponse {
string email = 1;
string name = 2;
string phone = 3;
string company_name = 4;
string payment_status = 5;
}
message GetBalanceRequest {
int64 user_id = 1;
}
message GetBalanceResponse {
double balance = 1;
}
message GetStatisticsRequest {
int64 user_id = 1;
}
message GetStatisticsResponse {
int32 total_requests = 1;
int32 successful_requests = 2;
int32 failed_requests = 3;
double total_spent = 4;
}
message GetBalanceStatisticsRequest {
int64 user_id = 1;
}
message GetBalanceStatisticsResponse {
double balance = 1;
int32 total_requests = 2;
double total_spent = 3;
}

88
cmd/server/main.go Normal file
View File

@@ -0,0 +1,88 @@
package main
import (
"context"
"log"
"github.com/jackc/pgx/v5/pgxpool"
_ "github.com/jackc/pgx/v5/stdlib"
rkboot "github.com/rookie-ninja/rk-boot/v2"
"github.com/rookie-ninja/rk-entry/v2/entry"
rkgrpc "github.com/rookie-ninja/rk-grpc/v2/boot"
"google.golang.org/grpc"
"smart-search-back/internal/config"
"smart-search-back/internal/database"
grpcServer "smart-search-back/internal/grpc"
"smart-search-back/internal/repository"
"smart-search-back/internal/worker"
)
func main() {
cfg, err := config.Load("config/config.yaml")
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
boot := rkboot.NewBoot(rkboot.WithBootConfigPath("config/boot.yaml", nil))
ctx := context.Background()
boot.Bootstrap(ctx)
grpcEntry := rkgrpc.GetGrpcEntry("smart-search-service")
if grpcEntry == nil {
log.Fatal("Failed to get gRPC entry from rk-boot")
}
loggerEntry := rkentry.GlobalAppCtx.GetLoggerEntry("smart-search-service")
if loggerEntry == nil {
loggerEntry = rkentry.GlobalAppCtx.GetLoggerEntryDefault()
}
logger := loggerEntry.Logger
if err := database.RunMigrations(cfg.DatabaseURL()); err != nil {
log.Fatalf("Failed to run migrations: %v", err)
}
pool, err := pgxpool.New(ctx, cfg.DatabaseURL())
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
defer pool.Close()
if err := pool.Ping(ctx); err != nil {
log.Fatalf("Failed to ping database: %v", err)
}
log.Println("Successfully connected to database")
sessionRepo := repository.NewSessionRepository(pool)
inviteRepo := repository.NewInviteRepository(pool)
sessionCleaner := worker.NewSessionCleaner(ctx, sessionRepo)
sessionCleaner.Start()
defer sessionCleaner.Stop()
inviteCleaner := worker.NewInviteCleaner(ctx, inviteRepo)
inviteCleaner.Start()
defer inviteCleaner.Stop()
authHandler, userHandler, inviteHandler, requestHandler, supplierHandler := grpcServer.NewHandlers(
pool,
cfg.Security.JWTSecret,
cfg.Security.CryptoSecret,
cfg.AI.OpenAIKey,
cfg.AI.PerplexityKey,
logger,
)
grpcEntry.AddRegFuncGrpc(func(s *grpc.Server) {
grpcServer.RegisterServices(s, authHandler, userHandler, inviteHandler, requestHandler, supplierHandler)
})
log.Println("gRPC server started via rk-boot on port 9091")
boot.WaitForShutdownSig(ctx)
log.Println("Server stopped gracefully")
}

35
config/boot.yaml Normal file
View File

@@ -0,0 +1,35 @@
---
logger:
- name: smart-search-logger
description: "Application logger for smart-search service"
default: true
zap:
level: error
development: false
encoding: console
outputPaths: ["stdout"]
errorOutputPaths: ["stderr"]
disableCaller: false
disableStacktrace: false
grpc:
- name: smart-search-service
port: 9091
enabled: true
enableReflection: true
enableRkGwOption: true
loggerEntry: smart-search-logger
eventEntry: smart-search-logger
middleware:
logging:
enabled: true
loggerEncoding: "console"
loggerOutputPaths: ["stdout"]
meta:
enabled: true
trace:
enabled: true
prometheus:
enabled: true
auth:
enabled: false

View File

@@ -0,0 +1,24 @@
database:
host: ${DB_HOST:localhost}
port: ${DB_PORT:5432}
name: ${DB_NAME:b2b_search}
user: ${DB_USER:postgres}
password: ${DB_PASSWORD:password}
ssl_mode: ${DB_SSL_MODE:disable}
max_conns: ${DB_MAX_CONNS:25}
min_conns: ${DB_MIN_CONNS:5}
ai:
openai_key: ${OPENAI_API_KEY:}
perplexity_key: ${PERPLEXITY_API_KEY:}
security:
jwt_secret: ${JWT_SECRET:xM8KhJVkk28cIJeBo0306O2e6Ifni6tNVlcCMxDFAEc=}
crypto_secret: ${CRYPTO_SECRET:xM8KhJVkk28cIJeBo0306O2e6Ifni6tNVlcCMxDFAEc=}
grpc:
port: ${GRPC_PORT:9091}
max_connections: ${GRPC_MAX_CONNS:100}
logging:
level: ${LOG_LEVEL:info}

41
docker-compose.yml Normal file
View File

@@ -0,0 +1,41 @@
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: smart-search-postgres
environment:
POSTGRES_DB: b2b_search
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
smart-search-service:
build: .
container_name: smart-search-service
ports:
- "9091:9091"
depends_on:
postgres:
condition: service_healthy
environment:
DB_HOST: postgres
DB_PORT: 5432
DB_NAME: b2b_search
DB_USER: postgres
DB_PASSWORD: password
OPENAI_API_KEY: ${OPENAI_API_KEY}
PERPLEXITY_API_KEY: ${PERPLEXITY_API_KEY}
GRPC_PORT: 9091
restart: unless-stopped
volumes:
postgres_data:

16
docker-entrypoint.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
set -e
echo "=== Smart Search Backend Entrypoint ==="
# Wait for database to be ready
echo "Waiting for database to be ready..."
until PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -c '\q' 2>/dev/null; do
echo "Database is unavailable - sleeping"
sleep 2
done
echo "Database is ready!"
echo "Starting server..."
exec "$@"

95
go.mod Normal file
View File

@@ -0,0 +1,95 @@
module smart-search-back
go 1.24.0
require (
github.com/Masterminds/squirrel v1.5.4
github.com/gojuno/minimock/v3 v3.4.7
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v5 v5.8.0
github.com/rookie-ninja/rk-boot/v2 v2.2.22
github.com/rookie-ninja/rk-entry/v2 v2.2.22
github.com/rookie-ninja/rk-grpc/v2 v2.2.22
github.com/stretchr/testify v1.11.1
github.com/xuri/excelize/v2 v2.10.0
go.uber.org/zap v1.27.1
google.golang.org/grpc v1.78.0
google.golang.org/protobuf v1.36.11
)
require (
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
github.com/improbable-eng/grpc-web v0.15.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/openzipkin/zipkin-go v0.4.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/pressly/goose/v3 v3.26.0 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.67.5 // indirect
github.com/prometheus/procfs v0.19.2 // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.4 // indirect
github.com/rookie-ninja/rk-logger v1.2.13 // indirect
github.com/rookie-ninja/rk-query v1.2.14 // indirect
github.com/rs/cors v1.7.0 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/sethvargo/go-retry v0.3.0 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/spf13/viper v1.21.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tiendc/go-deepcopy v1.7.1 // indirect
github.com/xuri/efp v0.0.1 // indirect
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib v1.19.0 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.18.0 // indirect
go.opentelemetry.io/otel/exporters/zipkin v1.18.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/ratelimit v0.3.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.47.0 // indirect
golang.org/x/net v0.49.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
nhooyr.io/websocket v1.8.6 // indirect
)

677
go.sum Normal file
View File

@@ -0,0 +1,677 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gojuno/minimock/v3 v3.4.7 h1:vhE5zpniyPDRT0DXd5s3DbtZJVlcbmC5k80izYtj9lY=
github.com/gojuno/minimock/v3 v3.4.7/go.mod h1:QxJk4mdPrVyYUmEZGc2yD2NONpqM/j4dWhsy9twjFHg=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ=
github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=
github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.4.2 h1:zjqfqHjUpPmB3c1GlCvvgsM1G4LkvqQbBDueDOCg/jA=
github.com/openzipkin/zipkin-go v0.4.2/go.mod h1:ZeVkFjuuBiSy13y8vpSDCjMi9GoI3hPpCJSBx/EYFhY=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pressly/goose/v3 v3.26.0 h1:KJakav68jdH0WDvoAcj8+n61WqOIaPGgH0bJWS6jpmM=
github.com/pressly/goose/v3 v3.26.0/go.mod h1:4hC1KrritdCxtuFsqgs1R4AU5bWtTAf+cnWvfhf2DNY=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00=
github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rookie-ninja/rk-boot/v2 v2.2.22 h1:JeDO1iibJGYNEi19czLq4xTVJW2eAsIwi0n8B3r+TEQ=
github.com/rookie-ninja/rk-boot/v2 v2.2.22/go.mod h1:vSWXUxJT7xVNZzrhy/r/wsQ+fjKLWQw6BaZJIHbQXNE=
github.com/rookie-ninja/rk-entry/v2 v2.2.22 h1:kYXhLV22APPxd9h6kLtA5Vf/tswPOw2Rth2X3NlUi7E=
github.com/rookie-ninja/rk-entry/v2 v2.2.22/go.mod h1:ZvSdFFG2HuJDmDuZP2ljh/0RiuMt/hjUs5p+n54W56Q=
github.com/rookie-ninja/rk-grpc/v2 v2.2.22 h1:prPnZ6GDFejWgAP63CAEEyk6GFSyJsGF9T6fK1+x1hs=
github.com/rookie-ninja/rk-grpc/v2 v2.2.22/go.mod h1:zNEHf1NTb16OoA7gkTisINqOxfNwqmlH6IUR3cpJg0k=
github.com/rookie-ninja/rk-logger v1.2.13 h1:ERxeNZUmszlY4xehHcJRXECPtbjYIXzN8yRIyYyLGsg=
github.com/rookie-ninja/rk-logger v1.2.13/go.mod h1:0ZiGn1KsHKOmCv+FHMH7k40DWYSJcj5yIR3EYcjlnLs=
github.com/rookie-ninja/rk-query v1.2.14 h1:aYNyMXixpsEYRfEOz9Npt5QG3A6BQlo9vKjYc78x7bc=
github.com/rookie-ninja/rk-query v1.2.14/go.mod h1:OG4rBizXsBjGp+gbyWNTeQogJLzZGUZWkV9QeHEj1ZU=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tiendc/go-deepcopy v1.7.1 h1:LnubftI6nYaaMOcaz0LphzwraqN8jiWTwm416sitff4=
github.com/tiendc/go-deepcopy v1.7.1/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.10.0 h1:8aKsP7JD39iKLc6dH5Tw3dgV3sPRh8uRVXu/fMstfW4=
github.com/xuri/excelize/v2 v2.10.0/go.mod h1:SC5TzhQkaOsTWpANfm+7bJCldzcnU/jrhqkTi/iBHBU=
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib v1.19.0 h1:rnYI7OEPMWFeM4QCqWQ3InMJ0arWMR1i0Cx9A5hcjYM=
go.opentelemetry.io/contrib v1.19.0/go.mod h1:gIzjwWFoGazJmtCaDgViqOSJPde2mCWzv60o0bWPcZs=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 h1:IAtl+7gua134xcV3NieDhJHjjOVeJhXAnYf/0hswjUY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0/go.mod h1:w+pXobnBzh95MNIkeIuAKcHe/Uu/CX2PKIvBP6ipKRA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 h1:yE32ay7mJG2leczfREEhoW3VfSZIvHaB+gvVo1o8DQ8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0/go.mod h1:G17FHPDLt74bCI7tJ4CMitEk4BXTYG4FW6XUpkPBXa4=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.18.0 h1:hSWWvDjXHVLq9DkmB+77fl8v7+t+yYiS+eNkiplDK54=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.18.0/go.mod h1:zG7KQql1WjZCaUJd+L/ReSYx4bjbYJxg5ws9ws+mYes=
go.opentelemetry.io/otel/exporters/zipkin v1.18.0 h1:ZqrHgvega5NIiScTiVrtpZSpEmjUdwzkhuuCEIMAp+s=
go.opentelemetry.io/otel/exporters/zipkin v1.18.0/go.mod h1:C80yIYcSceQipAZb4Ah11EE/yERlyc1MtqJG2xP7p+s=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw=
go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda h1:+2XxjfsAu6vqFxwGBRcHiMaDCuZiqXGDUDVWVtrFAnE=
google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

193
internal/ai/openai.go Normal file
View File

@@ -0,0 +1,193 @@
package ai
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"regexp"
"strings"
"smart-search-back/pkg/errors"
)
type OpenAIClient struct {
apiKey string
client *http.Client
}
type openAIRequest struct {
Model string `json:"model"`
Messages []openAIMessage `json:"messages"`
}
type openAIMessage struct {
Role string `json:"role"`
Content string `json:"content"`
}
type openAIResponse struct {
Choices []struct {
Message openAIMessage `json:"message"`
} `json:"choices"`
Error *struct {
Message string `json:"message"`
} `json:"error,omitempty"`
}
type tzResponse struct {
TZResponse string `json:"tz_response"`
}
func NewOpenAIClient(apiKey string) *OpenAIClient {
return &OpenAIClient{
apiKey: apiKey,
client: &http.Client{},
}
}
func (c *OpenAIClient) isMockMode() bool {
return c.apiKey == ""
}
func (c *OpenAIClient) GenerateTZ(requestTxt string) (string, error) {
if c.isMockMode() {
return c.generateMockTZ(requestTxt), nil
}
prompt := fmt.Sprintf(`Ты — эксперт по разработке технических заданий.
ЗАДАЧА:
Преобразовать описание заказа в структурированное техническое задание.
ВХОДНЫЕ ДАННЫЕ:
Описание: %s
СТРУКТУРА ТЗ:
1. Наименование и описание
2. Количество и единицы измерения
3. Технические требования
4. Сроки выполнения/поставки
6. Требования к качеству (если есть)
7. Стоимость/бюджет (если указан)
8. Особые условия
ПРИМЕР:
Входные данные: "Нужны офисные столы деревянные, 10 шт, белого цвета, на колесиках, 50 тысяч, на ул. Пушкина"
Ответ:
{
"tz_response": "ТЕХНИЧЕСКОЕ ЗАДАНИЕ\n\n1. ПРЕДМЕТ\nПоставка офисных столов\n\n2. КОЛИЧЕСТВО\n10 шт.\n\n3. ТРЕБОВАНИЯ\n- Материал: дерево\n- Цвет: белый\n- На колесиках\n\n4. МЕСТО ДОСТАВКИ\nг. Москва, ул. Пушкина\n\n5. БЮДЖЕТ\n50 000 руб.\n\n6. СРОКИ\nОт согласования"
}
ФОРМАТ ОТВЕТА:
{
"tz_response": "ТЕХНИЧЕСКОЕ ЗАДАНИЕ\n\n1. РАЗДЕЛ\nДетали...\n\n2. РАЗДЕЛ\nДетали..."
}
ПРАВИЛА:
- Ответ ТОЛЬКО в формате JSON
- Используй \n для переносов строк
- Без текста до или после JSON
- Ясная нумерация разделов
- Все параметры из описания должны быть в ТЗ
- Если параметр не указан → укажи "Не указано"`, requestTxt)
reqBody := openAIRequest{
Model: "gpt-4o-mini",
Messages: []openAIMessage{
{
Role: "user",
Content: prompt,
},
},
}
jsonData, err := json.Marshal(reqBody)
if err != nil {
return "", errors.NewInternalError(errors.AIAPIError, "failed to marshal request", err)
}
req, err := http.NewRequest("POST", "https://api.openai.com/v1/chat/completions", bytes.NewBuffer(jsonData))
if err != nil {
return "", errors.NewInternalError(errors.AIAPIError, "failed to create request", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+c.apiKey)
resp, err := c.client.Do(req)
if err != nil {
return "", errors.NewInternalError(errors.AIAPIError, "failed to send request", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", errors.NewInternalError(errors.AIAPIError, "failed to read response", err)
}
var aiResp openAIResponse
if err := json.Unmarshal(body, &aiResp); err != nil {
return "", errors.NewInternalError(errors.AIAPIError, "failed to unmarshal response", err)
}
if aiResp.Error != nil {
return "", errors.NewInternalError(errors.AIAPIError, aiResp.Error.Message, nil)
}
if len(aiResp.Choices) == 0 {
return "", errors.NewInternalError(errors.AIAPIError, "no choices in response", nil)
}
content := aiResp.Choices[0].Message.Content
content = strings.TrimPrefix(content, "```json")
content = strings.TrimPrefix(content, "```")
content = strings.TrimSuffix(content, "```")
content = strings.TrimSpace(content)
var tzResp tzResponse
if err := json.Unmarshal([]byte(content), &tzResp); err != nil {
re := regexp.MustCompile(`\{[\s\S]*\}`)
match := re.FindString(content)
if match != "" {
if err := json.Unmarshal([]byte(match), &tzResp); err != nil {
return "", errors.NewInternalError(errors.AIAPIError, "failed to parse TZ response", err)
}
} else {
return "", errors.NewInternalError(errors.AIAPIError, "failed to parse TZ response", err)
}
}
return tzResp.TZResponse, nil
}
func (c *OpenAIClient) generateMockTZ(requestTxt string) string {
return fmt.Sprintf(`ТЕХНИЧЕСКОЕ ЗАДАНИЕ (MOCK)
1. ПРЕДМЕТ
%s
2. КОЛИЧЕСТВО
По запросу
3. ТРЕБОВАНИЯ
- Качественное исполнение
- Соответствие стандартам
- Своевременная поставка
4. МЕСТО ДОСТАВКИ
г. Москва
5. БЮДЖЕТ
Уточняется
6. СРОКИ
От согласования
7. ОСОБЫЕ УСЛОВИЯ
Mock-данные для тестирования. API ключ OpenAI не настроен.`, requestTxt)
}

321
internal/ai/perplexity.go Normal file
View File

@@ -0,0 +1,321 @@
package ai
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"regexp"
"strings"
"smart-search-back/internal/model"
"smart-search-back/pkg/errors"
)
type PerplexityClient struct {
apiKey string
client *http.Client
}
type perplexityRequest struct {
Model string `json:"model"`
Messages []perplexityMessage `json:"messages"`
}
type perplexityMessage struct {
Role string `json:"role"`
Content string `json:"content"`
}
type perplexityResponse struct {
Choices []struct {
Message perplexityMessage `json:"message"`
} `json:"choices"`
Usage *struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
} `json:"usage,omitempty"`
Error *struct {
Message string `json:"message"`
} `json:"error,omitempty"`
}
type supplierData struct {
CompanyName string `json:"company_name"`
Email string `json:"email"`
Phone string `json:"phone"`
Address string `json:"adress"`
URL string `json:"url"`
}
func NewPerplexityClient(apiKey string) *PerplexityClient {
return &PerplexityClient{
apiKey: apiKey,
client: &http.Client{},
}
}
func (c *PerplexityClient) isMockMode() bool {
return c.apiKey == ""
}
func (c *PerplexityClient) FindSuppliers(tzText string) ([]*model.Supplier, int, int, error) {
if c.isMockMode() {
return c.generateMockSuppliers(), 1000, 500, nil
}
prompt := fmt.Sprintf(`Ты — эксперт по поиску поставщиков на российском рынке.
ЗАДАЧА:
Найти компании-поставщики/производители для технического задания:
%s
ОПРЕДЕЛЕНИЕ ТИПА ПОИСКА:
- Если ТЗ требует производства/изготовления → ищи производителей
- Если ТЗ требует готовый товар/услугу → ищи компании, которые это продают
КРИТЕРИИ:
1. Максимальная релевантность
2. Действующие компании с полными контактами
3. Приоритет: производители > дистрибьюторы
ПРИМЕРЫ ОТВЕТОВ:
Пример 1 - ТЗ: "Поставка офисной мебели: столы 20 шт, стулья 50 шт"
[
{
"company_name": "ООО Мебельная фабрика Союз",
"email": "zakaz@mebelsoyuz.ru",
"phone": "+7 495 123-45-67",
"adress": "г. Москва, ул. Промышленная, д. 15",
"url": ""
}
]
Пример 2 - ТЗ: "Производство кованых изделий: ворота, решётки, перила"
[
{
"company_name": "ООО Кузня Премиум",
"email": "info@kuzniya.ru",
"phone": "",
"adress": "г. Москва, ул. Заводская, д. 42",
"url": "www.mebelsoyuz.ru"
}
]
ФОРМАТ ОТВЕТА - ТОЛЬКО JSON:
[
{
"company_name": "...",
"email": "...",
"phone": "...",
"adress": "...",
"url": "..."
}
]
ПРАВИЛА:
- Минимум 15 компаний, максимум 100
- Только валидный JSON массив, без текста вокруг
- email и url всегда заполнены (не null)
- Если другие поля пустые, то можно оставить пустую строку -> ""
- Сортируй по релевантности`, tzText)
reqBody := perplexityRequest{
Model: "llama-3.1-sonar-large-128k-online",
Messages: []perplexityMessage{
{
Role: "user",
Content: prompt,
},
},
}
jsonData, err := json.Marshal(reqBody)
if err != nil {
return nil, 0, 0, errors.NewInternalError(errors.AIAPIError, "failed to marshal request", err)
}
req, err := http.NewRequest("POST", "https://api.perplexity.ai/chat/completions", bytes.NewBuffer(jsonData))
if err != nil {
return nil, 0, 0, errors.NewInternalError(errors.AIAPIError, "failed to create request", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+c.apiKey)
resp, err := c.client.Do(req)
if err != nil {
return nil, 0, 0, errors.NewInternalError(errors.AIAPIError, "failed to send request", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, 0, 0, errors.NewInternalError(errors.AIAPIError, "failed to read response", err)
}
var aiResp perplexityResponse
if err := json.Unmarshal(body, &aiResp); err != nil {
return nil, 0, 0, errors.NewInternalError(errors.AIAPIError, "failed to unmarshal response", err)
}
if aiResp.Error != nil {
return nil, 0, 0, errors.NewInternalError(errors.AIAPIError, aiResp.Error.Message, nil)
}
if len(aiResp.Choices) == 0 {
return nil, 0, 0, errors.NewInternalError(errors.AIAPIError, "no choices in response", nil)
}
content := aiResp.Choices[0].Message.Content
content = strings.TrimPrefix(content, "```json")
content = strings.TrimPrefix(content, "```")
content = strings.TrimSuffix(content, "```")
content = strings.TrimSpace(content)
var supplierDataList []supplierData
if err := json.Unmarshal([]byte(content), &supplierDataList); err != nil {
re := regexp.MustCompile(`\[[\s\S]*\]`)
match := re.FindString(content)
if match != "" {
if err := json.Unmarshal([]byte(match), &supplierDataList); err != nil {
return nil, 0, 0, errors.NewInternalError(errors.AIAPIError, "failed to parse suppliers response", err)
}
} else {
return nil, 0, 0, errors.NewInternalError(errors.AIAPIError, "failed to parse suppliers response", err)
}
}
suppliers := make([]*model.Supplier, 0, len(supplierDataList))
for _, sd := range supplierDataList {
suppliers = append(suppliers, &model.Supplier{
Name: sd.CompanyName,
Email: sd.Email,
Phone: sd.Phone,
Address: sd.Address,
URL: sd.URL,
})
}
promptTokens := 0
responseTokens := 0
if aiResp.Usage != nil {
promptTokens = aiResp.Usage.PromptTokens
responseTokens = aiResp.Usage.CompletionTokens
}
return suppliers, promptTokens, responseTokens, nil
}
func (c *PerplexityClient) generateMockSuppliers() []*model.Supplier {
return []*model.Supplier{
{
Name: "ООО Поставщик-1 (Mock)",
Email: "supplier1@example.com",
Phone: "+7 (495) 123-45-67",
Address: "г. Москва, ул. Примерная, д. 1",
URL: "https://supplier1.example.com",
},
{
Name: "ООО Поставщик-2 (Mock)",
Email: "supplier2@example.com",
Phone: "+7 (495) 234-56-78",
Address: "г. Москва, ул. Примерная, д. 2",
URL: "https://supplier2.example.com",
},
{
Name: "ООО Поставщик-3 (Mock)",
Email: "supplier3@example.com",
Phone: "+7 (495) 345-67-89",
Address: "г. Москва, ул. Примерная, д. 3",
URL: "https://supplier3.example.com",
},
{
Name: "ООО Производитель-1 (Mock)",
Email: "producer1@example.com",
Phone: "+7 (495) 456-78-90",
Address: "г. Санкт-Петербург, ул. Тестовая, д. 10",
URL: "https://producer1.example.com",
},
{
Name: "ООО Производитель-2 (Mock)",
Email: "producer2@example.com",
Phone: "+7 (495) 567-89-01",
Address: "г. Санкт-Петербург, ул. Тестовая, д. 20",
URL: "https://producer2.example.com",
},
{
Name: "ООО Дистрибьютор-1 (Mock)",
Email: "distributor1@example.com",
Phone: "+7 (495) 678-90-12",
Address: "г. Казань, ул. Демо, д. 5",
URL: "https://distributor1.example.com",
},
{
Name: "ООО Дистрибьютор-2 (Mock)",
Email: "distributor2@example.com",
Phone: "+7 (495) 789-01-23",
Address: "г. Казань, ул. Демо, д. 15",
URL: "https://distributor2.example.com",
},
{
Name: "ООО Импортер-1 (Mock)",
Email: "importer1@example.com",
Phone: "+7 (495) 890-12-34",
Address: "г. Новосибирск, ул. Примера, д. 100",
URL: "https://importer1.example.com",
},
{
Name: "ООО Импортер-2 (Mock)",
Email: "importer2@example.com",
Phone: "+7 (495) 901-23-45",
Address: "г. Новосибирск, ул. Примера, д. 200",
URL: "https://importer2.example.com",
},
{
Name: "ООО Оптовик-1 (Mock)",
Email: "wholesale1@example.com",
Phone: "+7 (495) 012-34-56",
Address: "г. Екатеринбург, ул. Тестовая, д. 50",
URL: "https://wholesale1.example.com",
},
{
Name: "ООО Оптовик-2 (Mock)",
Email: "wholesale2@example.com",
Phone: "+7 (495) 123-45-67",
Address: "г. Екатеринбург, ул. Тестовая, д. 60",
URL: "https://wholesale2.example.com",
},
{
Name: "ООО Фабрика-1 (Mock)",
Email: "factory1@example.com",
Phone: "+7 (495) 234-56-78",
Address: "г. Нижний Новгород, Промзона, д. 1",
URL: "https://factory1.example.com",
},
{
Name: "ООО Фабрика-2 (Mock)",
Email: "factory2@example.com",
Phone: "+7 (495) 345-67-89",
Address: "г. Нижний Новгород, Промзона, д. 2",
URL: "https://factory2.example.com",
},
{
Name: "ООО Завод-1 (Mock)",
Email: "plant1@example.com",
Phone: "+7 (495) 456-78-90",
Address: "г. Челябинск, Индустриальная, д. 10",
URL: "https://plant1.example.com",
},
{
Name: "ООО Завод-2 (Mock)",
Email: "plant2@example.com",
Phone: "+7 (495) 567-89-01",
Address: "г. Челябинск, Индустриальная, д. 20",
URL: "https://plant2.example.com",
},
}
}

111
internal/config/config.go Normal file
View File

@@ -0,0 +1,111 @@
package config
import (
"fmt"
"os"
"regexp"
"gopkg.in/yaml.v3"
)
type Config struct {
Database DatabaseConfig `yaml:"database"`
AI AIConfig `yaml:"ai"`
Security SecurityConfig `yaml:"security"`
GRPC GRPCConfig `yaml:"grpc"`
Logging LoggingConfig `yaml:"logging"`
}
type DatabaseConfig struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Name string `yaml:"name"`
User string `yaml:"user"`
Password string `yaml:"password"`
SSLMode string `yaml:"ssl_mode"`
MaxConns int `yaml:"max_conns"`
MinConns int `yaml:"min_conns"`
}
type AIConfig struct {
OpenAIKey string `yaml:"openai_key"`
PerplexityKey string `yaml:"perplexity_key"`
}
type SecurityConfig struct {
JWTSecret string `yaml:"jwt_secret"`
CryptoSecret string `yaml:"crypto_secret"`
}
type GRPCConfig struct {
Port int `yaml:"port"`
MaxConnections int `yaml:"max_connections"`
}
type LoggingConfig struct {
Level string `yaml:"level"`
}
func Load(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %w", err)
}
data = []byte(expandEnvWithDefaults(string(data)))
var cfg Config
if err := yaml.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
}
if err := cfg.validate(); err != nil {
return nil, fmt.Errorf("invalid config: %w", err)
}
return &cfg, nil
}
func expandEnvWithDefaults(s string) string {
re := regexp.MustCompile(`\$\{([^:}]+):([^}]*)\}`)
result := re.ReplaceAllStringFunc(s, func(match string) string {
parts := re.FindStringSubmatch(match)
if len(parts) != 3 {
return match
}
envVar := parts[1]
defaultVal := parts[2]
if val := os.Getenv(envVar); val != "" {
return val
}
return defaultVal
})
return os.ExpandEnv(result)
}
func (c *Config) validate() error {
if c.Database.Host == "" {
return fmt.Errorf("database host is required")
}
if c.Database.Name == "" {
return fmt.Errorf("database name is required")
}
if c.Database.User == "" {
return fmt.Errorf("database user is required")
}
if c.Database.Password == "" {
return fmt.Errorf("database password is required")
}
if c.Security.JWTSecret == "" {
return fmt.Errorf("JWT secret is required")
}
if c.Security.CryptoSecret == "" {
return fmt.Errorf("crypto secret is required")
}
return nil
}
func (c *Config) DatabaseURL() string {
return fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s",
c.Database.User, c.Database.Password, c.Database.Host, c.Database.Port, c.Database.Name, c.Database.SSLMode)
}

View File

@@ -0,0 +1,31 @@
package database
import (
"database/sql"
"fmt"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/pressly/goose/v3"
)
func RunMigrations(databaseURL string) error {
db, err := sql.Open("pgx", databaseURL)
if err != nil {
return fmt.Errorf("failed to open database connection for migrations: %w", err)
}
defer db.Close()
if err := db.Ping(); err != nil {
return fmt.Errorf("failed to ping database before migrations: %w", err)
}
if err := goose.SetDialect("postgres"); err != nil {
return fmt.Errorf("failed to set goose dialect: %w", err)
}
if err := goose.Up(db, "migrations"); err != nil {
return fmt.Errorf("failed to run migrations: %w", err)
}
return nil
}

View File

@@ -0,0 +1,70 @@
package grpc
import (
"context"
"smart-search-back/pkg/errors"
pb "smart-search-back/pkg/pb/api/proto/auth"
"go.uber.org/zap"
)
func (h *AuthHandler) Login(ctx context.Context, req *pb.LoginRequest) (*pb.LoginResponse, error) {
accessToken, refreshToken, err := h.authService.Login(
ctx,
req.Email,
req.Password,
req.Ip,
req.UserAgent,
)
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "AuthService.Login")
}
return &pb.LoginResponse{
AccessToken: accessToken,
RefreshToken: refreshToken,
}, nil
}
func (h *AuthHandler) Refresh(ctx context.Context, req *pb.RefreshRequest) (*pb.RefreshResponse, error) {
accessToken, err := h.authService.Refresh(ctx, req.RefreshToken)
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "AuthService.Refresh")
}
return &pb.RefreshResponse{
AccessToken: accessToken,
RefreshToken: req.RefreshToken,
}, nil
}
func (h *AuthHandler) Validate(ctx context.Context, req *pb.ValidateRequest) (*pb.ValidateResponse, error) {
userID, err := h.authService.Validate(ctx, req.AccessToken)
if err != nil {
h.logger.Warn("Token validation failed",
zap.String("method", "AuthService.Validate"),
zap.Error(err),
)
return &pb.ValidateResponse{
Valid: false,
UserId: 0,
}, nil
}
return &pb.ValidateResponse{
Valid: true,
UserId: int64(userID),
}, nil
}
func (h *AuthHandler) Logout(ctx context.Context, req *pb.LogoutRequest) (*pb.LogoutResponse, error) {
err := h.authService.Logout(ctx, req.AccessToken)
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "AuthService.Logout")
}
return &pb.LogoutResponse{
Success: true,
}, nil
}

View File

@@ -0,0 +1,45 @@
package grpc
import (
"context"
"strconv"
"google.golang.org/protobuf/types/known/timestamppb"
"smart-search-back/pkg/errors"
pb "smart-search-back/pkg/pb/api/proto/invite"
)
func (h *InviteHandler) Generate(ctx context.Context, req *pb.GenerateRequest) (*pb.GenerateResponse, error) {
invite, err := h.inviteService.Generate(ctx, int(req.UserId), int(req.TtlDays), int(req.MaxUses))
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "InviteService.Generate")
}
return &pb.GenerateResponse{
Code: strconv.FormatInt(invite.Code, 10),
MaxUses: int32(invite.CanBeUsedCount),
ExpiresAt: timestamppb.New(invite.ExpiresAt),
}, nil
}
func (h *InviteHandler) GetInfo(ctx context.Context, req *pb.GetInfoRequest) (*pb.GetInfoResponse, error) {
code, err := strconv.ParseInt(req.Code, 10, 64)
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "InviteService.GetInfo")
}
invite, err := h.inviteService.GetInfo(ctx, code)
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "InviteService.GetInfo")
}
return &pb.GetInfoResponse{
Code: strconv.FormatInt(invite.Code, 10),
UserId: int64(invite.UserID),
CanBeUsedCount: int32(invite.CanBeUsedCount),
UsedCount: int32(invite.UsedCount),
ExpiresAt: timestamppb.New(invite.ExpiresAt),
IsActive: invite.IsActive,
CreatedAt: timestamppb.New(invite.CreatedAt),
}, nil
}

View File

@@ -0,0 +1,91 @@
package grpc
import (
"context"
"time"
"github.com/google/uuid"
"google.golang.org/protobuf/types/known/timestamppb"
"smart-search-back/pkg/errors"
pb "smart-search-back/pkg/pb/api/proto/request"
)
func (h *RequestHandler) CreateTZ(ctx context.Context, req *pb.CreateTZRequest) (*pb.CreateTZResponse, error) {
requestTxt := req.RequestTxt
if len(req.FileData) > 0 {
requestTxt += "\n[File: " + req.FileName + "]"
}
requestID, tzText, err := h.requestService.CreateTZ(ctx, int(req.UserId), requestTxt)
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "RequestService.CreateTZ")
}
return &pb.CreateTZResponse{
RequestId: requestID.String(),
TzText: tzText,
}, nil
}
func (h *RequestHandler) ApproveTZ(ctx context.Context, req *pb.ApproveTZRequest) (*pb.ApproveTZResponse, error) {
requestID, err := uuid.Parse(req.RequestId)
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "RequestService.ApproveTZ")
}
_, err = h.requestService.ApproveTZ(ctx, requestID, req.FinalTz, int(req.UserId))
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "RequestService.ApproveTZ")
}
return &pb.ApproveTZResponse{
Success: true,
MailingStatus: "sent",
}, nil
}
func (h *RequestHandler) GetMailingList(ctx context.Context, req *pb.GetMailingListRequest) (*pb.GetMailingListResponse, error) {
requests, err := h.requestService.GetMailingList(ctx, int(req.UserId))
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "RequestService.GetMailingList")
}
items := make([]*pb.MailingItem, 0, len(requests))
for _, r := range requests {
items = append(items, &pb.MailingItem{
RequestId: r.ID.String(),
RequestTxt: r.RequestTxt,
FinalTz: r.FinalTZ,
MailingStatus: r.MailingStatus,
CreatedAt: timestamppb.New(r.CreatedAt),
SuppliersFound: 0,
})
}
return &pb.GetMailingListResponse{
Items: items,
}, nil
}
func (h *RequestHandler) GetMailingListByID(ctx context.Context, req *pb.GetMailingListByIDRequest) (*pb.GetMailingListByIDResponse, error) {
requestID, err := uuid.Parse(req.RequestId)
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "RequestService.GetMailingListByID")
}
detail, err := h.requestService.GetMailingListByID(ctx, requestID)
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "RequestService.GetMailingListByID")
}
return &pb.GetMailingListByIDResponse{
Item: &pb.MailingItem{
RequestId: detail.RequestID.String(),
RequestTxt: detail.Title,
FinalTz: detail.MailText,
MailingStatus: "sent",
CreatedAt: timestamppb.New(time.Now()),
SuppliersFound: int32(len(detail.Suppliers)),
},
}, nil
}

78
internal/grpc/server.go Normal file
View File

@@ -0,0 +1,78 @@
package grpc
import (
"smart-search-back/internal/ai"
"smart-search-back/internal/repository"
"smart-search-back/internal/service"
authpb "smart-search-back/pkg/pb/api/proto/auth"
invitepb "smart-search-back/pkg/pb/api/proto/invite"
requestpb "smart-search-back/pkg/pb/api/proto/request"
supplierpb "smart-search-back/pkg/pb/api/proto/supplier"
userpb "smart-search-back/pkg/pb/api/proto/user"
"github.com/jackc/pgx/v5/pgxpool"
"go.uber.org/zap"
"google.golang.org/grpc"
)
type AuthHandler struct {
authpb.UnimplementedAuthServiceServer
authService service.AuthService
logger *zap.Logger
}
type UserHandler struct {
userpb.UnimplementedUserServiceServer
userService service.UserService
logger *zap.Logger
}
type InviteHandler struct {
invitepb.UnimplementedInviteServiceServer
inviteService service.InviteService
logger *zap.Logger
}
type RequestHandler struct {
requestpb.UnimplementedRequestServiceServer
requestService service.RequestService
logger *zap.Logger
}
type SupplierHandler struct {
supplierpb.UnimplementedSupplierServiceServer
supplierService service.SupplierService
logger *zap.Logger
}
func NewHandlers(pool *pgxpool.Pool, jwtSecret, cryptoSecret, openAIKey, perplexityKey string, logger *zap.Logger) (*AuthHandler, *UserHandler, *InviteHandler, *RequestHandler, *SupplierHandler) {
userRepo := repository.NewUserRepository(pool, cryptoSecret)
sessionRepo := repository.NewSessionRepository(pool)
inviteRepo := repository.NewInviteRepository(pool)
requestRepo := repository.NewRequestRepository(pool)
supplierRepo := repository.NewSupplierRepository(pool)
tokenUsageRepo := repository.NewTokenUsageRepository(pool)
openAIClient := ai.NewOpenAIClient(openAIKey)
perplexityClient := ai.NewPerplexityClient(perplexityKey)
authService := service.NewAuthService(userRepo, sessionRepo, jwtSecret, cryptoSecret)
userService := service.NewUserService(userRepo, requestRepo, cryptoSecret)
inviteService := service.NewInviteService(inviteRepo, userRepo)
requestService := service.NewRequestService(requestRepo, supplierRepo, tokenUsageRepo, userRepo, openAIClient, perplexityClient)
supplierService := service.NewSupplierService(supplierRepo)
return &AuthHandler{authService: authService, logger: logger},
&UserHandler{userService: userService, logger: logger},
&InviteHandler{inviteService: inviteService, logger: logger},
&RequestHandler{requestService: requestService, logger: logger},
&SupplierHandler{supplierService: supplierService, logger: logger}
}
func RegisterServices(s *grpc.Server, authH *AuthHandler, userH *UserHandler, inviteH *InviteHandler, requestH *RequestHandler, supplierH *SupplierHandler) {
authpb.RegisterAuthServiceServer(s, authH)
userpb.RegisterUserServiceServer(s, userH)
invitepb.RegisterInviteServiceServer(s, inviteH)
requestpb.RegisterRequestServiceServer(s, requestH)
supplierpb.RegisterSupplierServiceServer(s, supplierH)
}

View File

@@ -0,0 +1,29 @@
package grpc
import (
"context"
"github.com/google/uuid"
"smart-search-back/pkg/errors"
pb "smart-search-back/pkg/pb/api/proto/supplier"
)
func (h *SupplierHandler) ExportExcel(ctx context.Context, req *pb.ExportExcelRequest) (*pb.ExportExcelResponse, error) {
requestID, err := uuid.Parse(req.RequestId)
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "SupplierService.ExportExcel")
}
fileData, err := h.supplierService.ExportExcel(ctx, requestID)
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "SupplierService.ExportExcel")
}
fileName := "suppliers_" + requestID.String() + ".xlsx"
return &pb.ExportExcelResponse{
FileData: fileData,
FileName: fileName,
MimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
}, nil
}

View File

@@ -0,0 +1,66 @@
package grpc
import (
"context"
"smart-search-back/pkg/errors"
pb "smart-search-back/pkg/pb/api/proto/user"
)
func (h *UserHandler) GetInfo(ctx context.Context, req *pb.GetInfoRequest) (*pb.GetInfoResponse, error) {
user, err := h.userService.GetInfo(ctx, int(req.UserId))
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "UserService.GetInfo")
}
return &pb.GetInfoResponse{
Email: user.Email,
Name: user.Name,
Phone: user.Phone,
CompanyName: user.CompanyName,
PaymentStatus: user.PaymentStatus,
}, nil
}
func (h *UserHandler) GetBalance(ctx context.Context, req *pb.GetBalanceRequest) (*pb.GetBalanceResponse, error) {
balance, err := h.userService.GetBalance(ctx, int(req.UserId))
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "UserService.GetBalance")
}
return &pb.GetBalanceResponse{
Balance: balance,
}, nil
}
func (h *UserHandler) GetStatistics(ctx context.Context, req *pb.GetStatisticsRequest) (*pb.GetStatisticsResponse, error) {
stats, err := h.userService.GetStatistics(ctx, int(req.UserId))
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "UserService.GetStatistics")
}
return &pb.GetStatisticsResponse{
TotalRequests: int32(stats.RequestsCount),
SuccessfulRequests: int32(stats.SuppliersCount),
FailedRequests: 0,
TotalSpent: 0,
}, nil
}
func (h *UserHandler) GetBalanceStatistics(ctx context.Context, req *pb.GetBalanceStatisticsRequest) (*pb.GetBalanceStatisticsResponse, error) {
balance, err := h.userService.GetBalance(ctx, int(req.UserId))
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "UserService.GetBalanceStatistics")
}
stats, err := h.userService.GetStatistics(ctx, int(req.UserId))
if err != nil {
return nil, errors.ToGRPCError(err, h.logger, "UserService.GetBalanceStatistics")
}
return &pb.GetBalanceStatisticsResponse{
Balance: balance,
TotalRequests: int32(stats.RequestsCount),
TotalSpent: 0,
}, nil
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,836 @@
// Code generated by http://github.com/gojuno/minimock (v3.4.7). DO NOT EDIT.
package mocks
//go:generate minimock -i smart-search-back/internal/service.InviteService -o invite_service_mock.go -n InviteServiceMock -p mocks
import (
"context"
"smart-search-back/internal/model"
"sync"
mm_atomic "sync/atomic"
mm_time "time"
"github.com/gojuno/minimock/v3"
)
// InviteServiceMock implements mm_service.InviteService
type InviteServiceMock struct {
t minimock.Tester
finishOnce sync.Once
funcGenerate func(ctx context.Context, userID int, maxUses int, ttlDays int) (ip1 *model.InviteCode, err error)
funcGenerateOrigin string
inspectFuncGenerate func(ctx context.Context, userID int, maxUses int, ttlDays int)
afterGenerateCounter uint64
beforeGenerateCounter uint64
GenerateMock mInviteServiceMockGenerate
funcGetInfo func(ctx context.Context, code int64) (ip1 *model.InviteCode, err error)
funcGetInfoOrigin string
inspectFuncGetInfo func(ctx context.Context, code int64)
afterGetInfoCounter uint64
beforeGetInfoCounter uint64
GetInfoMock mInviteServiceMockGetInfo
}
// NewInviteServiceMock returns a mock for mm_service.InviteService
func NewInviteServiceMock(t minimock.Tester) *InviteServiceMock {
m := &InviteServiceMock{t: t}
if controller, ok := t.(minimock.MockController); ok {
controller.RegisterMocker(m)
}
m.GenerateMock = mInviteServiceMockGenerate{mock: m}
m.GenerateMock.callArgs = []*InviteServiceMockGenerateParams{}
m.GetInfoMock = mInviteServiceMockGetInfo{mock: m}
m.GetInfoMock.callArgs = []*InviteServiceMockGetInfoParams{}
t.Cleanup(m.MinimockFinish)
return m
}
type mInviteServiceMockGenerate struct {
optional bool
mock *InviteServiceMock
defaultExpectation *InviteServiceMockGenerateExpectation
expectations []*InviteServiceMockGenerateExpectation
callArgs []*InviteServiceMockGenerateParams
mutex sync.RWMutex
expectedInvocations uint64
expectedInvocationsOrigin string
}
// InviteServiceMockGenerateExpectation specifies expectation struct of the InviteService.Generate
type InviteServiceMockGenerateExpectation struct {
mock *InviteServiceMock
params *InviteServiceMockGenerateParams
paramPtrs *InviteServiceMockGenerateParamPtrs
expectationOrigins InviteServiceMockGenerateExpectationOrigins
results *InviteServiceMockGenerateResults
returnOrigin string
Counter uint64
}
// InviteServiceMockGenerateParams contains parameters of the InviteService.Generate
type InviteServiceMockGenerateParams struct {
ctx context.Context
userID int
maxUses int
ttlDays int
}
// InviteServiceMockGenerateParamPtrs contains pointers to parameters of the InviteService.Generate
type InviteServiceMockGenerateParamPtrs struct {
ctx *context.Context
userID *int
maxUses *int
ttlDays *int
}
// InviteServiceMockGenerateResults contains results of the InviteService.Generate
type InviteServiceMockGenerateResults struct {
ip1 *model.InviteCode
err error
}
// InviteServiceMockGenerateOrigins contains origins of expectations of the InviteService.Generate
type InviteServiceMockGenerateExpectationOrigins struct {
origin string
originCtx string
originUserID string
originMaxUses string
originTtlDays string
}
// Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning
// the test will fail minimock's automatic final call check if the mocked method was not called at least once.
// Optional() makes method check to work in '0 or more' mode.
// It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to
// catch the problems when the expected method call is totally skipped during test run.
func (mmGenerate *mInviteServiceMockGenerate) Optional() *mInviteServiceMockGenerate {
mmGenerate.optional = true
return mmGenerate
}
// Expect sets up expected params for InviteService.Generate
func (mmGenerate *mInviteServiceMockGenerate) Expect(ctx context.Context, userID int, maxUses int, ttlDays int) *mInviteServiceMockGenerate {
if mmGenerate.mock.funcGenerate != nil {
mmGenerate.mock.t.Fatalf("InviteServiceMock.Generate mock is already set by Set")
}
if mmGenerate.defaultExpectation == nil {
mmGenerate.defaultExpectation = &InviteServiceMockGenerateExpectation{}
}
if mmGenerate.defaultExpectation.paramPtrs != nil {
mmGenerate.mock.t.Fatalf("InviteServiceMock.Generate mock is already set by ExpectParams functions")
}
mmGenerate.defaultExpectation.params = &InviteServiceMockGenerateParams{ctx, userID, maxUses, ttlDays}
mmGenerate.defaultExpectation.expectationOrigins.origin = minimock.CallerInfo(1)
for _, e := range mmGenerate.expectations {
if minimock.Equal(e.params, mmGenerate.defaultExpectation.params) {
mmGenerate.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmGenerate.defaultExpectation.params)
}
}
return mmGenerate
}
// ExpectCtxParam1 sets up expected param ctx for InviteService.Generate
func (mmGenerate *mInviteServiceMockGenerate) ExpectCtxParam1(ctx context.Context) *mInviteServiceMockGenerate {
if mmGenerate.mock.funcGenerate != nil {
mmGenerate.mock.t.Fatalf("InviteServiceMock.Generate mock is already set by Set")
}
if mmGenerate.defaultExpectation == nil {
mmGenerate.defaultExpectation = &InviteServiceMockGenerateExpectation{}
}
if mmGenerate.defaultExpectation.params != nil {
mmGenerate.mock.t.Fatalf("InviteServiceMock.Generate mock is already set by Expect")
}
if mmGenerate.defaultExpectation.paramPtrs == nil {
mmGenerate.defaultExpectation.paramPtrs = &InviteServiceMockGenerateParamPtrs{}
}
mmGenerate.defaultExpectation.paramPtrs.ctx = &ctx
mmGenerate.defaultExpectation.expectationOrigins.originCtx = minimock.CallerInfo(1)
return mmGenerate
}
// ExpectUserIDParam2 sets up expected param userID for InviteService.Generate
func (mmGenerate *mInviteServiceMockGenerate) ExpectUserIDParam2(userID int) *mInviteServiceMockGenerate {
if mmGenerate.mock.funcGenerate != nil {
mmGenerate.mock.t.Fatalf("InviteServiceMock.Generate mock is already set by Set")
}
if mmGenerate.defaultExpectation == nil {
mmGenerate.defaultExpectation = &InviteServiceMockGenerateExpectation{}
}
if mmGenerate.defaultExpectation.params != nil {
mmGenerate.mock.t.Fatalf("InviteServiceMock.Generate mock is already set by Expect")
}
if mmGenerate.defaultExpectation.paramPtrs == nil {
mmGenerate.defaultExpectation.paramPtrs = &InviteServiceMockGenerateParamPtrs{}
}
mmGenerate.defaultExpectation.paramPtrs.userID = &userID
mmGenerate.defaultExpectation.expectationOrigins.originUserID = minimock.CallerInfo(1)
return mmGenerate
}
// ExpectMaxUsesParam3 sets up expected param maxUses for InviteService.Generate
func (mmGenerate *mInviteServiceMockGenerate) ExpectMaxUsesParam3(maxUses int) *mInviteServiceMockGenerate {
if mmGenerate.mock.funcGenerate != nil {
mmGenerate.mock.t.Fatalf("InviteServiceMock.Generate mock is already set by Set")
}
if mmGenerate.defaultExpectation == nil {
mmGenerate.defaultExpectation = &InviteServiceMockGenerateExpectation{}
}
if mmGenerate.defaultExpectation.params != nil {
mmGenerate.mock.t.Fatalf("InviteServiceMock.Generate mock is already set by Expect")
}
if mmGenerate.defaultExpectation.paramPtrs == nil {
mmGenerate.defaultExpectation.paramPtrs = &InviteServiceMockGenerateParamPtrs{}
}
mmGenerate.defaultExpectation.paramPtrs.maxUses = &maxUses
mmGenerate.defaultExpectation.expectationOrigins.originMaxUses = minimock.CallerInfo(1)
return mmGenerate
}
// ExpectTtlDaysParam4 sets up expected param ttlDays for InviteService.Generate
func (mmGenerate *mInviteServiceMockGenerate) ExpectTtlDaysParam4(ttlDays int) *mInviteServiceMockGenerate {
if mmGenerate.mock.funcGenerate != nil {
mmGenerate.mock.t.Fatalf("InviteServiceMock.Generate mock is already set by Set")
}
if mmGenerate.defaultExpectation == nil {
mmGenerate.defaultExpectation = &InviteServiceMockGenerateExpectation{}
}
if mmGenerate.defaultExpectation.params != nil {
mmGenerate.mock.t.Fatalf("InviteServiceMock.Generate mock is already set by Expect")
}
if mmGenerate.defaultExpectation.paramPtrs == nil {
mmGenerate.defaultExpectation.paramPtrs = &InviteServiceMockGenerateParamPtrs{}
}
mmGenerate.defaultExpectation.paramPtrs.ttlDays = &ttlDays
mmGenerate.defaultExpectation.expectationOrigins.originTtlDays = minimock.CallerInfo(1)
return mmGenerate
}
// Inspect accepts an inspector function that has same arguments as the InviteService.Generate
func (mmGenerate *mInviteServiceMockGenerate) Inspect(f func(ctx context.Context, userID int, maxUses int, ttlDays int)) *mInviteServiceMockGenerate {
if mmGenerate.mock.inspectFuncGenerate != nil {
mmGenerate.mock.t.Fatalf("Inspect function is already set for InviteServiceMock.Generate")
}
mmGenerate.mock.inspectFuncGenerate = f
return mmGenerate
}
// Return sets up results that will be returned by InviteService.Generate
func (mmGenerate *mInviteServiceMockGenerate) Return(ip1 *model.InviteCode, err error) *InviteServiceMock {
if mmGenerate.mock.funcGenerate != nil {
mmGenerate.mock.t.Fatalf("InviteServiceMock.Generate mock is already set by Set")
}
if mmGenerate.defaultExpectation == nil {
mmGenerate.defaultExpectation = &InviteServiceMockGenerateExpectation{mock: mmGenerate.mock}
}
mmGenerate.defaultExpectation.results = &InviteServiceMockGenerateResults{ip1, err}
mmGenerate.defaultExpectation.returnOrigin = minimock.CallerInfo(1)
return mmGenerate.mock
}
// Set uses given function f to mock the InviteService.Generate method
func (mmGenerate *mInviteServiceMockGenerate) Set(f func(ctx context.Context, userID int, maxUses int, ttlDays int) (ip1 *model.InviteCode, err error)) *InviteServiceMock {
if mmGenerate.defaultExpectation != nil {
mmGenerate.mock.t.Fatalf("Default expectation is already set for the InviteService.Generate method")
}
if len(mmGenerate.expectations) > 0 {
mmGenerate.mock.t.Fatalf("Some expectations are already set for the InviteService.Generate method")
}
mmGenerate.mock.funcGenerate = f
mmGenerate.mock.funcGenerateOrigin = minimock.CallerInfo(1)
return mmGenerate.mock
}
// When sets expectation for the InviteService.Generate which will trigger the result defined by the following
// Then helper
func (mmGenerate *mInviteServiceMockGenerate) When(ctx context.Context, userID int, maxUses int, ttlDays int) *InviteServiceMockGenerateExpectation {
if mmGenerate.mock.funcGenerate != nil {
mmGenerate.mock.t.Fatalf("InviteServiceMock.Generate mock is already set by Set")
}
expectation := &InviteServiceMockGenerateExpectation{
mock: mmGenerate.mock,
params: &InviteServiceMockGenerateParams{ctx, userID, maxUses, ttlDays},
expectationOrigins: InviteServiceMockGenerateExpectationOrigins{origin: minimock.CallerInfo(1)},
}
mmGenerate.expectations = append(mmGenerate.expectations, expectation)
return expectation
}
// Then sets up InviteService.Generate return parameters for the expectation previously defined by the When method
func (e *InviteServiceMockGenerateExpectation) Then(ip1 *model.InviteCode, err error) *InviteServiceMock {
e.results = &InviteServiceMockGenerateResults{ip1, err}
return e.mock
}
// Times sets number of times InviteService.Generate should be invoked
func (mmGenerate *mInviteServiceMockGenerate) Times(n uint64) *mInviteServiceMockGenerate {
if n == 0 {
mmGenerate.mock.t.Fatalf("Times of InviteServiceMock.Generate mock can not be zero")
}
mm_atomic.StoreUint64(&mmGenerate.expectedInvocations, n)
mmGenerate.expectedInvocationsOrigin = minimock.CallerInfo(1)
return mmGenerate
}
func (mmGenerate *mInviteServiceMockGenerate) invocationsDone() bool {
if len(mmGenerate.expectations) == 0 && mmGenerate.defaultExpectation == nil && mmGenerate.mock.funcGenerate == nil {
return true
}
totalInvocations := mm_atomic.LoadUint64(&mmGenerate.mock.afterGenerateCounter)
expectedInvocations := mm_atomic.LoadUint64(&mmGenerate.expectedInvocations)
return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations)
}
// Generate implements mm_service.InviteService
func (mmGenerate *InviteServiceMock) Generate(ctx context.Context, userID int, maxUses int, ttlDays int) (ip1 *model.InviteCode, err error) {
mm_atomic.AddUint64(&mmGenerate.beforeGenerateCounter, 1)
defer mm_atomic.AddUint64(&mmGenerate.afterGenerateCounter, 1)
mmGenerate.t.Helper()
if mmGenerate.inspectFuncGenerate != nil {
mmGenerate.inspectFuncGenerate(ctx, userID, maxUses, ttlDays)
}
mm_params := InviteServiceMockGenerateParams{ctx, userID, maxUses, ttlDays}
// Record call args
mmGenerate.GenerateMock.mutex.Lock()
mmGenerate.GenerateMock.callArgs = append(mmGenerate.GenerateMock.callArgs, &mm_params)
mmGenerate.GenerateMock.mutex.Unlock()
for _, e := range mmGenerate.GenerateMock.expectations {
if minimock.Equal(*e.params, mm_params) {
mm_atomic.AddUint64(&e.Counter, 1)
return e.results.ip1, e.results.err
}
}
if mmGenerate.GenerateMock.defaultExpectation != nil {
mm_atomic.AddUint64(&mmGenerate.GenerateMock.defaultExpectation.Counter, 1)
mm_want := mmGenerate.GenerateMock.defaultExpectation.params
mm_want_ptrs := mmGenerate.GenerateMock.defaultExpectation.paramPtrs
mm_got := InviteServiceMockGenerateParams{ctx, userID, maxUses, ttlDays}
if mm_want_ptrs != nil {
if mm_want_ptrs.ctx != nil && !minimock.Equal(*mm_want_ptrs.ctx, mm_got.ctx) {
mmGenerate.t.Errorf("InviteServiceMock.Generate got unexpected parameter ctx, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmGenerate.GenerateMock.defaultExpectation.expectationOrigins.originCtx, *mm_want_ptrs.ctx, mm_got.ctx, minimock.Diff(*mm_want_ptrs.ctx, mm_got.ctx))
}
if mm_want_ptrs.userID != nil && !minimock.Equal(*mm_want_ptrs.userID, mm_got.userID) {
mmGenerate.t.Errorf("InviteServiceMock.Generate got unexpected parameter userID, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmGenerate.GenerateMock.defaultExpectation.expectationOrigins.originUserID, *mm_want_ptrs.userID, mm_got.userID, minimock.Diff(*mm_want_ptrs.userID, mm_got.userID))
}
if mm_want_ptrs.maxUses != nil && !minimock.Equal(*mm_want_ptrs.maxUses, mm_got.maxUses) {
mmGenerate.t.Errorf("InviteServiceMock.Generate got unexpected parameter maxUses, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmGenerate.GenerateMock.defaultExpectation.expectationOrigins.originMaxUses, *mm_want_ptrs.maxUses, mm_got.maxUses, minimock.Diff(*mm_want_ptrs.maxUses, mm_got.maxUses))
}
if mm_want_ptrs.ttlDays != nil && !minimock.Equal(*mm_want_ptrs.ttlDays, mm_got.ttlDays) {
mmGenerate.t.Errorf("InviteServiceMock.Generate got unexpected parameter ttlDays, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmGenerate.GenerateMock.defaultExpectation.expectationOrigins.originTtlDays, *mm_want_ptrs.ttlDays, mm_got.ttlDays, minimock.Diff(*mm_want_ptrs.ttlDays, mm_got.ttlDays))
}
} else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) {
mmGenerate.t.Errorf("InviteServiceMock.Generate got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmGenerate.GenerateMock.defaultExpectation.expectationOrigins.origin, *mm_want, mm_got, minimock.Diff(*mm_want, mm_got))
}
mm_results := mmGenerate.GenerateMock.defaultExpectation.results
if mm_results == nil {
mmGenerate.t.Fatal("No results are set for the InviteServiceMock.Generate")
}
return (*mm_results).ip1, (*mm_results).err
}
if mmGenerate.funcGenerate != nil {
return mmGenerate.funcGenerate(ctx, userID, maxUses, ttlDays)
}
mmGenerate.t.Fatalf("Unexpected call to InviteServiceMock.Generate. %v %v %v %v", ctx, userID, maxUses, ttlDays)
return
}
// GenerateAfterCounter returns a count of finished InviteServiceMock.Generate invocations
func (mmGenerate *InviteServiceMock) GenerateAfterCounter() uint64 {
return mm_atomic.LoadUint64(&mmGenerate.afterGenerateCounter)
}
// GenerateBeforeCounter returns a count of InviteServiceMock.Generate invocations
func (mmGenerate *InviteServiceMock) GenerateBeforeCounter() uint64 {
return mm_atomic.LoadUint64(&mmGenerate.beforeGenerateCounter)
}
// Calls returns a list of arguments used in each call to InviteServiceMock.Generate.
// The list is in the same order as the calls were made (i.e. recent calls have a higher index)
func (mmGenerate *mInviteServiceMockGenerate) Calls() []*InviteServiceMockGenerateParams {
mmGenerate.mutex.RLock()
argCopy := make([]*InviteServiceMockGenerateParams, len(mmGenerate.callArgs))
copy(argCopy, mmGenerate.callArgs)
mmGenerate.mutex.RUnlock()
return argCopy
}
// MinimockGenerateDone returns true if the count of the Generate invocations corresponds
// the number of defined expectations
func (m *InviteServiceMock) MinimockGenerateDone() bool {
if m.GenerateMock.optional {
// Optional methods provide '0 or more' call count restriction.
return true
}
for _, e := range m.GenerateMock.expectations {
if mm_atomic.LoadUint64(&e.Counter) < 1 {
return false
}
}
return m.GenerateMock.invocationsDone()
}
// MinimockGenerateInspect logs each unmet expectation
func (m *InviteServiceMock) MinimockGenerateInspect() {
for _, e := range m.GenerateMock.expectations {
if mm_atomic.LoadUint64(&e.Counter) < 1 {
m.t.Errorf("Expected call to InviteServiceMock.Generate at\n%s with params: %#v", e.expectationOrigins.origin, *e.params)
}
}
afterGenerateCounter := mm_atomic.LoadUint64(&m.afterGenerateCounter)
// if default expectation was set then invocations count should be greater than zero
if m.GenerateMock.defaultExpectation != nil && afterGenerateCounter < 1 {
if m.GenerateMock.defaultExpectation.params == nil {
m.t.Errorf("Expected call to InviteServiceMock.Generate at\n%s", m.GenerateMock.defaultExpectation.returnOrigin)
} else {
m.t.Errorf("Expected call to InviteServiceMock.Generate at\n%s with params: %#v", m.GenerateMock.defaultExpectation.expectationOrigins.origin, *m.GenerateMock.defaultExpectation.params)
}
}
// if func was set then invocations count should be greater than zero
if m.funcGenerate != nil && afterGenerateCounter < 1 {
m.t.Errorf("Expected call to InviteServiceMock.Generate at\n%s", m.funcGenerateOrigin)
}
if !m.GenerateMock.invocationsDone() && afterGenerateCounter > 0 {
m.t.Errorf("Expected %d calls to InviteServiceMock.Generate at\n%s but found %d calls",
mm_atomic.LoadUint64(&m.GenerateMock.expectedInvocations), m.GenerateMock.expectedInvocationsOrigin, afterGenerateCounter)
}
}
type mInviteServiceMockGetInfo struct {
optional bool
mock *InviteServiceMock
defaultExpectation *InviteServiceMockGetInfoExpectation
expectations []*InviteServiceMockGetInfoExpectation
callArgs []*InviteServiceMockGetInfoParams
mutex sync.RWMutex
expectedInvocations uint64
expectedInvocationsOrigin string
}
// InviteServiceMockGetInfoExpectation specifies expectation struct of the InviteService.GetInfo
type InviteServiceMockGetInfoExpectation struct {
mock *InviteServiceMock
params *InviteServiceMockGetInfoParams
paramPtrs *InviteServiceMockGetInfoParamPtrs
expectationOrigins InviteServiceMockGetInfoExpectationOrigins
results *InviteServiceMockGetInfoResults
returnOrigin string
Counter uint64
}
// InviteServiceMockGetInfoParams contains parameters of the InviteService.GetInfo
type InviteServiceMockGetInfoParams struct {
ctx context.Context
code int64
}
// InviteServiceMockGetInfoParamPtrs contains pointers to parameters of the InviteService.GetInfo
type InviteServiceMockGetInfoParamPtrs struct {
ctx *context.Context
code *int64
}
// InviteServiceMockGetInfoResults contains results of the InviteService.GetInfo
type InviteServiceMockGetInfoResults struct {
ip1 *model.InviteCode
err error
}
// InviteServiceMockGetInfoOrigins contains origins of expectations of the InviteService.GetInfo
type InviteServiceMockGetInfoExpectationOrigins struct {
origin string
originCtx string
originCode string
}
// Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning
// the test will fail minimock's automatic final call check if the mocked method was not called at least once.
// Optional() makes method check to work in '0 or more' mode.
// It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to
// catch the problems when the expected method call is totally skipped during test run.
func (mmGetInfo *mInviteServiceMockGetInfo) Optional() *mInviteServiceMockGetInfo {
mmGetInfo.optional = true
return mmGetInfo
}
// Expect sets up expected params for InviteService.GetInfo
func (mmGetInfo *mInviteServiceMockGetInfo) Expect(ctx context.Context, code int64) *mInviteServiceMockGetInfo {
if mmGetInfo.mock.funcGetInfo != nil {
mmGetInfo.mock.t.Fatalf("InviteServiceMock.GetInfo mock is already set by Set")
}
if mmGetInfo.defaultExpectation == nil {
mmGetInfo.defaultExpectation = &InviteServiceMockGetInfoExpectation{}
}
if mmGetInfo.defaultExpectation.paramPtrs != nil {
mmGetInfo.mock.t.Fatalf("InviteServiceMock.GetInfo mock is already set by ExpectParams functions")
}
mmGetInfo.defaultExpectation.params = &InviteServiceMockGetInfoParams{ctx, code}
mmGetInfo.defaultExpectation.expectationOrigins.origin = minimock.CallerInfo(1)
for _, e := range mmGetInfo.expectations {
if minimock.Equal(e.params, mmGetInfo.defaultExpectation.params) {
mmGetInfo.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmGetInfo.defaultExpectation.params)
}
}
return mmGetInfo
}
// ExpectCtxParam1 sets up expected param ctx for InviteService.GetInfo
func (mmGetInfo *mInviteServiceMockGetInfo) ExpectCtxParam1(ctx context.Context) *mInviteServiceMockGetInfo {
if mmGetInfo.mock.funcGetInfo != nil {
mmGetInfo.mock.t.Fatalf("InviteServiceMock.GetInfo mock is already set by Set")
}
if mmGetInfo.defaultExpectation == nil {
mmGetInfo.defaultExpectation = &InviteServiceMockGetInfoExpectation{}
}
if mmGetInfo.defaultExpectation.params != nil {
mmGetInfo.mock.t.Fatalf("InviteServiceMock.GetInfo mock is already set by Expect")
}
if mmGetInfo.defaultExpectation.paramPtrs == nil {
mmGetInfo.defaultExpectation.paramPtrs = &InviteServiceMockGetInfoParamPtrs{}
}
mmGetInfo.defaultExpectation.paramPtrs.ctx = &ctx
mmGetInfo.defaultExpectation.expectationOrigins.originCtx = minimock.CallerInfo(1)
return mmGetInfo
}
// ExpectCodeParam2 sets up expected param code for InviteService.GetInfo
func (mmGetInfo *mInviteServiceMockGetInfo) ExpectCodeParam2(code int64) *mInviteServiceMockGetInfo {
if mmGetInfo.mock.funcGetInfo != nil {
mmGetInfo.mock.t.Fatalf("InviteServiceMock.GetInfo mock is already set by Set")
}
if mmGetInfo.defaultExpectation == nil {
mmGetInfo.defaultExpectation = &InviteServiceMockGetInfoExpectation{}
}
if mmGetInfo.defaultExpectation.params != nil {
mmGetInfo.mock.t.Fatalf("InviteServiceMock.GetInfo mock is already set by Expect")
}
if mmGetInfo.defaultExpectation.paramPtrs == nil {
mmGetInfo.defaultExpectation.paramPtrs = &InviteServiceMockGetInfoParamPtrs{}
}
mmGetInfo.defaultExpectation.paramPtrs.code = &code
mmGetInfo.defaultExpectation.expectationOrigins.originCode = minimock.CallerInfo(1)
return mmGetInfo
}
// Inspect accepts an inspector function that has same arguments as the InviteService.GetInfo
func (mmGetInfo *mInviteServiceMockGetInfo) Inspect(f func(ctx context.Context, code int64)) *mInviteServiceMockGetInfo {
if mmGetInfo.mock.inspectFuncGetInfo != nil {
mmGetInfo.mock.t.Fatalf("Inspect function is already set for InviteServiceMock.GetInfo")
}
mmGetInfo.mock.inspectFuncGetInfo = f
return mmGetInfo
}
// Return sets up results that will be returned by InviteService.GetInfo
func (mmGetInfo *mInviteServiceMockGetInfo) Return(ip1 *model.InviteCode, err error) *InviteServiceMock {
if mmGetInfo.mock.funcGetInfo != nil {
mmGetInfo.mock.t.Fatalf("InviteServiceMock.GetInfo mock is already set by Set")
}
if mmGetInfo.defaultExpectation == nil {
mmGetInfo.defaultExpectation = &InviteServiceMockGetInfoExpectation{mock: mmGetInfo.mock}
}
mmGetInfo.defaultExpectation.results = &InviteServiceMockGetInfoResults{ip1, err}
mmGetInfo.defaultExpectation.returnOrigin = minimock.CallerInfo(1)
return mmGetInfo.mock
}
// Set uses given function f to mock the InviteService.GetInfo method
func (mmGetInfo *mInviteServiceMockGetInfo) Set(f func(ctx context.Context, code int64) (ip1 *model.InviteCode, err error)) *InviteServiceMock {
if mmGetInfo.defaultExpectation != nil {
mmGetInfo.mock.t.Fatalf("Default expectation is already set for the InviteService.GetInfo method")
}
if len(mmGetInfo.expectations) > 0 {
mmGetInfo.mock.t.Fatalf("Some expectations are already set for the InviteService.GetInfo method")
}
mmGetInfo.mock.funcGetInfo = f
mmGetInfo.mock.funcGetInfoOrigin = minimock.CallerInfo(1)
return mmGetInfo.mock
}
// When sets expectation for the InviteService.GetInfo which will trigger the result defined by the following
// Then helper
func (mmGetInfo *mInviteServiceMockGetInfo) When(ctx context.Context, code int64) *InviteServiceMockGetInfoExpectation {
if mmGetInfo.mock.funcGetInfo != nil {
mmGetInfo.mock.t.Fatalf("InviteServiceMock.GetInfo mock is already set by Set")
}
expectation := &InviteServiceMockGetInfoExpectation{
mock: mmGetInfo.mock,
params: &InviteServiceMockGetInfoParams{ctx, code},
expectationOrigins: InviteServiceMockGetInfoExpectationOrigins{origin: minimock.CallerInfo(1)},
}
mmGetInfo.expectations = append(mmGetInfo.expectations, expectation)
return expectation
}
// Then sets up InviteService.GetInfo return parameters for the expectation previously defined by the When method
func (e *InviteServiceMockGetInfoExpectation) Then(ip1 *model.InviteCode, err error) *InviteServiceMock {
e.results = &InviteServiceMockGetInfoResults{ip1, err}
return e.mock
}
// Times sets number of times InviteService.GetInfo should be invoked
func (mmGetInfo *mInviteServiceMockGetInfo) Times(n uint64) *mInviteServiceMockGetInfo {
if n == 0 {
mmGetInfo.mock.t.Fatalf("Times of InviteServiceMock.GetInfo mock can not be zero")
}
mm_atomic.StoreUint64(&mmGetInfo.expectedInvocations, n)
mmGetInfo.expectedInvocationsOrigin = minimock.CallerInfo(1)
return mmGetInfo
}
func (mmGetInfo *mInviteServiceMockGetInfo) invocationsDone() bool {
if len(mmGetInfo.expectations) == 0 && mmGetInfo.defaultExpectation == nil && mmGetInfo.mock.funcGetInfo == nil {
return true
}
totalInvocations := mm_atomic.LoadUint64(&mmGetInfo.mock.afterGetInfoCounter)
expectedInvocations := mm_atomic.LoadUint64(&mmGetInfo.expectedInvocations)
return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations)
}
// GetInfo implements mm_service.InviteService
func (mmGetInfo *InviteServiceMock) GetInfo(ctx context.Context, code int64) (ip1 *model.InviteCode, err error) {
mm_atomic.AddUint64(&mmGetInfo.beforeGetInfoCounter, 1)
defer mm_atomic.AddUint64(&mmGetInfo.afterGetInfoCounter, 1)
mmGetInfo.t.Helper()
if mmGetInfo.inspectFuncGetInfo != nil {
mmGetInfo.inspectFuncGetInfo(ctx, code)
}
mm_params := InviteServiceMockGetInfoParams{ctx, code}
// Record call args
mmGetInfo.GetInfoMock.mutex.Lock()
mmGetInfo.GetInfoMock.callArgs = append(mmGetInfo.GetInfoMock.callArgs, &mm_params)
mmGetInfo.GetInfoMock.mutex.Unlock()
for _, e := range mmGetInfo.GetInfoMock.expectations {
if minimock.Equal(*e.params, mm_params) {
mm_atomic.AddUint64(&e.Counter, 1)
return e.results.ip1, e.results.err
}
}
if mmGetInfo.GetInfoMock.defaultExpectation != nil {
mm_atomic.AddUint64(&mmGetInfo.GetInfoMock.defaultExpectation.Counter, 1)
mm_want := mmGetInfo.GetInfoMock.defaultExpectation.params
mm_want_ptrs := mmGetInfo.GetInfoMock.defaultExpectation.paramPtrs
mm_got := InviteServiceMockGetInfoParams{ctx, code}
if mm_want_ptrs != nil {
if mm_want_ptrs.ctx != nil && !minimock.Equal(*mm_want_ptrs.ctx, mm_got.ctx) {
mmGetInfo.t.Errorf("InviteServiceMock.GetInfo got unexpected parameter ctx, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmGetInfo.GetInfoMock.defaultExpectation.expectationOrigins.originCtx, *mm_want_ptrs.ctx, mm_got.ctx, minimock.Diff(*mm_want_ptrs.ctx, mm_got.ctx))
}
if mm_want_ptrs.code != nil && !minimock.Equal(*mm_want_ptrs.code, mm_got.code) {
mmGetInfo.t.Errorf("InviteServiceMock.GetInfo got unexpected parameter code, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmGetInfo.GetInfoMock.defaultExpectation.expectationOrigins.originCode, *mm_want_ptrs.code, mm_got.code, minimock.Diff(*mm_want_ptrs.code, mm_got.code))
}
} else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) {
mmGetInfo.t.Errorf("InviteServiceMock.GetInfo got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmGetInfo.GetInfoMock.defaultExpectation.expectationOrigins.origin, *mm_want, mm_got, minimock.Diff(*mm_want, mm_got))
}
mm_results := mmGetInfo.GetInfoMock.defaultExpectation.results
if mm_results == nil {
mmGetInfo.t.Fatal("No results are set for the InviteServiceMock.GetInfo")
}
return (*mm_results).ip1, (*mm_results).err
}
if mmGetInfo.funcGetInfo != nil {
return mmGetInfo.funcGetInfo(ctx, code)
}
mmGetInfo.t.Fatalf("Unexpected call to InviteServiceMock.GetInfo. %v %v", ctx, code)
return
}
// GetInfoAfterCounter returns a count of finished InviteServiceMock.GetInfo invocations
func (mmGetInfo *InviteServiceMock) GetInfoAfterCounter() uint64 {
return mm_atomic.LoadUint64(&mmGetInfo.afterGetInfoCounter)
}
// GetInfoBeforeCounter returns a count of InviteServiceMock.GetInfo invocations
func (mmGetInfo *InviteServiceMock) GetInfoBeforeCounter() uint64 {
return mm_atomic.LoadUint64(&mmGetInfo.beforeGetInfoCounter)
}
// Calls returns a list of arguments used in each call to InviteServiceMock.GetInfo.
// The list is in the same order as the calls were made (i.e. recent calls have a higher index)
func (mmGetInfo *mInviteServiceMockGetInfo) Calls() []*InviteServiceMockGetInfoParams {
mmGetInfo.mutex.RLock()
argCopy := make([]*InviteServiceMockGetInfoParams, len(mmGetInfo.callArgs))
copy(argCopy, mmGetInfo.callArgs)
mmGetInfo.mutex.RUnlock()
return argCopy
}
// MinimockGetInfoDone returns true if the count of the GetInfo invocations corresponds
// the number of defined expectations
func (m *InviteServiceMock) MinimockGetInfoDone() bool {
if m.GetInfoMock.optional {
// Optional methods provide '0 or more' call count restriction.
return true
}
for _, e := range m.GetInfoMock.expectations {
if mm_atomic.LoadUint64(&e.Counter) < 1 {
return false
}
}
return m.GetInfoMock.invocationsDone()
}
// MinimockGetInfoInspect logs each unmet expectation
func (m *InviteServiceMock) MinimockGetInfoInspect() {
for _, e := range m.GetInfoMock.expectations {
if mm_atomic.LoadUint64(&e.Counter) < 1 {
m.t.Errorf("Expected call to InviteServiceMock.GetInfo at\n%s with params: %#v", e.expectationOrigins.origin, *e.params)
}
}
afterGetInfoCounter := mm_atomic.LoadUint64(&m.afterGetInfoCounter)
// if default expectation was set then invocations count should be greater than zero
if m.GetInfoMock.defaultExpectation != nil && afterGetInfoCounter < 1 {
if m.GetInfoMock.defaultExpectation.params == nil {
m.t.Errorf("Expected call to InviteServiceMock.GetInfo at\n%s", m.GetInfoMock.defaultExpectation.returnOrigin)
} else {
m.t.Errorf("Expected call to InviteServiceMock.GetInfo at\n%s with params: %#v", m.GetInfoMock.defaultExpectation.expectationOrigins.origin, *m.GetInfoMock.defaultExpectation.params)
}
}
// if func was set then invocations count should be greater than zero
if m.funcGetInfo != nil && afterGetInfoCounter < 1 {
m.t.Errorf("Expected call to InviteServiceMock.GetInfo at\n%s", m.funcGetInfoOrigin)
}
if !m.GetInfoMock.invocationsDone() && afterGetInfoCounter > 0 {
m.t.Errorf("Expected %d calls to InviteServiceMock.GetInfo at\n%s but found %d calls",
mm_atomic.LoadUint64(&m.GetInfoMock.expectedInvocations), m.GetInfoMock.expectedInvocationsOrigin, afterGetInfoCounter)
}
}
// MinimockFinish checks that all mocked methods have been called the expected number of times
func (m *InviteServiceMock) MinimockFinish() {
m.finishOnce.Do(func() {
if !m.minimockDone() {
m.MinimockGenerateInspect()
m.MinimockGetInfoInspect()
}
})
}
// MinimockWait waits for all mocked methods to be called the expected number of times
func (m *InviteServiceMock) MinimockWait(timeout mm_time.Duration) {
timeoutCh := mm_time.After(timeout)
for {
if m.minimockDone() {
return
}
select {
case <-timeoutCh:
m.MinimockFinish()
return
case <-mm_time.After(10 * mm_time.Millisecond):
}
}
}
func (m *InviteServiceMock) minimockDone() bool {
done := true
return done &&
m.MinimockGenerateDone() &&
m.MinimockGetInfoDone()
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,418 @@
// Code generated by http://github.com/gojuno/minimock (v3.4.7). DO NOT EDIT.
package mocks
//go:generate minimock -i smart-search-back/internal/service.SupplierService -o supplier_service_mock.go -n SupplierServiceMock -p mocks
import (
"context"
"sync"
mm_atomic "sync/atomic"
mm_time "time"
"github.com/gojuno/minimock/v3"
"github.com/google/uuid"
)
// SupplierServiceMock implements mm_service.SupplierService
type SupplierServiceMock struct {
t minimock.Tester
finishOnce sync.Once
funcExportExcel func(ctx context.Context, requestID uuid.UUID) (ba1 []byte, err error)
funcExportExcelOrigin string
inspectFuncExportExcel func(ctx context.Context, requestID uuid.UUID)
afterExportExcelCounter uint64
beforeExportExcelCounter uint64
ExportExcelMock mSupplierServiceMockExportExcel
}
// NewSupplierServiceMock returns a mock for mm_service.SupplierService
func NewSupplierServiceMock(t minimock.Tester) *SupplierServiceMock {
m := &SupplierServiceMock{t: t}
if controller, ok := t.(minimock.MockController); ok {
controller.RegisterMocker(m)
}
m.ExportExcelMock = mSupplierServiceMockExportExcel{mock: m}
m.ExportExcelMock.callArgs = []*SupplierServiceMockExportExcelParams{}
t.Cleanup(m.MinimockFinish)
return m
}
type mSupplierServiceMockExportExcel struct {
optional bool
mock *SupplierServiceMock
defaultExpectation *SupplierServiceMockExportExcelExpectation
expectations []*SupplierServiceMockExportExcelExpectation
callArgs []*SupplierServiceMockExportExcelParams
mutex sync.RWMutex
expectedInvocations uint64
expectedInvocationsOrigin string
}
// SupplierServiceMockExportExcelExpectation specifies expectation struct of the SupplierService.ExportExcel
type SupplierServiceMockExportExcelExpectation struct {
mock *SupplierServiceMock
params *SupplierServiceMockExportExcelParams
paramPtrs *SupplierServiceMockExportExcelParamPtrs
expectationOrigins SupplierServiceMockExportExcelExpectationOrigins
results *SupplierServiceMockExportExcelResults
returnOrigin string
Counter uint64
}
// SupplierServiceMockExportExcelParams contains parameters of the SupplierService.ExportExcel
type SupplierServiceMockExportExcelParams struct {
ctx context.Context
requestID uuid.UUID
}
// SupplierServiceMockExportExcelParamPtrs contains pointers to parameters of the SupplierService.ExportExcel
type SupplierServiceMockExportExcelParamPtrs struct {
ctx *context.Context
requestID *uuid.UUID
}
// SupplierServiceMockExportExcelResults contains results of the SupplierService.ExportExcel
type SupplierServiceMockExportExcelResults struct {
ba1 []byte
err error
}
// SupplierServiceMockExportExcelOrigins contains origins of expectations of the SupplierService.ExportExcel
type SupplierServiceMockExportExcelExpectationOrigins struct {
origin string
originCtx string
originRequestID string
}
// Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning
// the test will fail minimock's automatic final call check if the mocked method was not called at least once.
// Optional() makes method check to work in '0 or more' mode.
// It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to
// catch the problems when the expected method call is totally skipped during test run.
func (mmExportExcel *mSupplierServiceMockExportExcel) Optional() *mSupplierServiceMockExportExcel {
mmExportExcel.optional = true
return mmExportExcel
}
// Expect sets up expected params for SupplierService.ExportExcel
func (mmExportExcel *mSupplierServiceMockExportExcel) Expect(ctx context.Context, requestID uuid.UUID) *mSupplierServiceMockExportExcel {
if mmExportExcel.mock.funcExportExcel != nil {
mmExportExcel.mock.t.Fatalf("SupplierServiceMock.ExportExcel mock is already set by Set")
}
if mmExportExcel.defaultExpectation == nil {
mmExportExcel.defaultExpectation = &SupplierServiceMockExportExcelExpectation{}
}
if mmExportExcel.defaultExpectation.paramPtrs != nil {
mmExportExcel.mock.t.Fatalf("SupplierServiceMock.ExportExcel mock is already set by ExpectParams functions")
}
mmExportExcel.defaultExpectation.params = &SupplierServiceMockExportExcelParams{ctx, requestID}
mmExportExcel.defaultExpectation.expectationOrigins.origin = minimock.CallerInfo(1)
for _, e := range mmExportExcel.expectations {
if minimock.Equal(e.params, mmExportExcel.defaultExpectation.params) {
mmExportExcel.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmExportExcel.defaultExpectation.params)
}
}
return mmExportExcel
}
// ExpectCtxParam1 sets up expected param ctx for SupplierService.ExportExcel
func (mmExportExcel *mSupplierServiceMockExportExcel) ExpectCtxParam1(ctx context.Context) *mSupplierServiceMockExportExcel {
if mmExportExcel.mock.funcExportExcel != nil {
mmExportExcel.mock.t.Fatalf("SupplierServiceMock.ExportExcel mock is already set by Set")
}
if mmExportExcel.defaultExpectation == nil {
mmExportExcel.defaultExpectation = &SupplierServiceMockExportExcelExpectation{}
}
if mmExportExcel.defaultExpectation.params != nil {
mmExportExcel.mock.t.Fatalf("SupplierServiceMock.ExportExcel mock is already set by Expect")
}
if mmExportExcel.defaultExpectation.paramPtrs == nil {
mmExportExcel.defaultExpectation.paramPtrs = &SupplierServiceMockExportExcelParamPtrs{}
}
mmExportExcel.defaultExpectation.paramPtrs.ctx = &ctx
mmExportExcel.defaultExpectation.expectationOrigins.originCtx = minimock.CallerInfo(1)
return mmExportExcel
}
// ExpectRequestIDParam2 sets up expected param requestID for SupplierService.ExportExcel
func (mmExportExcel *mSupplierServiceMockExportExcel) ExpectRequestIDParam2(requestID uuid.UUID) *mSupplierServiceMockExportExcel {
if mmExportExcel.mock.funcExportExcel != nil {
mmExportExcel.mock.t.Fatalf("SupplierServiceMock.ExportExcel mock is already set by Set")
}
if mmExportExcel.defaultExpectation == nil {
mmExportExcel.defaultExpectation = &SupplierServiceMockExportExcelExpectation{}
}
if mmExportExcel.defaultExpectation.params != nil {
mmExportExcel.mock.t.Fatalf("SupplierServiceMock.ExportExcel mock is already set by Expect")
}
if mmExportExcel.defaultExpectation.paramPtrs == nil {
mmExportExcel.defaultExpectation.paramPtrs = &SupplierServiceMockExportExcelParamPtrs{}
}
mmExportExcel.defaultExpectation.paramPtrs.requestID = &requestID
mmExportExcel.defaultExpectation.expectationOrigins.originRequestID = minimock.CallerInfo(1)
return mmExportExcel
}
// Inspect accepts an inspector function that has same arguments as the SupplierService.ExportExcel
func (mmExportExcel *mSupplierServiceMockExportExcel) Inspect(f func(ctx context.Context, requestID uuid.UUID)) *mSupplierServiceMockExportExcel {
if mmExportExcel.mock.inspectFuncExportExcel != nil {
mmExportExcel.mock.t.Fatalf("Inspect function is already set for SupplierServiceMock.ExportExcel")
}
mmExportExcel.mock.inspectFuncExportExcel = f
return mmExportExcel
}
// Return sets up results that will be returned by SupplierService.ExportExcel
func (mmExportExcel *mSupplierServiceMockExportExcel) Return(ba1 []byte, err error) *SupplierServiceMock {
if mmExportExcel.mock.funcExportExcel != nil {
mmExportExcel.mock.t.Fatalf("SupplierServiceMock.ExportExcel mock is already set by Set")
}
if mmExportExcel.defaultExpectation == nil {
mmExportExcel.defaultExpectation = &SupplierServiceMockExportExcelExpectation{mock: mmExportExcel.mock}
}
mmExportExcel.defaultExpectation.results = &SupplierServiceMockExportExcelResults{ba1, err}
mmExportExcel.defaultExpectation.returnOrigin = minimock.CallerInfo(1)
return mmExportExcel.mock
}
// Set uses given function f to mock the SupplierService.ExportExcel method
func (mmExportExcel *mSupplierServiceMockExportExcel) Set(f func(ctx context.Context, requestID uuid.UUID) (ba1 []byte, err error)) *SupplierServiceMock {
if mmExportExcel.defaultExpectation != nil {
mmExportExcel.mock.t.Fatalf("Default expectation is already set for the SupplierService.ExportExcel method")
}
if len(mmExportExcel.expectations) > 0 {
mmExportExcel.mock.t.Fatalf("Some expectations are already set for the SupplierService.ExportExcel method")
}
mmExportExcel.mock.funcExportExcel = f
mmExportExcel.mock.funcExportExcelOrigin = minimock.CallerInfo(1)
return mmExportExcel.mock
}
// When sets expectation for the SupplierService.ExportExcel which will trigger the result defined by the following
// Then helper
func (mmExportExcel *mSupplierServiceMockExportExcel) When(ctx context.Context, requestID uuid.UUID) *SupplierServiceMockExportExcelExpectation {
if mmExportExcel.mock.funcExportExcel != nil {
mmExportExcel.mock.t.Fatalf("SupplierServiceMock.ExportExcel mock is already set by Set")
}
expectation := &SupplierServiceMockExportExcelExpectation{
mock: mmExportExcel.mock,
params: &SupplierServiceMockExportExcelParams{ctx, requestID},
expectationOrigins: SupplierServiceMockExportExcelExpectationOrigins{origin: minimock.CallerInfo(1)},
}
mmExportExcel.expectations = append(mmExportExcel.expectations, expectation)
return expectation
}
// Then sets up SupplierService.ExportExcel return parameters for the expectation previously defined by the When method
func (e *SupplierServiceMockExportExcelExpectation) Then(ba1 []byte, err error) *SupplierServiceMock {
e.results = &SupplierServiceMockExportExcelResults{ba1, err}
return e.mock
}
// Times sets number of times SupplierService.ExportExcel should be invoked
func (mmExportExcel *mSupplierServiceMockExportExcel) Times(n uint64) *mSupplierServiceMockExportExcel {
if n == 0 {
mmExportExcel.mock.t.Fatalf("Times of SupplierServiceMock.ExportExcel mock can not be zero")
}
mm_atomic.StoreUint64(&mmExportExcel.expectedInvocations, n)
mmExportExcel.expectedInvocationsOrigin = minimock.CallerInfo(1)
return mmExportExcel
}
func (mmExportExcel *mSupplierServiceMockExportExcel) invocationsDone() bool {
if len(mmExportExcel.expectations) == 0 && mmExportExcel.defaultExpectation == nil && mmExportExcel.mock.funcExportExcel == nil {
return true
}
totalInvocations := mm_atomic.LoadUint64(&mmExportExcel.mock.afterExportExcelCounter)
expectedInvocations := mm_atomic.LoadUint64(&mmExportExcel.expectedInvocations)
return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations)
}
// ExportExcel implements mm_service.SupplierService
func (mmExportExcel *SupplierServiceMock) ExportExcel(ctx context.Context, requestID uuid.UUID) (ba1 []byte, err error) {
mm_atomic.AddUint64(&mmExportExcel.beforeExportExcelCounter, 1)
defer mm_atomic.AddUint64(&mmExportExcel.afterExportExcelCounter, 1)
mmExportExcel.t.Helper()
if mmExportExcel.inspectFuncExportExcel != nil {
mmExportExcel.inspectFuncExportExcel(ctx, requestID)
}
mm_params := SupplierServiceMockExportExcelParams{ctx, requestID}
// Record call args
mmExportExcel.ExportExcelMock.mutex.Lock()
mmExportExcel.ExportExcelMock.callArgs = append(mmExportExcel.ExportExcelMock.callArgs, &mm_params)
mmExportExcel.ExportExcelMock.mutex.Unlock()
for _, e := range mmExportExcel.ExportExcelMock.expectations {
if minimock.Equal(*e.params, mm_params) {
mm_atomic.AddUint64(&e.Counter, 1)
return e.results.ba1, e.results.err
}
}
if mmExportExcel.ExportExcelMock.defaultExpectation != nil {
mm_atomic.AddUint64(&mmExportExcel.ExportExcelMock.defaultExpectation.Counter, 1)
mm_want := mmExportExcel.ExportExcelMock.defaultExpectation.params
mm_want_ptrs := mmExportExcel.ExportExcelMock.defaultExpectation.paramPtrs
mm_got := SupplierServiceMockExportExcelParams{ctx, requestID}
if mm_want_ptrs != nil {
if mm_want_ptrs.ctx != nil && !minimock.Equal(*mm_want_ptrs.ctx, mm_got.ctx) {
mmExportExcel.t.Errorf("SupplierServiceMock.ExportExcel got unexpected parameter ctx, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmExportExcel.ExportExcelMock.defaultExpectation.expectationOrigins.originCtx, *mm_want_ptrs.ctx, mm_got.ctx, minimock.Diff(*mm_want_ptrs.ctx, mm_got.ctx))
}
if mm_want_ptrs.requestID != nil && !minimock.Equal(*mm_want_ptrs.requestID, mm_got.requestID) {
mmExportExcel.t.Errorf("SupplierServiceMock.ExportExcel got unexpected parameter requestID, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmExportExcel.ExportExcelMock.defaultExpectation.expectationOrigins.originRequestID, *mm_want_ptrs.requestID, mm_got.requestID, minimock.Diff(*mm_want_ptrs.requestID, mm_got.requestID))
}
} else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) {
mmExportExcel.t.Errorf("SupplierServiceMock.ExportExcel got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmExportExcel.ExportExcelMock.defaultExpectation.expectationOrigins.origin, *mm_want, mm_got, minimock.Diff(*mm_want, mm_got))
}
mm_results := mmExportExcel.ExportExcelMock.defaultExpectation.results
if mm_results == nil {
mmExportExcel.t.Fatal("No results are set for the SupplierServiceMock.ExportExcel")
}
return (*mm_results).ba1, (*mm_results).err
}
if mmExportExcel.funcExportExcel != nil {
return mmExportExcel.funcExportExcel(ctx, requestID)
}
mmExportExcel.t.Fatalf("Unexpected call to SupplierServiceMock.ExportExcel. %v %v", ctx, requestID)
return
}
// ExportExcelAfterCounter returns a count of finished SupplierServiceMock.ExportExcel invocations
func (mmExportExcel *SupplierServiceMock) ExportExcelAfterCounter() uint64 {
return mm_atomic.LoadUint64(&mmExportExcel.afterExportExcelCounter)
}
// ExportExcelBeforeCounter returns a count of SupplierServiceMock.ExportExcel invocations
func (mmExportExcel *SupplierServiceMock) ExportExcelBeforeCounter() uint64 {
return mm_atomic.LoadUint64(&mmExportExcel.beforeExportExcelCounter)
}
// Calls returns a list of arguments used in each call to SupplierServiceMock.ExportExcel.
// The list is in the same order as the calls were made (i.e. recent calls have a higher index)
func (mmExportExcel *mSupplierServiceMockExportExcel) Calls() []*SupplierServiceMockExportExcelParams {
mmExportExcel.mutex.RLock()
argCopy := make([]*SupplierServiceMockExportExcelParams, len(mmExportExcel.callArgs))
copy(argCopy, mmExportExcel.callArgs)
mmExportExcel.mutex.RUnlock()
return argCopy
}
// MinimockExportExcelDone returns true if the count of the ExportExcel invocations corresponds
// the number of defined expectations
func (m *SupplierServiceMock) MinimockExportExcelDone() bool {
if m.ExportExcelMock.optional {
// Optional methods provide '0 or more' call count restriction.
return true
}
for _, e := range m.ExportExcelMock.expectations {
if mm_atomic.LoadUint64(&e.Counter) < 1 {
return false
}
}
return m.ExportExcelMock.invocationsDone()
}
// MinimockExportExcelInspect logs each unmet expectation
func (m *SupplierServiceMock) MinimockExportExcelInspect() {
for _, e := range m.ExportExcelMock.expectations {
if mm_atomic.LoadUint64(&e.Counter) < 1 {
m.t.Errorf("Expected call to SupplierServiceMock.ExportExcel at\n%s with params: %#v", e.expectationOrigins.origin, *e.params)
}
}
afterExportExcelCounter := mm_atomic.LoadUint64(&m.afterExportExcelCounter)
// if default expectation was set then invocations count should be greater than zero
if m.ExportExcelMock.defaultExpectation != nil && afterExportExcelCounter < 1 {
if m.ExportExcelMock.defaultExpectation.params == nil {
m.t.Errorf("Expected call to SupplierServiceMock.ExportExcel at\n%s", m.ExportExcelMock.defaultExpectation.returnOrigin)
} else {
m.t.Errorf("Expected call to SupplierServiceMock.ExportExcel at\n%s with params: %#v", m.ExportExcelMock.defaultExpectation.expectationOrigins.origin, *m.ExportExcelMock.defaultExpectation.params)
}
}
// if func was set then invocations count should be greater than zero
if m.funcExportExcel != nil && afterExportExcelCounter < 1 {
m.t.Errorf("Expected call to SupplierServiceMock.ExportExcel at\n%s", m.funcExportExcelOrigin)
}
if !m.ExportExcelMock.invocationsDone() && afterExportExcelCounter > 0 {
m.t.Errorf("Expected %d calls to SupplierServiceMock.ExportExcel at\n%s but found %d calls",
mm_atomic.LoadUint64(&m.ExportExcelMock.expectedInvocations), m.ExportExcelMock.expectedInvocationsOrigin, afterExportExcelCounter)
}
}
// MinimockFinish checks that all mocked methods have been called the expected number of times
func (m *SupplierServiceMock) MinimockFinish() {
m.finishOnce.Do(func() {
if !m.minimockDone() {
m.MinimockExportExcelInspect()
}
})
}
// MinimockWait waits for all mocked methods to be called the expected number of times
func (m *SupplierServiceMock) MinimockWait(timeout mm_time.Duration) {
timeoutCh := mm_time.After(timeout)
for {
if m.minimockDone() {
return
}
select {
case <-timeoutCh:
m.MinimockFinish()
return
case <-mm_time.After(10 * mm_time.Millisecond):
}
}
}
func (m *SupplierServiceMock) minimockDone() bool {
done := true
return done &&
m.MinimockExportExcelDone()
}

View File

@@ -0,0 +1,417 @@
// Code generated by http://github.com/gojuno/minimock (v3.4.7). DO NOT EDIT.
package mocks
//go:generate minimock -i smart-search-back/internal/repository.TokenUsageRepository -o token_usage_repository_mock.go -n TokenUsageRepositoryMock -p mocks
import (
"context"
"smart-search-back/internal/model"
"sync"
mm_atomic "sync/atomic"
mm_time "time"
"github.com/gojuno/minimock/v3"
)
// TokenUsageRepositoryMock implements mm_repository.TokenUsageRepository
type TokenUsageRepositoryMock struct {
t minimock.Tester
finishOnce sync.Once
funcCreate func(ctx context.Context, usage *model.TokenUsage) (err error)
funcCreateOrigin string
inspectFuncCreate func(ctx context.Context, usage *model.TokenUsage)
afterCreateCounter uint64
beforeCreateCounter uint64
CreateMock mTokenUsageRepositoryMockCreate
}
// NewTokenUsageRepositoryMock returns a mock for mm_repository.TokenUsageRepository
func NewTokenUsageRepositoryMock(t minimock.Tester) *TokenUsageRepositoryMock {
m := &TokenUsageRepositoryMock{t: t}
if controller, ok := t.(minimock.MockController); ok {
controller.RegisterMocker(m)
}
m.CreateMock = mTokenUsageRepositoryMockCreate{mock: m}
m.CreateMock.callArgs = []*TokenUsageRepositoryMockCreateParams{}
t.Cleanup(m.MinimockFinish)
return m
}
type mTokenUsageRepositoryMockCreate struct {
optional bool
mock *TokenUsageRepositoryMock
defaultExpectation *TokenUsageRepositoryMockCreateExpectation
expectations []*TokenUsageRepositoryMockCreateExpectation
callArgs []*TokenUsageRepositoryMockCreateParams
mutex sync.RWMutex
expectedInvocations uint64
expectedInvocationsOrigin string
}
// TokenUsageRepositoryMockCreateExpectation specifies expectation struct of the TokenUsageRepository.Create
type TokenUsageRepositoryMockCreateExpectation struct {
mock *TokenUsageRepositoryMock
params *TokenUsageRepositoryMockCreateParams
paramPtrs *TokenUsageRepositoryMockCreateParamPtrs
expectationOrigins TokenUsageRepositoryMockCreateExpectationOrigins
results *TokenUsageRepositoryMockCreateResults
returnOrigin string
Counter uint64
}
// TokenUsageRepositoryMockCreateParams contains parameters of the TokenUsageRepository.Create
type TokenUsageRepositoryMockCreateParams struct {
ctx context.Context
usage *model.TokenUsage
}
// TokenUsageRepositoryMockCreateParamPtrs contains pointers to parameters of the TokenUsageRepository.Create
type TokenUsageRepositoryMockCreateParamPtrs struct {
ctx *context.Context
usage **model.TokenUsage
}
// TokenUsageRepositoryMockCreateResults contains results of the TokenUsageRepository.Create
type TokenUsageRepositoryMockCreateResults struct {
err error
}
// TokenUsageRepositoryMockCreateOrigins contains origins of expectations of the TokenUsageRepository.Create
type TokenUsageRepositoryMockCreateExpectationOrigins struct {
origin string
originCtx string
originUsage string
}
// Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning
// the test will fail minimock's automatic final call check if the mocked method was not called at least once.
// Optional() makes method check to work in '0 or more' mode.
// It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to
// catch the problems when the expected method call is totally skipped during test run.
func (mmCreate *mTokenUsageRepositoryMockCreate) Optional() *mTokenUsageRepositoryMockCreate {
mmCreate.optional = true
return mmCreate
}
// Expect sets up expected params for TokenUsageRepository.Create
func (mmCreate *mTokenUsageRepositoryMockCreate) Expect(ctx context.Context, usage *model.TokenUsage) *mTokenUsageRepositoryMockCreate {
if mmCreate.mock.funcCreate != nil {
mmCreate.mock.t.Fatalf("TokenUsageRepositoryMock.Create mock is already set by Set")
}
if mmCreate.defaultExpectation == nil {
mmCreate.defaultExpectation = &TokenUsageRepositoryMockCreateExpectation{}
}
if mmCreate.defaultExpectation.paramPtrs != nil {
mmCreate.mock.t.Fatalf("TokenUsageRepositoryMock.Create mock is already set by ExpectParams functions")
}
mmCreate.defaultExpectation.params = &TokenUsageRepositoryMockCreateParams{ctx, usage}
mmCreate.defaultExpectation.expectationOrigins.origin = minimock.CallerInfo(1)
for _, e := range mmCreate.expectations {
if minimock.Equal(e.params, mmCreate.defaultExpectation.params) {
mmCreate.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmCreate.defaultExpectation.params)
}
}
return mmCreate
}
// ExpectCtxParam1 sets up expected param ctx for TokenUsageRepository.Create
func (mmCreate *mTokenUsageRepositoryMockCreate) ExpectCtxParam1(ctx context.Context) *mTokenUsageRepositoryMockCreate {
if mmCreate.mock.funcCreate != nil {
mmCreate.mock.t.Fatalf("TokenUsageRepositoryMock.Create mock is already set by Set")
}
if mmCreate.defaultExpectation == nil {
mmCreate.defaultExpectation = &TokenUsageRepositoryMockCreateExpectation{}
}
if mmCreate.defaultExpectation.params != nil {
mmCreate.mock.t.Fatalf("TokenUsageRepositoryMock.Create mock is already set by Expect")
}
if mmCreate.defaultExpectation.paramPtrs == nil {
mmCreate.defaultExpectation.paramPtrs = &TokenUsageRepositoryMockCreateParamPtrs{}
}
mmCreate.defaultExpectation.paramPtrs.ctx = &ctx
mmCreate.defaultExpectation.expectationOrigins.originCtx = minimock.CallerInfo(1)
return mmCreate
}
// ExpectUsageParam2 sets up expected param usage for TokenUsageRepository.Create
func (mmCreate *mTokenUsageRepositoryMockCreate) ExpectUsageParam2(usage *model.TokenUsage) *mTokenUsageRepositoryMockCreate {
if mmCreate.mock.funcCreate != nil {
mmCreate.mock.t.Fatalf("TokenUsageRepositoryMock.Create mock is already set by Set")
}
if mmCreate.defaultExpectation == nil {
mmCreate.defaultExpectation = &TokenUsageRepositoryMockCreateExpectation{}
}
if mmCreate.defaultExpectation.params != nil {
mmCreate.mock.t.Fatalf("TokenUsageRepositoryMock.Create mock is already set by Expect")
}
if mmCreate.defaultExpectation.paramPtrs == nil {
mmCreate.defaultExpectation.paramPtrs = &TokenUsageRepositoryMockCreateParamPtrs{}
}
mmCreate.defaultExpectation.paramPtrs.usage = &usage
mmCreate.defaultExpectation.expectationOrigins.originUsage = minimock.CallerInfo(1)
return mmCreate
}
// Inspect accepts an inspector function that has same arguments as the TokenUsageRepository.Create
func (mmCreate *mTokenUsageRepositoryMockCreate) Inspect(f func(ctx context.Context, usage *model.TokenUsage)) *mTokenUsageRepositoryMockCreate {
if mmCreate.mock.inspectFuncCreate != nil {
mmCreate.mock.t.Fatalf("Inspect function is already set for TokenUsageRepositoryMock.Create")
}
mmCreate.mock.inspectFuncCreate = f
return mmCreate
}
// Return sets up results that will be returned by TokenUsageRepository.Create
func (mmCreate *mTokenUsageRepositoryMockCreate) Return(err error) *TokenUsageRepositoryMock {
if mmCreate.mock.funcCreate != nil {
mmCreate.mock.t.Fatalf("TokenUsageRepositoryMock.Create mock is already set by Set")
}
if mmCreate.defaultExpectation == nil {
mmCreate.defaultExpectation = &TokenUsageRepositoryMockCreateExpectation{mock: mmCreate.mock}
}
mmCreate.defaultExpectation.results = &TokenUsageRepositoryMockCreateResults{err}
mmCreate.defaultExpectation.returnOrigin = minimock.CallerInfo(1)
return mmCreate.mock
}
// Set uses given function f to mock the TokenUsageRepository.Create method
func (mmCreate *mTokenUsageRepositoryMockCreate) Set(f func(ctx context.Context, usage *model.TokenUsage) (err error)) *TokenUsageRepositoryMock {
if mmCreate.defaultExpectation != nil {
mmCreate.mock.t.Fatalf("Default expectation is already set for the TokenUsageRepository.Create method")
}
if len(mmCreate.expectations) > 0 {
mmCreate.mock.t.Fatalf("Some expectations are already set for the TokenUsageRepository.Create method")
}
mmCreate.mock.funcCreate = f
mmCreate.mock.funcCreateOrigin = minimock.CallerInfo(1)
return mmCreate.mock
}
// When sets expectation for the TokenUsageRepository.Create which will trigger the result defined by the following
// Then helper
func (mmCreate *mTokenUsageRepositoryMockCreate) When(ctx context.Context, usage *model.TokenUsage) *TokenUsageRepositoryMockCreateExpectation {
if mmCreate.mock.funcCreate != nil {
mmCreate.mock.t.Fatalf("TokenUsageRepositoryMock.Create mock is already set by Set")
}
expectation := &TokenUsageRepositoryMockCreateExpectation{
mock: mmCreate.mock,
params: &TokenUsageRepositoryMockCreateParams{ctx, usage},
expectationOrigins: TokenUsageRepositoryMockCreateExpectationOrigins{origin: minimock.CallerInfo(1)},
}
mmCreate.expectations = append(mmCreate.expectations, expectation)
return expectation
}
// Then sets up TokenUsageRepository.Create return parameters for the expectation previously defined by the When method
func (e *TokenUsageRepositoryMockCreateExpectation) Then(err error) *TokenUsageRepositoryMock {
e.results = &TokenUsageRepositoryMockCreateResults{err}
return e.mock
}
// Times sets number of times TokenUsageRepository.Create should be invoked
func (mmCreate *mTokenUsageRepositoryMockCreate) Times(n uint64) *mTokenUsageRepositoryMockCreate {
if n == 0 {
mmCreate.mock.t.Fatalf("Times of TokenUsageRepositoryMock.Create mock can not be zero")
}
mm_atomic.StoreUint64(&mmCreate.expectedInvocations, n)
mmCreate.expectedInvocationsOrigin = minimock.CallerInfo(1)
return mmCreate
}
func (mmCreate *mTokenUsageRepositoryMockCreate) invocationsDone() bool {
if len(mmCreate.expectations) == 0 && mmCreate.defaultExpectation == nil && mmCreate.mock.funcCreate == nil {
return true
}
totalInvocations := mm_atomic.LoadUint64(&mmCreate.mock.afterCreateCounter)
expectedInvocations := mm_atomic.LoadUint64(&mmCreate.expectedInvocations)
return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations)
}
// Create implements mm_repository.TokenUsageRepository
func (mmCreate *TokenUsageRepositoryMock) Create(ctx context.Context, usage *model.TokenUsage) (err error) {
mm_atomic.AddUint64(&mmCreate.beforeCreateCounter, 1)
defer mm_atomic.AddUint64(&mmCreate.afterCreateCounter, 1)
mmCreate.t.Helper()
if mmCreate.inspectFuncCreate != nil {
mmCreate.inspectFuncCreate(ctx, usage)
}
mm_params := TokenUsageRepositoryMockCreateParams{ctx, usage}
// Record call args
mmCreate.CreateMock.mutex.Lock()
mmCreate.CreateMock.callArgs = append(mmCreate.CreateMock.callArgs, &mm_params)
mmCreate.CreateMock.mutex.Unlock()
for _, e := range mmCreate.CreateMock.expectations {
if minimock.Equal(*e.params, mm_params) {
mm_atomic.AddUint64(&e.Counter, 1)
return e.results.err
}
}
if mmCreate.CreateMock.defaultExpectation != nil {
mm_atomic.AddUint64(&mmCreate.CreateMock.defaultExpectation.Counter, 1)
mm_want := mmCreate.CreateMock.defaultExpectation.params
mm_want_ptrs := mmCreate.CreateMock.defaultExpectation.paramPtrs
mm_got := TokenUsageRepositoryMockCreateParams{ctx, usage}
if mm_want_ptrs != nil {
if mm_want_ptrs.ctx != nil && !minimock.Equal(*mm_want_ptrs.ctx, mm_got.ctx) {
mmCreate.t.Errorf("TokenUsageRepositoryMock.Create got unexpected parameter ctx, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmCreate.CreateMock.defaultExpectation.expectationOrigins.originCtx, *mm_want_ptrs.ctx, mm_got.ctx, minimock.Diff(*mm_want_ptrs.ctx, mm_got.ctx))
}
if mm_want_ptrs.usage != nil && !minimock.Equal(*mm_want_ptrs.usage, mm_got.usage) {
mmCreate.t.Errorf("TokenUsageRepositoryMock.Create got unexpected parameter usage, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmCreate.CreateMock.defaultExpectation.expectationOrigins.originUsage, *mm_want_ptrs.usage, mm_got.usage, minimock.Diff(*mm_want_ptrs.usage, mm_got.usage))
}
} else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) {
mmCreate.t.Errorf("TokenUsageRepositoryMock.Create got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmCreate.CreateMock.defaultExpectation.expectationOrigins.origin, *mm_want, mm_got, minimock.Diff(*mm_want, mm_got))
}
mm_results := mmCreate.CreateMock.defaultExpectation.results
if mm_results == nil {
mmCreate.t.Fatal("No results are set for the TokenUsageRepositoryMock.Create")
}
return (*mm_results).err
}
if mmCreate.funcCreate != nil {
return mmCreate.funcCreate(ctx, usage)
}
mmCreate.t.Fatalf("Unexpected call to TokenUsageRepositoryMock.Create. %v %v", ctx, usage)
return
}
// CreateAfterCounter returns a count of finished TokenUsageRepositoryMock.Create invocations
func (mmCreate *TokenUsageRepositoryMock) CreateAfterCounter() uint64 {
return mm_atomic.LoadUint64(&mmCreate.afterCreateCounter)
}
// CreateBeforeCounter returns a count of TokenUsageRepositoryMock.Create invocations
func (mmCreate *TokenUsageRepositoryMock) CreateBeforeCounter() uint64 {
return mm_atomic.LoadUint64(&mmCreate.beforeCreateCounter)
}
// Calls returns a list of arguments used in each call to TokenUsageRepositoryMock.Create.
// The list is in the same order as the calls were made (i.e. recent calls have a higher index)
func (mmCreate *mTokenUsageRepositoryMockCreate) Calls() []*TokenUsageRepositoryMockCreateParams {
mmCreate.mutex.RLock()
argCopy := make([]*TokenUsageRepositoryMockCreateParams, len(mmCreate.callArgs))
copy(argCopy, mmCreate.callArgs)
mmCreate.mutex.RUnlock()
return argCopy
}
// MinimockCreateDone returns true if the count of the Create invocations corresponds
// the number of defined expectations
func (m *TokenUsageRepositoryMock) MinimockCreateDone() bool {
if m.CreateMock.optional {
// Optional methods provide '0 or more' call count restriction.
return true
}
for _, e := range m.CreateMock.expectations {
if mm_atomic.LoadUint64(&e.Counter) < 1 {
return false
}
}
return m.CreateMock.invocationsDone()
}
// MinimockCreateInspect logs each unmet expectation
func (m *TokenUsageRepositoryMock) MinimockCreateInspect() {
for _, e := range m.CreateMock.expectations {
if mm_atomic.LoadUint64(&e.Counter) < 1 {
m.t.Errorf("Expected call to TokenUsageRepositoryMock.Create at\n%s with params: %#v", e.expectationOrigins.origin, *e.params)
}
}
afterCreateCounter := mm_atomic.LoadUint64(&m.afterCreateCounter)
// if default expectation was set then invocations count should be greater than zero
if m.CreateMock.defaultExpectation != nil && afterCreateCounter < 1 {
if m.CreateMock.defaultExpectation.params == nil {
m.t.Errorf("Expected call to TokenUsageRepositoryMock.Create at\n%s", m.CreateMock.defaultExpectation.returnOrigin)
} else {
m.t.Errorf("Expected call to TokenUsageRepositoryMock.Create at\n%s with params: %#v", m.CreateMock.defaultExpectation.expectationOrigins.origin, *m.CreateMock.defaultExpectation.params)
}
}
// if func was set then invocations count should be greater than zero
if m.funcCreate != nil && afterCreateCounter < 1 {
m.t.Errorf("Expected call to TokenUsageRepositoryMock.Create at\n%s", m.funcCreateOrigin)
}
if !m.CreateMock.invocationsDone() && afterCreateCounter > 0 {
m.t.Errorf("Expected %d calls to TokenUsageRepositoryMock.Create at\n%s but found %d calls",
mm_atomic.LoadUint64(&m.CreateMock.expectedInvocations), m.CreateMock.expectedInvocationsOrigin, afterCreateCounter)
}
}
// MinimockFinish checks that all mocked methods have been called the expected number of times
func (m *TokenUsageRepositoryMock) MinimockFinish() {
m.finishOnce.Do(func() {
if !m.minimockDone() {
m.MinimockCreateInspect()
}
})
}
// MinimockWait waits for all mocked methods to be called the expected number of times
func (m *TokenUsageRepositoryMock) MinimockWait(timeout mm_time.Duration) {
timeoutCh := mm_time.After(timeout)
for {
if m.minimockDone() {
return
}
select {
case <-timeoutCh:
m.MinimockFinish()
return
case <-mm_time.After(10 * mm_time.Millisecond):
}
}
}
func (m *TokenUsageRepositoryMock) minimockDone() bool {
done := true
return done &&
m.MinimockCreateDone()
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

14
internal/model/invite.go Normal file
View File

@@ -0,0 +1,14 @@
package model
import "time"
type InviteCode struct {
ID int
UserID int
Code int64
CanBeUsedCount int
UsedCount int
IsActive bool
CreatedAt time.Time
ExpiresAt time.Time
}

40
internal/model/request.go Normal file
View File

@@ -0,0 +1,40 @@
package model
import (
"time"
"github.com/google/uuid"
)
type Request struct {
ID uuid.UUID
UserID int
RequestTxt string
GeneratedTZ bool
FinalTZ string
GeneratedFinalTZ bool
FinalUpdateTZ string
MailingStatusID int
MailingStatus string
CreatedAt time.Time
}
type MailingStatus struct {
ID int
StatusName string
}
type RequestDetail struct {
RequestID uuid.UUID
Title string
MailText string
Suppliers []SupplierInfo
}
type SupplierInfo struct {
CompanyID int `json:"company_id"`
Email string `json:"email"`
Phone string `json:"phone"`
CompanyName string `json:"company_name"`
URL string `json:"url"`
}

15
internal/model/session.go Normal file
View File

@@ -0,0 +1,15 @@
package model
import "time"
type Session struct {
ID int
UserID int
AccessToken string
RefreshToken string
IP string
UserAgent string
CreatedAt time.Time
ExpiresAt time.Time
RevokedAt *time.Time
}

View File

@@ -0,0 +1,28 @@
package model
import (
"time"
"github.com/google/uuid"
)
type Supplier struct {
ID int
RequestID uuid.UUID
Name string
Email string
Phone string
Address string
URL string
CreatedAt time.Time
}
type TokenUsage struct {
ID int
RequestID uuid.UUID
RequestTokenCount int
ResponseTokenCount int
TokenCost float64
Type string
CreatedAt time.Time
}

18
internal/model/user.go Normal file
View File

@@ -0,0 +1,18 @@
package model
import "time"
type User struct {
ID int
Email string
EmailHash string
PasswordHash string
Phone string
UserName string
CompanyName string
Balance float64
PaymentStatus string
InvitesIssued int
InvitesLimit int
CreatedAt time.Time
}

View File

@@ -0,0 +1,54 @@
package repository
import (
"context"
"github.com/google/uuid"
"smart-search-back/internal/model"
)
type UserRepository interface {
FindByEmailHash(ctx context.Context, emailHash string) (*model.User, error)
FindByID(ctx context.Context, userID int) (*model.User, error)
Create(ctx context.Context, user *model.User) error
UpdateBalance(ctx context.Context, userID int, delta float64) error
GetBalance(ctx context.Context, userID int) (float64, error)
IncrementInvitesIssued(ctx context.Context, userID int) error
CheckInviteLimit(ctx context.Context, userID int) (bool, error)
}
type SessionRepository interface {
Create(ctx context.Context, session *model.Session) error
FindByRefreshToken(ctx context.Context, token string) (*model.Session, error)
UpdateAccessToken(ctx context.Context, refreshToken, newAccessToken string) error
Revoke(ctx context.Context, refreshToken string) error
DeleteExpired(ctx context.Context) (int, error)
}
type InviteRepository interface {
Create(ctx context.Context, invite *model.InviteCode) error
FindByCode(ctx context.Context, code int64) (*model.InviteCode, error)
IncrementUsedCount(ctx context.Context, code int64) error
DeactivateExpired(ctx context.Context) (int, error)
GetUserInvites(ctx context.Context, userID int) ([]*model.InviteCode, error)
}
type RequestRepository interface {
Create(ctx context.Context, req *model.Request) error
UpdateWithTZ(ctx context.Context, id uuid.UUID, tz string, generated bool) error
UpdateFinalTZ(ctx context.Context, id uuid.UUID, finalTZ string) error
GetByUserID(ctx context.Context, userID int) ([]*model.Request, error)
GetByID(ctx context.Context, id uuid.UUID) (*model.Request, error)
GetDetailByID(ctx context.Context, id uuid.UUID) (*model.RequestDetail, error)
GetUserStatistics(ctx context.Context, userID int) (requestsCount, suppliersCount, createdTZ int, err error)
}
type SupplierRepository interface {
BulkInsert(ctx context.Context, requestID uuid.UUID, suppliers []*model.Supplier) error
GetByRequestID(ctx context.Context, requestID uuid.UUID) ([]*model.Supplier, error)
DeleteByRequestID(ctx context.Context, requestID uuid.UUID) error
}
type TokenUsageRepository interface {
Create(ctx context.Context, usage *model.TokenUsage) error
}

View File

@@ -0,0 +1,146 @@
package repository
import (
"context"
"errors"
"smart-search-back/internal/model"
errs "smart-search-back/pkg/errors"
sq "github.com/Masterminds/squirrel"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
type inviteRepository struct {
pool *pgxpool.Pool
qb sq.StatementBuilderType
}
func NewInviteRepository(pool *pgxpool.Pool) InviteRepository {
return &inviteRepository{
pool: pool,
qb: sq.StatementBuilder.PlaceholderFormat(sq.Dollar),
}
}
func (r *inviteRepository) Create(ctx context.Context, invite *model.InviteCode) error {
query := r.qb.Insert("invite_codes").Columns(
"user_id", "code", "can_be_used_count", "expires_at",
).Values(
invite.UserID, invite.Code, invite.CanBeUsedCount, invite.ExpiresAt,
).Suffix("RETURNING id, created_at")
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(&invite.ID, &invite.CreatedAt)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to create invite code", err)
}
return nil
}
func (r *inviteRepository) FindByCode(ctx context.Context, code int64) (*model.InviteCode, error) {
query := r.qb.Select(
"id", "user_id", "code", "can_be_used_count", "used_count",
"is_active", "created_at", "expires_at",
).From("invite_codes").Where(sq.Eq{"code": code})
sqlQuery, args, err := query.ToSql()
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
invite := &model.InviteCode{}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(
&invite.ID, &invite.UserID, &invite.Code, &invite.CanBeUsedCount,
&invite.UsedCount, &invite.IsActive, &invite.CreatedAt, &invite.ExpiresAt,
)
if errors.Is(err, pgx.ErrNoRows) {
return nil, errs.NewBusinessError(errs.UserNotFound, "invite code not found")
}
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to find invite code", err)
}
return invite, nil
}
func (r *inviteRepository) IncrementUsedCount(ctx context.Context, code int64) error {
query := r.qb.Update("invite_codes").
Set("used_count", sq.Expr("used_count + 1")).
Where(sq.Eq{"code": code})
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
_, err = r.pool.Exec(ctx, sqlQuery, args...)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to increment used count", err)
}
return nil
}
func (r *inviteRepository) DeactivateExpired(ctx context.Context) (int, error) {
query := r.qb.Update("invite_codes").
Set("is_active", false).
Where(sq.And{
sq.Expr("expires_at < now()"),
sq.Eq{"is_active": true},
})
sqlQuery, args, err := query.ToSql()
if err != nil {
return 0, errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
result, err := r.pool.Exec(ctx, sqlQuery, args...)
if err != nil {
return 0, errs.NewInternalError(errs.DatabaseError, "failed to deactivate expired invites", err)
}
return int(result.RowsAffected()), nil
}
func (r *inviteRepository) GetUserInvites(ctx context.Context, userID int) ([]*model.InviteCode, error) {
query := r.qb.Select(
"id", "user_id", "code", "can_be_used_count", "used_count",
"is_active", "created_at", "expires_at",
).From("invite_codes").
Where(sq.Eq{"user_id": userID}).
OrderBy("created_at DESC")
sqlQuery, args, err := query.ToSql()
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
rows, err := r.pool.Query(ctx, sqlQuery, args...)
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to get user invites", err)
}
defer rows.Close()
var invites []*model.InviteCode
for rows.Next() {
invite := &model.InviteCode{}
err := rows.Scan(
&invite.ID, &invite.UserID, &invite.Code, &invite.CanBeUsedCount,
&invite.UsedCount, &invite.IsActive, &invite.CreatedAt, &invite.ExpiresAt,
)
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to scan invite", err)
}
invites = append(invites, invite)
}
return invites, nil
}

View File

@@ -0,0 +1,209 @@
package repository
import (
"context"
"encoding/json"
"errors"
"smart-search-back/internal/model"
errs "smart-search-back/pkg/errors"
sq "github.com/Masterminds/squirrel"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
type requestRepository struct {
pool *pgxpool.Pool
qb sq.StatementBuilderType
}
func NewRequestRepository(pool *pgxpool.Pool) RequestRepository {
return &requestRepository{
pool: pool,
qb: sq.StatementBuilder.PlaceholderFormat(sq.Dollar),
}
}
func (r *requestRepository) Create(ctx context.Context, req *model.Request) error {
query := r.qb.Insert("requests_for_suppliers").Columns(
"user_id", "request_txt",
).Values(
req.UserID, req.RequestTxt,
).Suffix("RETURNING id, created_at")
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(&req.ID, &req.CreatedAt)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to create request", err)
}
return nil
}
func (r *requestRepository) UpdateWithTZ(ctx context.Context, id uuid.UUID, tz string, generated bool) error {
query := r.qb.Update("requests_for_suppliers").
Set("final_tz", tz).
Set("generated_tz", generated).
Set("generated_final_tz", generated).
Where(sq.Eq{"id": id})
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
_, err = r.pool.Exec(ctx, sqlQuery, args...)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to update request", err)
}
return nil
}
func (r *requestRepository) UpdateFinalTZ(ctx context.Context, id uuid.UUID, finalTZ string) error {
query := r.qb.Update("requests_for_suppliers").
Set("final_update_tz", finalTZ).
Where(sq.Eq{"id": id})
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
_, err = r.pool.Exec(ctx, sqlQuery, args...)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to update final TZ", err)
}
return nil
}
func (r *requestRepository) GetByUserID(ctx context.Context, userID int) ([]*model.Request, error) {
query := r.qb.Select(
"r.id", "r.request_txt", "ms.status_name as mailing_status",
).From("requests_for_suppliers r").
Join("mailing_status ms ON r.mailling_status_id = ms.id").
Where(sq.Eq{"r.user_id": userID}).
OrderBy("r.created_at DESC")
sqlQuery, args, err := query.ToSql()
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
rows, err := r.pool.Query(ctx, sqlQuery, args...)
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to get requests", err)
}
defer rows.Close()
var requests []*model.Request
for rows.Next() {
req := &model.Request{}
var statusName string
err := rows.Scan(&req.ID, &req.RequestTxt, &statusName)
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to scan request", err)
}
requests = append(requests, req)
}
return requests, nil
}
func (r *requestRepository) GetByID(ctx context.Context, id uuid.UUID) (*model.Request, error) {
query := r.qb.Select(
"id", "user_id", "request_txt", "generated_tz", "final_tz",
"generated_final_tz", "final_update_tz", "mailling_status_id", "created_at",
).From("requests_for_suppliers").Where(sq.Eq{"id": id})
sqlQuery, args, err := query.ToSql()
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
req := &model.Request{}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(
&req.ID, &req.UserID, &req.RequestTxt, &req.GeneratedTZ,
&req.FinalTZ, &req.GeneratedFinalTZ, &req.FinalUpdateTZ,
&req.MailingStatusID, &req.CreatedAt,
)
if errors.Is(err, pgx.ErrNoRows) {
return nil, errs.NewBusinessError(errs.RequestNotFound, "request not found")
}
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to get request", err)
}
return req, nil
}
func (r *requestRepository) GetDetailByID(ctx context.Context, id uuid.UUID) (*model.RequestDetail, error) {
sqlQuery := `
SELECT
r.id AS request_id,
r.request_txt AS title,
r.final_update_tz AS mail_text,
COALESCE(json_agg(
json_build_object(
'email', COALESCE(s.email, ''),
'phone', COALESCE(s.phone, ''),
'company_name', COALESCE(s.name, ''),
'company_id', s.id,
'url', COALESCE(s.url, '')
)
) FILTER (WHERE s.id IS NOT NULL), '[]') AS suppliers
FROM requests_for_suppliers r
LEFT JOIN suppliers s ON s.request_id = r.id
WHERE r.id = $1
GROUP BY r.id, r.request_txt, r.final_update_tz
`
detail := &model.RequestDetail{}
var suppliersJSON []byte
err := r.pool.QueryRow(ctx, sqlQuery, id).Scan(
&detail.RequestID, &detail.Title, &detail.MailText, &suppliersJSON,
)
if err == pgx.ErrNoRows {
return nil, errs.NewBusinessError(errs.RequestNotFound, "request not found")
}
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to get request detail", err)
}
if len(suppliersJSON) > 0 && string(suppliersJSON) != "[]" {
if err := json.Unmarshal(suppliersJSON, &detail.Suppliers); err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to parse suppliers", err)
}
}
return detail, nil
}
func (r *requestRepository) GetUserStatistics(ctx context.Context, userID int) (requestsCount, suppliersCount, createdTZ int, err error) {
sqlQuery := `
SELECT
COUNT(DISTINCT r.id) AS requests_count,
COUNT(s.id) AS suppliers_count,
COUNT(r.request_txt) AS created_tz
FROM requests_for_suppliers r
LEFT JOIN suppliers s ON s.request_id = r.id
WHERE r.user_id = $1
`
err = r.pool.QueryRow(ctx, sqlQuery, userID).Scan(&requestsCount, &suppliersCount, &createdTZ)
if err != nil {
return 0, 0, 0, errs.NewInternalError(errs.DatabaseError, "failed to get statistics", err)
}
return requestsCount, suppliersCount, createdTZ, nil
}

View File

@@ -0,0 +1,134 @@
package repository
import (
"context"
"errors"
"time"
"smart-search-back/internal/model"
errs "smart-search-back/pkg/errors"
sq "github.com/Masterminds/squirrel"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
type sessionRepository struct {
pool *pgxpool.Pool
qb sq.StatementBuilderType
}
func NewSessionRepository(pool *pgxpool.Pool) SessionRepository {
return &sessionRepository{
pool: pool,
qb: sq.StatementBuilder.PlaceholderFormat(sq.Dollar),
}
}
func (r *sessionRepository) Create(ctx context.Context, session *model.Session) error {
query := r.qb.Insert("sessions").Columns(
"user_id", "access_token", "refresh_token", "ip", "user_agent", "expires_at",
).Values(
session.UserID, session.AccessToken, session.RefreshToken,
session.IP, session.UserAgent, session.ExpiresAt,
).Suffix("RETURNING id")
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(&session.ID)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to create session", err)
}
return nil
}
func (r *sessionRepository) FindByRefreshToken(ctx context.Context, token string) (*model.Session, error) {
query := r.qb.Select(
"id", "user_id", "access_token", "refresh_token", "ip",
"user_agent", "created_at", "expires_at", "revoked_at",
).From("sessions").Where(sq.And{
sq.Eq{"refresh_token": token},
sq.Expr("revoked_at IS NULL"),
sq.Expr("expires_at > now()"),
})
sqlQuery, args, err := query.ToSql()
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
session := &model.Session{}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(
&session.ID, &session.UserID, &session.AccessToken, &session.RefreshToken,
&session.IP, &session.UserAgent, &session.CreatedAt, &session.ExpiresAt,
&session.RevokedAt,
)
if errors.Is(err, pgx.ErrNoRows) {
return nil, errs.NewBusinessError(errs.RefreshInvalid, "refresh token is invalid or expired")
}
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to find session", err)
}
return session, nil
}
func (r *sessionRepository) UpdateAccessToken(ctx context.Context, refreshToken, newAccessToken string) error {
query := r.qb.Update("sessions").
Set("access_token", newAccessToken).
Where(sq.Eq{"refresh_token": refreshToken})
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
_, err = r.pool.Exec(ctx, sqlQuery, args...)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to update access token", err)
}
return nil
}
func (r *sessionRepository) Revoke(ctx context.Context, refreshToken string) error {
query := r.qb.Update("sessions").
Set("revoked_at", time.Now()).
Where(sq.Eq{"refresh_token": refreshToken})
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
_, err = r.pool.Exec(ctx, sqlQuery, args...)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to revoke session", err)
}
return nil
}
func (r *sessionRepository) DeleteExpired(ctx context.Context) (int, error) {
query := r.qb.Delete("sessions").Where(sq.Or{
sq.Expr("expires_at < now()"),
sq.Expr("(revoked_at IS NOT NULL AND revoked_at < now() - interval '30 days')"),
})
sqlQuery, args, err := query.ToSql()
if err != nil {
return 0, errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
result, err := r.pool.Exec(ctx, sqlQuery, args...)
if err != nil {
return 0, errs.NewInternalError(errs.DatabaseError, "failed to delete expired sessions", err)
}
return int(result.RowsAffected()), nil
}

View File

@@ -0,0 +1,97 @@
package repository
import (
"context"
"smart-search-back/internal/model"
errs "smart-search-back/pkg/errors"
sq "github.com/Masterminds/squirrel"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgxpool"
)
type supplierRepository struct {
pool *pgxpool.Pool
qb sq.StatementBuilderType
}
func NewSupplierRepository(pool *pgxpool.Pool) SupplierRepository {
return &supplierRepository{
pool: pool,
qb: sq.StatementBuilder.PlaceholderFormat(sq.Dollar),
}
}
func (r *supplierRepository) BulkInsert(ctx context.Context, requestID uuid.UUID, suppliers []*model.Supplier) error {
if len(suppliers) == 0 {
return nil
}
query := r.qb.Insert("suppliers").Columns(
"request_id", "name", "email", "phone", "adress", "url",
)
for _, s := range suppliers {
query = query.Values(requestID, s.Name, s.Email, s.Phone, s.Address, s.URL)
}
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
_, err = r.pool.Exec(ctx, sqlQuery, args...)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to bulk insert suppliers", err)
}
return nil
}
func (r *supplierRepository) GetByRequestID(ctx context.Context, requestID uuid.UUID) ([]*model.Supplier, error) {
query := r.qb.Select(
"id", "request_id", "name", "email", "phone", "adress", "url", "created_at",
).From("suppliers").Where(sq.Eq{"request_id": requestID}).OrderBy("id")
sqlQuery, args, err := query.ToSql()
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
rows, err := r.pool.Query(ctx, sqlQuery, args...)
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to get suppliers", err)
}
defer rows.Close()
var suppliers []*model.Supplier
for rows.Next() {
s := &model.Supplier{}
err := rows.Scan(
&s.ID, &s.RequestID, &s.Name, &s.Email, &s.Phone, &s.Address, &s.URL, &s.CreatedAt,
)
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to scan supplier", err)
}
suppliers = append(suppliers, s)
}
return suppliers, nil
}
func (r *supplierRepository) DeleteByRequestID(ctx context.Context, requestID uuid.UUID) error {
query := r.qb.Delete("suppliers").Where(sq.Eq{"request_id": requestID})
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
_, err = r.pool.Exec(ctx, sqlQuery, args...)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to delete suppliers", err)
}
return nil
}

View File

@@ -0,0 +1,43 @@
package repository
import (
"context"
"smart-search-back/internal/model"
errs "smart-search-back/pkg/errors"
sq "github.com/Masterminds/squirrel"
"github.com/jackc/pgx/v5/pgxpool"
)
type tokenUsageRepository struct {
pool *pgxpool.Pool
qb sq.StatementBuilderType
}
func NewTokenUsageRepository(pool *pgxpool.Pool) TokenUsageRepository {
return &tokenUsageRepository{
pool: pool,
qb: sq.StatementBuilder.PlaceholderFormat(sq.Dollar),
}
}
func (r *tokenUsageRepository) Create(ctx context.Context, usage *model.TokenUsage) error {
query := r.qb.Insert("request_token_usage").Columns(
"request_id", "request_token_count", "response_token_count", "token_cost", "type",
).Values(
usage.RequestID, usage.RequestTokenCount, usage.ResponseTokenCount, usage.TokenCost, usage.Type,
).Suffix("RETURNING id, created_at")
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(&usage.ID, &usage.CreatedAt)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to create token usage", err)
}
return nil
}

199
internal/repository/user.go Normal file
View File

@@ -0,0 +1,199 @@
package repository
import (
"context"
"errors"
"smart-search-back/internal/model"
"smart-search-back/pkg/crypto"
errs "smart-search-back/pkg/errors"
sq "github.com/Masterminds/squirrel"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
type userRepository struct {
pool *pgxpool.Pool
qb sq.StatementBuilderType
cryptoHelper *crypto.Crypto
}
func NewUserRepository(pool *pgxpool.Pool, cryptoSecret string) UserRepository {
return &userRepository{
pool: pool,
qb: sq.StatementBuilder.PlaceholderFormat(sq.Dollar),
cryptoHelper: crypto.NewCrypto(cryptoSecret),
}
}
func (r *userRepository) FindByEmailHash(ctx context.Context, emailHash string) (*model.User, error) {
query := r.qb.Select(
"id", "email", "email_hash", "password_hash", "phone",
"user_name", "company_name", "balance", "payment_status",
"invites_issued", "invites_limit", "created_at",
).From("users").Where(sq.Eq{"email_hash": emailHash})
sqlQuery, args, err := query.ToSql()
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
user := &model.User{}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(
&user.ID, &user.Email, &user.EmailHash, &user.PasswordHash,
&user.Phone, &user.UserName, &user.CompanyName, &user.Balance,
&user.PaymentStatus, &user.InvitesIssued, &user.InvitesLimit, &user.CreatedAt,
)
if errors.Is(err, pgx.ErrNoRows) {
return nil, errs.NewBusinessError(errs.UserNotFound, "user not found")
}
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to find user", err)
}
return user, nil
}
func (r *userRepository) FindByID(ctx context.Context, userID int) (*model.User, error) {
query := r.qb.Select(
"id", "email", "email_hash", "password_hash", "phone",
"user_name", "company_name", "balance", "payment_status",
"invites_issued", "invites_limit", "created_at",
).From("users").Where(sq.Eq{"id": userID})
sqlQuery, args, err := query.ToSql()
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
user := &model.User{}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(
&user.ID, &user.Email, &user.EmailHash, &user.PasswordHash,
&user.Phone, &user.UserName, &user.CompanyName, &user.Balance,
&user.PaymentStatus, &user.InvitesIssued, &user.InvitesLimit, &user.CreatedAt,
)
if errors.Is(err, pgx.ErrNoRows) {
return nil, errs.NewBusinessError(errs.UserNotFound, "user not found")
}
if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to find user", err)
}
return user, nil
}
func (r *userRepository) Create(ctx context.Context, user *model.User) error {
encryptedEmail, err := r.cryptoHelper.Encrypt(user.Email)
if err != nil {
return errs.NewInternalError(errs.EncryptionError, "failed to encrypt email", err)
}
encryptedPhone, err := r.cryptoHelper.Encrypt(user.Phone)
if err != nil {
return errs.NewInternalError(errs.EncryptionError, "failed to encrypt phone", err)
}
encryptedUserName, err := r.cryptoHelper.Encrypt(user.UserName)
if err != nil {
return errs.NewInternalError(errs.EncryptionError, "failed to encrypt user name", err)
}
query := r.qb.Insert("users").Columns(
"email", "email_hash", "password_hash", "phone", "user_name",
"company_name", "balance", "payment_status",
).Values(
encryptedEmail, user.EmailHash, user.PasswordHash, encryptedPhone,
encryptedUserName, user.CompanyName, user.Balance, user.PaymentStatus,
).Suffix("RETURNING id")
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(&user.ID)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to create user", err)
}
return nil
}
func (r *userRepository) UpdateBalance(ctx context.Context, userID int, delta float64) error {
query := r.qb.Update("users").
Set("balance", sq.Expr("balance + ?", delta)).
Where(sq.Eq{"id": userID})
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
_, err = r.pool.Exec(ctx, sqlQuery, args...)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to update balance", err)
}
return nil
}
func (r *userRepository) GetBalance(ctx context.Context, userID int) (float64, error) {
query := r.qb.Select("balance").From("users").Where(sq.Eq{"id": userID})
sqlQuery, args, err := query.ToSql()
if err != nil {
return 0, errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
var balance float64
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(&balance)
if errors.Is(err, pgx.ErrNoRows) {
return 0, errs.NewBusinessError(errs.UserNotFound, "user not found")
}
if err != nil {
return 0, errs.NewInternalError(errs.DatabaseError, "failed to get balance", err)
}
return balance, nil
}
func (r *userRepository) IncrementInvitesIssued(ctx context.Context, userID int) error {
query := r.qb.Update("users").
Set("invites_issued", sq.Expr("invites_issued + 1")).
Where(sq.Eq{"id": userID})
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
_, err = r.pool.Exec(ctx, sqlQuery, args...)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to increment invites issued", err)
}
return nil
}
func (r *userRepository) CheckInviteLimit(ctx context.Context, userID int) (bool, error) {
query := r.qb.Select("invites_issued", "invites_limit").
From("users").
Where(sq.Eq{"id": userID}).
Suffix("FOR UPDATE")
sqlQuery, args, err := query.ToSql()
if err != nil {
return false, errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
var issued, limit int
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(&issued, &limit)
if err != nil {
return false, errs.NewInternalError(errs.DatabaseError, "failed to check invite limit", err)
}
return issued < limit, nil
}

107
internal/service/auth.go Normal file
View File

@@ -0,0 +1,107 @@
package service
import (
"context"
"time"
"smart-search-back/internal/model"
"smart-search-back/internal/repository"
"smart-search-back/pkg/crypto"
"smart-search-back/pkg/errors"
"smart-search-back/pkg/jwt"
)
type authService struct {
userRepo repository.UserRepository
sessionRepo repository.SessionRepository
jwtSecret string
cryptoHelper *crypto.Crypto
}
func NewAuthService(userRepo repository.UserRepository, sessionRepo repository.SessionRepository, jwtSecret, cryptoSecret string) AuthService {
return &authService{
userRepo: userRepo,
sessionRepo: sessionRepo,
jwtSecret: jwtSecret,
cryptoHelper: crypto.NewCrypto(cryptoSecret),
}
}
func (s *authService) Login(ctx context.Context, email, password, ip, userAgent string) (accessToken, refreshToken string, err error) {
emailHash := s.cryptoHelper.EmailHash(email)
user, err := s.userRepo.FindByEmailHash(ctx, emailHash)
if err != nil {
return "", "", err
}
passwordHash := crypto.PasswordHash(password)
if user.PasswordHash != passwordHash {
return "", "", errors.NewBusinessError(errors.AuthInvalidCredentials, "Invalid email or password")
}
accessToken, err = jwt.GenerateAccessToken(user.ID, s.jwtSecret)
if err != nil {
return "", "", errors.NewInternalError(errors.InternalError, "failed to generate access token", err)
}
refreshToken, err = jwt.GenerateRefreshToken(user.ID, s.jwtSecret)
if err != nil {
return "", "", errors.NewInternalError(errors.InternalError, "failed to generate refresh token", err)
}
session := &model.Session{
UserID: user.ID,
AccessToken: accessToken,
RefreshToken: refreshToken,
IP: ip,
UserAgent: userAgent,
ExpiresAt: time.Now().Add(30 * 24 * time.Hour),
}
if err := s.sessionRepo.Create(ctx, session); err != nil {
return "", "", err
}
return accessToken, refreshToken, nil
}
func (s *authService) Refresh(ctx context.Context, refreshToken string) (string, error) {
session, err := s.sessionRepo.FindByRefreshToken(ctx, refreshToken)
if err != nil {
return "", err
}
newAccessToken, err := jwt.GenerateAccessToken(session.UserID, s.jwtSecret)
if err != nil {
return "", errors.NewInternalError(errors.InternalError, "failed to generate access token", err)
}
if err := s.sessionRepo.UpdateAccessToken(ctx, refreshToken, newAccessToken); err != nil {
return "", err
}
return newAccessToken, nil
}
func (s *authService) Validate(ctx context.Context, accessToken string) (int, error) {
claims, err := jwt.ValidateToken(accessToken, s.jwtSecret)
if err != nil {
return 0, errors.NewBusinessError(errors.AuthInvalidToken, "Invalid or expired token")
}
if claims.Type != "access" {
return 0, errors.NewBusinessError(errors.AuthInvalidToken, "Token is not an access token")
}
userID, err := jwt.GetUserIDFromToken(accessToken, s.jwtSecret)
if err != nil {
return 0, errors.NewBusinessError(errors.AuthInvalidToken, "Invalid user ID in token")
}
return userID, nil
}
func (s *authService) Logout(ctx context.Context, refreshToken string) error {
return s.sessionRepo.Revoke(ctx, refreshToken)
}

View File

@@ -0,0 +1,38 @@
package service
import (
"context"
"smart-search-back/internal/model"
"github.com/google/uuid"
)
type AuthService interface {
Login(ctx context.Context, email, password, ip, userAgent string) (accessToken, refreshToken string, err error)
Refresh(ctx context.Context, refreshToken string) (string, error)
Validate(ctx context.Context, accessToken string) (int, error)
Logout(ctx context.Context, refreshToken string) error
}
type UserService interface {
GetInfo(ctx context.Context, userID int) (*UserInfo, error)
GetBalance(ctx context.Context, userID int) (float64, error)
GetStatistics(ctx context.Context, userID int) (*Statistics, error)
}
type InviteService interface {
Generate(ctx context.Context, userID, maxUses, ttlDays int) (*model.InviteCode, error)
GetInfo(ctx context.Context, code int64) (*model.InviteCode, error)
}
type RequestService interface {
CreateTZ(ctx context.Context, userID int, requestTxt string) (uuid.UUID, string, error)
ApproveTZ(ctx context.Context, requestID uuid.UUID, tzText string, userID int) ([]*model.Supplier, error)
GetMailingList(ctx context.Context, userID int) ([]*model.Request, error)
GetMailingListByID(ctx context.Context, requestID uuid.UUID) (*model.RequestDetail, error)
}
type SupplierService interface {
ExportExcel(ctx context.Context, requestID uuid.UUID) ([]byte, error)
}

View File

@@ -0,0 +1,57 @@
package service
import (
"context"
"math/rand"
"time"
"smart-search-back/internal/model"
"smart-search-back/internal/repository"
"smart-search-back/pkg/errors"
)
type inviteService struct {
inviteRepo repository.InviteRepository
userRepo repository.UserRepository
}
func NewInviteService(inviteRepo repository.InviteRepository, userRepo repository.UserRepository) InviteService {
return &inviteService{
inviteRepo: inviteRepo,
userRepo: userRepo,
}
}
func (s *inviteService) Generate(ctx context.Context, userID, maxUses, ttlDays int) (*model.InviteCode, error) {
canIssue, err := s.userRepo.CheckInviteLimit(ctx, userID)
if err != nil {
return nil, err
}
if !canIssue {
return nil, errors.NewBusinessError(errors.InviteLimitReached, "User reached maximum invite codes limit")
}
code := rand.Int63n(90000000) + 10000000
invite := &model.InviteCode{
UserID: userID,
Code: code,
CanBeUsedCount: maxUses,
ExpiresAt: time.Now().Add(time.Duration(ttlDays) * 24 * time.Hour),
}
if err := s.inviteRepo.Create(ctx, invite); err != nil {
return nil, err
}
if err := s.userRepo.IncrementInvitesIssued(ctx, userID); err != nil {
return nil, err
}
return invite, nil
}
func (s *inviteService) GetInfo(ctx context.Context, code int64) (*model.InviteCode, error) {
return s.inviteRepo.FindByCode(ctx, code)
}

154
internal/service/request.go Normal file
View File

@@ -0,0 +1,154 @@
package service
import (
"context"
"math"
"github.com/google/uuid"
"smart-search-back/internal/ai"
"smart-search-back/internal/model"
"smart-search-back/internal/repository"
"smart-search-back/pkg/errors"
)
type requestService struct {
requestRepo repository.RequestRepository
supplierRepo repository.SupplierRepository
tokenUsageRepo repository.TokenUsageRepository
userRepo repository.UserRepository
openAI *ai.OpenAIClient
perplexity *ai.PerplexityClient
}
func NewRequestService(
requestRepo repository.RequestRepository,
supplierRepo repository.SupplierRepository,
tokenUsageRepo repository.TokenUsageRepository,
userRepo repository.UserRepository,
openAI *ai.OpenAIClient,
perplexity *ai.PerplexityClient,
) RequestService {
return &requestService{
requestRepo: requestRepo,
supplierRepo: supplierRepo,
tokenUsageRepo: tokenUsageRepo,
userRepo: userRepo,
openAI: openAI,
perplexity: perplexity,
}
}
func (s *requestService) CreateTZ(ctx context.Context, userID int, requestTxt string) (uuid.UUID, string, error) {
req := &model.Request{
UserID: userID,
RequestTxt: requestTxt,
}
if err := s.requestRepo.Create(ctx, req); err != nil {
return uuid.Nil, "", err
}
if requestTxt == "" {
return req.ID, "", nil
}
tzText, err := s.openAI.GenerateTZ(requestTxt)
if err != nil {
if err := s.requestRepo.UpdateWithTZ(ctx, req.ID, "", false); err != nil {
return req.ID, "", err
}
return req.ID, "", err
}
inputLen := len(requestTxt)
outputLen := len(tzText)
promptTokens := 500
inputTokens := int(math.Ceil(float64(inputLen) / 2.0))
outputTokens := int(math.Ceil(float64(outputLen) / 2.0))
totalTokens := inputTokens + outputTokens + promptTokens
tokenPrice := 25000.0 / 1000000.0
cost := float64(totalTokens) * tokenPrice
tokenUsage := &model.TokenUsage{
RequestID: req.ID,
RequestTokenCount: inputTokens + promptTokens,
ResponseTokenCount: outputTokens,
TokenCost: cost,
Type: "tz",
}
if err := s.tokenUsageRepo.Create(ctx, tokenUsage); err != nil {
return req.ID, "", err
}
if err := s.userRepo.UpdateBalance(ctx, userID, -cost); err != nil {
return req.ID, "", err
}
if err := s.requestRepo.UpdateWithTZ(ctx, req.ID, tzText, true); err != nil {
return req.ID, "", err
}
return req.ID, tzText, nil
}
func (s *requestService) ApproveTZ(ctx context.Context, requestID uuid.UUID, tzText string, userID int) ([]*model.Supplier, error) {
if err := s.requestRepo.UpdateFinalTZ(ctx, requestID, tzText); err != nil {
return nil, err
}
var suppliers []*model.Supplier
var promptTokens, responseTokens int
var err error
for attempt := 0; attempt < 3; attempt++ {
suppliers, promptTokens, responseTokens, err = s.perplexity.FindSuppliers(tzText)
if err == nil {
break
}
}
if err != nil {
return nil, err
}
if len(suppliers) == 0 {
return nil, errors.NewInternalError(errors.AIAPIError, "no suppliers found", nil)
}
if err := s.supplierRepo.BulkInsert(ctx, requestID, suppliers); err != nil {
return nil, err
}
tokenPrice := 25000.0 / 1000000.0
totalTokens := promptTokens + responseTokens
cost := float64(totalTokens) * tokenPrice
tokenUsage := &model.TokenUsage{
RequestID: requestID,
RequestTokenCount: promptTokens,
ResponseTokenCount: responseTokens,
TokenCost: cost,
Type: "suppliers",
}
if err := s.tokenUsageRepo.Create(ctx, tokenUsage); err != nil {
return nil, err
}
if err := s.userRepo.UpdateBalance(ctx, userID, -cost); err != nil {
return nil, err
}
return suppliers, nil
}
func (s *requestService) GetMailingList(ctx context.Context, userID int) ([]*model.Request, error) {
return s.requestRepo.GetByUserID(ctx, userID)
}
func (s *requestService) GetMailingListByID(ctx context.Context, requestID uuid.UUID) (*model.RequestDetail, error) {
return s.requestRepo.GetDetailByID(ctx, requestID)
}

View File

@@ -0,0 +1,75 @@
package service
import (
"context"
"fmt"
"github.com/google/uuid"
"github.com/xuri/excelize/v2"
"smart-search-back/internal/repository"
)
type supplierService struct {
supplierRepo repository.SupplierRepository
}
func NewSupplierService(supplierRepo repository.SupplierRepository) SupplierService {
return &supplierService{
supplierRepo: supplierRepo,
}
}
func (s *supplierService) ExportExcel(ctx context.Context, requestID uuid.UUID) ([]byte, error) {
suppliers, err := s.supplierRepo.GetByRequestID(ctx, requestID)
if err != nil {
return nil, err
}
f := excelize.NewFile()
defer f.Close()
sheetName := "Suppliers"
index, err := f.NewSheet(sheetName)
if err != nil {
return nil, err
}
headers := []string{"Company ID", "Email", "Phone", "Company Name", "URL"}
for i, header := range headers {
cell := fmt.Sprintf("%c1", 'A'+i)
f.SetCellValue(sheetName, cell, header)
}
style, err := f.NewStyle(&excelize.Style{
Font: &excelize.Font{Bold: true},
Fill: excelize.Fill{Type: "pattern", Color: []string{"#E0E0E0"}, Pattern: 1},
})
if err == nil {
f.SetCellStyle(sheetName, "A1", fmt.Sprintf("%c1", 'A'+len(headers)-1), style)
}
for i, supplier := range suppliers {
row := i + 2
f.SetCellValue(sheetName, fmt.Sprintf("A%d", row), supplier.ID)
f.SetCellValue(sheetName, fmt.Sprintf("B%d", row), supplier.Email)
f.SetCellValue(sheetName, fmt.Sprintf("C%d", row), supplier.Phone)
f.SetCellValue(sheetName, fmt.Sprintf("D%d", row), supplier.Name)
f.SetCellValue(sheetName, fmt.Sprintf("E%d", row), supplier.URL)
}
f.SetColWidth(sheetName, "A", "A", 12)
f.SetColWidth(sheetName, "B", "B", 30)
f.SetColWidth(sheetName, "C", "C", 20)
f.SetColWidth(sheetName, "D", "D", 40)
f.SetColWidth(sheetName, "E", "E", 40)
f.SetActiveSheet(index)
f.DeleteSheet("Sheet1")
buffer, err := f.WriteToBuffer()
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}

View File

@@ -0,0 +1,449 @@
package tests
import (
"context"
"errors"
"testing"
"time"
"github.com/gojuno/minimock/v3"
"github.com/stretchr/testify/suite"
"smart-search-back/internal/mocks"
"smart-search-back/internal/model"
"smart-search-back/internal/service"
"smart-search-back/pkg/crypto"
apperrors "smart-search-back/pkg/errors"
"smart-search-back/pkg/jwt"
)
type Suite struct {
suite.Suite
ctx context.Context
authService service.AuthService
userRepo *mocks.UserRepositoryMock
sessionRepo *mocks.SessionRepositoryMock
}
func newSuite(ctx context.Context) *Suite {
return &Suite{ctx: ctx}
}
func TestAuthService(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
suite.Run(t, newSuite(ctx))
}
func (s *Suite) SetupSuite() {
s.ctx = context.Background()
}
func (s *Suite) SetupTest() {
ctrl := minimock.NewController(s.T())
s.userRepo = mocks.NewUserRepositoryMock(ctrl)
s.sessionRepo = mocks.NewSessionRepositoryMock(ctrl)
s.authService = service.NewAuthService(s.userRepo, s.sessionRepo)
}
func createTestUser(password string) *model.User {
return &model.User{
ID: 1,
Email: "test@example.com",
EmailHash: crypto.EmailHash("test@example.com"),
PasswordHash: crypto.PasswordHash(password),
CreatedAt: time.Now(),
}
}
func createTestSession(userID int) *model.Session {
return &model.Session{
ID: 1,
UserID: userID,
AccessToken: "test-access-token",
RefreshToken: "test-refresh-token",
IP: "127.0.0.1",
UserAgent: "test-agent",
CreatedAt: time.Now(),
ExpiresAt: time.Now().Add(30 * 24 * time.Hour),
}
}
func (s *Suite) TestAuthService_Login_Success() {
password := "testpassword"
user := createTestUser(password)
s.userRepo.FindByEmailHashMock.Return(user, nil)
s.sessionRepo.CreateMock.Return(nil)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
"test@example.com",
password,
"127.0.0.1",
"test-agent",
)
s.NoError(err)
s.NotEmpty(accessToken)
s.NotEmpty(refreshToken)
}
func (s *Suite) TestAuthService_Login_UserNotFound() {
err := apperrors.NewBusinessError(apperrors.UserNotFound, "user not found")
s.userRepo.FindByEmailHashMock.Return(nil, err)
accessToken, refreshToken, loginErr := s.authService.Login(
s.ctx,
"test@example.com",
"password",
"127.0.0.1",
"test-agent",
)
s.Error(loginErr)
s.Empty(accessToken)
s.Empty(refreshToken)
var appErr *apperrors.AppError
s.True(errors.As(loginErr, &appErr))
s.Equal(apperrors.UserNotFound, appErr.Code)
}
func (s *Suite) TestAuthService_Login_DatabaseError_OnFindUser() {
dbErr := apperrors.NewInternalError(apperrors.DatabaseError, "failed to find user", nil)
s.userRepo.FindByEmailHashMock.Return(nil, dbErr)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
"test@example.com",
"password",
"127.0.0.1",
"test-agent",
)
s.Error(err)
s.Empty(accessToken)
s.Empty(refreshToken)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.InternalErrorType, appErr.Type)
}
func (s *Suite) TestAuthService_Login_InvalidPassword() {
password := "correctpassword"
user := createTestUser(password)
s.userRepo.FindByEmailHashMock.Return(user, nil)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
"test@example.com",
"wrongpassword",
"127.0.0.1",
"test-agent",
)
s.Error(err)
s.Empty(accessToken)
s.Empty(refreshToken)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.AuthInvalidCredentials, appErr.Code)
s.Contains(appErr.Message, "Invalid email or password")
}
func (s *Suite) TestAuthService_Login_EmptyPassword() {
user := createTestUser("")
s.userRepo.FindByEmailHashMock.Return(user, nil)
s.sessionRepo.CreateMock.Return(nil)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
"test@example.com",
"",
"127.0.0.1",
"test-agent",
)
s.NoError(err)
s.NotEmpty(accessToken)
s.NotEmpty(refreshToken)
}
func (s *Suite) TestAuthService_Login_EmailWithSpacesAndCase() {
password := "testpassword"
normalizedEmail := "test@example.com"
user := createTestUser(password)
user.EmailHash = crypto.EmailHash(normalizedEmail)
s.userRepo.FindByEmailHashMock.Return(user, nil)
s.sessionRepo.CreateMock.Return(nil)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
" TEST@EXAMPLE.COM ",
password,
"127.0.0.1",
"test-agent",
)
s.NoError(err)
s.NotEmpty(accessToken)
s.NotEmpty(refreshToken)
}
func (s *Suite) TestAuthService_Login_EmptyEmail() {
err := apperrors.NewBusinessError(apperrors.UserNotFound, "user not found")
s.userRepo.FindByEmailHashMock.Return(nil, err)
accessToken, refreshToken, loginErr := s.authService.Login(
s.ctx,
"",
"password",
"127.0.0.1",
"test-agent",
)
s.Error(loginErr)
s.Empty(accessToken)
s.Empty(refreshToken)
}
func (s *Suite) TestAuthService_Login_SessionCreateError() {
password := "testpassword"
user := createTestUser(password)
s.userRepo.FindByEmailHashMock.Return(user, nil)
dbErr := apperrors.NewInternalError(apperrors.DatabaseError, "failed to create session", nil)
s.sessionRepo.CreateMock.Return(dbErr)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
"test@example.com",
password,
"127.0.0.1",
"test-agent",
)
s.Error(err)
s.Empty(accessToken)
s.Empty(refreshToken)
}
func (s *Suite) TestAuthService_Login_EmptyIPAndUserAgent() {
password := "testpassword"
user := createTestUser(password)
s.userRepo.FindByEmailHashMock.Return(user, nil)
s.sessionRepo.CreateMock.Return(nil)
accessToken, refreshToken, err := s.authService.Login(
s.ctx,
"test@example.com",
password,
"",
"",
)
s.NoError(err)
s.NotEmpty(accessToken)
s.NotEmpty(refreshToken)
}
func (s *Suite) TestAuthService_Refresh_Success() {
session := createTestSession(1)
s.sessionRepo.FindByRefreshTokenMock.Return(session, nil)
s.sessionRepo.UpdateAccessTokenMock.Return(nil)
accessToken, err := s.authService.Refresh(s.ctx, "test-refresh-token")
s.NoError(err)
s.NotEmpty(accessToken)
}
func (s *Suite) TestAuthService_Refresh_RefreshInvalid() {
err := apperrors.NewBusinessError(apperrors.RefreshInvalid, "refresh token is invalid or expired")
s.sessionRepo.FindByRefreshTokenMock.Return(nil, err)
accessToken, refreshErr := s.authService.Refresh(s.ctx, "invalid-token")
s.Error(refreshErr)
s.Empty(accessToken)
var appErr *apperrors.AppError
s.True(errors.As(refreshErr, &appErr))
s.Equal(apperrors.RefreshInvalid, appErr.Code)
}
func (s *Suite) TestAuthService_Refresh_DatabaseError_OnFindSession() {
dbErr := apperrors.NewInternalError(apperrors.DatabaseError, "failed to find session", nil)
s.sessionRepo.FindByRefreshTokenMock.Return(nil, dbErr)
accessToken, err := s.authService.Refresh(s.ctx, "test-refresh-token")
s.Error(err)
s.Empty(accessToken)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.InternalErrorType, appErr.Type)
}
func (s *Suite) TestAuthService_Refresh_EmptyToken() {
err := apperrors.NewBusinessError(apperrors.RefreshInvalid, "refresh token is invalid or expired")
s.sessionRepo.FindByRefreshTokenMock.Return(nil, err)
accessToken, refreshErr := s.authService.Refresh(s.ctx, "")
s.Error(refreshErr)
s.Empty(accessToken)
}
func (s *Suite) TestAuthService_Refresh_UpdateAccessTokenError() {
session := createTestSession(1)
s.sessionRepo.FindByRefreshTokenMock.Return(session, nil)
dbErr := apperrors.NewInternalError(apperrors.DatabaseError, "failed to update access token", nil)
s.sessionRepo.UpdateAccessTokenMock.Return(dbErr)
accessToken, err := s.authService.Refresh(s.ctx, "test-refresh-token")
s.Error(err)
s.Empty(accessToken)
}
func (s *Suite) TestAuthService_Refresh_UserIDZero() {
session := createTestSession(0)
s.sessionRepo.FindByRefreshTokenMock.Return(session, nil)
s.sessionRepo.UpdateAccessTokenMock.Return(nil)
accessToken, err := s.authService.Refresh(s.ctx, "test-refresh-token")
s.NoError(err)
s.NotEmpty(accessToken)
}
func (s *Suite) TestAuthService_Validate_Success() {
s.T().Parallel()
userID := 1
accessToken, err := jwt.GenerateAccessToken(userID)
s.NoError(err)
validatedUserID, validateErr := s.authService.Validate(s.ctx, accessToken)
s.NoError(validateErr)
s.Equal(userID, validatedUserID)
}
func (s *Suite) TestAuthService_Validate_EmptyToken() {
s.T().Parallel()
userID, err := s.authService.Validate(s.ctx, "")
s.Error(err)
s.Equal(0, userID)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.AuthInvalidToken, appErr.Code)
}
func (s *Suite) TestAuthService_Validate_InvalidTokenFormat() {
s.T().Parallel()
userID, err := s.authService.Validate(s.ctx, "invalid.token.format")
s.Error(err)
s.Equal(0, userID)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.AuthInvalidToken, appErr.Code)
}
func (s *Suite) TestAuthService_Validate_RefreshTokenInsteadOfAccess() {
s.T().Parallel()
userID := 1
refreshToken, err := jwt.GenerateRefreshToken(userID)
s.NoError(err)
validatedUserID, validateErr := s.authService.Validate(s.ctx, refreshToken)
s.Error(validateErr)
s.Equal(0, validatedUserID)
var appErr *apperrors.AppError
s.True(errors.As(validateErr, &appErr))
s.Equal(apperrors.AuthInvalidToken, appErr.Code)
s.Contains(appErr.Message, "not an access token")
}
func (s *Suite) TestAuthService_Validate_UserIDZero() {
s.T().Parallel()
accessToken, err := jwt.GenerateAccessToken(0)
s.NoError(err)
validatedUserID, validateErr := s.authService.Validate(s.ctx, accessToken)
s.NoError(validateErr)
s.Equal(0, validatedUserID)
}
func (s *Suite) TestAuthService_Validate_InvalidSignature() {
s.T().Parallel()
invalidToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwidHlwZSI6ImFjY2VzcyIsImlhdCI6MTYwOTQ1NjgwMCwiZXhwIjoxNjA5NDY1ODAwfQ.invalid-signature"
userID, err := s.authService.Validate(s.ctx, invalidToken)
s.Error(err)
s.Equal(0, userID)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.AuthInvalidToken, appErr.Code)
}
func (s *Suite) TestAuthService_Logout_Success() {
s.sessionRepo.RevokeMock.Return(nil)
err := s.authService.Logout(s.ctx, "test-refresh-token")
s.NoError(err)
}
func (s *Suite) TestAuthService_Logout_DatabaseError() {
dbErr := apperrors.NewInternalError(apperrors.DatabaseError, "failed to revoke session", nil)
s.sessionRepo.RevokeMock.Return(dbErr)
err := s.authService.Logout(s.ctx, "test-refresh-token")
s.Error(err)
var appErr *apperrors.AppError
s.True(errors.As(err, &appErr))
s.Equal(apperrors.InternalErrorType, appErr.Type)
}
func (s *Suite) TestAuthService_Logout_EmptyToken() {
s.sessionRepo.RevokeMock.Return(nil)
err := s.authService.Logout(s.ctx, "")
s.NoError(err)
}
func (s *Suite) TestAuthService_Logout_NonExistentToken() {
s.sessionRepo.RevokeMock.Return(nil)
err := s.authService.Logout(s.ctx, "non-existent-token")
s.NoError(err)
}

83
internal/service/user.go Normal file
View File

@@ -0,0 +1,83 @@
package service
import (
"context"
"smart-search-back/internal/repository"
"smart-search-back/pkg/crypto"
)
type userService struct {
userRepo repository.UserRepository
requestRepo repository.RequestRepository
cryptoHelper *crypto.Crypto
}
type UserInfo struct {
Email string
Name string
Phone string
CompanyName string
PaymentStatus string
}
type Statistics struct {
RequestsCount int
SuppliersCount int
CreatedTZ int
}
func NewUserService(userRepo repository.UserRepository, requestRepo repository.RequestRepository, cryptoSecret string) UserService {
return &userService{
userRepo: userRepo,
requestRepo: requestRepo,
cryptoHelper: crypto.NewCrypto(cryptoSecret),
}
}
func (s *userService) GetInfo(ctx context.Context, userID int) (*UserInfo, error) {
user, err := s.userRepo.FindByID(ctx, userID)
if err != nil {
return nil, err
}
email, err := s.cryptoHelper.Decrypt(user.Email)
if err != nil {
return nil, err
}
phone, err := s.cryptoHelper.Decrypt(user.Phone)
if err != nil {
return nil, err
}
userName, err := s.cryptoHelper.Decrypt(user.UserName)
if err != nil {
return nil, err
}
return &UserInfo{
Email: email,
Name: userName,
Phone: phone,
CompanyName: user.CompanyName,
PaymentStatus: user.PaymentStatus,
}, nil
}
func (s *userService) GetBalance(ctx context.Context, userID int) (float64, error) {
return s.userRepo.GetBalance(ctx, userID)
}
func (s *userService) GetStatistics(ctx context.Context, userID int) (*Statistics, error) {
requestsCount, suppliersCount, createdTZ, err := s.requestRepo.GetUserStatistics(ctx, userID)
if err != nil {
return nil, err
}
return &Statistics{
RequestsCount: requestsCount,
SuppliersCount: suppliersCount,
CreatedTZ: createdTZ,
}, nil
}

View File

@@ -0,0 +1,69 @@
package worker
import (
"context"
"log"
"time"
"smart-search-back/internal/repository"
)
type InviteCleaner struct {
inviteRepo repository.InviteRepository
ctx context.Context
ticker *time.Ticker
done chan bool
}
func NewInviteCleaner(ctx context.Context, inviteRepo repository.InviteRepository) *InviteCleaner {
return &InviteCleaner{
inviteRepo: inviteRepo,
ctx: ctx,
done: make(chan bool),
}
}
func (w *InviteCleaner) Start() {
w.ticker = time.NewTicker(6 * time.Hour)
w.deactivateExpiredInvites()
go func() {
for {
select {
case <-w.ticker.C:
w.deactivateExpiredInvites()
case <-w.done:
return
case <-w.ctx.Done():
log.Println("Invite cleaner context cancelled, stopping worker")
return
}
}
}()
log.Println("Invite cleaner worker started (runs every 6 hours)")
}
func (w *InviteCleaner) Stop() {
if w.ticker != nil {
w.ticker.Stop()
}
select {
case w.done <- true:
default:
}
log.Println("Invite cleaner worker stopped")
}
func (w *InviteCleaner) deactivateExpiredInvites() {
count, err := w.inviteRepo.DeactivateExpired(w.ctx)
if err != nil {
log.Printf("Error deactivating expired invites: %v", err)
return
}
if count > 0 {
log.Printf("Deactivated %d expired invite codes", count)
}
}

View File

@@ -0,0 +1,69 @@
package worker
import (
"context"
"log"
"time"
"smart-search-back/internal/repository"
)
type SessionCleaner struct {
sessionRepo repository.SessionRepository
ctx context.Context
ticker *time.Ticker
done chan bool
}
func NewSessionCleaner(ctx context.Context, sessionRepo repository.SessionRepository) *SessionCleaner {
return &SessionCleaner{
sessionRepo: sessionRepo,
ctx: ctx,
done: make(chan bool),
}
}
func (w *SessionCleaner) Start() {
w.ticker = time.NewTicker(1 * time.Hour)
w.cleanExpiredSessions()
go func() {
for {
select {
case <-w.ticker.C:
w.cleanExpiredSessions()
case <-w.done:
return
case <-w.ctx.Done():
log.Println("Session cleaner context cancelled, stopping worker")
return
}
}
}()
log.Println("Session cleaner worker started (runs every hour)")
}
func (w *SessionCleaner) Stop() {
if w.ticker != nil {
w.ticker.Stop()
}
select {
case w.done <- true:
default:
}
log.Println("Session cleaner worker stopped")
}
func (w *SessionCleaner) cleanExpiredSessions() {
count, err := w.sessionRepo.DeleteExpired(w.ctx)
if err != nil {
log.Printf("Error cleaning expired sessions: %v", err)
return
}
if count > 0 {
log.Printf("Cleaned %d expired sessions", count)
}
}

View File

@@ -0,0 +1,20 @@
-- +goose Up
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email TEXT NOT NULL,
email_hash TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
phone TEXT,
user_name TEXT,
company_name TEXT,
balance NUMERIC(10, 5) DEFAULT 0.0,
payment_status TEXT DEFAULT 'unpaid',
invites_issued INT DEFAULT 0,
invites_limit INT DEFAULT 10,
created_at TIMESTAMP DEFAULT now()
);
CREATE INDEX idx_users_email_hash ON users(email_hash);
-- +goose Down
DROP TABLE users;

View File

@@ -0,0 +1,18 @@
-- +goose Up
CREATE TABLE sessions (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
access_token TEXT NOT NULL,
refresh_token TEXT NOT NULL UNIQUE,
ip TEXT,
user_agent TEXT,
created_at TIMESTAMP DEFAULT now(),
expires_at TIMESTAMP NOT NULL,
revoked_at TIMESTAMP
);
CREATE INDEX idx_sessions_refresh ON sessions(refresh_token);
CREATE INDEX idx_sessions_user_id ON sessions(user_id);
-- +goose Down
DROP TABLE sessions;

View File

@@ -0,0 +1,17 @@
-- +goose Up
CREATE TABLE invite_codes (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
code BIGINT NOT NULL UNIQUE,
can_be_used_count INT DEFAULT 10,
used_count INT DEFAULT 0,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT now(),
expires_at TIMESTAMP NOT NULL
);
CREATE INDEX idx_invite_codes_code ON invite_codes(code);
CREATE INDEX idx_invite_codes_user_id ON invite_codes(user_id);
-- +goose Down
DROP TABLE invite_codes;

View File

@@ -0,0 +1,14 @@
-- +goose Up
CREATE TABLE mailing_status (
id SERIAL PRIMARY KEY,
status_name TEXT NOT NULL UNIQUE
);
INSERT INTO mailing_status (status_name) VALUES
('pending'),
('in_progress'),
('completed'),
('failed');
-- +goose Down
DROP TABLE mailing_status;

View File

@@ -0,0 +1,18 @@
-- +goose Up
CREATE TABLE requests_for_suppliers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
request_txt TEXT,
generated_tz BOOLEAN DEFAULT false,
final_tz TEXT,
generated_final_tz BOOLEAN DEFAULT false,
final_update_tz TEXT,
mailling_status_id INT REFERENCES mailing_status(id) DEFAULT 1,
created_at TIMESTAMP DEFAULT now()
);
CREATE INDEX idx_requests_user_id ON requests_for_suppliers(user_id);
CREATE INDEX idx_requests_status ON requests_for_suppliers(mailling_status_id);
-- +goose Down
DROP TABLE requests_for_suppliers;

View File

@@ -0,0 +1,16 @@
-- +goose Up
CREATE TABLE suppliers (
id SERIAL PRIMARY KEY,
request_id UUID NOT NULL REFERENCES requests_for_suppliers(id) ON DELETE CASCADE,
name TEXT NOT NULL,
email TEXT,
phone TEXT,
adress TEXT,
url TEXT,
created_at TIMESTAMP DEFAULT now()
);
CREATE INDEX idx_suppliers_request_id ON suppliers(request_id);
-- +goose Down
DROP TABLE suppliers;

View File

@@ -0,0 +1,15 @@
-- +goose Up
CREATE TABLE request_token_usage (
id SERIAL PRIMARY KEY,
request_id UUID NOT NULL REFERENCES requests_for_suppliers(id) ON DELETE CASCADE,
request_token_count INT DEFAULT 0,
response_token_count INT DEFAULT 0,
token_cost NUMERIC(10, 5) DEFAULT 0.0,
type TEXT NOT NULL,
created_at TIMESTAMP DEFAULT now()
);
CREATE INDEX idx_token_usage_request_id ON request_token_usage(request_id);
-- +goose Down
DROP TABLE request_token_usage;

125
pkg/crypto/crypto.go Normal file
View File

@@ -0,0 +1,125 @@
package crypto
import (
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"errors"
"fmt"
"strings"
)
type Crypto struct {
secret string
}
func NewCrypto(secret string) *Crypto {
return &Crypto{secret: secret}
}
func (c *Crypto) EmailHash(email string) string {
if email == "" {
return email
}
normalized := strings.TrimSpace(strings.ToLower(email))
h := hmac.New(sha256.New, []byte(c.secret))
h.Write([]byte(normalized))
return hex.EncodeToString(h.Sum(nil))
}
func PasswordHash(password string) string {
h := sha512.New()
h.Write([]byte(password))
return hex.EncodeToString(h.Sum(nil))
}
func (c *Crypto) getKey() []byte {
h := sha256.New()
h.Write([]byte(c.secret))
return h.Sum(nil)
}
func (c *Crypto) Encrypt(plaintext string) (string, error) {
if plaintext == "" {
return plaintext, nil
}
key := c.getKey()
block, err := aes.NewCipher(key)
if err != nil {
return "", fmt.Errorf("failed to create cipher: %w", err)
}
aesGCM, err := cipher.NewGCM(block)
if err != nil {
return "", fmt.Errorf("failed to create GCM: %w", err)
}
iv := make([]byte, aesGCM.NonceSize())
if _, err := rand.Read(iv); err != nil {
return "", fmt.Errorf("failed to generate IV: %w", err)
}
ciphertext := aesGCM.Seal(nil, iv, []byte(plaintext), nil)
tag := ciphertext[len(ciphertext)-aesGCM.Overhead():]
cipherOnly := ciphertext[:len(ciphertext)-aesGCM.Overhead()]
return fmt.Sprintf("%s:%s:%s",
hex.EncodeToString(iv),
hex.EncodeToString(tag),
hex.EncodeToString(cipherOnly),
), nil
}
func (c *Crypto) Decrypt(ciphertext string) (string, error) {
if ciphertext == "" {
return ciphertext, nil
}
parts := strings.Split(ciphertext, ":")
if len(parts) != 3 {
return "", errors.New("invalid encrypted value format")
}
ivHex, tagHex, cipherHex := parts[0], parts[1], parts[2]
iv, err := hex.DecodeString(ivHex)
if err != nil {
return "", fmt.Errorf("failed to decode IV: %w", err)
}
tag, err := hex.DecodeString(tagHex)
if err != nil {
return "", fmt.Errorf("failed to decode tag: %w", err)
}
cipherOnly, err := hex.DecodeString(cipherHex)
if err != nil {
return "", fmt.Errorf("failed to decode ciphertext: %w", err)
}
key := c.getKey()
block, err := aes.NewCipher(key)
if err != nil {
return "", fmt.Errorf("failed to create cipher: %w", err)
}
aesGCM, err := cipher.NewGCM(block)
if err != nil {
return "", fmt.Errorf("failed to create GCM: %w", err)
}
ciphertextWithTag := append(cipherOnly, tag...)
plaintext, err := aesGCM.Open(nil, iv, ciphertextWithTag, nil)
if err != nil {
return "", fmt.Errorf("failed to decrypt: %w", err)
}
return string(plaintext), nil
}

17
pkg/errors/codes.go Normal file
View File

@@ -0,0 +1,17 @@
package errors
const (
AuthInvalidCredentials = "AUTH_INVALID_CREDENTIALS"
AuthMissing = "AUTH_MISSING"
AuthInvalidToken = "AUTH_INVALID_TOKEN"
RefreshInvalid = "REFRESH_INVALID"
InviteLimitReached = "INVITE_LIMIT_REACHED"
InsufficientBalance = "INSUFFICIENT_BALANCE"
UserNotFound = "USER_NOT_FOUND"
RequestNotFound = "REQUEST_NOT_FOUND"
DatabaseError = "DATABASE_ERROR"
EncryptionError = "ENCRYPTION_ERROR"
AIAPIError = "AI_API_ERROR"
InternalError = "INTERNAL_ERROR"
)

95
pkg/errors/errors.go Normal file
View File

@@ -0,0 +1,95 @@
package errors
import (
"errors"
"fmt"
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type ErrorType int
const (
BusinessError ErrorType = iota
InternalErrorType
)
type AppError struct {
Code string
Message string
Type ErrorType
Err error
}
func (e *AppError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%s: %s (%v)", e.Code, e.Message, e.Err)
}
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
func (e *AppError) Unwrap() error {
return e.Err
}
func NewBusinessError(code, message string) *AppError {
return &AppError{
Code: code,
Message: message,
Type: BusinessError,
}
}
func NewInternalError(code, message string, err error) *AppError {
return &AppError{
Code: code,
Message: message,
Type: InternalErrorType,
Err: err,
}
}
func ToGRPCError(err error, zapLogger *zap.Logger, method string) error {
if err == nil {
return nil
}
var appErr *AppError
ok := errors.As(err, &appErr)
if !ok {
if zapLogger != nil {
zapLogger.Error("gRPC handler error: unknown error type",
zap.String("method", method),
zap.Error(err),
)
}
return status.Error(codes.Internal, "internal server error")
}
if appErr.Type == InternalErrorType {
if zapLogger != nil {
zapLogger.Error("gRPC handler error: internal error",
zap.String("method", method),
zap.String("code", appErr.Code),
zap.String("message", appErr.Message),
zap.Error(appErr.Err),
)
}
return status.Error(codes.Internal, "internal server error")
}
switch appErr.Code {
case AuthInvalidCredentials, AuthMissing, AuthInvalidToken, RefreshInvalid:
return status.Error(codes.Unauthenticated, appErr.Message)
case InviteLimitReached:
return status.Error(codes.ResourceExhausted, appErr.Message)
case InsufficientBalance:
return status.Error(codes.FailedPrecondition, appErr.Message)
case UserNotFound, RequestNotFound:
return status.Error(codes.NotFound, appErr.Message)
default:
return status.Error(codes.Unknown, appErr.Message)
}
}

79
pkg/jwt/jwt.go Normal file
View File

@@ -0,0 +1,79 @@
package jwt
import (
"errors"
"fmt"
"strconv"
"time"
"github.com/golang-jwt/jwt/v5"
)
type Claims struct {
Sub string `json:"sub"`
Type string `json:"type"`
jwt.RegisteredClaims
}
func GenerateAccessToken(userID int, secret string) (string, error) {
now := time.Now()
claims := Claims{
Sub: strconv.Itoa(userID),
Type: "access",
RegisteredClaims: jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(now),
ExpiresAt: jwt.NewNumericDate(now.Add(15 * time.Minute)),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret))
}
func GenerateRefreshToken(userID int, secret string) (string, error) {
now := time.Now()
claims := Claims{
Sub: strconv.Itoa(userID),
Type: "refresh",
RegisteredClaims: jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(now),
ExpiresAt: jwt.NewNumericDate(now.Add(30 * 24 * time.Hour)),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret))
}
func ValidateToken(tokenString, secret string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(secret), nil
})
if err != nil {
return nil, fmt.Errorf("failed to parse token: %w", err)
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
func GetUserIDFromToken(tokenString, secret string) (int, error) {
claims, err := ValidateToken(tokenString, secret)
if err != nil {
return 0, err
}
userID, err := strconv.Atoi(claims.Sub)
if err != nil {
return 0, fmt.Errorf("invalid user ID in token: %w", err)
}
return userID, nil
}

View File

@@ -0,0 +1,538 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v5.29.3
// source: api/proto/auth/auth.proto
package auth
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type LoginRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
Ip string `protobuf:"bytes,3,opt,name=ip,proto3" json:"ip,omitempty"`
UserAgent string `protobuf:"bytes,4,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *LoginRequest) Reset() {
*x = LoginRequest{}
mi := &file_api_proto_auth_auth_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *LoginRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*LoginRequest) ProtoMessage() {}
func (x *LoginRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_auth_auth_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use LoginRequest.ProtoReflect.Descriptor instead.
func (*LoginRequest) Descriptor() ([]byte, []int) {
return file_api_proto_auth_auth_proto_rawDescGZIP(), []int{0}
}
func (x *LoginRequest) GetEmail() string {
if x != nil {
return x.Email
}
return ""
}
func (x *LoginRequest) GetPassword() string {
if x != nil {
return x.Password
}
return ""
}
func (x *LoginRequest) GetIp() string {
if x != nil {
return x.Ip
}
return ""
}
func (x *LoginRequest) GetUserAgent() string {
if x != nil {
return x.UserAgent
}
return ""
}
type LoginResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
RefreshToken string `protobuf:"bytes,2,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *LoginResponse) Reset() {
*x = LoginResponse{}
mi := &file_api_proto_auth_auth_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *LoginResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*LoginResponse) ProtoMessage() {}
func (x *LoginResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_auth_auth_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use LoginResponse.ProtoReflect.Descriptor instead.
func (*LoginResponse) Descriptor() ([]byte, []int) {
return file_api_proto_auth_auth_proto_rawDescGZIP(), []int{1}
}
func (x *LoginResponse) GetAccessToken() string {
if x != nil {
return x.AccessToken
}
return ""
}
func (x *LoginResponse) GetRefreshToken() string {
if x != nil {
return x.RefreshToken
}
return ""
}
type RefreshRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
RefreshToken string `protobuf:"bytes,1,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
Ip string `protobuf:"bytes,2,opt,name=ip,proto3" json:"ip,omitempty"`
UserAgent string `protobuf:"bytes,3,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RefreshRequest) Reset() {
*x = RefreshRequest{}
mi := &file_api_proto_auth_auth_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RefreshRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RefreshRequest) ProtoMessage() {}
func (x *RefreshRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_auth_auth_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RefreshRequest.ProtoReflect.Descriptor instead.
func (*RefreshRequest) Descriptor() ([]byte, []int) {
return file_api_proto_auth_auth_proto_rawDescGZIP(), []int{2}
}
func (x *RefreshRequest) GetRefreshToken() string {
if x != nil {
return x.RefreshToken
}
return ""
}
func (x *RefreshRequest) GetIp() string {
if x != nil {
return x.Ip
}
return ""
}
func (x *RefreshRequest) GetUserAgent() string {
if x != nil {
return x.UserAgent
}
return ""
}
type RefreshResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
RefreshToken string `protobuf:"bytes,2,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RefreshResponse) Reset() {
*x = RefreshResponse{}
mi := &file_api_proto_auth_auth_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RefreshResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RefreshResponse) ProtoMessage() {}
func (x *RefreshResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_auth_auth_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RefreshResponse.ProtoReflect.Descriptor instead.
func (*RefreshResponse) Descriptor() ([]byte, []int) {
return file_api_proto_auth_auth_proto_rawDescGZIP(), []int{3}
}
func (x *RefreshResponse) GetAccessToken() string {
if x != nil {
return x.AccessToken
}
return ""
}
func (x *RefreshResponse) GetRefreshToken() string {
if x != nil {
return x.RefreshToken
}
return ""
}
type ValidateRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ValidateRequest) Reset() {
*x = ValidateRequest{}
mi := &file_api_proto_auth_auth_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ValidateRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ValidateRequest) ProtoMessage() {}
func (x *ValidateRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_auth_auth_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ValidateRequest.ProtoReflect.Descriptor instead.
func (*ValidateRequest) Descriptor() ([]byte, []int) {
return file_api_proto_auth_auth_proto_rawDescGZIP(), []int{4}
}
func (x *ValidateRequest) GetAccessToken() string {
if x != nil {
return x.AccessToken
}
return ""
}
type ValidateResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Valid bool `protobuf:"varint,1,opt,name=valid,proto3" json:"valid,omitempty"`
UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ValidateResponse) Reset() {
*x = ValidateResponse{}
mi := &file_api_proto_auth_auth_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ValidateResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ValidateResponse) ProtoMessage() {}
func (x *ValidateResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_auth_auth_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ValidateResponse.ProtoReflect.Descriptor instead.
func (*ValidateResponse) Descriptor() ([]byte, []int) {
return file_api_proto_auth_auth_proto_rawDescGZIP(), []int{5}
}
func (x *ValidateResponse) GetValid() bool {
if x != nil {
return x.Valid
}
return false
}
func (x *ValidateResponse) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
type LogoutRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *LogoutRequest) Reset() {
*x = LogoutRequest{}
mi := &file_api_proto_auth_auth_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *LogoutRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*LogoutRequest) ProtoMessage() {}
func (x *LogoutRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_auth_auth_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use LogoutRequest.ProtoReflect.Descriptor instead.
func (*LogoutRequest) Descriptor() ([]byte, []int) {
return file_api_proto_auth_auth_proto_rawDescGZIP(), []int{6}
}
func (x *LogoutRequest) GetAccessToken() string {
if x != nil {
return x.AccessToken
}
return ""
}
type LogoutResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *LogoutResponse) Reset() {
*x = LogoutResponse{}
mi := &file_api_proto_auth_auth_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *LogoutResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*LogoutResponse) ProtoMessage() {}
func (x *LogoutResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_auth_auth_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use LogoutResponse.ProtoReflect.Descriptor instead.
func (*LogoutResponse) Descriptor() ([]byte, []int) {
return file_api_proto_auth_auth_proto_rawDescGZIP(), []int{7}
}
func (x *LogoutResponse) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
var File_api_proto_auth_auth_proto protoreflect.FileDescriptor
const file_api_proto_auth_auth_proto_rawDesc = "" +
"\n" +
"\x19api/proto/auth/auth.proto\x12\x04auth\"o\n" +
"\fLoginRequest\x12\x14\n" +
"\x05email\x18\x01 \x01(\tR\x05email\x12\x1a\n" +
"\bpassword\x18\x02 \x01(\tR\bpassword\x12\x0e\n" +
"\x02ip\x18\x03 \x01(\tR\x02ip\x12\x1d\n" +
"\n" +
"user_agent\x18\x04 \x01(\tR\tuserAgent\"W\n" +
"\rLoginResponse\x12!\n" +
"\faccess_token\x18\x01 \x01(\tR\vaccessToken\x12#\n" +
"\rrefresh_token\x18\x02 \x01(\tR\frefreshToken\"d\n" +
"\x0eRefreshRequest\x12#\n" +
"\rrefresh_token\x18\x01 \x01(\tR\frefreshToken\x12\x0e\n" +
"\x02ip\x18\x02 \x01(\tR\x02ip\x12\x1d\n" +
"\n" +
"user_agent\x18\x03 \x01(\tR\tuserAgent\"Y\n" +
"\x0fRefreshResponse\x12!\n" +
"\faccess_token\x18\x01 \x01(\tR\vaccessToken\x12#\n" +
"\rrefresh_token\x18\x02 \x01(\tR\frefreshToken\"4\n" +
"\x0fValidateRequest\x12!\n" +
"\faccess_token\x18\x01 \x01(\tR\vaccessToken\"A\n" +
"\x10ValidateResponse\x12\x14\n" +
"\x05valid\x18\x01 \x01(\bR\x05valid\x12\x17\n" +
"\auser_id\x18\x02 \x01(\x03R\x06userId\"2\n" +
"\rLogoutRequest\x12!\n" +
"\faccess_token\x18\x01 \x01(\tR\vaccessToken\"*\n" +
"\x0eLogoutResponse\x12\x18\n" +
"\asuccess\x18\x01 \x01(\bR\asuccess2\xe7\x01\n" +
"\vAuthService\x120\n" +
"\x05Login\x12\x12.auth.LoginRequest\x1a\x13.auth.LoginResponse\x126\n" +
"\aRefresh\x12\x14.auth.RefreshRequest\x1a\x15.auth.RefreshResponse\x129\n" +
"\bValidate\x12\x15.auth.ValidateRequest\x1a\x16.auth.ValidateResponse\x123\n" +
"\x06Logout\x12\x13.auth.LogoutRequest\x1a\x14.auth.LogoutResponseB5Z3github.com/smart-search-gateway/api/proto/auth/authb\x06proto3"
var (
file_api_proto_auth_auth_proto_rawDescOnce sync.Once
file_api_proto_auth_auth_proto_rawDescData []byte
)
func file_api_proto_auth_auth_proto_rawDescGZIP() []byte {
file_api_proto_auth_auth_proto_rawDescOnce.Do(func() {
file_api_proto_auth_auth_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_proto_auth_auth_proto_rawDesc), len(file_api_proto_auth_auth_proto_rawDesc)))
})
return file_api_proto_auth_auth_proto_rawDescData
}
var file_api_proto_auth_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_api_proto_auth_auth_proto_goTypes = []any{
(*LoginRequest)(nil), // 0: auth.LoginRequest
(*LoginResponse)(nil), // 1: auth.LoginResponse
(*RefreshRequest)(nil), // 2: auth.RefreshRequest
(*RefreshResponse)(nil), // 3: auth.RefreshResponse
(*ValidateRequest)(nil), // 4: auth.ValidateRequest
(*ValidateResponse)(nil), // 5: auth.ValidateResponse
(*LogoutRequest)(nil), // 6: auth.LogoutRequest
(*LogoutResponse)(nil), // 7: auth.LogoutResponse
}
var file_api_proto_auth_auth_proto_depIdxs = []int32{
0, // 0: auth.AuthService.Login:input_type -> auth.LoginRequest
2, // 1: auth.AuthService.Refresh:input_type -> auth.RefreshRequest
4, // 2: auth.AuthService.Validate:input_type -> auth.ValidateRequest
6, // 3: auth.AuthService.Logout:input_type -> auth.LogoutRequest
1, // 4: auth.AuthService.Login:output_type -> auth.LoginResponse
3, // 5: auth.AuthService.Refresh:output_type -> auth.RefreshResponse
5, // 6: auth.AuthService.Validate:output_type -> auth.ValidateResponse
7, // 7: auth.AuthService.Logout:output_type -> auth.LogoutResponse
4, // [4:8] is the sub-list for method output_type
0, // [0:4] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_api_proto_auth_auth_proto_init() }
func file_api_proto_auth_auth_proto_init() {
if File_api_proto_auth_auth_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_proto_auth_auth_proto_rawDesc), len(file_api_proto_auth_auth_proto_rawDesc)),
NumEnums: 0,
NumMessages: 8,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_api_proto_auth_auth_proto_goTypes,
DependencyIndexes: file_api_proto_auth_auth_proto_depIdxs,
MessageInfos: file_api_proto_auth_auth_proto_msgTypes,
}.Build()
File_api_proto_auth_auth_proto = out.File
file_api_proto_auth_auth_proto_goTypes = nil
file_api_proto_auth_auth_proto_depIdxs = nil
}

View File

@@ -0,0 +1,235 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.29.3
// source: api/proto/auth/auth.proto
package auth
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
AuthService_Login_FullMethodName = "/auth.AuthService/Login"
AuthService_Refresh_FullMethodName = "/auth.AuthService/Refresh"
AuthService_Validate_FullMethodName = "/auth.AuthService/Validate"
AuthService_Logout_FullMethodName = "/auth.AuthService/Logout"
)
// AuthServiceClient is the client API for AuthService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type AuthServiceClient interface {
Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error)
Refresh(ctx context.Context, in *RefreshRequest, opts ...grpc.CallOption) (*RefreshResponse, error)
Validate(ctx context.Context, in *ValidateRequest, opts ...grpc.CallOption) (*ValidateResponse, error)
Logout(ctx context.Context, in *LogoutRequest, opts ...grpc.CallOption) (*LogoutResponse, error)
}
type authServiceClient struct {
cc grpc.ClientConnInterface
}
func NewAuthServiceClient(cc grpc.ClientConnInterface) AuthServiceClient {
return &authServiceClient{cc}
}
func (c *authServiceClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LoginResponse)
err := c.cc.Invoke(ctx, AuthService_Login_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authServiceClient) Refresh(ctx context.Context, in *RefreshRequest, opts ...grpc.CallOption) (*RefreshResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RefreshResponse)
err := c.cc.Invoke(ctx, AuthService_Refresh_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authServiceClient) Validate(ctx context.Context, in *ValidateRequest, opts ...grpc.CallOption) (*ValidateResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ValidateResponse)
err := c.cc.Invoke(ctx, AuthService_Validate_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authServiceClient) Logout(ctx context.Context, in *LogoutRequest, opts ...grpc.CallOption) (*LogoutResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LogoutResponse)
err := c.cc.Invoke(ctx, AuthService_Logout_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// AuthServiceServer is the server API for AuthService service.
// All implementations must embed UnimplementedAuthServiceServer
// for forward compatibility.
type AuthServiceServer interface {
Login(context.Context, *LoginRequest) (*LoginResponse, error)
Refresh(context.Context, *RefreshRequest) (*RefreshResponse, error)
Validate(context.Context, *ValidateRequest) (*ValidateResponse, error)
Logout(context.Context, *LogoutRequest) (*LogoutResponse, error)
mustEmbedUnimplementedAuthServiceServer()
}
// UnimplementedAuthServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedAuthServiceServer struct{}
func (UnimplementedAuthServiceServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Login not implemented")
}
func (UnimplementedAuthServiceServer) Refresh(context.Context, *RefreshRequest) (*RefreshResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Refresh not implemented")
}
func (UnimplementedAuthServiceServer) Validate(context.Context, *ValidateRequest) (*ValidateResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Validate not implemented")
}
func (UnimplementedAuthServiceServer) Logout(context.Context, *LogoutRequest) (*LogoutResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Logout not implemented")
}
func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {}
func (UnimplementedAuthServiceServer) testEmbeddedByValue() {}
// UnsafeAuthServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to AuthServiceServer will
// result in compilation errors.
type UnsafeAuthServiceServer interface {
mustEmbedUnimplementedAuthServiceServer()
}
func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) {
// If the following call pancis, it indicates UnimplementedAuthServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&AuthService_ServiceDesc, srv)
}
func _AuthService_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LoginRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServiceServer).Login(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: AuthService_Login_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServiceServer).Login(ctx, req.(*LoginRequest))
}
return interceptor(ctx, in, info, handler)
}
func _AuthService_Refresh_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RefreshRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServiceServer).Refresh(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: AuthService_Refresh_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServiceServer).Refresh(ctx, req.(*RefreshRequest))
}
return interceptor(ctx, in, info, handler)
}
func _AuthService_Validate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ValidateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServiceServer).Validate(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: AuthService_Validate_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServiceServer).Validate(ctx, req.(*ValidateRequest))
}
return interceptor(ctx, in, info, handler)
}
func _AuthService_Logout_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LogoutRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServiceServer).Logout(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: AuthService_Logout_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServiceServer).Logout(ctx, req.(*LogoutRequest))
}
return interceptor(ctx, in, info, handler)
}
// AuthService_ServiceDesc is the grpc.ServiceDesc for AuthService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var AuthService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "auth.AuthService",
HandlerType: (*AuthServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Login",
Handler: _AuthService_Login_Handler,
},
{
MethodName: "Refresh",
Handler: _AuthService_Refresh_Handler,
},
{
MethodName: "Validate",
Handler: _AuthService_Validate_Handler,
},
{
MethodName: "Logout",
Handler: _AuthService_Logout_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/proto/auth/auth.proto",
}

View File

@@ -0,0 +1,369 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v5.29.3
// source: api/proto/invite/invite.proto
package invite
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type GenerateRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
TtlDays int32 `protobuf:"varint,2,opt,name=ttl_days,json=ttlDays,proto3" json:"ttl_days,omitempty"`
MaxUses int32 `protobuf:"varint,3,opt,name=max_uses,json=maxUses,proto3" json:"max_uses,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GenerateRequest) Reset() {
*x = GenerateRequest{}
mi := &file_api_proto_invite_invite_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GenerateRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GenerateRequest) ProtoMessage() {}
func (x *GenerateRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_invite_invite_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GenerateRequest.ProtoReflect.Descriptor instead.
func (*GenerateRequest) Descriptor() ([]byte, []int) {
return file_api_proto_invite_invite_proto_rawDescGZIP(), []int{0}
}
func (x *GenerateRequest) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
func (x *GenerateRequest) GetTtlDays() int32 {
if x != nil {
return x.TtlDays
}
return 0
}
func (x *GenerateRequest) GetMaxUses() int32 {
if x != nil {
return x.MaxUses
}
return 0
}
type GenerateResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"`
MaxUses int32 `protobuf:"varint,2,opt,name=max_uses,json=maxUses,proto3" json:"max_uses,omitempty"`
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GenerateResponse) Reset() {
*x = GenerateResponse{}
mi := &file_api_proto_invite_invite_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GenerateResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GenerateResponse) ProtoMessage() {}
func (x *GenerateResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_invite_invite_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GenerateResponse.ProtoReflect.Descriptor instead.
func (*GenerateResponse) Descriptor() ([]byte, []int) {
return file_api_proto_invite_invite_proto_rawDescGZIP(), []int{1}
}
func (x *GenerateResponse) GetCode() string {
if x != nil {
return x.Code
}
return ""
}
func (x *GenerateResponse) GetMaxUses() int32 {
if x != nil {
return x.MaxUses
}
return 0
}
func (x *GenerateResponse) GetExpiresAt() *timestamppb.Timestamp {
if x != nil {
return x.ExpiresAt
}
return nil
}
type GetInfoRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetInfoRequest) Reset() {
*x = GetInfoRequest{}
mi := &file_api_proto_invite_invite_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetInfoRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetInfoRequest) ProtoMessage() {}
func (x *GetInfoRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_invite_invite_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetInfoRequest.ProtoReflect.Descriptor instead.
func (*GetInfoRequest) Descriptor() ([]byte, []int) {
return file_api_proto_invite_invite_proto_rawDescGZIP(), []int{2}
}
func (x *GetInfoRequest) GetCode() string {
if x != nil {
return x.Code
}
return ""
}
type GetInfoResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"`
UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
CanBeUsedCount int32 `protobuf:"varint,3,opt,name=can_be_used_count,json=canBeUsedCount,proto3" json:"can_be_used_count,omitempty"`
UsedCount int32 `protobuf:"varint,4,opt,name=used_count,json=usedCount,proto3" json:"used_count,omitempty"`
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
IsActive bool `protobuf:"varint,6,opt,name=is_active,json=isActive,proto3" json:"is_active,omitempty"`
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetInfoResponse) Reset() {
*x = GetInfoResponse{}
mi := &file_api_proto_invite_invite_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetInfoResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetInfoResponse) ProtoMessage() {}
func (x *GetInfoResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_invite_invite_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetInfoResponse.ProtoReflect.Descriptor instead.
func (*GetInfoResponse) Descriptor() ([]byte, []int) {
return file_api_proto_invite_invite_proto_rawDescGZIP(), []int{3}
}
func (x *GetInfoResponse) GetCode() string {
if x != nil {
return x.Code
}
return ""
}
func (x *GetInfoResponse) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
func (x *GetInfoResponse) GetCanBeUsedCount() int32 {
if x != nil {
return x.CanBeUsedCount
}
return 0
}
func (x *GetInfoResponse) GetUsedCount() int32 {
if x != nil {
return x.UsedCount
}
return 0
}
func (x *GetInfoResponse) GetExpiresAt() *timestamppb.Timestamp {
if x != nil {
return x.ExpiresAt
}
return nil
}
func (x *GetInfoResponse) GetIsActive() bool {
if x != nil {
return x.IsActive
}
return false
}
func (x *GetInfoResponse) GetCreatedAt() *timestamppb.Timestamp {
if x != nil {
return x.CreatedAt
}
return nil
}
var File_api_proto_invite_invite_proto protoreflect.FileDescriptor
const file_api_proto_invite_invite_proto_rawDesc = "" +
"\n" +
"\x1dapi/proto/invite/invite.proto\x12\x06invite\x1a\x1fgoogle/protobuf/timestamp.proto\"`\n" +
"\x0fGenerateRequest\x12\x17\n" +
"\auser_id\x18\x01 \x01(\x03R\x06userId\x12\x19\n" +
"\bttl_days\x18\x02 \x01(\x05R\attlDays\x12\x19\n" +
"\bmax_uses\x18\x03 \x01(\x05R\amaxUses\"|\n" +
"\x10GenerateResponse\x12\x12\n" +
"\x04code\x18\x01 \x01(\tR\x04code\x12\x19\n" +
"\bmax_uses\x18\x02 \x01(\x05R\amaxUses\x129\n" +
"\n" +
"expires_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"$\n" +
"\x0eGetInfoRequest\x12\x12\n" +
"\x04code\x18\x01 \x01(\tR\x04code\"\x9b\x02\n" +
"\x0fGetInfoResponse\x12\x12\n" +
"\x04code\x18\x01 \x01(\tR\x04code\x12\x17\n" +
"\auser_id\x18\x02 \x01(\x03R\x06userId\x12)\n" +
"\x11can_be_used_count\x18\x03 \x01(\x05R\x0ecanBeUsedCount\x12\x1d\n" +
"\n" +
"used_count\x18\x04 \x01(\x05R\tusedCount\x129\n" +
"\n" +
"expires_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\x12\x1b\n" +
"\tis_active\x18\x06 \x01(\bR\bisActive\x129\n" +
"\n" +
"created_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt2\x8a\x01\n" +
"\rInviteService\x12=\n" +
"\bGenerate\x12\x17.invite.GenerateRequest\x1a\x18.invite.GenerateResponse\x12:\n" +
"\aGetInfo\x12\x16.invite.GetInfoRequest\x1a\x17.invite.GetInfoResponseB9Z7github.com/smart-search-gateway/api/proto/invite/inviteb\x06proto3"
var (
file_api_proto_invite_invite_proto_rawDescOnce sync.Once
file_api_proto_invite_invite_proto_rawDescData []byte
)
func file_api_proto_invite_invite_proto_rawDescGZIP() []byte {
file_api_proto_invite_invite_proto_rawDescOnce.Do(func() {
file_api_proto_invite_invite_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_proto_invite_invite_proto_rawDesc), len(file_api_proto_invite_invite_proto_rawDesc)))
})
return file_api_proto_invite_invite_proto_rawDescData
}
var file_api_proto_invite_invite_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_api_proto_invite_invite_proto_goTypes = []any{
(*GenerateRequest)(nil), // 0: invite.GenerateRequest
(*GenerateResponse)(nil), // 1: invite.GenerateResponse
(*GetInfoRequest)(nil), // 2: invite.GetInfoRequest
(*GetInfoResponse)(nil), // 3: invite.GetInfoResponse
(*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp
}
var file_api_proto_invite_invite_proto_depIdxs = []int32{
4, // 0: invite.GenerateResponse.expires_at:type_name -> google.protobuf.Timestamp
4, // 1: invite.GetInfoResponse.expires_at:type_name -> google.protobuf.Timestamp
4, // 2: invite.GetInfoResponse.created_at:type_name -> google.protobuf.Timestamp
0, // 3: invite.InviteService.Generate:input_type -> invite.GenerateRequest
2, // 4: invite.InviteService.GetInfo:input_type -> invite.GetInfoRequest
1, // 5: invite.InviteService.Generate:output_type -> invite.GenerateResponse
3, // 6: invite.InviteService.GetInfo:output_type -> invite.GetInfoResponse
5, // [5:7] is the sub-list for method output_type
3, // [3:5] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_api_proto_invite_invite_proto_init() }
func file_api_proto_invite_invite_proto_init() {
if File_api_proto_invite_invite_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_proto_invite_invite_proto_rawDesc), len(file_api_proto_invite_invite_proto_rawDesc)),
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_api_proto_invite_invite_proto_goTypes,
DependencyIndexes: file_api_proto_invite_invite_proto_depIdxs,
MessageInfos: file_api_proto_invite_invite_proto_msgTypes,
}.Build()
File_api_proto_invite_invite_proto = out.File
file_api_proto_invite_invite_proto_goTypes = nil
file_api_proto_invite_invite_proto_depIdxs = nil
}

View File

@@ -0,0 +1,159 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.29.3
// source: api/proto/invite/invite.proto
package invite
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
InviteService_Generate_FullMethodName = "/invite.InviteService/Generate"
InviteService_GetInfo_FullMethodName = "/invite.InviteService/GetInfo"
)
// InviteServiceClient is the client API for InviteService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type InviteServiceClient interface {
Generate(ctx context.Context, in *GenerateRequest, opts ...grpc.CallOption) (*GenerateResponse, error)
GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error)
}
type inviteServiceClient struct {
cc grpc.ClientConnInterface
}
func NewInviteServiceClient(cc grpc.ClientConnInterface) InviteServiceClient {
return &inviteServiceClient{cc}
}
func (c *inviteServiceClient) Generate(ctx context.Context, in *GenerateRequest, opts ...grpc.CallOption) (*GenerateResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GenerateResponse)
err := c.cc.Invoke(ctx, InviteService_Generate_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *inviteServiceClient) GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetInfoResponse)
err := c.cc.Invoke(ctx, InviteService_GetInfo_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// InviteServiceServer is the server API for InviteService service.
// All implementations must embed UnimplementedInviteServiceServer
// for forward compatibility.
type InviteServiceServer interface {
Generate(context.Context, *GenerateRequest) (*GenerateResponse, error)
GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error)
mustEmbedUnimplementedInviteServiceServer()
}
// UnimplementedInviteServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedInviteServiceServer struct{}
func (UnimplementedInviteServiceServer) Generate(context.Context, *GenerateRequest) (*GenerateResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Generate not implemented")
}
func (UnimplementedInviteServiceServer) GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented")
}
func (UnimplementedInviteServiceServer) mustEmbedUnimplementedInviteServiceServer() {}
func (UnimplementedInviteServiceServer) testEmbeddedByValue() {}
// UnsafeInviteServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to InviteServiceServer will
// result in compilation errors.
type UnsafeInviteServiceServer interface {
mustEmbedUnimplementedInviteServiceServer()
}
func RegisterInviteServiceServer(s grpc.ServiceRegistrar, srv InviteServiceServer) {
// If the following call pancis, it indicates UnimplementedInviteServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&InviteService_ServiceDesc, srv)
}
func _InviteService_Generate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GenerateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(InviteServiceServer).Generate(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: InviteService_Generate_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(InviteServiceServer).Generate(ctx, req.(*GenerateRequest))
}
return interceptor(ctx, in, info, handler)
}
func _InviteService_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetInfoRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(InviteServiceServer).GetInfo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: InviteService_GetInfo_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(InviteServiceServer).GetInfo(ctx, req.(*GetInfoRequest))
}
return interceptor(ctx, in, info, handler)
}
// InviteService_ServiceDesc is the grpc.ServiceDesc for InviteService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var InviteService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "invite.InviteService",
HandlerType: (*InviteServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Generate",
Handler: _InviteService_Generate_Handler,
},
{
MethodName: "GetInfo",
Handler: _InviteService_GetInfo_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/proto/invite/invite.proto",
}

View File

@@ -0,0 +1,640 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v5.29.3
// source: api/proto/request/request.proto
package request
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type CreateTZRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
RequestTxt string `protobuf:"bytes,2,opt,name=request_txt,json=requestTxt,proto3" json:"request_txt,omitempty"`
FileData []byte `protobuf:"bytes,3,opt,name=file_data,json=fileData,proto3" json:"file_data,omitempty"`
FileName string `protobuf:"bytes,4,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CreateTZRequest) Reset() {
*x = CreateTZRequest{}
mi := &file_api_proto_request_request_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CreateTZRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateTZRequest) ProtoMessage() {}
func (x *CreateTZRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_request_request_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateTZRequest.ProtoReflect.Descriptor instead.
func (*CreateTZRequest) Descriptor() ([]byte, []int) {
return file_api_proto_request_request_proto_rawDescGZIP(), []int{0}
}
func (x *CreateTZRequest) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
func (x *CreateTZRequest) GetRequestTxt() string {
if x != nil {
return x.RequestTxt
}
return ""
}
func (x *CreateTZRequest) GetFileData() []byte {
if x != nil {
return x.FileData
}
return nil
}
func (x *CreateTZRequest) GetFileName() string {
if x != nil {
return x.FileName
}
return ""
}
type CreateTZResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
TzText string `protobuf:"bytes,2,opt,name=tz_text,json=tzText,proto3" json:"tz_text,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CreateTZResponse) Reset() {
*x = CreateTZResponse{}
mi := &file_api_proto_request_request_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CreateTZResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateTZResponse) ProtoMessage() {}
func (x *CreateTZResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_request_request_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateTZResponse.ProtoReflect.Descriptor instead.
func (*CreateTZResponse) Descriptor() ([]byte, []int) {
return file_api_proto_request_request_proto_rawDescGZIP(), []int{1}
}
func (x *CreateTZResponse) GetRequestId() string {
if x != nil {
return x.RequestId
}
return ""
}
func (x *CreateTZResponse) GetTzText() string {
if x != nil {
return x.TzText
}
return ""
}
type ApproveTZRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
FinalTz string `protobuf:"bytes,2,opt,name=final_tz,json=finalTz,proto3" json:"final_tz,omitempty"`
UserId int64 `protobuf:"varint,3,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ApproveTZRequest) Reset() {
*x = ApproveTZRequest{}
mi := &file_api_proto_request_request_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ApproveTZRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ApproveTZRequest) ProtoMessage() {}
func (x *ApproveTZRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_request_request_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ApproveTZRequest.ProtoReflect.Descriptor instead.
func (*ApproveTZRequest) Descriptor() ([]byte, []int) {
return file_api_proto_request_request_proto_rawDescGZIP(), []int{2}
}
func (x *ApproveTZRequest) GetRequestId() string {
if x != nil {
return x.RequestId
}
return ""
}
func (x *ApproveTZRequest) GetFinalTz() string {
if x != nil {
return x.FinalTz
}
return ""
}
func (x *ApproveTZRequest) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
type ApproveTZResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
MailingStatus string `protobuf:"bytes,2,opt,name=mailing_status,json=mailingStatus,proto3" json:"mailing_status,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ApproveTZResponse) Reset() {
*x = ApproveTZResponse{}
mi := &file_api_proto_request_request_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ApproveTZResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ApproveTZResponse) ProtoMessage() {}
func (x *ApproveTZResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_request_request_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ApproveTZResponse.ProtoReflect.Descriptor instead.
func (*ApproveTZResponse) Descriptor() ([]byte, []int) {
return file_api_proto_request_request_proto_rawDescGZIP(), []int{3}
}
func (x *ApproveTZResponse) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
func (x *ApproveTZResponse) GetMailingStatus() string {
if x != nil {
return x.MailingStatus
}
return ""
}
type GetMailingListRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetMailingListRequest) Reset() {
*x = GetMailingListRequest{}
mi := &file_api_proto_request_request_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetMailingListRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetMailingListRequest) ProtoMessage() {}
func (x *GetMailingListRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_request_request_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetMailingListRequest.ProtoReflect.Descriptor instead.
func (*GetMailingListRequest) Descriptor() ([]byte, []int) {
return file_api_proto_request_request_proto_rawDescGZIP(), []int{4}
}
func (x *GetMailingListRequest) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
type GetMailingListResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Items []*MailingItem `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetMailingListResponse) Reset() {
*x = GetMailingListResponse{}
mi := &file_api_proto_request_request_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetMailingListResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetMailingListResponse) ProtoMessage() {}
func (x *GetMailingListResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_request_request_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetMailingListResponse.ProtoReflect.Descriptor instead.
func (*GetMailingListResponse) Descriptor() ([]byte, []int) {
return file_api_proto_request_request_proto_rawDescGZIP(), []int{5}
}
func (x *GetMailingListResponse) GetItems() []*MailingItem {
if x != nil {
return x.Items
}
return nil
}
type GetMailingListByIDRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetMailingListByIDRequest) Reset() {
*x = GetMailingListByIDRequest{}
mi := &file_api_proto_request_request_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetMailingListByIDRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetMailingListByIDRequest) ProtoMessage() {}
func (x *GetMailingListByIDRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_request_request_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetMailingListByIDRequest.ProtoReflect.Descriptor instead.
func (*GetMailingListByIDRequest) Descriptor() ([]byte, []int) {
return file_api_proto_request_request_proto_rawDescGZIP(), []int{6}
}
func (x *GetMailingListByIDRequest) GetRequestId() string {
if x != nil {
return x.RequestId
}
return ""
}
func (x *GetMailingListByIDRequest) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
type GetMailingListByIDResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Item *MailingItem `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetMailingListByIDResponse) Reset() {
*x = GetMailingListByIDResponse{}
mi := &file_api_proto_request_request_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetMailingListByIDResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetMailingListByIDResponse) ProtoMessage() {}
func (x *GetMailingListByIDResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_request_request_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetMailingListByIDResponse.ProtoReflect.Descriptor instead.
func (*GetMailingListByIDResponse) Descriptor() ([]byte, []int) {
return file_api_proto_request_request_proto_rawDescGZIP(), []int{7}
}
func (x *GetMailingListByIDResponse) GetItem() *MailingItem {
if x != nil {
return x.Item
}
return nil
}
type MailingItem struct {
state protoimpl.MessageState `protogen:"open.v1"`
RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
RequestTxt string `protobuf:"bytes,2,opt,name=request_txt,json=requestTxt,proto3" json:"request_txt,omitempty"`
FinalTz string `protobuf:"bytes,3,opt,name=final_tz,json=finalTz,proto3" json:"final_tz,omitempty"`
MailingStatus string `protobuf:"bytes,4,opt,name=mailing_status,json=mailingStatus,proto3" json:"mailing_status,omitempty"`
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
SuppliersFound int32 `protobuf:"varint,6,opt,name=suppliers_found,json=suppliersFound,proto3" json:"suppliers_found,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *MailingItem) Reset() {
*x = MailingItem{}
mi := &file_api_proto_request_request_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *MailingItem) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MailingItem) ProtoMessage() {}
func (x *MailingItem) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_request_request_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MailingItem.ProtoReflect.Descriptor instead.
func (*MailingItem) Descriptor() ([]byte, []int) {
return file_api_proto_request_request_proto_rawDescGZIP(), []int{8}
}
func (x *MailingItem) GetRequestId() string {
if x != nil {
return x.RequestId
}
return ""
}
func (x *MailingItem) GetRequestTxt() string {
if x != nil {
return x.RequestTxt
}
return ""
}
func (x *MailingItem) GetFinalTz() string {
if x != nil {
return x.FinalTz
}
return ""
}
func (x *MailingItem) GetMailingStatus() string {
if x != nil {
return x.MailingStatus
}
return ""
}
func (x *MailingItem) GetCreatedAt() *timestamppb.Timestamp {
if x != nil {
return x.CreatedAt
}
return nil
}
func (x *MailingItem) GetSuppliersFound() int32 {
if x != nil {
return x.SuppliersFound
}
return 0
}
var File_api_proto_request_request_proto protoreflect.FileDescriptor
const file_api_proto_request_request_proto_rawDesc = "" +
"\n" +
"\x1fapi/proto/request/request.proto\x12\arequest\x1a\x1fgoogle/protobuf/timestamp.proto\"\x85\x01\n" +
"\x0fCreateTZRequest\x12\x17\n" +
"\auser_id\x18\x01 \x01(\x03R\x06userId\x12\x1f\n" +
"\vrequest_txt\x18\x02 \x01(\tR\n" +
"requestTxt\x12\x1b\n" +
"\tfile_data\x18\x03 \x01(\fR\bfileData\x12\x1b\n" +
"\tfile_name\x18\x04 \x01(\tR\bfileName\"J\n" +
"\x10CreateTZResponse\x12\x1d\n" +
"\n" +
"request_id\x18\x01 \x01(\tR\trequestId\x12\x17\n" +
"\atz_text\x18\x02 \x01(\tR\x06tzText\"e\n" +
"\x10ApproveTZRequest\x12\x1d\n" +
"\n" +
"request_id\x18\x01 \x01(\tR\trequestId\x12\x19\n" +
"\bfinal_tz\x18\x02 \x01(\tR\afinalTz\x12\x17\n" +
"\auser_id\x18\x03 \x01(\x03R\x06userId\"T\n" +
"\x11ApproveTZResponse\x12\x18\n" +
"\asuccess\x18\x01 \x01(\bR\asuccess\x12%\n" +
"\x0emailing_status\x18\x02 \x01(\tR\rmailingStatus\"0\n" +
"\x15GetMailingListRequest\x12\x17\n" +
"\auser_id\x18\x01 \x01(\x03R\x06userId\"D\n" +
"\x16GetMailingListResponse\x12*\n" +
"\x05items\x18\x01 \x03(\v2\x14.request.MailingItemR\x05items\"S\n" +
"\x19GetMailingListByIDRequest\x12\x1d\n" +
"\n" +
"request_id\x18\x01 \x01(\tR\trequestId\x12\x17\n" +
"\auser_id\x18\x02 \x01(\x03R\x06userId\"F\n" +
"\x1aGetMailingListByIDResponse\x12(\n" +
"\x04item\x18\x01 \x01(\v2\x14.request.MailingItemR\x04item\"\xf3\x01\n" +
"\vMailingItem\x12\x1d\n" +
"\n" +
"request_id\x18\x01 \x01(\tR\trequestId\x12\x1f\n" +
"\vrequest_txt\x18\x02 \x01(\tR\n" +
"requestTxt\x12\x19\n" +
"\bfinal_tz\x18\x03 \x01(\tR\afinalTz\x12%\n" +
"\x0emailing_status\x18\x04 \x01(\tR\rmailingStatus\x129\n" +
"\n" +
"created_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12'\n" +
"\x0fsuppliers_found\x18\x06 \x01(\x05R\x0esuppliersFound2\xc7\x02\n" +
"\x0eRequestService\x12?\n" +
"\bCreateTZ\x12\x18.request.CreateTZRequest\x1a\x19.request.CreateTZResponse\x12B\n" +
"\tApproveTZ\x12\x19.request.ApproveTZRequest\x1a\x1a.request.ApproveTZResponse\x12Q\n" +
"\x0eGetMailingList\x12\x1e.request.GetMailingListRequest\x1a\x1f.request.GetMailingListResponse\x12]\n" +
"\x12GetMailingListByID\x12\".request.GetMailingListByIDRequest\x1a#.request.GetMailingListByIDResponseB;Z9github.com/smart-search-gateway/api/proto/request/requestb\x06proto3"
var (
file_api_proto_request_request_proto_rawDescOnce sync.Once
file_api_proto_request_request_proto_rawDescData []byte
)
func file_api_proto_request_request_proto_rawDescGZIP() []byte {
file_api_proto_request_request_proto_rawDescOnce.Do(func() {
file_api_proto_request_request_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_proto_request_request_proto_rawDesc), len(file_api_proto_request_request_proto_rawDesc)))
})
return file_api_proto_request_request_proto_rawDescData
}
var file_api_proto_request_request_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
var file_api_proto_request_request_proto_goTypes = []any{
(*CreateTZRequest)(nil), // 0: request.CreateTZRequest
(*CreateTZResponse)(nil), // 1: request.CreateTZResponse
(*ApproveTZRequest)(nil), // 2: request.ApproveTZRequest
(*ApproveTZResponse)(nil), // 3: request.ApproveTZResponse
(*GetMailingListRequest)(nil), // 4: request.GetMailingListRequest
(*GetMailingListResponse)(nil), // 5: request.GetMailingListResponse
(*GetMailingListByIDRequest)(nil), // 6: request.GetMailingListByIDRequest
(*GetMailingListByIDResponse)(nil), // 7: request.GetMailingListByIDResponse
(*MailingItem)(nil), // 8: request.MailingItem
(*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp
}
var file_api_proto_request_request_proto_depIdxs = []int32{
8, // 0: request.GetMailingListResponse.items:type_name -> request.MailingItem
8, // 1: request.GetMailingListByIDResponse.item:type_name -> request.MailingItem
9, // 2: request.MailingItem.created_at:type_name -> google.protobuf.Timestamp
0, // 3: request.RequestService.CreateTZ:input_type -> request.CreateTZRequest
2, // 4: request.RequestService.ApproveTZ:input_type -> request.ApproveTZRequest
4, // 5: request.RequestService.GetMailingList:input_type -> request.GetMailingListRequest
6, // 6: request.RequestService.GetMailingListByID:input_type -> request.GetMailingListByIDRequest
1, // 7: request.RequestService.CreateTZ:output_type -> request.CreateTZResponse
3, // 8: request.RequestService.ApproveTZ:output_type -> request.ApproveTZResponse
5, // 9: request.RequestService.GetMailingList:output_type -> request.GetMailingListResponse
7, // 10: request.RequestService.GetMailingListByID:output_type -> request.GetMailingListByIDResponse
7, // [7:11] is the sub-list for method output_type
3, // [3:7] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_api_proto_request_request_proto_init() }
func file_api_proto_request_request_proto_init() {
if File_api_proto_request_request_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_proto_request_request_proto_rawDesc), len(file_api_proto_request_request_proto_rawDesc)),
NumEnums: 0,
NumMessages: 9,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_api_proto_request_request_proto_goTypes,
DependencyIndexes: file_api_proto_request_request_proto_depIdxs,
MessageInfos: file_api_proto_request_request_proto_msgTypes,
}.Build()
File_api_proto_request_request_proto = out.File
file_api_proto_request_request_proto_goTypes = nil
file_api_proto_request_request_proto_depIdxs = nil
}

View File

@@ -0,0 +1,235 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.29.3
// source: api/proto/request/request.proto
package request
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
RequestService_CreateTZ_FullMethodName = "/request.RequestService/CreateTZ"
RequestService_ApproveTZ_FullMethodName = "/request.RequestService/ApproveTZ"
RequestService_GetMailingList_FullMethodName = "/request.RequestService/GetMailingList"
RequestService_GetMailingListByID_FullMethodName = "/request.RequestService/GetMailingListByID"
)
// RequestServiceClient is the client API for RequestService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type RequestServiceClient interface {
CreateTZ(ctx context.Context, in *CreateTZRequest, opts ...grpc.CallOption) (*CreateTZResponse, error)
ApproveTZ(ctx context.Context, in *ApproveTZRequest, opts ...grpc.CallOption) (*ApproveTZResponse, error)
GetMailingList(ctx context.Context, in *GetMailingListRequest, opts ...grpc.CallOption) (*GetMailingListResponse, error)
GetMailingListByID(ctx context.Context, in *GetMailingListByIDRequest, opts ...grpc.CallOption) (*GetMailingListByIDResponse, error)
}
type requestServiceClient struct {
cc grpc.ClientConnInterface
}
func NewRequestServiceClient(cc grpc.ClientConnInterface) RequestServiceClient {
return &requestServiceClient{cc}
}
func (c *requestServiceClient) CreateTZ(ctx context.Context, in *CreateTZRequest, opts ...grpc.CallOption) (*CreateTZResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateTZResponse)
err := c.cc.Invoke(ctx, RequestService_CreateTZ_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *requestServiceClient) ApproveTZ(ctx context.Context, in *ApproveTZRequest, opts ...grpc.CallOption) (*ApproveTZResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ApproveTZResponse)
err := c.cc.Invoke(ctx, RequestService_ApproveTZ_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *requestServiceClient) GetMailingList(ctx context.Context, in *GetMailingListRequest, opts ...grpc.CallOption) (*GetMailingListResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetMailingListResponse)
err := c.cc.Invoke(ctx, RequestService_GetMailingList_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *requestServiceClient) GetMailingListByID(ctx context.Context, in *GetMailingListByIDRequest, opts ...grpc.CallOption) (*GetMailingListByIDResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetMailingListByIDResponse)
err := c.cc.Invoke(ctx, RequestService_GetMailingListByID_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// RequestServiceServer is the server API for RequestService service.
// All implementations must embed UnimplementedRequestServiceServer
// for forward compatibility.
type RequestServiceServer interface {
CreateTZ(context.Context, *CreateTZRequest) (*CreateTZResponse, error)
ApproveTZ(context.Context, *ApproveTZRequest) (*ApproveTZResponse, error)
GetMailingList(context.Context, *GetMailingListRequest) (*GetMailingListResponse, error)
GetMailingListByID(context.Context, *GetMailingListByIDRequest) (*GetMailingListByIDResponse, error)
mustEmbedUnimplementedRequestServiceServer()
}
// UnimplementedRequestServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedRequestServiceServer struct{}
func (UnimplementedRequestServiceServer) CreateTZ(context.Context, *CreateTZRequest) (*CreateTZResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateTZ not implemented")
}
func (UnimplementedRequestServiceServer) ApproveTZ(context.Context, *ApproveTZRequest) (*ApproveTZResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ApproveTZ not implemented")
}
func (UnimplementedRequestServiceServer) GetMailingList(context.Context, *GetMailingListRequest) (*GetMailingListResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMailingList not implemented")
}
func (UnimplementedRequestServiceServer) GetMailingListByID(context.Context, *GetMailingListByIDRequest) (*GetMailingListByIDResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMailingListByID not implemented")
}
func (UnimplementedRequestServiceServer) mustEmbedUnimplementedRequestServiceServer() {}
func (UnimplementedRequestServiceServer) testEmbeddedByValue() {}
// UnsafeRequestServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to RequestServiceServer will
// result in compilation errors.
type UnsafeRequestServiceServer interface {
mustEmbedUnimplementedRequestServiceServer()
}
func RegisterRequestServiceServer(s grpc.ServiceRegistrar, srv RequestServiceServer) {
// If the following call pancis, it indicates UnimplementedRequestServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&RequestService_ServiceDesc, srv)
}
func _RequestService_CreateTZ_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateTZRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RequestServiceServer).CreateTZ(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: RequestService_CreateTZ_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RequestServiceServer).CreateTZ(ctx, req.(*CreateTZRequest))
}
return interceptor(ctx, in, info, handler)
}
func _RequestService_ApproveTZ_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ApproveTZRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RequestServiceServer).ApproveTZ(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: RequestService_ApproveTZ_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RequestServiceServer).ApproveTZ(ctx, req.(*ApproveTZRequest))
}
return interceptor(ctx, in, info, handler)
}
func _RequestService_GetMailingList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetMailingListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RequestServiceServer).GetMailingList(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: RequestService_GetMailingList_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RequestServiceServer).GetMailingList(ctx, req.(*GetMailingListRequest))
}
return interceptor(ctx, in, info, handler)
}
func _RequestService_GetMailingListByID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetMailingListByIDRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RequestServiceServer).GetMailingListByID(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: RequestService_GetMailingListByID_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RequestServiceServer).GetMailingListByID(ctx, req.(*GetMailingListByIDRequest))
}
return interceptor(ctx, in, info, handler)
}
// RequestService_ServiceDesc is the grpc.ServiceDesc for RequestService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var RequestService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "request.RequestService",
HandlerType: (*RequestServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "CreateTZ",
Handler: _RequestService_CreateTZ_Handler,
},
{
MethodName: "ApproveTZ",
Handler: _RequestService_ApproveTZ_Handler,
},
{
MethodName: "GetMailingList",
Handler: _RequestService_GetMailingList_Handler,
},
{
MethodName: "GetMailingListByID",
Handler: _RequestService_GetMailingListByID_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/proto/request/request.proto",
}

View File

@@ -0,0 +1,201 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v5.29.3
// source: api/proto/supplier/supplier.proto
package supplier
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ExportExcelRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ExportExcelRequest) Reset() {
*x = ExportExcelRequest{}
mi := &file_api_proto_supplier_supplier_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ExportExcelRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ExportExcelRequest) ProtoMessage() {}
func (x *ExportExcelRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_supplier_supplier_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ExportExcelRequest.ProtoReflect.Descriptor instead.
func (*ExportExcelRequest) Descriptor() ([]byte, []int) {
return file_api_proto_supplier_supplier_proto_rawDescGZIP(), []int{0}
}
func (x *ExportExcelRequest) GetRequestId() string {
if x != nil {
return x.RequestId
}
return ""
}
func (x *ExportExcelRequest) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
type ExportExcelResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
FileData []byte `protobuf:"bytes,1,opt,name=file_data,json=fileData,proto3" json:"file_data,omitempty"`
FileName string `protobuf:"bytes,2,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"`
MimeType string `protobuf:"bytes,3,opt,name=mime_type,json=mimeType,proto3" json:"mime_type,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ExportExcelResponse) Reset() {
*x = ExportExcelResponse{}
mi := &file_api_proto_supplier_supplier_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ExportExcelResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ExportExcelResponse) ProtoMessage() {}
func (x *ExportExcelResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_supplier_supplier_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ExportExcelResponse.ProtoReflect.Descriptor instead.
func (*ExportExcelResponse) Descriptor() ([]byte, []int) {
return file_api_proto_supplier_supplier_proto_rawDescGZIP(), []int{1}
}
func (x *ExportExcelResponse) GetFileData() []byte {
if x != nil {
return x.FileData
}
return nil
}
func (x *ExportExcelResponse) GetFileName() string {
if x != nil {
return x.FileName
}
return ""
}
func (x *ExportExcelResponse) GetMimeType() string {
if x != nil {
return x.MimeType
}
return ""
}
var File_api_proto_supplier_supplier_proto protoreflect.FileDescriptor
const file_api_proto_supplier_supplier_proto_rawDesc = "" +
"\n" +
"!api/proto/supplier/supplier.proto\x12\bsupplier\"L\n" +
"\x12ExportExcelRequest\x12\x1d\n" +
"\n" +
"request_id\x18\x01 \x01(\tR\trequestId\x12\x17\n" +
"\auser_id\x18\x02 \x01(\x03R\x06userId\"l\n" +
"\x13ExportExcelResponse\x12\x1b\n" +
"\tfile_data\x18\x01 \x01(\fR\bfileData\x12\x1b\n" +
"\tfile_name\x18\x02 \x01(\tR\bfileName\x12\x1b\n" +
"\tmime_type\x18\x03 \x01(\tR\bmimeType2]\n" +
"\x0fSupplierService\x12J\n" +
"\vExportExcel\x12\x1c.supplier.ExportExcelRequest\x1a\x1d.supplier.ExportExcelResponseB=Z;github.com/smart-search-gateway/api/proto/supplier/supplierb\x06proto3"
var (
file_api_proto_supplier_supplier_proto_rawDescOnce sync.Once
file_api_proto_supplier_supplier_proto_rawDescData []byte
)
func file_api_proto_supplier_supplier_proto_rawDescGZIP() []byte {
file_api_proto_supplier_supplier_proto_rawDescOnce.Do(func() {
file_api_proto_supplier_supplier_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_proto_supplier_supplier_proto_rawDesc), len(file_api_proto_supplier_supplier_proto_rawDesc)))
})
return file_api_proto_supplier_supplier_proto_rawDescData
}
var file_api_proto_supplier_supplier_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_api_proto_supplier_supplier_proto_goTypes = []any{
(*ExportExcelRequest)(nil), // 0: supplier.ExportExcelRequest
(*ExportExcelResponse)(nil), // 1: supplier.ExportExcelResponse
}
var file_api_proto_supplier_supplier_proto_depIdxs = []int32{
0, // 0: supplier.SupplierService.ExportExcel:input_type -> supplier.ExportExcelRequest
1, // 1: supplier.SupplierService.ExportExcel:output_type -> supplier.ExportExcelResponse
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_api_proto_supplier_supplier_proto_init() }
func file_api_proto_supplier_supplier_proto_init() {
if File_api_proto_supplier_supplier_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_proto_supplier_supplier_proto_rawDesc), len(file_api_proto_supplier_supplier_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_api_proto_supplier_supplier_proto_goTypes,
DependencyIndexes: file_api_proto_supplier_supplier_proto_depIdxs,
MessageInfos: file_api_proto_supplier_supplier_proto_msgTypes,
}.Build()
File_api_proto_supplier_supplier_proto = out.File
file_api_proto_supplier_supplier_proto_goTypes = nil
file_api_proto_supplier_supplier_proto_depIdxs = nil
}

View File

@@ -0,0 +1,121 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.29.3
// source: api/proto/supplier/supplier.proto
package supplier
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
SupplierService_ExportExcel_FullMethodName = "/supplier.SupplierService/ExportExcel"
)
// SupplierServiceClient is the client API for SupplierService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type SupplierServiceClient interface {
ExportExcel(ctx context.Context, in *ExportExcelRequest, opts ...grpc.CallOption) (*ExportExcelResponse, error)
}
type supplierServiceClient struct {
cc grpc.ClientConnInterface
}
func NewSupplierServiceClient(cc grpc.ClientConnInterface) SupplierServiceClient {
return &supplierServiceClient{cc}
}
func (c *supplierServiceClient) ExportExcel(ctx context.Context, in *ExportExcelRequest, opts ...grpc.CallOption) (*ExportExcelResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ExportExcelResponse)
err := c.cc.Invoke(ctx, SupplierService_ExportExcel_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// SupplierServiceServer is the server API for SupplierService service.
// All implementations must embed UnimplementedSupplierServiceServer
// for forward compatibility.
type SupplierServiceServer interface {
ExportExcel(context.Context, *ExportExcelRequest) (*ExportExcelResponse, error)
mustEmbedUnimplementedSupplierServiceServer()
}
// UnimplementedSupplierServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedSupplierServiceServer struct{}
func (UnimplementedSupplierServiceServer) ExportExcel(context.Context, *ExportExcelRequest) (*ExportExcelResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ExportExcel not implemented")
}
func (UnimplementedSupplierServiceServer) mustEmbedUnimplementedSupplierServiceServer() {}
func (UnimplementedSupplierServiceServer) testEmbeddedByValue() {}
// UnsafeSupplierServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to SupplierServiceServer will
// result in compilation errors.
type UnsafeSupplierServiceServer interface {
mustEmbedUnimplementedSupplierServiceServer()
}
func RegisterSupplierServiceServer(s grpc.ServiceRegistrar, srv SupplierServiceServer) {
// If the following call pancis, it indicates UnimplementedSupplierServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&SupplierService_ServiceDesc, srv)
}
func _SupplierService_ExportExcel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ExportExcelRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SupplierServiceServer).ExportExcel(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SupplierService_ExportExcel_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SupplierServiceServer).ExportExcel(ctx, req.(*ExportExcelRequest))
}
return interceptor(ctx, in, info, handler)
}
// SupplierService_ServiceDesc is the grpc.ServiceDesc for SupplierService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var SupplierService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "supplier.SupplierService",
HandlerType: (*SupplierServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ExportExcel",
Handler: _SupplierService_ExportExcel_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/proto/supplier/supplier.proto",
}

View File

@@ -0,0 +1,548 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v5.29.3
// source: api/proto/user/user.proto
package user
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type GetInfoRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetInfoRequest) Reset() {
*x = GetInfoRequest{}
mi := &file_api_proto_user_user_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetInfoRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetInfoRequest) ProtoMessage() {}
func (x *GetInfoRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_user_user_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetInfoRequest.ProtoReflect.Descriptor instead.
func (*GetInfoRequest) Descriptor() ([]byte, []int) {
return file_api_proto_user_user_proto_rawDescGZIP(), []int{0}
}
func (x *GetInfoRequest) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
type GetInfoResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Phone string `protobuf:"bytes,3,opt,name=phone,proto3" json:"phone,omitempty"`
CompanyName string `protobuf:"bytes,4,opt,name=company_name,json=companyName,proto3" json:"company_name,omitempty"`
PaymentStatus string `protobuf:"bytes,5,opt,name=payment_status,json=paymentStatus,proto3" json:"payment_status,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetInfoResponse) Reset() {
*x = GetInfoResponse{}
mi := &file_api_proto_user_user_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetInfoResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetInfoResponse) ProtoMessage() {}
func (x *GetInfoResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_user_user_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetInfoResponse.ProtoReflect.Descriptor instead.
func (*GetInfoResponse) Descriptor() ([]byte, []int) {
return file_api_proto_user_user_proto_rawDescGZIP(), []int{1}
}
func (x *GetInfoResponse) GetEmail() string {
if x != nil {
return x.Email
}
return ""
}
func (x *GetInfoResponse) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *GetInfoResponse) GetPhone() string {
if x != nil {
return x.Phone
}
return ""
}
func (x *GetInfoResponse) GetCompanyName() string {
if x != nil {
return x.CompanyName
}
return ""
}
func (x *GetInfoResponse) GetPaymentStatus() string {
if x != nil {
return x.PaymentStatus
}
return ""
}
type GetBalanceRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetBalanceRequest) Reset() {
*x = GetBalanceRequest{}
mi := &file_api_proto_user_user_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetBalanceRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetBalanceRequest) ProtoMessage() {}
func (x *GetBalanceRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_user_user_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetBalanceRequest.ProtoReflect.Descriptor instead.
func (*GetBalanceRequest) Descriptor() ([]byte, []int) {
return file_api_proto_user_user_proto_rawDescGZIP(), []int{2}
}
func (x *GetBalanceRequest) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
type GetBalanceResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Balance float64 `protobuf:"fixed64,1,opt,name=balance,proto3" json:"balance,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetBalanceResponse) Reset() {
*x = GetBalanceResponse{}
mi := &file_api_proto_user_user_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetBalanceResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetBalanceResponse) ProtoMessage() {}
func (x *GetBalanceResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_user_user_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetBalanceResponse.ProtoReflect.Descriptor instead.
func (*GetBalanceResponse) Descriptor() ([]byte, []int) {
return file_api_proto_user_user_proto_rawDescGZIP(), []int{3}
}
func (x *GetBalanceResponse) GetBalance() float64 {
if x != nil {
return x.Balance
}
return 0
}
type GetStatisticsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetStatisticsRequest) Reset() {
*x = GetStatisticsRequest{}
mi := &file_api_proto_user_user_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetStatisticsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetStatisticsRequest) ProtoMessage() {}
func (x *GetStatisticsRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_user_user_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetStatisticsRequest.ProtoReflect.Descriptor instead.
func (*GetStatisticsRequest) Descriptor() ([]byte, []int) {
return file_api_proto_user_user_proto_rawDescGZIP(), []int{4}
}
func (x *GetStatisticsRequest) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
type GetStatisticsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
TotalRequests int32 `protobuf:"varint,1,opt,name=total_requests,json=totalRequests,proto3" json:"total_requests,omitempty"`
SuccessfulRequests int32 `protobuf:"varint,2,opt,name=successful_requests,json=successfulRequests,proto3" json:"successful_requests,omitempty"`
FailedRequests int32 `protobuf:"varint,3,opt,name=failed_requests,json=failedRequests,proto3" json:"failed_requests,omitempty"`
TotalSpent float64 `protobuf:"fixed64,4,opt,name=total_spent,json=totalSpent,proto3" json:"total_spent,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetStatisticsResponse) Reset() {
*x = GetStatisticsResponse{}
mi := &file_api_proto_user_user_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetStatisticsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetStatisticsResponse) ProtoMessage() {}
func (x *GetStatisticsResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_user_user_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetStatisticsResponse.ProtoReflect.Descriptor instead.
func (*GetStatisticsResponse) Descriptor() ([]byte, []int) {
return file_api_proto_user_user_proto_rawDescGZIP(), []int{5}
}
func (x *GetStatisticsResponse) GetTotalRequests() int32 {
if x != nil {
return x.TotalRequests
}
return 0
}
func (x *GetStatisticsResponse) GetSuccessfulRequests() int32 {
if x != nil {
return x.SuccessfulRequests
}
return 0
}
func (x *GetStatisticsResponse) GetFailedRequests() int32 {
if x != nil {
return x.FailedRequests
}
return 0
}
func (x *GetStatisticsResponse) GetTotalSpent() float64 {
if x != nil {
return x.TotalSpent
}
return 0
}
type GetBalanceStatisticsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetBalanceStatisticsRequest) Reset() {
*x = GetBalanceStatisticsRequest{}
mi := &file_api_proto_user_user_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetBalanceStatisticsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetBalanceStatisticsRequest) ProtoMessage() {}
func (x *GetBalanceStatisticsRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_user_user_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetBalanceStatisticsRequest.ProtoReflect.Descriptor instead.
func (*GetBalanceStatisticsRequest) Descriptor() ([]byte, []int) {
return file_api_proto_user_user_proto_rawDescGZIP(), []int{6}
}
func (x *GetBalanceStatisticsRequest) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
type GetBalanceStatisticsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Balance float64 `protobuf:"fixed64,1,opt,name=balance,proto3" json:"balance,omitempty"`
TotalRequests int32 `protobuf:"varint,2,opt,name=total_requests,json=totalRequests,proto3" json:"total_requests,omitempty"`
TotalSpent float64 `protobuf:"fixed64,3,opt,name=total_spent,json=totalSpent,proto3" json:"total_spent,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetBalanceStatisticsResponse) Reset() {
*x = GetBalanceStatisticsResponse{}
mi := &file_api_proto_user_user_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetBalanceStatisticsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetBalanceStatisticsResponse) ProtoMessage() {}
func (x *GetBalanceStatisticsResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_proto_user_user_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetBalanceStatisticsResponse.ProtoReflect.Descriptor instead.
func (*GetBalanceStatisticsResponse) Descriptor() ([]byte, []int) {
return file_api_proto_user_user_proto_rawDescGZIP(), []int{7}
}
func (x *GetBalanceStatisticsResponse) GetBalance() float64 {
if x != nil {
return x.Balance
}
return 0
}
func (x *GetBalanceStatisticsResponse) GetTotalRequests() int32 {
if x != nil {
return x.TotalRequests
}
return 0
}
func (x *GetBalanceStatisticsResponse) GetTotalSpent() float64 {
if x != nil {
return x.TotalSpent
}
return 0
}
var File_api_proto_user_user_proto protoreflect.FileDescriptor
const file_api_proto_user_user_proto_rawDesc = "" +
"\n" +
"\x19api/proto/user/user.proto\x12\x04user\")\n" +
"\x0eGetInfoRequest\x12\x17\n" +
"\auser_id\x18\x01 \x01(\x03R\x06userId\"\x9b\x01\n" +
"\x0fGetInfoResponse\x12\x14\n" +
"\x05email\x18\x01 \x01(\tR\x05email\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12\x14\n" +
"\x05phone\x18\x03 \x01(\tR\x05phone\x12!\n" +
"\fcompany_name\x18\x04 \x01(\tR\vcompanyName\x12%\n" +
"\x0epayment_status\x18\x05 \x01(\tR\rpaymentStatus\",\n" +
"\x11GetBalanceRequest\x12\x17\n" +
"\auser_id\x18\x01 \x01(\x03R\x06userId\".\n" +
"\x12GetBalanceResponse\x12\x18\n" +
"\abalance\x18\x01 \x01(\x01R\abalance\"/\n" +
"\x14GetStatisticsRequest\x12\x17\n" +
"\auser_id\x18\x01 \x01(\x03R\x06userId\"\xb9\x01\n" +
"\x15GetStatisticsResponse\x12%\n" +
"\x0etotal_requests\x18\x01 \x01(\x05R\rtotalRequests\x12/\n" +
"\x13successful_requests\x18\x02 \x01(\x05R\x12successfulRequests\x12'\n" +
"\x0ffailed_requests\x18\x03 \x01(\x05R\x0efailedRequests\x12\x1f\n" +
"\vtotal_spent\x18\x04 \x01(\x01R\n" +
"totalSpent\"6\n" +
"\x1bGetBalanceStatisticsRequest\x12\x17\n" +
"\auser_id\x18\x01 \x01(\x03R\x06userId\"\x80\x01\n" +
"\x1cGetBalanceStatisticsResponse\x12\x18\n" +
"\abalance\x18\x01 \x01(\x01R\abalance\x12%\n" +
"\x0etotal_requests\x18\x02 \x01(\x05R\rtotalRequests\x12\x1f\n" +
"\vtotal_spent\x18\x03 \x01(\x01R\n" +
"totalSpent2\xaf\x02\n" +
"\vUserService\x126\n" +
"\aGetInfo\x12\x14.user.GetInfoRequest\x1a\x15.user.GetInfoResponse\x12?\n" +
"\n" +
"GetBalance\x12\x17.user.GetBalanceRequest\x1a\x18.user.GetBalanceResponse\x12H\n" +
"\rGetStatistics\x12\x1a.user.GetStatisticsRequest\x1a\x1b.user.GetStatisticsResponse\x12]\n" +
"\x14GetBalanceStatistics\x12!.user.GetBalanceStatisticsRequest\x1a\".user.GetBalanceStatisticsResponseB5Z3github.com/smart-search-gateway/api/proto/user/userb\x06proto3"
var (
file_api_proto_user_user_proto_rawDescOnce sync.Once
file_api_proto_user_user_proto_rawDescData []byte
)
func file_api_proto_user_user_proto_rawDescGZIP() []byte {
file_api_proto_user_user_proto_rawDescOnce.Do(func() {
file_api_proto_user_user_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_proto_user_user_proto_rawDesc), len(file_api_proto_user_user_proto_rawDesc)))
})
return file_api_proto_user_user_proto_rawDescData
}
var file_api_proto_user_user_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_api_proto_user_user_proto_goTypes = []any{
(*GetInfoRequest)(nil), // 0: user.GetInfoRequest
(*GetInfoResponse)(nil), // 1: user.GetInfoResponse
(*GetBalanceRequest)(nil), // 2: user.GetBalanceRequest
(*GetBalanceResponse)(nil), // 3: user.GetBalanceResponse
(*GetStatisticsRequest)(nil), // 4: user.GetStatisticsRequest
(*GetStatisticsResponse)(nil), // 5: user.GetStatisticsResponse
(*GetBalanceStatisticsRequest)(nil), // 6: user.GetBalanceStatisticsRequest
(*GetBalanceStatisticsResponse)(nil), // 7: user.GetBalanceStatisticsResponse
}
var file_api_proto_user_user_proto_depIdxs = []int32{
0, // 0: user.UserService.GetInfo:input_type -> user.GetInfoRequest
2, // 1: user.UserService.GetBalance:input_type -> user.GetBalanceRequest
4, // 2: user.UserService.GetStatistics:input_type -> user.GetStatisticsRequest
6, // 3: user.UserService.GetBalanceStatistics:input_type -> user.GetBalanceStatisticsRequest
1, // 4: user.UserService.GetInfo:output_type -> user.GetInfoResponse
3, // 5: user.UserService.GetBalance:output_type -> user.GetBalanceResponse
5, // 6: user.UserService.GetStatistics:output_type -> user.GetStatisticsResponse
7, // 7: user.UserService.GetBalanceStatistics:output_type -> user.GetBalanceStatisticsResponse
4, // [4:8] is the sub-list for method output_type
0, // [0:4] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_api_proto_user_user_proto_init() }
func file_api_proto_user_user_proto_init() {
if File_api_proto_user_user_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_proto_user_user_proto_rawDesc), len(file_api_proto_user_user_proto_rawDesc)),
NumEnums: 0,
NumMessages: 8,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_api_proto_user_user_proto_goTypes,
DependencyIndexes: file_api_proto_user_user_proto_depIdxs,
MessageInfos: file_api_proto_user_user_proto_msgTypes,
}.Build()
File_api_proto_user_user_proto = out.File
file_api_proto_user_user_proto_goTypes = nil
file_api_proto_user_user_proto_depIdxs = nil
}

View File

@@ -0,0 +1,235 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.29.3
// source: api/proto/user/user.proto
package user
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
UserService_GetInfo_FullMethodName = "/user.UserService/GetInfo"
UserService_GetBalance_FullMethodName = "/user.UserService/GetBalance"
UserService_GetStatistics_FullMethodName = "/user.UserService/GetStatistics"
UserService_GetBalanceStatistics_FullMethodName = "/user.UserService/GetBalanceStatistics"
)
// UserServiceClient is the client API for UserService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type UserServiceClient interface {
GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error)
GetBalance(ctx context.Context, in *GetBalanceRequest, opts ...grpc.CallOption) (*GetBalanceResponse, error)
GetStatistics(ctx context.Context, in *GetStatisticsRequest, opts ...grpc.CallOption) (*GetStatisticsResponse, error)
GetBalanceStatistics(ctx context.Context, in *GetBalanceStatisticsRequest, opts ...grpc.CallOption) (*GetBalanceStatisticsResponse, error)
}
type userServiceClient struct {
cc grpc.ClientConnInterface
}
func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient {
return &userServiceClient{cc}
}
func (c *userServiceClient) GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetInfoResponse)
err := c.cc.Invoke(ctx, UserService_GetInfo_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) GetBalance(ctx context.Context, in *GetBalanceRequest, opts ...grpc.CallOption) (*GetBalanceResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetBalanceResponse)
err := c.cc.Invoke(ctx, UserService_GetBalance_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) GetStatistics(ctx context.Context, in *GetStatisticsRequest, opts ...grpc.CallOption) (*GetStatisticsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetStatisticsResponse)
err := c.cc.Invoke(ctx, UserService_GetStatistics_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) GetBalanceStatistics(ctx context.Context, in *GetBalanceStatisticsRequest, opts ...grpc.CallOption) (*GetBalanceStatisticsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetBalanceStatisticsResponse)
err := c.cc.Invoke(ctx, UserService_GetBalanceStatistics_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// UserServiceServer is the server API for UserService service.
// All implementations must embed UnimplementedUserServiceServer
// for forward compatibility.
type UserServiceServer interface {
GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error)
GetBalance(context.Context, *GetBalanceRequest) (*GetBalanceResponse, error)
GetStatistics(context.Context, *GetStatisticsRequest) (*GetStatisticsResponse, error)
GetBalanceStatistics(context.Context, *GetBalanceStatisticsRequest) (*GetBalanceStatisticsResponse, error)
mustEmbedUnimplementedUserServiceServer()
}
// UnimplementedUserServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedUserServiceServer struct{}
func (UnimplementedUserServiceServer) GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented")
}
func (UnimplementedUserServiceServer) GetBalance(context.Context, *GetBalanceRequest) (*GetBalanceResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetBalance not implemented")
}
func (UnimplementedUserServiceServer) GetStatistics(context.Context, *GetStatisticsRequest) (*GetStatisticsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetStatistics not implemented")
}
func (UnimplementedUserServiceServer) GetBalanceStatistics(context.Context, *GetBalanceStatisticsRequest) (*GetBalanceStatisticsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetBalanceStatistics not implemented")
}
func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {}
func (UnimplementedUserServiceServer) testEmbeddedByValue() {}
// UnsafeUserServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to UserServiceServer will
// result in compilation errors.
type UnsafeUserServiceServer interface {
mustEmbedUnimplementedUserServiceServer()
}
func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) {
// If the following call pancis, it indicates UnimplementedUserServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&UserService_ServiceDesc, srv)
}
func _UserService_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetInfoRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).GetInfo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_GetInfo_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).GetInfo(ctx, req.(*GetInfoRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_GetBalance_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetBalanceRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).GetBalance(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_GetBalance_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).GetBalance(ctx, req.(*GetBalanceRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_GetStatistics_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetStatisticsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).GetStatistics(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_GetStatistics_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).GetStatistics(ctx, req.(*GetStatisticsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_GetBalanceStatistics_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetBalanceStatisticsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).GetBalanceStatistics(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_GetBalanceStatistics_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).GetBalanceStatistics(ctx, req.(*GetBalanceStatisticsRequest))
}
return interceptor(ctx, in, info, handler)
}
// UserService_ServiceDesc is the grpc.ServiceDesc for UserService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var UserService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "user.UserService",
HandlerType: (*UserServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetInfo",
Handler: _UserService_GetInfo_Handler,
},
{
MethodName: "GetBalance",
Handler: _UserService_GetBalance_Handler,
},
{
MethodName: "GetStatistics",
Handler: _UserService_GetStatistics_Handler,
},
{
MethodName: "GetBalanceStatistics",
Handler: _UserService_GetBalanceStatistics_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/proto/user/user.proto",
}