merge dev in ref intrf

This commit is contained in:
Pavel 2024-01-31 23:25:30 +03:00
commit d614710c80
43 changed files with 1635 additions and 251 deletions

@ -1,57 +1,16 @@
include:
- project: "devops/pena-continuous-integration"
file: "/templates/docker/build-template.gitlab-ci.yml"
- project: "devops/pena-continuous-integration"
file: "/templates/docker/deploy-template.gitlab-ci.yml"
stages: stages:
- lint
- test
- clean
- build - build
- deploy - deploy
lint:
image: golangci/golangci-lint:v1.55-alpine
stage: lint
before_script:
- go install github.com/vektra/mockery/v2@v2.26.0
- go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4
script:
- go generate ./internal/...
- golangci-lint version
- golangci-lint run ./internal/...
test:
image: golang:1.21-alpine
stage: test
coverage: /\(statements\)(?:\s+)?(\d+(?:\.\d+)?%)/
script:
- CGO_ENABLED=0 go test ./internal/... -coverprofile=coverage.out
- go tool cover -html=coverage.out -o coverage.html
- go tool cover -func coverage.out
artifacts:
expire_in: "3 days"
paths:
- coverage.html
clean-old:
stage: clean
image:
name: docker/compose:1.28.0
entrypoint: [""]
allow_failure: true
variables:
PRODUCTION_BRANCH: main
STAGING_BRANCH: "staging"
DEPLOY_TO: "staging"
rules:
- if: $CI_COMMIT_BRANCH == $PRODUCTION_BRANCH || $CI_COMMIT_BRANCH == $STAGING_BRANCH
when: on_success
before_script:
- echo DEPLOY_TO = $DEPLOY_TO
script:
- docker-compose -f deployments/$DEPLOY_TO/docker-compose.yaml down --volumes --rmi local
build-app: build-app:
stage: build stage: build
image: tags:
name: gcr.io/kaniko-project/executor:debug - gobuild
entrypoint: [""]
variables: variables:
DOCKER_BUILD_PATH: "./Dockerfile" DOCKER_BUILD_PATH: "./Dockerfile"
STAGING_BRANCH: "staging" STAGING_BRANCH: "staging"
@ -69,32 +28,22 @@ build-app:
- echo CI_COMMIT_REF_SLUG = $CI_COMMIT_REF_SLUG - echo CI_COMMIT_REF_SLUG = $CI_COMMIT_REF_SLUG
- echo DOCKER_BUILD_PATH = $DOCKER_BUILD_PATH - echo DOCKER_BUILD_PATH = $DOCKER_BUILD_PATH
- echo CI_PIPELINE_ID = $CI_PIPELINE_ID - echo CI_PIPELINE_ID = $CI_PIPELINE_ID
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script: script:
- mkdir -p /kaniko/.docker - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID --build-arg GITLAB_TOKEN=$GITLAB_TOKEN $CI_PROJECT_DIR
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID
- |
/kaniko/executor --context $CI_PROJECT_DIR \
--cache=true --cache-repo=$CI_REGISTRY_IMAGE \
--dockerfile $CI_PROJECT_DIR/$DOCKER_BUILD_PATH --target production \
--destination $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID
deploy-to-staging: deploy-to-staging:
stage: deploy stage: deploy
image: extends: .deploy_template
name: docker/compose:1.28.0
entrypoint: [""]
variables: variables:
DEPLOY_TO: "staging" DEPLOY_TO: "staging"
BRANCH: "staging" BRANCH: "staging"
rules: rules:
- if: $CI_COMMIT_BRANCH == $BRANCH - if: "$CI_COMMIT_BRANCH == $BRANCH"
before_script: after_script:
- echo CI_PROJECT_NAME = $CI_PROJECT_NAME - ls
- echo CI_REGISTRY = $CI_REGISTRY
- echo REGISTRY_USER = $REGISTRY_USER
- echo REGISTRY_TOKEN = $REGISTRY_TOKEN
- echo DEPLOY_TO = $DEPLOY_TO
- echo BRANCH = $BRANCH
script:
- docker login -u $REGISTRY_USER -p $REGISTRY_TOKEN $CI_REGISTRY
- docker-compose -f deployments/$DEPLOY_TO/docker-compose.yaml up -d

@ -44,7 +44,7 @@ linters:
- revive - revive
- rowserrcheck - rowserrcheck
- staticcheck - staticcheck
- stylecheck # - stylecheck
- thelper - thelper
- typecheck - typecheck
- unconvert - unconvert

@ -2,7 +2,7 @@
FROM golang:1.20.3-alpine AS build FROM golang:1.20.3-alpine AS build
# Update packages and clear cache # Update packages and clear cache
RUN apk update && apk add --no-cache curl && rm -rf /var/cache/apk/* RUN apk add --no-cache curl
# Set work directory # Set work directory
WORKDIR /app WORKDIR /app
# Create binary directory # Create binary directory
@ -24,9 +24,7 @@ RUN GOOS=linux go build -o bin ./...
FROM alpine:3.18.3 AS test FROM alpine:3.18.3 AS test
# Install packages # Install packages
RUN apk --no-cache add ca-certificates && rm -rf /var/cache/apk/* RUN apk --no-cache add ca-certificates
# Set GO111MODULE env
ENV GO111MODULE=off
# Create home directory # Create home directory
WORKDIR /app WORKDIR /app
# Copy build file # Copy build file
@ -44,7 +42,7 @@ CMD [ "./app" ]
FROM alpine:3.18.3 AS production FROM alpine:3.18.3 AS production
# Install packages # Install packages
RUN apk --no-cache add ca-certificates && rm -rf /var/cache/apk/* RUN apk --no-cache add ca-certificates
# Create home directory # Create home directory
WORKDIR /app WORKDIR /app
# Copy build file # Copy build file

@ -25,15 +25,16 @@ test.unit: ## run unit tests
go test ./... go test ./...
test.integration: ## run integration tests test.integration: ## run integration tests
go mod vendor
@make test.integration.up @make test.integration.up
@make test.integration.start @make test.integration.start
@make test.integration.down @make test.integration.down
test.integration.up: ## build integration test environment test.integration.up: ## build integration test environment
docker-compose -f deployments/test/docker-compose.yaml up -d docker-compose -f deployments/test/docker-compose.yaml up -d --remove-orphans
test.integration.down: ## shutting down integration environment test.integration.down: ## shutting down integration environment
docker-compose -f deployments/test/docker-compose.yaml down --volumes --rmi local docker-compose -f deployments/test/docker-compose.yaml down --volumes
test.integration.start: ## run integration test test.integration.start: ## run integration test
go test -count=1 ./tests/integration/... go test -count=1 ./tests/integration/...

@ -42,3 +42,4 @@ KAFKA_TOPIC_TARIFF - название топика для сообщений т
## Полезные ссылки: ## Полезные ссылки:
- [**Диаграммы**](./docs/diagram/README.md) - [**Диаграммы**](./docs/diagram/README.md)
- Для того чтобы создать новые endpoint, нужно прописать их в customer/api/openapi/v1/openapi.yaml, сделать его описание, с помощью инструкций в makefile сгенерировать файлы

@ -18,16 +18,6 @@ tags:
description: история description: история
paths: paths:
/health:
get:
summary: Health Check
description: Check the health status of the API
responses:
200:
description: OK
503:
description: Service Unavailable
/account: /account:
get: get:
tags: tags:
@ -536,7 +526,7 @@ paths:
summary: Получение лога событий связанных с аккаунтом summary: Получение лога событий связанных с аккаунтом
operationId: getHistory operationId: getHistory
security: security:
- Bearer: [] - Bearer: [ ]
parameters: parameters:
- name: page - name: page
in: query in: query
@ -561,6 +551,13 @@ paths:
explode: false explode: false
schema: schema:
type: string type: string
- name: accountID
in: query
description: Идентификатор аккаунта. Если не указан, будет использоваться идентификатор из токена.
required: false
explode: false
schema:
type: string
responses: responses:
"200": "200":
description: Успешное получение событий description: Успешное получение событий
@ -583,6 +580,140 @@ paths:
schema: schema:
$ref: "#/components/schemas/Error" $ref: "#/components/schemas/Error"
/recent:
get:
tags:
- history
summary: Получение недавних тарифов
operationId: getRecentTariffs
description: Возвращает список уникальных тарифов из истории. Айди аккаунта получается из заголовка.
security:
- Bearer: []
responses:
'200':
description: Успешный запрос
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/TariffID"
'400':
description: Неверный запрос
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'404':
description: Тарифы не найдены
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'500':
description: Внутренняя ошибка сервера
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
requestBody:
content:
application/json:
schema:
type: object
required: [id]
properties:
id:
type: string
example: "807f1f77bcf81cd799439011"
/sendReport:
post:
tags:
- history
summary: отправить акт проделанных работ на почту
operationId: sendReport
security:
- Bearer: []
description: Запрос на отправку акта проделанных работ
requestBody:
content:
application/json:
schema:
type: object
required: [id]
properties:
id:
type: string
example: "807f1f77bcf81cd799439011"
responses:
"200":
description: успешная отправка
"401":
description: Неавторизован
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/history/ltv:
post:
tags:
- history
summary: Расчет среднего времени жизни платящего клиента (LTV)
operationId: calculateLTV
security:
- Bearer: [ ]
requestBody:
description: Период для расчета LTV
required: true
content:
application/json:
schema:
type: object
properties:
from:
type: integer
format: int64
description: Начальная дата в формате Unix timestamp. Если 0, устанавливает начало истории.
to:
type: integer
format: int64
description: Конечная дата в формате Unix timestamp. Если 0, устанавливает текущее время.
required:
- from
- to
responses:
'200':
description: Успешный расчет LTV
content:
application/json:
schema:
type: object
properties:
ltv:
type: integer
format: int64
description: Среднее количество дней между первым и последним платежом
'400':
description: Неверный запрос, если from больше, чем to
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Неавторизован
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'500':
description: Внутренняя ошибка сервера
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components: components:
schemas: schemas:
Account: Account:
@ -748,6 +879,14 @@ components:
"alfabank", "alfabank",
] ]
TariffID:
type: object
properties:
ID:
type: string
example: "807f1f77bcf81cd799439011"
AccountStatus: AccountStatus:
type: string type: string
enum: ["no", "nko", "org"] enum: ["no", "nko", "org"]

@ -0,0 +1,9 @@
DB_HOST=test-pena-auth-db
DB_PORT=27017
ENVIRONMENT=staging
HTTP_HOST=0.0.0.0
HTTP_PORT=8000
DB_USERNAME=test
DB_PASSWORD=test
DB_NAME=admin
DB_AUTH=admin

@ -0,0 +1,191 @@
version: "3"
volumes:
redpanda: null
test-mongodb: null
test-mongoconfdb: null
services:
customer-service:
container_name: customer-service
build:
context: ../../.
dockerfile: Dockerfile
target: test
env_file:
- .env.test
environment:
- HTTP_HOST=0.0.0.0
- HTTP_PORT=8000
- GRPC_HOST=0.0.0.0
- GRPC_PORT=9000
- GRPC_DOMEN=customer-service:9000
- MONGO_HOST=customer-db
- MONGO_PORT=27017
- MONGO_USER=test
- MONGO_PASSWORD=test
- MONGO_DB_NAME=admin
- MONGO_AUTH=admin
- KAFKA_BROKERS=customer-redpanda:9092
- KAFKA_TOPIC_TARIFF=tariffs
- AUTH_MICROSERVICE_USER_URL=http://pena-auth-service:8000/user
- HUBADMIN_MICROSERVICE_TARIFF_URL=http://hub-admin-backend-service:8000/tariff
- CURRENCY_MICROSERVICE_TRANSLATE_URL=http://cbrfworker-service:8000/change
- DISCOUNT_MICROSERVICE_GRPC_HOST=discount-service:9000
- PAYMENT_MICROSERVICE_GRPC_HOST=treasurer-service:9085
ports:
- 8082:8000
- 9092:9000
depends_on:
- customer-db
- customer-migration
- redpanda
networks:
- test
customer-migration:
container_name: customer-migration
build:
context: ../../.
dockerfile: Dockerfile
target: test
command:
[
"sh",
"-c",
'migrate -source file://migrations -database "mongodb://test:test@customer-db:27017/admin?authSource=admin" up',
]
depends_on:
- customer-db
networks:
- test
customer-db:
container_name: customer-db
image: "mongo:6.0.3"
environment:
MONGO_INITDB_ROOT_USERNAME: test
MONGO_INITDB_ROOT_PASSWORD: test
ports:
- "27024:27017"
networks:
- test
redpanda:
container_name: customer-redpanda
tty: true
image: docker.redpanda.com/redpandadata/redpanda:v23.1.13
command:
- redpanda start
- --smp 1
- --overprovisioned
- --kafka-addr internal://0.0.0.0:9092,external://0.0.0.0:19092
# Address the broker advertises to clients that connect to the Kafka API.
# Use the internal addresses to connect to the Redpanda brokers
# from inside the same Docker network.
# Use the external addresses to connect to the Redpanda brokers
# from outside the Docker network.
- --advertise-kafka-addr internal://redpanda:9092,external://localhost:19092
- --pandaproxy-addr internal://0.0.0.0:8082,external://0.0.0.0:18082
# Address the broker advertises to clients that connect to the HTTP Proxy.
- --advertise-pandaproxy-addr internal://redpanda:8082,external://localhost:18082
- --schema-registry-addr internal://0.0.0.0:8081,external://0.0.0.0:18081
# Redpanda brokers use the RPC API to communicate with each other internally.
- --rpc-addr redpanda:33145
- --advertise-rpc-addr redpanda:33145
ports:
- 18081:18081
- 18082:18082
- 19092:19092
- 19644:9644
volumes:
- redpanda:/var/lib/redpanda/data
networks:
- test
healthcheck:
test: ["CMD-SHELL", "rpk cluster health | grep -E 'Healthy:.+true' || exit 1"]
interval: 15s
timeout: 3s
retries: 5
start_period: 5s
console:
tty: true
image: docker.redpanda.com/redpandadata/console:v2.2.4
entrypoint: /bin/sh
command: -c "echo \"$$CONSOLE_CONFIG_FILE\" > /tmp/config.yml; /app/console"
environment:
CONFIG_FILEPATH: /tmp/config.yml
CONSOLE_CONFIG_FILE: |
kafka:
brokers: ["redpanda:9092"]
schemaRegistry:
enabled: true
urls: ["http://redpanda:8081"]
redpanda:
adminApi:
enabled: true
urls: ["http://redpanda:9644"]
connect:
enabled: true
clusters:
- name: local-connect-cluster
url: http://connect:8083
ports:
- 8080:8080
networks:
- test
depends_on:
- redpanda
connect:
tty: true
image: docker.redpanda.com/redpandadata/connectors:latest
hostname: connect
container_name: connect
networks:
- test
# platform: 'linux/amd64'
depends_on:
- redpanda
ports:
- "8083:8083"
environment:
CONNECT_CONFIGURATION: |
key.converter=org.apache.kafka.connect.converters.ByteArrayConverter
value.converter=org.apache.kafka.connect.converters.ByteArrayConverter
group.id=connectors-cluster
offset.storage.topic=_internal_connectors_offsets
config.storage.topic=_internal_connectors_configs
status.storage.topic=_internal_connectors_status
config.storage.replication.factor=-1
offset.storage.replication.factor=-1
status.storage.replication.factor=-1
offset.flush.interval.ms=1000
producer.linger.ms=50
producer.batch.size=131072
CONNECT_BOOTSTRAP_SERVERS: redpanda:9092
CONNECT_GC_LOG_ENABLED: "false"
CONNECT_HEAP_OPTS: -Xms512M -Xmx512M
CONNECT_LOG_LEVEL: info
test-pena-auth-db:
container_name: test-pena-auth-db
init: true
image: "mongo:6.0.3"
command: mongod --quiet --logpath /dev/null
volumes:
- test-mongodb:/data/db
- test-mongoconfdb:/data/configdb
environment:
MONGO_INITDB_ROOT_USERNAME: test
MONGO_INITDB_ROOT_PASSWORD: test
networks:
- test
networks:
test:

@ -24,11 +24,13 @@ services:
- KAFKA_BROKERS=10.6.0.11:9092 - KAFKA_BROKERS=10.6.0.11:9092
- KAFKA_TOPIC_TARIFF=tariffs - KAFKA_TOPIC_TARIFF=tariffs
- AUTH_MICROSERVICE_USER_URL=https://admin.pena.digital/user - AUTH_MICROSERVICE_USER_URL=http://10.6.0.11:59300/user
- HUBADMIN_MICROSERVICE_TARIFF_URL=https://admin.pena.digital/strator/tariff - HUBADMIN_MICROSERVICE_TARIFF_URL=http://10.6.0.11:59303/tariff
- CURRENCY_MICROSERVICE_TRANSLATE_URL=http://10.6.0.11:3131/change - CURRENCY_MICROSERVICE_TRANSLATE_URL=http://10.6.0.11:3131/change
- DISCOUNT_MICROSERVICE_GRPC_HOST=10.6.0.11:9001 - DISCOUNT_MICROSERVICE_GRPC_HOST=10.6.0.11:9001
- PAYMENT_MICROSERVICE_GRPC_HOST=10.6.0.11:9085 - PAYMENT_MICROSERVICE_GRPC_HOST=10.6.0.11:9085
- VERIFICATION_MICROSERVICE_USER_URL=http://10.6.0.17:7035/verification
- TEMPLATEGEN_MICROSERVICE_URL=10.6.0.17
- JWT_PUBLIC_KEY=$JWT_PUBLIC_KEY - JWT_PUBLIC_KEY=$JWT_PUBLIC_KEY
- JWT_ISSUER=pena-auth-service - JWT_ISSUER=pena-auth-service
@ -37,9 +39,4 @@ services:
- 8065:8065 - 8065:8065
- 9065:9065 - 9065:9065
networks: networks:
- marketplace_penahub_frontend
- default - default
networks:
marketplace_penahub_frontend:
external: true

@ -0,0 +1,18 @@
version: "3"
services:
integration:
container_name: customer-integration
image: golang:1
volumes:
- ../..:/app:ro,z
working_dir: /app
command: go test ./tests/integration/...
environment:
- CUSTOMER_SERVICE=customer-service:8000
networks:
- test_test
networks:
test_test:
external: true

@ -7,7 +7,7 @@ services:
customer-service: customer-service:
container_name: customer-service container_name: customer-service
build: build:
context: ../../. context: ../..
dockerfile: Dockerfile dockerfile: Dockerfile
target: test target: test
env_file: env_file:
@ -40,17 +40,11 @@ services:
- 9092:9000 - 9092:9000
depends_on: depends_on:
customer-db: customer-db:
condition: service_healthy condition: service_started
customer-migration: customer-migration:
condition: service_completed_successfully condition: service_completed_successfully
redpanda: redpanda:
condition: service_healthy condition: service_healthy
healthcheck:
test: wget -q --spider http://localhost:8000/health
interval: 2s
timeout: 2s
retries: 5
start_period: 5s
networks: networks:
- test - test
@ -75,12 +69,6 @@ services:
MONGO_INITDB_ROOT_PASSWORD: test MONGO_INITDB_ROOT_PASSWORD: test
ports: ports:
- "27024:27017" - "27024:27017"
healthcheck:
test: mongosh --quiet --eval "db.adminCommand('ping')" >/dev/null
interval: 2s
timeout: 2s
retries: 5
start_period: 5s
networks: networks:
- test - test
@ -150,38 +138,42 @@ services:
networks: networks:
- test - test
depends_on: depends_on:
- redpanda connect:
condition: service_started
redpanda:
condition: service_healthy
connect: connect:
tty: true tty: true
image: docker.redpanda.com/redpandadata/connectors:latest image: docker.redpanda.com/redpandadata/connectors:latest
hostname: connect # hostname: connect
container_name: connect container_name: customer-connect
networks:
- test
# platform: 'linux/amd64' # platform: 'linux/amd64'
depends_on: depends_on:
- redpanda redpanda:
condition: service_healthy
ports: ports:
- "8083:8083" - "8083:8083"
environment: environment:
CONNECT_CONFIGURATION: | CONNECT_CONFIGURATION: |
key.converter=org.apache.kafka.connect.converters.ByteArrayConverter key.converter=org.apache.kafka.connect.converters.ByteArrayConverter
value.converter=org.apache.kafka.connect.converters.ByteArrayConverter value.converter=org.apache.kafka.connect.converters.ByteArrayConverter
group.id=connectors-cluster group.id=connectors-cluster
offset.storage.topic=_internal_connectors_offsets offset.storage.topic=_internal_connectors_offsets
config.storage.topic=_internal_connectors_configs config.storage.topic=_internal_connectors_configs
status.storage.topic=_internal_connectors_status status.storage.topic=_internal_connectors_status
config.storage.replication.factor=-1 config.storage.replication.factor=-1
offset.storage.replication.factor=-1 offset.storage.replication.factor=-1
status.storage.replication.factor=-1 status.storage.replication.factor=-1
offset.flush.interval.ms=1000 offset.flush.interval.ms=1000
producer.linger.ms=50 producer.linger.ms=50
producer.batch.size=131072 producer.batch.size=131072
CONNECT_BOOTSTRAP_SERVERS: redpanda:9092 CONNECT_BOOTSTRAP_SERVERS: redpanda:9092
CONNECT_GC_LOG_ENABLED: "false" CONNECT_GC_LOG_ENABLED: "false"
CONNECT_HEAP_OPTS: -Xms512M -Xmx512M CONNECT_HEAP_OPTS: -Xms512M -Xmx512M
CONNECT_LOG_LEVEL: info CONNECT_LOG_LEVEL: info
networks:
- test
networks: networks:
test: test:

6
go.mod

@ -16,7 +16,7 @@ require (
github.com/twmb/franz-go v1.13.6 github.com/twmb/franz-go v1.13.6
github.com/twmb/franz-go/pkg/kadm v1.8.1 github.com/twmb/franz-go/pkg/kadm v1.8.1
go.mongodb.org/mongo-driver v1.11.4 go.mongodb.org/mongo-driver v1.11.4
go.uber.org/zap v1.24.0 go.uber.org/zap v1.26.0
google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 google.golang.org/genproto v0.0.0-20230223222841-637eb2293923
google.golang.org/grpc v1.53.0 google.golang.org/grpc v1.53.0
google.golang.org/protobuf v1.31.0 google.golang.org/protobuf v1.31.0
@ -24,7 +24,6 @@ require (
require ( require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/benbjohnson/clock v1.3.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/swag v0.22.3 // indirect github.com/go-openapi/swag v0.22.3 // indirect
@ -55,11 +54,10 @@ require (
github.com/xdg-go/scram v1.1.1 // indirect github.com/xdg-go/scram v1.1.1 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect github.com/xdg-go/stringprep v1.0.3 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.12.0 // indirect golang.org/x/crypto v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect golang.org/x/net v0.14.0 // indirect
golang.org/x/sync v0.2.0 // indirect golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.11.0 // indirect golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.12.0 // indirect golang.org/x/text v0.12.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect

16
go.sum

@ -4,8 +4,6 @@ github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMz
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.3 h1:g+rSsSaAzhHJYcIQE78hJ3AhyjjtQvleKDjlhdBnIhc=
github.com/benbjohnson/clock v1.3.3/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@ -149,6 +147,7 @@ github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
@ -167,16 +166,15 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas= go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas=
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
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.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 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/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/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.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -209,8 +207,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/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.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

@ -13,14 +13,18 @@ type ClientsDeps struct {
CurrencyURL *models.CurrencyMicroserviceURL CurrencyURL *models.CurrencyMicroserviceURL
DiscountServiceConfiguration *models.DiscountMicroserviceConfiguration DiscountServiceConfiguration *models.DiscountMicroserviceConfiguration
PaymentServiceConfiguration *models.PaymentMicroserviceConfiguration PaymentServiceConfiguration *models.PaymentMicroserviceConfiguration
VerificationURL *models.VerificationMicroserviceURL
TemplategenURL *models.TemplategenMicroserviceURL
} }
type Clients struct { type Clients struct {
AuthClient *client.AuthClient AuthClient *client.AuthClient
HubadminClient *client.HubadminClient HubadminClient *client.HubadminClient
CurrencyClient *client.CurrencyClient CurrencyClient *client.CurrencyClient
DiscountClient *client.DiscountClient DiscountClient *client.DiscountClient
PaymentClient *client.PaymentClient PaymentClient *client.PaymentClient
VerificationClient *client.VerificationClient
TemplateClient *client.TemplateClient
} }
func NewClients(deps ClientsDeps) *Clients { func NewClients(deps ClientsDeps) *Clients {
@ -45,5 +49,13 @@ func NewClients(deps ClientsDeps) *Clients {
Logger: deps.Logger, Logger: deps.Logger,
PaymentServiceHost: deps.PaymentServiceConfiguration.HostGRPC, PaymentServiceHost: deps.PaymentServiceConfiguration.HostGRPC,
}), }),
VerificationClient: client.NewVerificationClient(client.VerificationClientDeps{
Logger: deps.Logger,
URLs: deps.VerificationURL,
}),
TemplateClient: client.NewTemplateClient(client.TemplateClientDeps{
Logger: deps.Logger,
URLs: deps.TemplategenURL,
}),
} }
} }

@ -21,6 +21,8 @@ func TestNewClients(t *testing.T) {
CurrencyURL: &models.CurrencyMicroserviceURL{}, CurrencyURL: &models.CurrencyMicroserviceURL{},
DiscountServiceConfiguration: &models.DiscountMicroserviceConfiguration{HostGRPC: "host"}, DiscountServiceConfiguration: &models.DiscountMicroserviceConfiguration{HostGRPC: "host"},
PaymentServiceConfiguration: &models.PaymentMicroserviceConfiguration{HostGRPC: "host"}, PaymentServiceConfiguration: &models.PaymentMicroserviceConfiguration{HostGRPC: "host"},
VerificationURL: &models.VerificationMicroserviceURL{Verification: ""},
TemplategenURL: &models.TemplategenMicroserviceURL{Templategen: ""},
}) })
assert.NotNil(t, clients) assert.NotNil(t, clients)

@ -90,6 +90,16 @@ func setDefaultTestingENV(t *testing.T) *models.Config {
PaymentMicroservice: models.PaymentMicroserviceConfiguration{ PaymentMicroservice: models.PaymentMicroserviceConfiguration{
HostGRPC: "domen", HostGRPC: "domen",
}, },
VerificationMicroservice: models.VerificationMicroserviceConfiguration{
URL: models.VerificationMicroserviceURL{
Verification: "domen",
},
},
TemplategenMicroserviceURL: models.TemplategenMicroserviceConfiguration{
URL: models.TemplategenMicroserviceURL{
Templategen: "domen",
},
},
JWT: models.JWTConfiguration{ JWT: models.JWTConfiguration{
PrivateKey: "jwt private key", PrivateKey: "jwt private key",
PublicKey: "jwt public key", PublicKey: "jwt public key",
@ -126,6 +136,8 @@ func setDefaultTestingENV(t *testing.T) *models.Config {
t.Setenv("CURRENCY_MICROSERVICE_TRANSLATE_URL", defaultConfiguration.Service.CurrencyMicroservice.URL.Translate) t.Setenv("CURRENCY_MICROSERVICE_TRANSLATE_URL", defaultConfiguration.Service.CurrencyMicroservice.URL.Translate)
t.Setenv("DISCOUNT_MICROSERVICE_GRPC_HOST", defaultConfiguration.Service.DiscountMicroservice.HostGRPC) t.Setenv("DISCOUNT_MICROSERVICE_GRPC_HOST", defaultConfiguration.Service.DiscountMicroservice.HostGRPC)
t.Setenv("PAYMENT_MICROSERVICE_GRPC_HOST", defaultConfiguration.Service.PaymentMicroservice.HostGRPC) t.Setenv("PAYMENT_MICROSERVICE_GRPC_HOST", defaultConfiguration.Service.PaymentMicroservice.HostGRPC)
t.Setenv("VERIFICATION_MICROSERVICE_USER_URL", defaultConfiguration.Service.VerificationMicroservice.URL.Verification)
t.Setenv("TEMPLATEGEN_MICROSERVICE_URL", defaultConfiguration.Service.TemplategenMicroserviceURL.URL.Templategen)
t.Setenv("MONGO_HOST", defaultConfiguration.Database.Host) t.Setenv("MONGO_HOST", defaultConfiguration.Database.Host)
t.Setenv("MONGO_PORT", defaultConfiguration.Database.Port) t.Setenv("MONGO_PORT", defaultConfiguration.Database.Port)

@ -25,6 +25,8 @@ func TestNewControllers(t *testing.T) {
CurrencyURL: &models.CurrencyMicroserviceURL{}, CurrencyURL: &models.CurrencyMicroserviceURL{},
DiscountServiceConfiguration: &models.DiscountMicroserviceConfiguration{HostGRPC: "host"}, DiscountServiceConfiguration: &models.DiscountMicroserviceConfiguration{HostGRPC: "host"},
PaymentServiceConfiguration: &models.PaymentMicroserviceConfiguration{HostGRPC: "host"}, PaymentServiceConfiguration: &models.PaymentMicroserviceConfiguration{HostGRPC: "host"},
VerificationURL: &models.VerificationMicroserviceURL{Verification: ""},
TemplategenURL: &models.TemplategenMicroserviceURL{Templategen: ""},
}) })
repositories := initialize.NewRepositories(initialize.RepositoriesDeps{ repositories := initialize.NewRepositories(initialize.RepositoriesDeps{

@ -36,6 +36,7 @@ func NewServices(deps ServicesDeps) *Services {
historyService := history.New(history.Deps{ historyService := history.New(history.Deps{
Logger: deps.Logger, Logger: deps.Logger,
Repository: deps.Repositories.HistoryRepository, Repository: deps.Repositories.HistoryRepository,
AuthClient: deps.Clients.AuthClient,
}) })
walletService := wallet.New(wallet.Deps{ walletService := wallet.New(wallet.Deps{

@ -25,6 +25,8 @@ func TestNewServices(t *testing.T) {
CurrencyURL: &models.CurrencyMicroserviceURL{}, CurrencyURL: &models.CurrencyMicroserviceURL{},
DiscountServiceConfiguration: &models.DiscountMicroserviceConfiguration{HostGRPC: "host"}, DiscountServiceConfiguration: &models.DiscountMicroserviceConfiguration{HostGRPC: "host"},
PaymentServiceConfiguration: &models.PaymentMicroserviceConfiguration{HostGRPC: "host"}, PaymentServiceConfiguration: &models.PaymentMicroserviceConfiguration{HostGRPC: "host"},
VerificationURL: &models.VerificationMicroserviceURL{Verification: ""},
TemplategenURL: &models.TemplategenMicroserviceURL{Templategen: ""},
}) })
brokers := initialize.NewBrokers(initialize.BrokersDeps{ brokers := initialize.NewBrokers(initialize.BrokersDeps{

@ -0,0 +1,88 @@
package client
import (
"bytes"
"context"
"encoding/json"
"log"
"mime/multipart"
"net/http"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
)
type TemplateClientDeps struct {
Logger *zap.Logger
URLs *models.TemplategenMicroserviceURL
}
type TemplateClient struct {
logger *zap.Logger
urls *models.TemplategenMicroserviceURL
}
func NewTemplateClient(deps TemplateClientDeps) *TemplateClient {
if deps.Logger == nil {
log.Panicln("logger is nil on <NewTemplateClient>")
}
if deps.URLs == nil {
log.Panicln("urls is nil on <NewTemplateClient>")
}
return &TemplateClient{
logger: deps.Logger,
urls: deps.URLs,
}
}
func (receiver *TemplateClient) SendData(ctx context.Context, data models.RespGeneratorService, fileContents []byte, email string) errors.Error {
tmplURL := receiver.urls.Templategen
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
err := writer.WriteField("email", email)
if err != nil {
return errors.New(err, errors.ErrInternalError)
}
jsonData, err := json.Marshal(data)
if err != nil {
return errors.New(err, errors.ErrInternalError)
}
err = writer.WriteField("data", string(jsonData))
if err != nil {
return errors.New(err, errors.ErrInternalError)
}
fileWriter, err := writer.CreateFormFile("file", "report.docx")
if err != nil {
return errors.New(err, errors.ErrInternalError)
}
_, err = fileWriter.Write(fileContents)
if err != nil {
return errors.New(err, errors.ErrInternalError)
}
err = writer.Close()
if err != nil {
return errors.New(err, errors.ErrInternalError)
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, tmplURL, body)
if err != nil {
return errors.New(err, errors.ErrInternalError)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, err := http.DefaultClient.Do(req)
if err != nil {
return errors.New(err, errors.ErrInternalError)
}
defer resp.Body.Close()
return nil
}

@ -0,0 +1,58 @@
package client
import (
"context"
"fmt"
"log"
"net/url"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/client"
)
type VerificationClientDeps struct {
Logger *zap.Logger
URLs *models.VerificationMicroserviceURL
}
type VerificationClient struct {
logger *zap.Logger
urls *models.VerificationMicroserviceURL
}
func NewVerificationClient(deps VerificationClientDeps) *VerificationClient {
if deps.Logger == nil {
log.Panicln("logger is nil on <NewVerificationClient>")
}
if deps.URLs == nil {
log.Panicln("urls is nil on <NewVerificationClient>")
}
return &VerificationClient{
logger: deps.Logger,
urls: deps.URLs,
}
}
func (receiver *VerificationClient) GetVerification(ctx context.Context, userID string) (*models.Verification, errors.Error) {
verifURL, err := url.JoinPath(receiver.urls.Verification, userID)
if err != nil {
return nil, errors.New(
fmt.Errorf("failed to join path on <GetVerification> of <VerificationClient>: %w", err),
errors.ErrInternalError,
)
}
response, err := client.Get[models.Verification, models.FastifyError](ctx, &client.RequestSettings{
URL: verifURL,
Headers: map[string]string{"Content-Type": "application/json"},
})
if err != nil {
return nil, errors.New(err, errors.ErrInternalError)
}
return response.Body, nil
}

@ -1,6 +1,7 @@
package history package history
import ( import (
"fmt"
"log" "log"
"net/http" "net/http"
@ -38,8 +39,17 @@ func New(deps Deps) *Controller {
} }
func (receiver *Controller) GetHistoryList(ctx echo.Context, params swagger.GetHistoryParams) error { func (receiver *Controller) GetHistoryList(ctx echo.Context, params swagger.GetHistoryParams) error {
var userID string
if params.AccountID != nil && *params.AccountID != "" {
userID = *params.AccountID
} else {
userID, _ = ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
}
histories, err := receiver.historyService.GetHistoryList(ctx.Request().Context(), &history.GetHistories{ histories, err := receiver.historyService.GetHistoryList(ctx.Request().Context(), &history.GetHistories{
Type: params.Type, Type: params.Type,
UserID: userID,
Pagination: &models.Pagination{ Pagination: &models.Pagination{
Page: int64(*params.Page), Page: int64(*params.Page),
Limit: int64(*params.Limit), Limit: int64(*params.Limit),
@ -52,3 +62,74 @@ func (receiver *Controller) GetHistoryList(ctx echo.Context, params swagger.GetH
return ctx.JSON(http.StatusOK, histories) return ctx.JSON(http.StatusOK, histories)
} }
// TODO:tests.
func (receiver *Controller) GetRecentTariffs(ctx echo.Context) error {
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
if !ok {
receiver.logger.Error("failed to convert jwt payload to string on <GetRecentTariffs> of <HistoryController>")
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to convert jwt payload to string: %s", userID),
errors.ErrInvalidArgs,
))
}
tariffs, err := receiver.historyService.GetRecentTariffs(ctx.Request().Context(), userID)
if err != nil {
receiver.logger.Error("failed to get recent tariffs on <GetRecentTariffs> of <HistoryController>",
zap.String("userId", userID),
zap.Error(err),
)
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, tariffs)
}
// TODO:tests.
func (receiver *Controller) SendReport(ctx echo.Context) error {
historyID := ctx.Param("id")
err := receiver.historyService.GetHistoryByID(ctx.Request().Context(), historyID)
if err != nil {
receiver.logger.Error("failed to send report on <SendReport> of <HistoryController>", zap.Error(err))
return errors.HTTP(ctx, err)
}
return ctx.NoContent(http.StatusOK)
}
// TODO:tests.
func (receiver *Controller) CalculateLTV(ctx echo.Context) error {
var req swagger.CalculateLTVJSONBody
if err := ctx.Bind(&req); err != nil {
receiver.logger.Error("failed to bind request", zap.Error(err))
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to bind request: %s", err),
errors.ErrInvalidArgs,
))
}
if req.From > req.To && req.To != 0 {
receiver.logger.Error("From timestamp must be less than To timestamp unless To is 0")
return errors.HTTP(ctx, errors.New(
fmt.Errorf("From timestamp must be less than To timestamp unless To is 0"),
errors.ErrInvalidArgs,
))
}
ltv, err := receiver.historyService.CalculateCustomerLTV(ctx.Request().Context(), req.From, req.To)
if err != nil {
receiver.logger.Error("failed to calculate LTV", zap.Error(err))
return errors.HTTP(ctx, err)
}
response := struct {
LTV int64 `json:"LTV"`
}{
LTV: ltv,
}
return ctx.JSON(http.StatusOK, response)
}

@ -4,12 +4,15 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/options"
"go.uber.org/zap" "go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/fields"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/history" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/history"
mongoWrapper "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo" mongoWrapper "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo"
@ -80,6 +83,9 @@ func (receiver *HistoryRepository) FindMany(ctx context.Context, dto *history.Ge
findOptions.SetSkip((dto.Pagination.Page - 1) * dto.Pagination.Limit) findOptions.SetSkip((dto.Pagination.Page - 1) * dto.Pagination.Limit)
findOptions.SetLimit(dto.Pagination.Limit) findOptions.SetLimit(dto.Pagination.Limit)
findOptions.SetSort(bson.D{{
Key: "createdAt", Value: -1,
}})
histories, err := mongoWrapper.Find[models.History](ctx, &mongoWrapper.RequestSettings{ histories, err := mongoWrapper.Find[models.History](ctx, &mongoWrapper.RequestSettings{
Driver: receiver.mongoDB, Driver: receiver.mongoDB,
@ -118,3 +124,218 @@ func (receiver *HistoryRepository) CountAll(ctx context.Context, dto *history.Ge
return count, nil return count, nil
} }
// TODO:tests
// GetRecentTariffs method for processing a user request with data aggregation with a limit of 100 sorted in descending order.
func (receiver *HistoryRepository) GetRecentTariffs(ctx context.Context, userID string) ([]models.TariffID, errors.Error) {
matchStage := bson.D{
{Key: "$match", Value: bson.D{
{Key: fields.History.UserID, Value: userID},
{Key: fields.History.IsDeleted, Value: false},
{Key: fields.History.Type, Value: models.CustomerHistoryKeyPayCart},
}},
}
unwindStage := bson.D{
{Key: "$unwind", Value: bson.D{
{Key: "path", Value: "$rawDetails.tariffs"},
}},
}
groupStage := bson.D{
{Key: "$group", Value: bson.D{
{Key: "_id", Value: "$rawDetails.tariffs.id"},
}},
}
sortStage := bson.D{
{Key: "$sort", Value: bson.D{
{Key: "createdAt", Value: -1},
}},
}
limitStage := bson.D{
{Key: "$limit", Value: 100},
}
cursor, err := receiver.mongoDB.Aggregate(ctx, mongo.Pipeline{matchStage, unwindStage, sortStage, groupStage, limitStage})
if err != nil {
receiver.logger.Error("failed to get recent tariffs on <GetRecentTariffs> of <HistoryRepository>",
zap.String("userId", userID),
zap.Error(err),
)
return nil, errors.New(
fmt.Errorf("failed to get recent tariffs on <GetRecentTariffs> of <HistoryRepository>: %w", err),
errors.ErrInternalError,
)
}
var result []models.TariffID
if err := cursor.All(ctx, &result); err != nil {
receiver.logger.Error("failed to decode recent tariffs on <GetRecentTariffs> of <HistoryRepository>",
zap.String("userId", userID),
zap.Error(err),
)
return nil, errors.New(
fmt.Errorf("failed to decode recent tariffs on <GetRecentTariffs> of <HistoryRepository>: %w", err),
errors.ErrInternalError,
)
}
return result, nil
}
// TODO:tests.
func (receiver *HistoryRepository) GetHistoryByID(ctx context.Context, historyID string) (*models.ReportHistory, errors.Error) {
history := &models.ReportHistory{}
err := receiver.mongoDB.FindOne(ctx, bson.M{"_id": historyID}).Decode(history)
if err != nil {
receiver.logger.Error(
"failed to find by id in <GetHistoryById> of <HistoryRepository>",
zap.String("historyID", historyID),
zap.Error(err),
)
if err == mongo.ErrNoDocuments {
return nil, errors.New(
fmt.Errorf("history not found with ID: %s", historyID),
errors.ErrNotFound,
)
}
return nil, errors.New(
fmt.Errorf("failed to find by id: %w", err),
errors.ErrInternalError,
)
}
return history, nil
}
// TODO:tests.
func (receiver *HistoryRepository) GetDocNumber(ctx context.Context, userID string) (map[string]int, errors.Error) {
findOptions := options.Find()
findOptions.SetSort(bson.D{{Key: "createdAt", Value: 1}})
filter := bson.M{
fields.History.UserID: userID,
}
cursor, err := receiver.mongoDB.Find(ctx, filter, findOptions)
if err != nil {
receiver.logger.Error("failed to get DocNumber list on <GetDocNumber> of <HistoryRepository>",
zap.String("userId", userID),
zap.Error(err),
)
return nil, errors.New(
fmt.Errorf("failed to get DocNumber list on <GetDocNumber> of <HistoryRepository>: %w", err),
errors.ErrInternalError,
)
}
defer func() {
if err := cursor.Close(ctx); err != nil {
receiver.logger.Error("failed to close cursor on <GetDocNumber> of <HistoryRepository>",
zap.String("userId", userID),
zap.Error(err),
)
}
}()
result := make(map[string]int)
var count int
for cursor.Next(ctx) {
var history models.History
if err := cursor.Decode(&history); err != nil {
receiver.logger.Error("failed to decode history on <GetDocNumber> of <HistoryRepository>",
zap.String("userId", userID),
zap.Error(err),
)
return nil, errors.New(
fmt.Errorf("failed to decode history on <GetDocNumber> of <HistoryRepository>: %w", err),
errors.ErrInternalError,
)
}
result[history.ID] = count
count++
}
if err := cursor.Err(); err != nil {
receiver.logger.Error("cursor error on <GetDocNumber> of <HistoryRepository>",
zap.String("userId", userID),
zap.Error(err),
)
return nil, errors.New(
fmt.Errorf("cursor error on <GetDocNumber> of <HistoryRepository>: %w", err),
errors.ErrInternalError,
)
}
return result, nil
}
func (receiver *HistoryRepository) CalculateCustomerLTV(ctx context.Context, from, to int64) (int64, errors.Error) {
timeFilter := bson.M{}
if from != 0 || to != 0 {
timeRange := bson.M{}
if from != 0 {
timeRange["$gte"] = time.Unix(from, 0).UTC().Format(time.RFC3339Nano)
}
if to != 0 {
timeRange["$lte"] = time.Unix(to, 0).UTC().Format(time.RFC3339Nano)
}
timeFilter["createdAt"] = timeRange
}
pipeline := mongo.Pipeline{
{{Key: "$match", Value: bson.M{"key": models.CustomerHistoryKeyPayCart, "isDeleted": false}}},
{{Key: "$match", Value: timeFilter}},
{{Key: "$group", Value: bson.M{
"_id": "$userId",
"firstPayment": bson.M{"$min": "$createdAt"},
"lastPayment": bson.M{"$max": "$createdAt"},
}}},
{{Key: "$project", Value: bson.M{
"lifeTimeInDays": bson.M{"$divide": []interface{}{
bson.M{"$subtract": []interface{}{bson.M{"$toDate": "$lastPayment"}, bson.M{"$toDate": "$firstPayment"}}},
86400000,
}},
}}},
{{Key: "$group", Value: bson.M{
"_id": nil,
"averageLTV": bson.M{"$avg": "$lifeTimeInDays"},
}}},
}
cursor, err := receiver.mongoDB.Aggregate(ctx, pipeline)
if err != nil {
receiver.logger.Error("failed to calculate customer LTV <CalculateCustomerLTV> of <HistoryRepository>",
zap.Error(err),
)
return 0, errors.New(
fmt.Errorf("failed to calculate customer LTV <CalculateCustomerLTV> of <HistoryRepository>: %w", err),
errors.ErrInternalError,
)
}
defer func() {
if err := cursor.Close(ctx); err != nil {
receiver.logger.Error("failed to close cursor", zap.Error(err))
}
}()
var results []struct{ AverageLTV float64 }
if err := cursor.All(ctx, &results); err != nil {
receiver.logger.Error("failed to getting result LTV <CalculateCustomerLTV> of <HistoryRepository>",
zap.Error(err),
)
return 0, errors.New(
fmt.Errorf("failed to getting result LTV <CalculateCustomerLTV> of <HistoryRepository>: %w", err),
errors.ErrInternalError,
)
}
if len(results) == 0 {
return 0, nil
}
averageLTV := int64(results[0].AverageLTV)
return averageLTV, nil
}

@ -207,8 +207,9 @@ func (api *API2) PaginationAccounts(ctx echo.Context, params PaginationAccountsP
return api.error(ctx, http.StatusInternalServerError, "default values missing for PaginationAccounts") return api.error(ctx, http.StatusInternalServerError, "default values missing for PaginationAccounts")
} }
page := int64(max(*params.Page, 1)) page := int64(math.Max(float64(*params.Page), 1))
limit := min(int64(max(*params.Limit, 1)), models.DefaultLimit) limit := int64(math.Max(float64(*params.Limit), 1))
limit = int64(math.Min(float64(limit), float64(models.DefaultLimit)))
count, err := api.account.CountAll(ctx.Request().Context()) count, err := api.account.CountAll(ctx.Request().Context())
if err != nil { if err != nil {
@ -592,3 +593,18 @@ func (api *API2) ChangeCurrency(ctx echo.Context) error {
return ctx.JSON(http.StatusOK, updatedAccount) return ctx.JSON(http.StatusOK, updatedAccount)
} }
func (api *API2) CalculateLTV(ctx echo.Context) error {
//TODO implement me
panic("implement me")
}
func (api *API2) GetRecentTariffs(ctx echo.Context) error {
//TODO implement me
panic("implement me")
}
func (api *API2) SendReport(ctx echo.Context) error {
//TODO implement me
panic("implement me")
}

@ -59,12 +59,18 @@ type ServerInterface interface {
// обновляет список одобренных валют // обновляет список одобренных валют
// (PUT /currencies) // (PUT /currencies)
UpdateCurrencies(ctx echo.Context) error UpdateCurrencies(ctx echo.Context) error
// Health Check
// (GET /health)
GetHealth(ctx echo.Context) error
// Получение лога событий связанных с аккаунтом // Получение лога событий связанных с аккаунтом
// (GET /history) // (GET /history)
GetHistory(ctx echo.Context, params GetHistoryParams) error GetHistory(ctx echo.Context, params GetHistoryParams) error
// Расчет среднего времени жизни платящего клиента (LTV)
// (POST /history/ltv)
CalculateLTV(ctx echo.Context) error
// Получение недавних тарифов
// (GET /recent)
GetRecentTariffs(ctx echo.Context) error
// отправить акт проделанных работ на почту
// (POST /sendReport)
SendReport(ctx echo.Context) error
// Изменить валюту кошелька // Изменить валюту кошелька
// (PATCH /wallet) // (PATCH /wallet)
ChangeCurrency(ctx echo.Context) error ChangeCurrency(ctx echo.Context) error
@ -272,15 +278,6 @@ func (w *ServerInterfaceWrapper) UpdateCurrencies(ctx echo.Context) error {
return err return err
} }
// GetHealth converts echo context to params.
func (w *ServerInterfaceWrapper) GetHealth(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.GetHealth(ctx)
return err
}
// GetHistory converts echo context to params. // GetHistory converts echo context to params.
func (w *ServerInterfaceWrapper) GetHistory(ctx echo.Context) error { func (w *ServerInterfaceWrapper) GetHistory(ctx echo.Context) error {
var err error var err error
@ -310,11 +307,51 @@ func (w *ServerInterfaceWrapper) GetHistory(ctx echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter type: %s", err)) return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter type: %s", err))
} }
// ------------- Optional query parameter "accountID" -------------
err = runtime.BindQueryParameter("form", false, false, "accountID", ctx.QueryParams(), &params.AccountID)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter accountID: %s", err))
}
// Invoke the callback with all the unmarshaled arguments // Invoke the callback with all the unmarshaled arguments
err = w.Handler.GetHistory(ctx, params) err = w.Handler.GetHistory(ctx, params)
return err return err
} }
// CalculateLTV converts echo context to params.
func (w *ServerInterfaceWrapper) CalculateLTV(ctx echo.Context) error {
var err error
ctx.Set(BearerScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.CalculateLTV(ctx)
return err
}
// GetRecentTariffs converts echo context to params.
func (w *ServerInterfaceWrapper) GetRecentTariffs(ctx echo.Context) error {
var err error
ctx.Set(BearerScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.GetRecentTariffs(ctx)
return err
}
// SendReport converts echo context to params.
func (w *ServerInterfaceWrapper) SendReport(ctx echo.Context) error {
var err error
ctx.Set(BearerScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.SendReport(ctx)
return err
}
// ChangeCurrency converts echo context to params. // ChangeCurrency converts echo context to params.
func (w *ServerInterfaceWrapper) ChangeCurrency(ctx echo.Context) error { func (w *ServerInterfaceWrapper) ChangeCurrency(ctx echo.Context) error {
var err error var err error
@ -378,8 +415,10 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
router.POST(baseURL+"/cart/pay", wrapper.PayCart) router.POST(baseURL+"/cart/pay", wrapper.PayCart)
router.GET(baseURL+"/currencies", wrapper.GetCurrencies) router.GET(baseURL+"/currencies", wrapper.GetCurrencies)
router.PUT(baseURL+"/currencies", wrapper.UpdateCurrencies) router.PUT(baseURL+"/currencies", wrapper.UpdateCurrencies)
router.GET(baseURL+"/health", wrapper.GetHealth)
router.GET(baseURL+"/history", wrapper.GetHistory) router.GET(baseURL+"/history", wrapper.GetHistory)
router.POST(baseURL+"/history/ltv", wrapper.CalculateLTV)
router.GET(baseURL+"/recent", wrapper.GetRecentTariffs)
router.POST(baseURL+"/sendReport", wrapper.SendReport)
router.PATCH(baseURL+"/wallet", wrapper.ChangeCurrency) router.PATCH(baseURL+"/wallet", wrapper.ChangeCurrency)
router.POST(baseURL+"/wallet", wrapper.RequestMoney) router.POST(baseURL+"/wallet", wrapper.RequestMoney)
@ -388,72 +427,81 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
// Base64 encoded, gzipped, json marshaled Swagger object // Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{ var swaggerSpec = []string{
"H4sIAAAAAAAC/+xcbW8bSXL+K41JPtwhY4mStfGtvsnyXs4J7DVWZy8MS7gbkU1xzuQMPTO0VzEIiOKe", "H4sIAAAAAAAC/+xcbXMbR3L+K1ObfLArKxCk6ejMbzLlS5Q6+1x6c6lE1t0SGJB7Anbh3YVtxoUqAvBJ",
"fYa8dpwcEGOz9mbvFsi3hKJEi5JI6i9U/6OgqnvemxIlv9/uHaAlOS9dXV311FPV1b5vFN1a3XW4E/jG", "VlEWI+dScV0s+ey7qnxLQIgQQRIA/0LPP0p1z+z7gAQpihJj31XJILC709PT/fTTPT37pVFya3XX4U7g",
"/H3DL1Z4zaKPC8Wi23AC/Fj33Dr3ApvThd/ZJfwP/8qq1avcmDd+VbhQnilfuLBaLP9qpli68Omnc+c/", "GwtfGn5pjdcs+nilVHIbToAf655b515gc/rhd3YZ/8O/sGr1KjcWjF8VL1dmK5cvr5Qqv5otlS+/9978",
"LczMGKYRrNfxDj/wbGfNaJpG0fKC1NO3jnt8zKVZY8U07IDXSJ7cGOoHy/OsdRrT41bASws0cNn1alZg", "O+8VZ2cN0wjW63iFH3i2s2o0TaNkeUHq7rtH3T7hpzlj2TTsgNdIntwY6gvL86x1GtPjVsDLV2jgiuvV",
"zBslK+DnArvGdWKWeJWf8hHbvyQfSk2vbFV9Ht296rpVbjl4u2PVON759x4vG/PG303HCzGtVmH6Kt7T", "rMBYMMpWwC8Fdo3rxCzzKj/hLbZ/Vd6Uml7Fqvo8unrFdavccvByx6pxvPLvPV4xFoy/m4kXYkatwsxH",
"NA0/sIKGf9LdasGW5M1N02jUS6edd8Pn3uXXWN57VrXKg5Mk/VLe1WyahsfvNGwPlXaLDCsSQZlK9Eql", "eE3TNPzAChr+cVerBbshL26aRqNePum8Gz73rr3E8n5uVas8OE7ST+RVzaZpePzThu2h0u6SYUUiKFOJ",
"sUgZ0RoZyQVOTnolks9d/QMvBihfWkc4TadRw7EdF0e4jX9dby3xbDy3i5Zze9HySnmPKFpeqeJWS9zD", "Hqk0FikjWiMjucDJSS9H8rkrf+ClAOVL6win6TRqOLbj4gj38F/XW03cG8/tfcu5t2h55bxHlCyvvOZW",
"byXuFz27HtiuY8wb8BwG4imDLhxCB3ahB4fisXgAHQYH0BEbYlNsGWZC3ZdvLFxl+OfzG1oH8ouaQb6F", "y9zDv8rcL3l2PbBdx1gw4DsYii0GPTiALuxAHw7EI3Efugz2oSs2RFtsGmZC3dduX/mI4T+/va11IL+k",
"EeyyxRuLswz6cAh9tnjjxqzJzodf55hoQR8G0IURSmIyOIKeeAgdsQkd6IlN0UIxhyjYCLbFBl0Zwgj2", "GeTPMIYdtnh7cY7BAA5gwBZv354z2Tvhn/NMtGAAQ+jBGCUxGRxCXzyArmhDF/qiLVoo5ggFG8O22KBf",
"mWiJTRiJDRjBEHrjBC98opOXf1W3vfUrrhNUNHJ/Bz0cVzxg0KdRUKQeDKEvnuKwOORBSlfsF1eu/HLi", "RjCGPSZaog1jsQFjGEF/kuDFd3Xy8i/qtrf+oesEaxq5v4c+jivuMxjQKChSH0YwEFs4LA65n9IVe+vD",
"cW9yS7cm/0HqmnzImzdv3kwPOluY1TqA06itas3gBYxgAD2xMU59X1y/mH9hxkPU21OzS6tYZ/SfeZ7r", "D9+eetw73NKtyb+TuqYf8s6dO3fSg84V57QO4DRqK1ozeApjGEJfbExS3/Vb7+cfmPEQ9fTU7NIq1hn9",
"5a22xn3fWuNpZ0fvY44bsLLbcEq6GUr/W3RL6SfnCnNmDDK2E/zjXPy07QR8jXu5+RTxLWYkiU7439h+", "B57nenmrrXHft1Z52tnR+5jjBqziNpyybobS/xbdcvrO+eK8GYOM7QT/OB/fbTsBX+Vebj4lfIoZSaIT",
"4HrrGqdzazXupIOJgUv4DRormjs5G3TgEG19hL/CQLSZeEg3zM1q3evdxIrXgNhTxhnPuneJB5Zd9TVG", "/p9tP3C9dY3TubUad9LBxMAl/AaNFc2dnA26cIC2PsZvYSg6TDygC+bntO51PrHiJSD2hHHGsz6/ygPL",
"+X+kGWnu5NkDGMGOaDM4EhvQg126fAgjeAV9sSkem2S9cAB9vHtbtGFXtMUmE18jzojHYlNsiC16a+he", "rvoao/xf0ow0d/LsIYzhuegwOBQb0Icd+vkAxvACBqItHplkvbAPA7x6W3RgR3REm4mvEGfEI9EWG2KT",
"iBt96JtyEb6JliEaApdoBzriCYNuuHpMDdyTt43km3DwQ+jgF/GYUEl6Ky5qi/3Bd50pxuA5bOOtu3CI", "nhq6F+LGAAamXIRvomWIhsAleg5d8ZhBL1w9pgbuy8vG8kk4+AF08Q/xiFBJeisuaov9wXedAmPwHWzj",
"gIbivoJdnBdaxSb04QinuwcdOEIRoc8I1zpoMl0YiadTKb+8v2wElmeXy/6yMX9reexaLRvmcRdXmrrF", "pTtwgICG4r6AHZwXWkUbBnCI092FLhyiiDBghGtdNJkejMVWIeWXXy4ZgeXZlYq/ZCzcXZq4VkuGedSP",
"lD8kTaHY8AO3xr0pOehnTolr3fCdB/eM66ZjNd17ckg2I681Db8hfVzn9FcVOUp7fNn2/CDkTfEU4Dl0", "y03dYsovkqZQaviBW+NeQQ76gVPmWjc89+Cecd10rKZrjw/JZuS1puE3pI/rnP4jRY7SHl+xPT8IeVM8",
"oQND3ZRrdqlU5eOfgRF0oS8e6p51vTXNgy/x/2x5eRmD1wgt7CXamwqOHbygRU1edJ3S8YJodZ7TzTVr", "BfgOetCFkW7KNbtcrvLJ98AYejAQD3T3ut6q5sZn+H+2tLSEwWuMFvYM7U0Fxy7+oEVNXnKd8tGCaHWe",
"HTX429ByFIFZDdmJaQS2c9stl5GvGKZxx75no7pXubcqf1l33Zrr8HVEXnfVruLK2Y4fWNVqjRIB5F5+", "083H1jpq8GZoOYrArITsxDQC27nnVirIVwzT+NT+3EZ1r3BvRX6z7ro11+HriLzuil3FlbMdP7Cq1Rol",
"hR6qG6axOru6FD9tVcsWfdRxpC8j/pdlSL4u/P8g2jCAAXQYCSQ5CaJCF5FBPIMh6jTkJRir0dnFI+jD", "Asi9/DW6qW6Yxsrcyo34bqtaseijjiPdJKO9djW/ePK7UxpeTgmfRDwzy8R8Hc34UXRgCEPoMpq45D6I",
"PqOPG6KV9NaZwoXCzARxyDSKDc/jTnFdI9VftMMwWqhD8WSyyI26RR3nX/9nZBviMewg8kjcQQYiNhEU", "Pj1EIPEERrh2If9BToCgIh7CAPYYfdwQrSQqzBYvF2eniHemUWp4HndK6xqpftIOw8ggDsTj6RgCriGu",
"u4hYI9QD7BNR+OMUg/8h0NwmYEU0jfQkWviseIYcTvxRYTgyvD3kM4R9hxILkeD0YAfpoLyG8fAA8Rs6", "Zf7xf0JWIx7Bc0Q4iW/IdEQbwbeHyDhGPcAeEZI/Fhj8N4HzNgE4onakJ9HCe8UT5IrijypWIJPcRd5E",
"UxK+DxALxYZoQw8Gkg0eIDTuKhYYzx96ZoiZMgag9Hh9iMGAFnBI4+2bTAmiML8nnikpVdyBVzja2Zaw", "GHsgMReJVB+eI+2Uv2Hc3cc4Ad2CDBP7iLliQ3SgD0PJOvcRgncU24znD30zxGYZa1B6/H2EQYcWcETj",
"3vCKFcvn/kItzFQzqn4J2+IRGZBoRaYWa4Oi2UjGKLEldTcUz3CtKfyIx7BHCNEh0zsUj42JBPPr/BTi", "7ZlMCaJiS188UVKq+AYvcLTTLWG94ZXWLJ/7V2phRpxR9TPYFg/JgEQrMrVYGxQ1xzIWik2pu5F4gmtN",
"EDchvi156FBs4Vom1mwPb+uKFiqvS+odqHeIR2QEm7ToEkiQxlKIRutpwxBtwzgDNQv9IwKDEDOyag/n", "YU48gl1Coi6Z3oF4ZEwlmF/nJxCHOBDxesl3R2IT1zKxZrt4WU+0UHk9Uu9QPUM8JCNo06JLwEK6TFQA",
"mwdyiXsNzw7WlzD1k1hwkVuepMir9OnXoWT//OVvKYYkdfaZE3CPBRXOAvc2d9g9O6jQ19/L18yz37O6", "racDI7QN4xQUMPSPCHRCbMqqPZxvPmBIfG14drB+A1NMiQXvc8uTVHyFPv06lOxfPrlJsSqpsw+cgHss",
"x8v2VybjU2tTbFm9n1mrxRKfmT0/98mygRGdkk8iR3L8SNpKENSNJgprO2VXv2wJ2kEG01UZE30iY5cU", "WOMscO9xh31uB2v05+/lYxbY71nd4xX7C5PxwmqBLannM2ulVOazc+/Mv7tkIHOgJJdImBw/knYtCOpG",
"pY8kAl2zS9Siw87FPEfe0aFMkPKN0A3ydvaEia9x7eBAPEBfRVdDEaS9bsAe9NFMGEIB/vInaZ245FMU", "E4W1nYqrX7YEvSGD6anMjD6RsUsqNECygq7ZIwrTZZdiPiWv6FLGSXlN6AZ5O3vMxFe4drAv7qOvoquh",
"AAICpkXFJdi5lFghNxJtki0hkSRMhyiaNBwYQN8wjbvc86UyZqYKUwWKjnXuWHXbmDfOTxWmkC/UraBC", "CNJeN2AXBmgmDKEAv/laWicueYECTUDAtKg4C7uUEivkYKJDsiUkksTsAEWThgNDGBim8Rn3fKmM2UKx",
"CzxtxaUjyQg0Sj1S+dBDmX1mTJZJZIJDGQPEFmEERhQLX4AsxpA0OCxTofX6ddfxpZHNFgoySXAC5YtW", "UKQoXOeOVbeNBeOdQrGA4aFuBWu0wDNWXKKSzEOj1EOVdz2QWW7GZJlEJjiQMUBsEkZgRLHwAciWDEm3",
"vV61i/T4NFLFuNw1YWFF2kh6GqItWgTGfyKQ7EVyxyucdcWmacwVZt6YcDK504gGL6CHaxtR8L0QIlKO", "w3IYWq9fdx1fGtlcsSiTESdQvmjV61W7RLfPICWNy2pTFnCkjaSnITqiRWD8NYFkP5I7XuGsKzZNY744",
"aczfil3y1kpzBZlZrWZhymVEs+krxk1hKGH2YgsDQ2qKaH/Wmo8YEtrBStM01rgOEZ+T+pQLYOQJ6xAq", "e2bCySRSIxo8hT6ubUT1d0OISDmmsXA3dsm7y81lZIC1moWpnRHNZqCYPYWhhNmLTQwMqSmi/VmrPmJI",
"zu1hXKMY1GPiCeyREXcoUWiF6TtGlikG/wb7sAt9Wok+HKRul0O0Y2Prwx6joHpA8+hkIsF+zER2oYPx", "aAfLTdNY5TpE/I7Up1wAI09Y71BxbhfjGsWgPhOPYZeMuEsJSSssE2BkKTD4N9iDHRjQSgxgP3W5HKIT",
"VUrVlXnDjkqKuvg4sxpBxfXsf6XVy1npP/HgAzPRhDaUkX4QNokizL0DEf5C5irTwTHwS3yzJzYndxT4", "G9sAdhkF1X2aRzcTCfZiJrIDXYyvUqqezE+eq+Srh7czqxGsuZ79r7R6OSv9Jx68YSaa0IYy0jfCJlGE",
"PqvTJFvtycEySJCw0DE+U7eCYkVbUtxD7ISheBqaM9UYJZ+EvZAJ4GKjnnfUV/QmjCf9sRPPme9ixXLW", "+XMQ4ScyV5l2ToBf4pt90Z7eUeCHrE6TbLUvB8sgQcJCJ/hM3QpKa9rS5S5iJ4zEVmjOVMuUfBJ2QyaA",
"UiB7p8H94KJbWn9jayXL3Jql+k56PgpKc5S6parGB4H1LWLau7GyP2ash5eKuuN8DpDLUE5LxoH6nwTk", "i416fq7+RG/CeDKYOPGc+S6uWc5qCmQ/bXA/eN8tr5/ZWslyumapvpeej4LSHKVuqXryRmB9i5j2Tqzs",
"666vQ/kfQj0hkjKZ7GoiR8RLIr8wMWNoUQGb2Ejktz3xKMoktsUWonHOdBdKpQ+NHPxtGUy8rGQg45ZV", "i4z18ExRd5zPPnIZyp3JOFD/04B83fV1KP9jqCdEUiaTak3kiHhJ5BcmZgwtKpQTG4n8ti8eRpnEtthE",
"aytNM6KJ0/dlGal5LF/8DpFOlu0xK0IeIokBYh8N2kWwa6maDv4dyBAvifGIKfxAA38lzQsGZoILJ3hb", "NM6Z7pVy+U0jB/+/DCZeVjKQScuqtZWmGdHEmS9luap5JF/8HpFObg9gVoQ8RBIDxD4atIdg11K1I/x3",
"jgePIZ+XbI8XE/G9bnlWjQfc80lz6SlcvqTLy2y8hNQ53OGaj6tqcT4WeA1uJhY9WydaeT8WDn/9KOnv", "KEO8JMZjpvADDfyFNC8YmgkunOBtOR48gXxetT1eSsT3uuVZNR5wzyfNpadw7aouL7PxJ6TO4U7aQly9",
"u6IaLyg6p5FrXOCd3O3+muLkWQg9kkyDuPCpqPhbdrAs68SfJBCqesco7XI9HZP+2d9O5PI/e9yb97iY", "i/OxwGtwM7Ho2VLM8uuxcPjrhaS/50U1nlJ0TiPXpMA7vdv9NcXJsxB6KJkGceETUfFX7GBZ1olfSSBU",
"3I/3ObS2cdlEKlfdYuQh/bCqmtaYIuinTQjGpNGpYhT6KxWuNqAvC75xOqB8tCu2skUqWWjcpCDfppKW", "9Y5x2uX6Oib9i78dy+V/8biz97iY3E/2ObS2SdlEKlfdZOQhg7CqmtaYIugnTQgmpNGpYhT6KxWuNmAg",
"/h0mE0/xKZmSyHSZeJsicwxewLfw0mTwv/jlv6AvHkiRZIMC/EhJeXwh5/9LUSZ9g3t2WZnOUtgO8n6x", "C75xOqB8tCc2s0UqWWhsU5DvUElL/wyTiS28S6YkMl0m3qbIHIOn8Gd4ZjL4H/zjv2Ag7kuRZCME/I2S",
"4GxZUXqT5ExtPvktomaz+aGgk8aeiAYn7eljB4d/n2COY3zmJGZMi6eP1tmKFmleevlB1tRHGLlpo6lN", "8viHnP/fiDLp29yzK8p0boRtJ68XC06XFaU3SU7VTpTfhWk2m28KOmnsiWhw0p4uOjh8O8UcJ/jMccyY",
"Wy57MuxGrT0UqYfkdz32D0z2/zyCnmz1GRHVeBjvM2hen/PVa9aa7dDXhXAqJ3hookEmLZPYok2kDqKv", "Fk8frbMVLdK89PL9rKmPMXLTRlOHtlx2ZdiNWogoUo/I7/rsH5jsM3oIfdlSNCaq8SDeZ9A8PuerH1ur",
"2g0ULTYj+16q1Iaimh/Ine80uLce+3PdWuNG0ntLvGw1qoExP6PbAslKRWLsjZFrQhGqds0OxshQKGik", "tkN/XgmncoyHJhpx0jKJTdpE6iL6qt1A0WKzsr+mSu0uqsmC3PnTBvfWY3+uW6vcSHpvmVesRjUwFmZ1",
"eF1ykUaVpC1FPZMTeXi+kzJwA6t6zVqTb4637LS6zOPSGUqTOaXrrI+cNS5ah65woLVVJrf3OrCj7OkB", "WyBZqUiM3QlyTSlC1a7ZwQQZikWNFC9LLtKokrSlqDdzKg/Pd2wGbmBVP7ZW5ZPjLTutLvO4dIrSZE7p",
"cc79sc4YtqzGqWna1r/gNfcu/7Xn1hZlx2LGznU2YR8fYs7Un/HeOGki5RvJ0np6m+qnxkBlFXZf7uDG", "OusjZ42L1qEr7GttlcntvS48V/Z0nzjn3kRnDFtj49Q0bevXec39jP/ac2uLsjMyY+c6m7CPDjGn2o5/",
"8WafPsoocPp0D3mVZEtZ9SZfG9swWe1xnPFFNsmjcKhqfjJYUcOXapfTTiOzBy2Zp64QN1v8SbrGn1HD", "bZw0kfKNZWk9vU31c2Ogsgq7J3dw43izRx9lFDh5uoe8SrKlrHqTj41tmKz2KM74NJvkUThUNT8ZrKix",
"CWYwChs8otVr/+wcr+McCQWHDpJV8LHuEeL7dN2SLab6AvZ/QifsQ1Qt0pGjqD4NudcCR9QosIlhKg2B", "TLXlaaeR2YOWzFNXiJsr/Sxd40+o4QQzGIcNHtHqdX5xjpdxjoSCQwfJKvhI9wjxfaZuyVZWfQH7P6Eb",
"eYK0rqLFe69MU39VJHquyeDjqknH81Cp+gEVqaWr6ZdfNrootqTYdq7ytBjf9ZpLljj5IhvHri9dMkxj", "9juqVuzIUVSfhtxrgUNqFGhjmEpDYJ4grato8dor09RfFYmeazK4WDXpeB4qVd+nIrV0Nf3yy0YXxZYU",
"ceHSaU61nJVMRdSok+jnyhCoxIPRvn8iuRhRN+122OYlq3fxuxJKDluIKAw1NIq9Tp2hGd2eLZV+U2pt", "285Vnhbjq15yyRInbGTj2K0bVw3TWLxy9SSnZ05LpiJq1E30c2UIVOLGaN8/kVyMqWt3O2zzktW7+FkJ",
"fjTLi8gna7GHJy/wO/Lh65ajuiF46VReG01FbWm/vsmhb1e4VZXnTrRZ9GKFF29TN5m8kckKDHPL9OPC", "JYctRBSGGhrF3qIO1IxuT5dKn5VamxdmeRH5ZC324PgFPicfvmU5qhuCl0/ktdFU1Jb2y5sc+vZafEhh",
"tcu6IvRv5Ev1dpIe4PN/Qc1/Ujifv7TEvbt2kbPrjnXXsqvWapVn3FCOw0hIQ04nPgsxDqfC4xIfZbYN", "kmOH5xguZHoKf3kN6ampaZgdwGHYHbRJ2Ls15eiqQX1ydc3UdD/sqD2CuJxCISNX4isw+A+1fUxbxcn6",
"//0esm1T0//bh6Ow2WmLQsnTCUdXffBvb+Mgndt7vOh6pclT+9A63n5qP8lORVLDsP9x8Yx8vw91hO3I", "h5lqXR0kC6jxrrcsYw4mD5jtIypMOW2VYV67arzKXYZ0IcDjJdcrT18HCD3j1dcBptnWSFoX7F0sUpJv",
"YySJadGpEvE0LLoRdCGLzNYlBgkUC/1cglh8pnJcKvd91CwXslTJs3MJ3vyyc47BS6rj0RYidcwl2jyy", "DqL2sefybEtiWnTURWyFFTrCOaSc2SLGMAF5IcalEG+mGnyW5LOZph6rWmpUrYD/5uZt46yq1xXPrWnT",
"6V5HHl0M+747WSju0Atz+yPRxh7KQb126a7Y4qpXZr+gbgG8dSBl30XYEW3FsIeqbQp/km89Uv0GeKX3", "zC4VKdGzFNfcCXlmj4mvSK1DWXlltxz7CxbYNe4HVq2ecN+iyTAAUrowUgnVgNahLztK1CC5402F6Tq2",
"SzVydF5A9d8muvTbueZYJiuhI0xV6bxRfO8Ug2+jm59h0BlS2jASDylxEw/oNNmIJtlSIZf6H0OdyQb9", "A3fC4c0Rtai+KsFTPWL9RHN34RQd27QANJXlaXzuB5nkyx1f1QSxQV3OdBgBuiy2jjgVb54pJCkrzZ0K",
"ZEuK6v2lovKQWOg+gwPaNJVvjXqGmaSttAxPqPa6H95G2VH29JQEadklGR4r3KVS7aGUWnVihMcD5BmD", "kW1IoyNK0PLXPbnn/AJ26OCa3OwSm1QnkY31LSJHO+R2QxYlB3iPdKNptHwiAKO98qQmSY+EV8VzwitZ",
"fvqsZ1dOaihPD4xp/VqM+8DfzC5H8uBFnMxLlnT8kaM4uH+gex4yWqpt8E7qwEjOFCXcFd5Zw2Pek4eS", "v1HNubtx7ppop0JjYbAtQ574GvomI3gassB9Q0oS756Lvr5NH1oRW5QNi69hANvEoqM9VNmpOT3q/yVh",
"RKbc+SdVjfhef8ZDaSZRpjhFXIhaM0MoPA6OEpCvQH58E905Bj+SqgbxIZ0U5BOyJGAWx0sUJEwmaxf5", "AnR+ObRp2nhSri4jAoMX4cGcyEbFVtw5mupFYm/95ubttydGAI+XlLr0O0ffUkLcIwt9qNs9ooMH6vAQ",
"zo6B6gclMNoW39Ae0QD6GWjvyBGj0w/0MQvsqRKIaDE6oIRYKGs84fHy8J52qu+aIHlIgWCUbsCO4F9z", "graMQlERRW4nURkyjbRRC3a+0zW7XRXyqt18PzVRqBxLv06TkqfY/DMLWfYZHrpcfgX7sFPRtOho3xRJ",
"VCKHW19ImLqijqu8GdSyoiNGMTX6pDBb0B3/WU38QwHHWWb0Dwocexzt8tLn5+ZmZy4w8bVa/w6lPCee", "ZA6nkujwZmDUuTaCS4sWm6qTNFGZlHWviw5BGuI5Uh2+PUoTs449EVV87pSv87ort8KmLpOORTs6Y7SP",
"P6u6a7aTRln66XcB9wPdA/WK6/Cr0QH2+LELnxbC/+me83jQ8JzrXjX9VCUI6v789LRvB3zKa0yrEvDY", "MVq2FkcF1PBVABHZxUu38TZN+0ckwQX3/2NrsUmlXbSm4KTwcbcUdUkds+JhZR3G4oFop8q1aVuMX2Yz",
"M7DHqSp56DEbFhTnThxYUov1NsJE2iqqtnNbP+k1112r4rQnOdR5fFiRzVcpT4/Ohsn08l1EkR+S54dS", "aW/rh+j0UGiPoelndrwWlpxLDJ4Rq6SeSjpClOh7z+5/deU7Y8KDsN1sbapLD8w1jEWdjigHFQ3SxwRL",
"hU65WzAUbThkhG992IYD0T4Famrqq7qNwBBK+nkg0UEpaVr+mKPM2Q5Vlb9Z0d5n7gl1gEu04ShktukD", "K16FvUXcFS8dStkx33iAXjNSL4KQ70p4QGjxSEbV3ZAov61Gjg5QqwOJiWPLndxpQSZbQ8bQEx160UN8",
"oeoVkR1q3pEorBL6h4+gZ4y5PSKk8e1qgpoHEifuKWVVD4T5RHOl+f8BAAD//+CFq8HxRwAA", "bYFhAqQufgL7MstCZT6gnSxxn1ZyTJNsqRok4XmoM3liOdmjrw5DEgEfUVl+jyHjaIdPjQ5RKhpEy/CY",
"iNBeeBltF2VfWyGLcPLYWPg+lx2iFQdSatWaHp6XloeuB+mX7PTkpEbyOPWEszCL8cHYs0Gh5En0GItk",
"2fho2ImrnW9oE5ishiqW202doM+Z4vnRj58mePJIVtVT7vyz2p79QX/oPc+OThAPorNqIRQeBUcJyFcg",
"P/lU0SUGf1M1mOitBSnIJ2RJwCyOl9ihNZlkKflW92FUPBBt2BbfUCo0hEEG2rtyxOg4OH3MAntqT1i0",
"GL2xAbFQbnqH7/UKr+mkDqISJI8oEIzTJ1Ij+NecHc/h1nUJUx+q8/tng1pW9M6FuPz7bnGuqKuurSTe",
"0HaUZUZvcjvy/RzXbvz20vzc7OVUDW6aF3JU3VXbSaMsffW7gPuB7ob6muvwj6I3h8W3XX6vGP5Pd5/H",
"g4bn3PKq6bvWgqDuL8zM+HbAC15jRvXETHz50FGqSr5tJhsW1J5K4g0OarFeRZjIFPRs555+0quuu1rF",
"aU/zIpkTZq6Jl2WcX9b2Y/KFCqnOjzC17cBBIo8TnROgpqbhRNcZGULJIA8kOiglTcsvc5Q5e2QvvVFl",
"aPbh1BstRAcOQ2abfkOOekRkh5pnJDpNCP3DW9AzJlweEdL4cjVBzQ2JChVtSaobwnyiudz8vwAAAP//",
"WKMkDmpVAAA=",
} }
// GetSwagger returns the content of the embedded swagger specification file // GetSwagger returns the content of the embedded swagger specification file

@ -0,0 +1,173 @@
package swagger
import (
"log"
"github.com/labstack/echo/v4"
)
type accountController interface {
RemoveAccount(ctx echo.Context) error
GetAccount(ctx echo.Context) error
CreateAccount(ctx echo.Context) error
RemoveDirectAccount(ctx echo.Context, userID string) error
GetDirectAccount(ctx echo.Context, userID string) error
GetAccounts(echo.Context, PaginationAccountsParams) error
SetVerificationStatus(ctx echo.Context, userID string) error
UpdateAccountName(ctx echo.Context) error
}
type currencyController interface {
GetCurrencies(ctx echo.Context) error
PutCurrencies(ctx echo.Context) error
}
type cartController interface {
Remove(echo.Context, RemoveFromCartParams) error
Add(echo.Context, Add2cartParams) error
Pay(echo.Context) error
}
type walletController interface {
ChangeCurrency(ctx echo.Context) error
GetPaymentLink(ctx echo.Context) error
}
type historyController interface {
GetHistoryList(ctx echo.Context, params GetHistoryParams) error
GetRecentTariffs(ctx echo.Context) error
SendReport(ctx echo.Context) error
CalculateLTV(ctx echo.Context) error
}
type Deps struct {
AccountController accountController
CurrencyController currencyController
CartController cartController
WalletController walletController
HistoryController historyController
}
type API struct {
accountController accountController
currencyController currencyController
cartController cartController
walletController walletController
historyController historyController
}
func New(deps Deps) *API {
if deps.AccountController == nil {
log.Panicln("AccountController is nil on <New (API)>")
}
if deps.CurrencyController == nil {
log.Panicln("currencyController is nil on <New (API)>")
}
if deps.CartController == nil {
log.Panicln("cartController is nil on <New (API)>")
}
if deps.WalletController == nil {
log.Panicln("walletController is nil on <New (API)>")
}
if deps.HistoryController == nil {
log.Panicln("historyController is nil on <New (API)>")
}
return &API{
accountController: deps.AccountController,
currencyController: deps.CurrencyController,
cartController: deps.CartController,
walletController: deps.WalletController,
historyController: deps.HistoryController,
}
}
// Account
func (receiver *API) DeleteAccount(ctx echo.Context) error {
return receiver.accountController.RemoveAccount(ctx)
}
func (receiver *API) ChangeAccount(ctx echo.Context) error {
return receiver.accountController.UpdateAccountName(ctx)
}
func (receiver *API) SetAccountVerificationStatus(ctx echo.Context, userID string) error {
return receiver.accountController.SetVerificationStatus(ctx, userID)
}
func (receiver *API) GetAccount(ctx echo.Context) error {
return receiver.accountController.GetAccount(ctx)
}
func (receiver *API) AddAccount(ctx echo.Context) error {
return receiver.accountController.CreateAccount(ctx)
}
func (receiver *API) DeleteDirectAccount(ctx echo.Context, userID string) error {
return receiver.accountController.RemoveDirectAccount(ctx, userID)
}
func (receiver *API) GetDirectAccount(ctx echo.Context, userID string) error {
return receiver.accountController.GetDirectAccount(ctx, userID)
}
func (receiver *API) PaginationAccounts(ctx echo.Context, params PaginationAccountsParams) error {
return receiver.accountController.GetAccounts(ctx, params)
}
// Cart
func (receiver *API) RemoveFromCart(ctx echo.Context, params RemoveFromCartParams) error {
return receiver.cartController.Remove(ctx, params)
}
func (receiver *API) Add2cart(ctx echo.Context, params Add2cartParams) error {
return receiver.cartController.Add(ctx, params)
}
func (receiver *API) PayCart(ctx echo.Context) error {
return receiver.cartController.Pay(ctx)
}
// Currency
func (receiver *API) GetCurrencies(ctx echo.Context) error {
return receiver.currencyController.GetCurrencies(ctx)
}
func (receiver *API) UpdateCurrencies(ctx echo.Context) error {
return receiver.currencyController.PutCurrencies(ctx)
}
// History
func (receiver *API) GetHistory(ctx echo.Context, params GetHistoryParams) error {
return receiver.historyController.GetHistoryList(ctx, params)
}
func (receiver *API) GetRecentTariffs(ctx echo.Context) error {
return receiver.historyController.GetRecentTariffs(ctx)
}
func (receiver *API) SendReport(ctx echo.Context) error {
return receiver.historyController.SendReport(ctx)
}
func (receiver *API) CalculateLTV(ctx echo.Context) error {
return receiver.historyController.CalculateLTV(ctx)
}
// Wallet
func (receiver *API) RequestMoney(ctx echo.Context) error {
return receiver.walletController.GetPaymentLink(ctx)
}
func (receiver *API) ChangeCurrency(ctx echo.Context) error {
return receiver.walletController.ChangeCurrency(ctx)
}

@ -100,6 +100,11 @@ type Name struct {
// PaymentType defines model for PaymentType. // PaymentType defines model for PaymentType.
type PaymentType string type PaymentType string
// TariffID defines model for TariffID.
type TariffID struct {
ID *string `json:"ID,omitempty"`
}
// Wallet defines model for Wallet. // Wallet defines model for Wallet.
type Wallet struct { type Wallet struct {
// Cash Сумма money переведённая на текущий курс // Cash Сумма money переведённая на текущий курс
@ -155,6 +160,28 @@ type GetHistoryParams struct {
// Type Тип события // Type Тип события
Type *string `form:"type,omitempty" json:"type,omitempty"` Type *string `form:"type,omitempty" json:"type,omitempty"`
// AccountID Идентификатор аккаунта. Если не указан, будет использоваться идентификатор из токена.
AccountID *string `form:"accountID,omitempty" json:"accountID,omitempty"`
}
// CalculateLTVJSONBody defines parameters for CalculateLTV.
type CalculateLTVJSONBody struct {
// From Начальная дата в формате Unix timestamp. Если 0, устанавливает начало истории.
From int64 `json:"from"`
// To Конечная дата в формате Unix timestamp. Если 0, устанавливает текущее время.
To int64 `json:"to"`
}
// GetRecentTariffsJSONBody defines parameters for GetRecentTariffs.
type GetRecentTariffsJSONBody struct {
Id string `json:"id"`
}
// SendReportJSONBody defines parameters for SendReport.
type SendReportJSONBody struct {
Id string `json:"id"`
} }
// ChangeCurrencyJSONBody defines parameters for ChangeCurrency. // ChangeCurrencyJSONBody defines parameters for ChangeCurrency.
@ -184,6 +211,15 @@ type SetAccountVerificationStatusJSONRequestBody SetAccountVerificationStatusJSO
// UpdateCurrenciesJSONRequestBody defines body for UpdateCurrencies for application/json ContentType. // UpdateCurrenciesJSONRequestBody defines body for UpdateCurrencies for application/json ContentType.
type UpdateCurrenciesJSONRequestBody = UpdateCurrenciesJSONBody type UpdateCurrenciesJSONRequestBody = UpdateCurrenciesJSONBody
// CalculateLTVJSONRequestBody defines body for CalculateLTV for application/json ContentType.
type CalculateLTVJSONRequestBody CalculateLTVJSONBody
// GetRecentTariffsJSONRequestBody defines body for GetRecentTariffs for application/json ContentType.
type GetRecentTariffsJSONRequestBody GetRecentTariffsJSONBody
// SendReportJSONRequestBody defines body for SendReport for application/json ContentType.
type SendReportJSONRequestBody SendReportJSONBody
// ChangeCurrencyJSONRequestBody defines body for ChangeCurrency for application/json ContentType. // ChangeCurrencyJSONRequestBody defines body for ChangeCurrency for application/json ContentType.
type ChangeCurrencyJSONRequestBody ChangeCurrencyJSONBody type ChangeCurrencyJSONRequestBody ChangeCurrencyJSONBody

@ -26,13 +26,15 @@ type ConfigurationGRPC struct {
} }
type ServiceConfiguration struct { type ServiceConfiguration struct {
AuthMicroservice AuthMicroserviceConfiguration AuthMicroservice AuthMicroserviceConfiguration
HubadminMicroservice HubadminMicroserviceConfiguration HubadminMicroservice HubadminMicroserviceConfiguration
CurrencyMicroservice CurrencyMicroserviceConfiguration CurrencyMicroservice CurrencyMicroserviceConfiguration
DiscountMicroservice DiscountMicroserviceConfiguration DiscountMicroservice DiscountMicroserviceConfiguration
PaymentMicroservice PaymentMicroserviceConfiguration PaymentMicroservice PaymentMicroserviceConfiguration
JWT JWTConfiguration VerificationMicroservice VerificationMicroserviceConfiguration
Kafka KafkaConfiguration TemplategenMicroserviceURL TemplategenMicroserviceConfiguration
JWT JWTConfiguration
Kafka KafkaConfiguration
} }
type KafkaConfiguration struct { type KafkaConfiguration struct {
@ -65,6 +67,14 @@ type CurrencyMicroserviceConfiguration struct {
URL CurrencyMicroserviceURL URL CurrencyMicroserviceURL
} }
type VerificationMicroserviceConfiguration struct {
URL VerificationMicroserviceURL
}
type TemplategenMicroserviceConfiguration struct {
URL TemplategenMicroserviceURL
}
type PaymentMicroserviceConfiguration struct { type PaymentMicroserviceConfiguration struct {
HostGRPC string `env:"PAYMENT_MICROSERVICE_GRPC_HOST,required"` HostGRPC string `env:"PAYMENT_MICROSERVICE_GRPC_HOST,required"`
} }
@ -84,3 +94,11 @@ type HubadminMicroserviceURL struct {
type CurrencyMicroserviceURL struct { type CurrencyMicroserviceURL struct {
Translate string `env:"CURRENCY_MICROSERVICE_TRANSLATE_URL,required"` Translate string `env:"CURRENCY_MICROSERVICE_TRANSLATE_URL,required"`
} }
type VerificationMicroserviceURL struct {
Verification string `env:"VERIFICATION_MICROSERVICE_USER_URL,required"`
}
type TemplategenMicroserviceURL struct {
Templategen string `env:"TEMPLATEGEN_MICROSERVICE_URL,required"`
}

@ -14,6 +14,27 @@ type History struct {
DeletedAt *time.Time `json:"deletedAt,omitempty" bson:"deletedAt,omitempty"` DeletedAt *time.Time `json:"deletedAt,omitempty" bson:"deletedAt,omitempty"`
} }
type TariffID struct {
ID string `json:"id" bson:"_id"`
}
type ReportHistory struct {
ID string `json:"id" bson:"_id,omitempty"`
UserID string `json:"userId" bson:"userId"`
Comment string `json:"comment" bson:"comment"`
Key string `json:"key" bson:"key"`
RawDetails RawDetails `json:"rawDetails" bson:"rawDetails"`
Deleted bool `json:"isDeleted" bson:"isDeleted"`
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"`
DeletedAt *time.Time `json:"deletedAt,omitempty" bson:"deletedAt,omitempty"`
}
type RawDetails struct {
Tariffs []Tariff `json:"tariffs" bson:"tariffs"`
Price int64 `json:"price" bson:"price"`
}
func (receiver *History) Sanitize() *History { func (receiver *History) Sanitize() *History {
now := time.Now() now := time.Now()

@ -0,0 +1,12 @@
package models
type RespGeneratorService struct {
DocNumber int `json:"docnumber"`
Date string `json:"date"`
OrgTaxNum string `json:"orgtaxnum"`
OrgName Name `json:"orgname"`
Name string `json:"name"`
Amount uint64 `json:"amount"`
Price int64 `json:"price"`
Sum int64 `json:"sum"`
}

@ -0,0 +1,19 @@
package models
import "time"
type Verification struct {
ID string `json:"_id" bson:"_id,omitempty"`
UserID string `json:"userID" bson:"user_id,omitempty"`
Accepted bool `json:"accepted" bson:"accepted"`
Status string `json:"status" bson:"status,omitempty"`
UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
Comment string `json:"comment" bson:"comment,omitempty"`
Files []VerificationFile `json:"files" bson:"files,omitempty"`
TaxNumber string `json:"taxnumber" bson:"taxnumber,omitempty"`
}
type VerificationFile struct {
Name string `json:"name" bson:"name"`
URL string `json:"url" bson:"url"`
}

@ -165,7 +165,7 @@ func (receiver *Service) Pay(ctx context.Context, accessToken string, userID str
UserInformation: &discount.UserInformation{ UserInformation: &discount.UserInformation{
ID: account.UserID, ID: account.UserID,
Type: string(account.Status), Type: string(account.Status),
PurchasesAmount: uint64(account.Wallet.PurchasesAmount), PurchasesAmount: uint64(account.Wallet.Spent),
CartPurchasesAmount: tariffsAmount, CartPurchasesAmount: tariffsAmount,
}, },
Products: transfer.TariffsToProductInformations(tariffs), Products: transfer.TariffsToProductInformations(tariffs),
@ -180,7 +180,7 @@ func (receiver *Service) Pay(ctx context.Context, accessToken string, userID str
UserInformation: &discount.UserInformation{ UserInformation: &discount.UserInformation{
ID: account.UserID, ID: account.UserID,
Type: string(account.Status), Type: string(account.Status),
PurchasesAmount: uint64(account.Wallet.PurchasesAmount), PurchasesAmount: uint64(account.Wallet.Spent),
CartPurchasesAmount: tariffsAmount, CartPurchasesAmount: tariffsAmount,
}, },
Products: transfer.TariffsToProductInformations(tariffs), Products: transfer.TariffsToProductInformations(tariffs),
@ -203,10 +203,13 @@ func (receiver *Service) Pay(ctx context.Context, accessToken string, userID str
go func(tariffs []models.Tariff) { go func(tariffs []models.Tariff) {
if _, historyErr := receiver.historyService.CreateHistory(ctx, &models.History{ if _, historyErr := receiver.historyService.CreateHistory(ctx, &models.History{
Key: models.CustomerHistoryKeyPayCart, Key: models.CustomerHistoryKeyPayCart,
UserID: account.UserID, UserID: account.UserID,
Comment: "Успешная оплата корзины", Comment: "Успешная оплата корзины",
RawDetails: tariffs, RawDetails: map[string]any{
"tariffs": tariffs,
"price": discountResponse.Price,
},
}); historyErr != nil { }); historyErr != nil {
receiver.logger.Error("failed to insert history on <Pay> of <CartService>", zap.Error(historyErr)) receiver.logger.Error("failed to insert history on <Pay> of <CartService>", zap.Error(historyErr))
} }
@ -214,6 +217,8 @@ func (receiver *Service) Pay(ctx context.Context, accessToken string, userID str
// TODO: обработать ошибки при отправке сообщений // TODO: обработать ошибки при отправке сообщений
receiver.logger.Error("send to redpanda", zap.String("userID", account.UserID), zap.Any("bought tariffs", tariffs))
if sendErrors := receiver.tariffBrokerService.SendMany(ctx, account.UserID, tariffs); len(sendErrors) > 0 { if sendErrors := receiver.tariffBrokerService.SendMany(ctx, account.UserID, tariffs); len(sendErrors) > 0 {
for _, err := range sendErrors { for _, err := range sendErrors {
receiver.logger.Error("failed to send tariffs to broker on <Pay> of <CartService>", zap.Error(err)) receiver.logger.Error("failed to send tariffs to broker on <Pay> of <CartService>", zap.Error(err))

@ -5,6 +5,8 @@ import (
"fmt" "fmt"
"log" "log"
"math" "math"
"os"
"time"
"go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson"
"go.uber.org/zap" "go.uber.org/zap"
@ -16,12 +18,14 @@ import (
type GetHistories struct { type GetHistories struct {
Pagination *models.Pagination Pagination *models.Pagination
Type *string Type *string
UserID string
} }
func (receiver *GetHistories) BSON() bson.M { func (receiver *GetHistories) BSON() bson.M {
query := bson.M{ query := bson.M{
fields.History.IsDeleted: false, fields.History.IsDeleted: false,
fields.History.Type: *receiver.Type, fields.History.Type: *receiver.Type,
fields.History.UserID: receiver.UserID,
} }
return query return query
@ -31,16 +35,38 @@ type historyRepository interface {
CountAll(context.Context, *GetHistories) (int64, errors.Error) CountAll(context.Context, *GetHistories) (int64, errors.Error)
FindMany(context.Context, *GetHistories) ([]models.History, errors.Error) FindMany(context.Context, *GetHistories) ([]models.History, errors.Error)
Insert(context.Context, *models.History) (*models.History, errors.Error) Insert(context.Context, *models.History) (*models.History, errors.Error)
GetRecentTariffs(context.Context, string) ([]models.TariffID, errors.Error) // new
GetHistoryByID(context.Context, string) (*models.ReportHistory, errors.Error)
GetDocNumber(context.Context, string) (map[string]int, errors.Error)
CalculateCustomerLTV(ctx context.Context, from, to int64) (int64, errors.Error)
}
type authClient interface {
GetUser(ctx context.Context, userID string) (*models.User, errors.Error)
}
type verificationClient interface {
GetUser(ctx context.Context, userID string) (*models.Verification, errors.Error)
}
type temlategenClient interface {
SendData(ctx context.Context, data models.RespGeneratorService, fileContents []byte, email string) errors.Error
} }
type Deps struct { type Deps struct {
Logger *zap.Logger Logger *zap.Logger
Repository historyRepository Repository historyRepository
AuthClient authClient
VerificationClient verificationClient
TemlategenClient temlategenClient
} }
type Service struct { type Service struct {
logger *zap.Logger logger *zap.Logger
repository historyRepository repository historyRepository
AuthClient authClient
VerificationClient verificationClient
TemlategenClient temlategenClient
} }
func New(deps Deps) *Service { func New(deps Deps) *Service {
@ -52,9 +78,14 @@ func New(deps Deps) *Service {
log.Panicln("repository is nil on <New (history service)>") log.Panicln("repository is nil on <New (history service)>")
} }
if deps.AuthClient == nil {
log.Panicln("auth client is nil on <New (account service)>")
}
return &Service{ return &Service{
logger: deps.Logger, logger: deps.Logger,
repository: deps.Repository, repository: deps.Repository,
AuthClient: deps.AuthClient,
} }
} }
@ -105,3 +136,138 @@ func (receiver *Service) CreateHistory(ctx context.Context, history *models.Hist
return createdHistory, nil return createdHistory, nil
} }
// TODO:tests.
func (receiver *Service) GetRecentTariffs(ctx context.Context, userID string) ([]models.TariffID, errors.Error) {
if userID == "" {
receiver.logger.Error("user id is missing in <GetRecentTariffs> of <HistoryService>")
return nil, errors.New(
fmt.Errorf("user id is missing: %w", errors.ErrInvalidArgs),
errors.ErrInvalidArgs,
)
}
tariffs, err := receiver.repository.GetRecentTariffs(ctx, userID)
if err != nil {
receiver.logger.Error(
"failed to get recent tariffs in <GetRecentTariffs> of <HistoryService>",
zap.String("userId", userID),
zap.Error(err),
)
return nil, err
}
return tariffs, nil
}
func (receiver *Service) GetHistoryByID(ctx context.Context, historyID string) errors.Error {
if historyID == "" {
receiver.logger.Error("history id is missing in <GetHistoryById> of <HistoryService>")
return errors.New(
fmt.Errorf("history id is missing: %w", errors.ErrInvalidArgs),
errors.ErrInvalidArgs,
)
}
tariffs, err := receiver.repository.GetHistoryByID(ctx, historyID)
if err != nil {
receiver.logger.Error(
"failed to get history by id in <GetHistoryById> of <HistoryService>",
zap.String("historyID", historyID),
zap.Error(err),
)
return err
}
if tariffs.Key != models.CustomerHistoryKeyPayCart {
receiver.logger.Error(
"invalid history record key",
zap.String("historyID", historyID),
zap.Error(err),
)
return err
}
historyMap, err := receiver.repository.GetDocNumber(ctx, tariffs.UserID)
if err != nil {
receiver.logger.Error(
"failed to get history of sorting by date created in <GetDocNumber> of <HistoryService>",
zap.String("historyID", historyID),
zap.Error(err),
)
return err
}
verifuser, err := receiver.VerificationClient.GetUser(ctx, tariffs.UserID)
if err != nil {
receiver.logger.Error("failed to get user verification on <GetHistoryById> of <HistoryService>",
zap.Error(err),
zap.String("userID", tariffs.UserID),
)
return err
}
if !verifuser.Accepted {
receiver.logger.Error(
"verification not accepted",
zap.String("userID", tariffs.UserID),
zap.Error(err),
)
return err
}
authuser, err := receiver.AuthClient.GetUser(ctx, tariffs.UserID)
if err != nil {
receiver.logger.Error("failed to get user on <GetHistoryById> of <HistoryService>",
zap.Error(err),
zap.String("userID", tariffs.UserID),
)
return err
}
fileContents, readerr := os.ReadFile("./report.docx")
if readerr != nil {
return errors.New(
fmt.Errorf("failed to read file: %w", errors.ErrInternalError),
errors.ErrInternalError,
)
}
for _, tariff := range tariffs.RawDetails.Tariffs {
totalAmount := uint64(0)
for _, privilege := range tariff.Privileges {
totalAmount += privilege.Amount
}
data := models.RespGeneratorService{
DocNumber: historyMap[historyID] + 1,
Date: time.Now().Format("2006-01-02"),
OrgTaxNum: verifuser.TaxNumber,
OrgName: models.Name{Orgname: "Orgname"},
Name: tariff.Name,
Amount: totalAmount,
Price: tariffs.RawDetails.Price,
Sum: tariffs.RawDetails.Price,
}
err = receiver.TemlategenClient.SendData(ctx, data, fileContents, authuser.Email)
if err != nil {
receiver.logger.Error("failed to send report to user on <GetHistoryById> of <HistoryService>",
zap.Error(err),
zap.String("userID", tariffs.UserID),
)
return err
}
}
return nil
}
func (receiver *Service) CalculateCustomerLTV(ctx context.Context, from, to int64) (int64, errors.Error) {
ltv, err := receiver.repository.CalculateCustomerLTV(ctx, from, to)
if err != nil {
receiver.logger.Error("failed to calculate LTV", zap.Error(err))
return 0, err
}
return ltv, nil
}

@ -10,7 +10,7 @@ func CalculateCartPurchasesAmount(tariffs []models.Tariff) uint64 {
privilegesSum := uint64(0) privilegesSum := uint64(0)
for _, privilege := range tariff.Privileges { for _, privilege := range tariff.Privileges {
privilegesSum += privilege.Price privilegesSum += privilege.Price*privilege.Amount
} }
sum += privilegesSum sum += privilegesSum

@ -13,6 +13,12 @@ func TariffsToProductInformations(tarrifs []models.Tariff) []*discount.ProductIn
for _, privilege := range tariff.Privileges { for _, privilege := range tariff.Privileges {
productInformations = append(productInformations, PrivilegeToProductInformation(privilege)) productInformations = append(productInformations, PrivilegeToProductInformation(privilege))
} }
if tariff.Price != 0 {
for i := range productInformations {
productInformations[i].Price = tariff.Price
}
}
} }
return productInformations return productInformations

BIN
main Executable file

Binary file not shown.

@ -45,4 +45,4 @@
} }
] ]
} }
] ]

BIN
report.docx Normal file

Binary file not shown.

@ -0,0 +1,54 @@
package integration
import (
"context"
"fmt"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/tests/helpers"
"testing"
"time"
)
func TestCalculateLTV(t *testing.T) {
ctx := context.Background()
jwtUtil := helpers.InitializeJWT()
token, err := jwtUtil.Create("807f1f77bcf81cd799439077")
if ok := assert.NoError(t, err); !ok {
return
}
layout := "2006-01-02T15:04:05.000Z"
fromString := "2023-11-08T22:29:48.719Z"
toString := "2023-12-27T15:00:00.000Z"
fromTime, err := time.Parse(layout, fromString)
if err != nil {
fmt.Println("error:", err)
}
toTime, err := time.Parse(layout, toString)
if err != nil {
fmt.Println("error:", err)
}
from := fromTime.Unix()
to := toTime.Unix()
fmt.Println(from, to)
response, err := client.Post[struct{}, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://" + "localhost:8000" + "/history/ltv",
Body: swagger.CalculateLTVJSONBody{From: from, To: to},
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
})
if ok := assert.NoError(t, err); !ok {
return
}
if ok := assert.Nil(t, response.Error); !ok {
return
}
assert.Equal(t, 200, response.StatusCode)
fmt.Println(response.Body)
}

@ -0,0 +1,37 @@
package integration_test
import (
"context"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/tests/helpers"
)
func TestHistoryReport(t *testing.T) {
ctx := context.Background()
jwtUtil := helpers.InitializeJWT()
token, err := jwtUtil.Create("807f1f77bcf81cd799439077")
if ok := assert.NoError(t, err); !ok {
return
}
response, err := client.Post[struct{}, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://" + customerServiceBase + "/sendReport",
Body: swagger.SendReportJSONBody{Id: "10002"},
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
})
if ok := assert.NoError(t, err); !ok {
return
}
if ok := assert.Nil(t, response.Error); !ok {
return
}
assert.Equal(t, 200, response.StatusCode)
}

@ -3,6 +3,7 @@ package integration_test
import ( import (
"context" "context"
"fmt" "fmt"
"os"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -11,6 +12,8 @@ import (
"penahub.gitlab.yandexcloud.net/pena-services/customer/tests/helpers" "penahub.gitlab.yandexcloud.net/pena-services/customer/tests/helpers"
) )
var customerServiceBase = os.Getenv("CUSTOMER_SERVICE")
func TestSetAccountVerificationStatusNO(t *testing.T) { func TestSetAccountVerificationStatusNO(t *testing.T) {
jwtUtil := helpers.InitializeJWT() jwtUtil := helpers.InitializeJWT()
@ -25,7 +28,7 @@ func TestSetAccountVerificationStatusNO(t *testing.T) {
} }
response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{ response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8082/account/807f1f77bcf81cd799439077", URL: "http://" + customerServiceBase + "/account/807f1f77bcf81cd799439077",
Body: models.SetAccountStatus{Status: models.AccountStatusNo}, Body: models.SetAccountStatus{Status: models.AccountStatusNo},
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)}, Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
}) })
@ -56,7 +59,7 @@ func TestSetAccountVerificationStatusORG(t *testing.T) {
} }
response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{ response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8082/account/807f1f77bcf81cd799439077", URL: "http://" + customerServiceBase + "/account/807f1f77bcf81cd799439077",
Body: models.SetAccountStatus{Status: models.AccountStatusOrg}, Body: models.SetAccountStatus{Status: models.AccountStatusOrg},
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)}, Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
}) })
@ -87,7 +90,7 @@ func TestSetAccountVerificationStatusNKO(t *testing.T) {
} }
response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{ response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8082/account/807f1f77bcf81cd799439077", URL: "http://" + customerServiceBase + "/account/807f1f77bcf81cd799439077",
Body: models.SetAccountStatus{Status: models.AccountStatusNko}, Body: models.SetAccountStatus{Status: models.AccountStatusNko},
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)}, Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
}) })
@ -118,7 +121,7 @@ func TestSetAccountVerificationStatusFailure(t *testing.T) {
} }
response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{ response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8082/account/807f1f77bcf81cd799439077", URL: "http://" + customerServiceBase + "/account/807f1f77bcf81cd799439077",
Body: models.SetAccountStatus{Status: "radnom-status"}, Body: models.SetAccountStatus{Status: "radnom-status"},
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)}, Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
}) })

@ -25,7 +25,7 @@ func TestUpdateAccountName(t *testing.T) {
} }
response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{ response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8082/account", URL: "http://" + customerServiceBase + "/account",
Body: models.Name{ Body: models.Name{
FirstName: "Ivan", FirstName: "Ivan",
Secondname: "Ivanov", Secondname: "Ivanov",