mergeconflicts

This commit is contained in:
skeris 2023-12-01 20:46:53 +03:00
commit 4f500f5ea5
27 changed files with 908 additions and 254 deletions

@ -2,7 +2,7 @@
FROM golang:1.20.3-alpine AS build
# 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
WORKDIR /app
# Create binary directory
@ -13,8 +13,6 @@ RUN mkdir /bin/golang-migrate -p
ADD ./tools/migrate /bin/golang-migrate/
# Add main files to app
ADD . .
# Download go depences
RUN go mod download
# Build app
RUN GOOS=linux go build -o bin ./...
@ -24,9 +22,7 @@ RUN GOOS=linux go build -o bin ./...
FROM alpine:3.18.3 AS test
# Install packages
RUN apk --no-cache add ca-certificates && rm -rf /var/cache/apk/*
# Set GO111MODULE env
ENV GO111MODULE=off
RUN apk --no-cache add ca-certificates
# Create home directory
WORKDIR /app
# Copy build file
@ -44,7 +40,7 @@ CMD [ "./app" ]
FROM alpine:3.18.3 AS production
# Install packages
RUN apk --no-cache add ca-certificates && rm -rf /var/cache/apk/*
RUN apk --no-cache add ca-certificates
# Create home directory
WORKDIR /app
# Copy build file

@ -25,18 +25,21 @@ test.unit: ## run unit tests
go test ./...
test.integration: ## run integration tests
go mod vendor
@make test.integration.up
@make test.integration.start
@make test.integration.down
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
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
go test ./tests/integration/...
docker-compose -p integration -f deployments/test/docker-compose.integration.yaml down
docker-compose -p integration -f deployments/test/docker-compose.integration.yaml up --exit-code-from integration --remove-orphans
docker-compose -p integration -f deployments/test/docker-compose.integration.yaml down
test.e2e.start: ## run integration test
go test ./tests/e2e/...

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

@ -28,24 +28,24 @@ paths:
security:
- Bearer: []
responses:
'200':
"200":
description: успешное получение
content:
application/json:
schema:
$ref: '#/components/schemas/Account'
'401':
$ref: "#/components/schemas/Account"
"401":
description: Неавторизован
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
$ref: "#/components/schemas/Error"
"404":
description: Такого пользователя нет
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
$ref: "#/components/schemas/Error"
post:
tags:
- account
@ -55,18 +55,18 @@ paths:
security:
- Bearer: []
responses:
'200':
"200":
description: успешное создание аккаунта
content:
application/json:
schema:
$ref: '#/components/schemas/Account'
'401':
$ref: "#/components/schemas/Account"
"401":
description: Неавторизован
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
$ref: "#/components/schemas/Error"
patch:
tags:
- account
@ -80,20 +80,20 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/Name'
$ref: "#/components/schemas/Name"
responses:
'200':
"200":
description: успешное создание аккаунта
content:
application/json:
schema:
$ref: '#/components/schemas/Account'
'401':
$ref: "#/components/schemas/Account"
"401":
description: Неавторизован
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
$ref: "#/components/schemas/Error"
delete:
tags:
- account
@ -103,18 +103,18 @@ paths:
security:
- Bearer: []
responses:
'200':
"200":
description: успешное удаление аккаунта
content:
application/json:
schema:
$ref: '#/components/schemas/Account'
'401':
$ref: "#/components/schemas/Account"
"401":
description: Неавторизован
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
$ref: "#/components/schemas/Error"
/accounts:
get:
@ -141,7 +141,7 @@ paths:
type: integer
default: 100
responses:
'200':
"200":
description: успешное получение страницы аккаунтов
content:
application/json:
@ -173,24 +173,24 @@ paths:
schema:
type: string
responses:
'200':
"200":
description: Успешное получение аккаунта
content:
application/json:
schema:
$ref: '#/components/schemas/Account'
'401':
$ref: "#/components/schemas/Account"
"401":
description: Неавторизован
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
$ref: "#/components/schemas/Error"
"404":
description: Нет такого пользователя
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
$ref: "#/components/schemas/Error"
patch:
tags:
- account
@ -213,20 +213,20 @@ paths:
type: object
properties:
status:
$ref: '#/components/schemas/AccountStatus'
$ref: "#/components/schemas/AccountStatus"
responses:
'200':
"200":
description: Успешное выставление статуса
content:
application/json:
schema:
$ref: '#/components/schemas/Account'
'404':
$ref: "#/components/schemas/Account"
"404":
description: Нет такого пользователя
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
$ref: "#/components/schemas/Error"
delete:
tags:
- account
@ -243,24 +243,24 @@ paths:
schema:
type: string
responses:
'200':
"200":
description: Успешное удаление аккаунта
content:
application/json:
schema:
$ref: '#/components/schemas/Account'
'401':
$ref: "#/components/schemas/Account"
"401":
description: Неавторизован
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
$ref: "#/components/schemas/Error"
"404":
description: Нет такого пользователя
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
$ref: "#/components/schemas/Error"
/currencies:
get:
@ -269,7 +269,7 @@ paths:
summary: получить список одобренных валют
operationId: getCurrencies
responses:
'200':
"200":
description: успешное получение списка валют
content:
application/json:
@ -300,7 +300,7 @@ paths:
items:
type: string
responses:
'200':
"200":
description: успешное обновление списка валют
content:
application/json:
@ -312,12 +312,12 @@ paths:
- "CAD"
items:
type: string
'401':
"401":
description: Uanuthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
$ref: "#/components/schemas/Error"
/cart:
patch:
@ -336,24 +336,24 @@ paths:
type: string
example: "807f1f77bcf81cd799439011"
responses:
'200':
"200":
description: Добавлено в корзину
content:
application/json:
schema:
$ref: '#/components/schemas/Account'
'401':
$ref: "#/components/schemas/Account"
"401":
description: Неавторизован
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
$ref: "#/components/schemas/Error"
"404":
description: Не найден такой тариф
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
$ref: "#/components/schemas/Error"
delete:
tags:
- cart
@ -369,24 +369,24 @@ paths:
type: string
example: "807f1f77bcf81cd799439011"
responses:
'200':
"200":
description: Удалено из корзины
content:
application/json:
schema:
$ref: '#/components/schemas/Account'
'401':
$ref: "#/components/schemas/Account"
"401":
description: Неавторизован
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
$ref: "#/components/schemas/Error"
"404":
description: Не найден такой тариф
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
$ref: "#/components/schemas/Error"
/cart/pay:
post:
@ -398,18 +398,18 @@ paths:
- Bearer: []
description: Запрос на проведение оплаты корзины
responses:
'200':
"200":
description: успешная оплата корзины
content:
application/json:
schema:
$ref: '#/components/schemas/Account'
'401':
$ref: "#/components/schemas/Account"
"401":
description: Неавторизован
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
$ref: "#/components/schemas/Error"
/wallet:
patch:
@ -439,25 +439,25 @@ paths:
type: string
example: "USD"
responses:
'200':
"200":
description: Успешная смена валюты кошелька
content:
application/json:
schema:
$ref: "#/components/schemas/Account"
'401':
"401":
description: Неавторизован
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'400':
"400":
description: Такая валюта не одобрена
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
'404':
"404":
description: Пользователь не найден
content:
application/json:
@ -482,7 +482,7 @@ paths:
required: [type, currency, amount]
properties:
type:
$ref: '#/components/schemas/PaymentType'
$ref: "#/components/schemas/PaymentType"
currency:
type: string
description: "ISO-4217 формат"
@ -491,7 +491,7 @@ paths:
type: integer
example: 15020
bankCard:
$ref: '#/components/schemas/BankCard'
$ref: "#/components/schemas/BankCard"
phoneNumber:
type: string
example: "79000000000"
@ -502,7 +502,7 @@ paths:
type: string
example: "https://site.ru/cart"
responses:
'200':
"200":
description: Успешный запрос денег
content:
application/json:
@ -512,12 +512,12 @@ paths:
link:
type: string
example: "https://google.ru"
'500':
"500":
description: Сервис оплаты вернул ошибку
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
$ref: "#/components/schemas/Error"
/history:
get:
@ -552,7 +552,7 @@ paths:
schema:
type: string
responses:
'200':
"200":
description: Успешное получение событий
content:
application/json:
@ -565,13 +565,13 @@ paths:
records:
type: array
items:
$ref: '#/components/schemas/History'
'401':
$ref: "#/components/schemas/History"
"401":
description: Неавторизован
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
$ref: "#/components/schemas/Error"
/recent:
get:
@ -579,19 +579,80 @@ paths:
- history
summary: Получение недавних тарифов
description: Возвращает список уникальных тарифов из истории. Айди аккаунта получается из заголовка.
operationId: getRecentTariffs
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"
components:
schemas:
Account:
type: object
required: [_id, userId, cart, wallet, name, status, deleted, createdAt, updatedAt]
required:
[_id, userId, cart, wallet, name, status, deleted, createdAt, updatedAt]
properties:
_id:
type: string
@ -600,7 +661,7 @@ components:
type: string
example: "807f1f77bcf81cd799439011"
name:
$ref: '#/components/schemas/Name'
$ref: "#/components/schemas/Name"
cart:
type: array
items:
@ -611,7 +672,7 @@ components:
wallet:
$ref: "#/components/schemas/Wallet"
status:
$ref: '#/components/schemas/AccountStatus'
$ref: "#/components/schemas/AccountStatus"
isDeleted:
type: boolean
example: false
@ -698,7 +759,8 @@ components:
History:
type: object
required: [id, userId, type, deleted, createdAt, updatedAt, comment, subject]
required:
[id, userId, type, deleted, createdAt, updatedAt, comment, subject]
properties:
id:
type: string
@ -735,7 +797,28 @@ components:
PaymentType:
type: string
enum: ["bankCard", "tinkoffBank", "qiwi", "sberbank", "yoomoney", "mobile", "installments", "cash", "sbp", "b2bSberbank", "alfabank"]
enum:
[
"bankCard",
"tinkoffBank",
"qiwi",
"sberbank",
"yoomoney",
"mobile",
"installments",
"cash",
"sbp",
"b2bSberbank",
"alfabank",
]
TariffID:
type: object
properties:
ID:
type: string
example: "807f1f77bcf81cd799439011"
AccountStatus:
type: string

@ -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:
container_name: customer-service
build:
context: ../../.
context: ../..
dockerfile: Dockerfile
target: test
env_file:
@ -39,24 +39,23 @@ services:
- 8082:8000
- 9092:9000
depends_on:
- customer-db
- customer-migration
- redpanda
customer-db:
condition: service_started
customer-migration:
condition: service_completed_successfully
redpanda:
condition: service_healthy
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',
]
image: alpine
command: migrate -source file://migrations -database "mongodb://test:test@customer-db:27017/admin" up
volumes:
- ../../migrations/test:/app/migrations:ro
- ../../tools/migrate:/usr/local/bin/migrate:ro
working_dir: /app
depends_on:
- customer-db
networks:
@ -105,17 +104,18 @@ services:
networks:
- test
healthcheck:
test: ["CMD-SHELL", "rpk cluster health | grep -E 'Healthy:.+true' || exit 1"]
interval: 15s
timeout: 3s
test: rpk cluster health | grep -q 'Healthy:.*true'
interval: 2s
timeout: 2s
retries: 5
start_period: 5s
console:
tty: true
container_name: customer-console
image: docker.redpanda.com/redpandadata/console:v2.2.4
entrypoint: /bin/sh
command: -c "echo \"$$CONSOLE_CONFIG_FILE\" > /tmp/config.yml; /app/console"
entrypoint: ''
command: sh -c 'echo "$$CONSOLE_CONFIG_FILE" > /tmp/config.yml && exec /app/console'
environment:
CONFIG_FILEPATH: /tmp/config.yml
CONSOLE_CONFIG_FILE: |
@ -138,18 +138,20 @@ services:
networks:
- test
depends_on:
- redpanda
connect:
condition: service_started
redpanda:
condition: service_healthy
connect:
tty: true
image: docker.redpanda.com/redpandadata/connectors:latest
hostname: connect
container_name: connect
networks:
- test
# hostname: connect
container_name: customer-connect
# platform: 'linux/amd64'
depends_on:
- redpanda
redpanda:
condition: service_healthy
ports:
- "8083:8083"
environment:
@ -170,6 +172,8 @@ services:
CONNECT_GC_LOG_ENABLED: "false"
CONNECT_HEAP_OPTS: -Xms512M -Xmx512M
CONNECT_LOG_LEVEL: info
networks:
- test
networks:
test:

9
go.mod

@ -16,7 +16,7 @@ require (
github.com/twmb/franz-go v1.13.6
github.com/twmb/franz-go/pkg/kadm v1.8.1
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/grpc v1.53.0
google.golang.org/protobuf v1.31.0
@ -25,6 +25,7 @@ require (
require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/benbjohnson/clock v1.3.3 // indirect
github.com/daixiang0/gci v0.11.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
@ -34,6 +35,8 @@ require (
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/invopop/yaml v0.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.16.7 // indirect
@ -48,6 +51,8 @@ require (
github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/twmb/franz-go/pkg/kmsg v1.4.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
@ -59,7 +64,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.12.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/text v0.12.0 // indirect
golang.org/x/time v0.3.0 // indirect

17
go.sum

@ -10,7 +10,10 @@ github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvF
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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/daixiang0/gci v0.11.2 h1:Oji+oPsp3bQ6bNNgX30NBAVT18P4uBH4sRZnlOlTj7Y=
github.com/daixiang0/gci v0.11.2/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -63,6 +66,10 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc=
github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
@ -119,9 +126,14 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sethvargo/go-envconfig v0.9.0 h1:Q6FQ6hVEeTECULvkJZakq3dZMeBQ3JUpcKMfPQbKMDE=
github.com/sethvargo/go-envconfig v0.9.0/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -171,12 +183,15 @@ 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.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
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/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
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.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -211,6 +226,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
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.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

@ -72,7 +72,9 @@ func Run(config *models.Config, logger *zap.Logger) (appErr error) {
HubadminURL: &config.Service.HubadminMicroservice.URL,
CurrencyURL: &config.Service.CurrencyMicroservice.URL,
DiscountServiceConfiguration: &config.Service.DiscountMicroservice,
VerificationURL: &config.Service.VerificationMicroservice.URL,
PaymentServiceConfiguration: &config.Service.PaymentMicroservice,
TemplategenURL: &config.Service.TemplategenMicroserviceURL.URL,
})
repositories := initialize.NewRepositories(initialize.RepositoriesDeps{

@ -13,6 +13,8 @@ type ClientsDeps struct {
CurrencyURL *models.CurrencyMicroserviceURL
DiscountServiceConfiguration *models.DiscountMicroserviceConfiguration
PaymentServiceConfiguration *models.PaymentMicroserviceConfiguration
VerificationURL *models.VerificationMicroserviceURL
TemplategenURL *models.TemplategenMicroserviceURL
}
type Clients struct {
@ -21,6 +23,8 @@ type Clients struct {
CurrencyClient *client.CurrencyClient
DiscountClient *client.DiscountClient
PaymentClient *client.PaymentClient
VerificationClient *client.VerificationClient
TemplateClient *client.TemplateClient
}
func NewClients(deps ClientsDeps) *Clients {
@ -45,5 +49,13 @@ func NewClients(deps ClientsDeps) *Clients {
Logger: deps.Logger,
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,
}),
}
}

@ -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
}

@ -87,3 +87,16 @@ func (receiver *Controller) GetRecentTariffs(ctx echo.Context) error {
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)
}

@ -168,3 +168,93 @@ func (receiver *HistoryRepository) GetRecentTariffs(ctx context.Context, userID
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
}

@ -65,6 +65,9 @@ type ServerInterface interface {
// Получение недавних тарифов
// (GET /recent)
GetRecentTariffs(ctx echo.Context) error
// отправить акт проделанных работ на почту
// (POST /sendReport)
SendReport(ctx echo.Context) error
// Изменить валюту кошелька
// (PATCH /wallet)
ChangeCurrency(ctx echo.Context) error
@ -317,6 +320,17 @@ func (w *ServerInterfaceWrapper) GetRecentTariffs(ctx echo.Context) error {
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.
func (w *ServerInterfaceWrapper) ChangeCurrency(ctx echo.Context) error {
var err error
@ -382,6 +396,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
router.PUT(baseURL+"/currencies", wrapper.UpdateCurrencies)
router.GET(baseURL+"/history", wrapper.GetHistory)
router.GET(baseURL+"/recent", wrapper.GetRecentTariffs)
router.POST(baseURL+"/sendReport", wrapper.SendReport)
router.PATCH(baseURL+"/wallet", wrapper.ChangeCurrency)
router.POST(baseURL+"/wallet", wrapper.RequestMoney)
@ -390,73 +405,74 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
"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",
"SAAA",
"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",
}
// GetSwagger returns the content of the embedded swagger specification file

@ -36,6 +36,7 @@ type walletController interface {
type historyController interface {
GetHistoryList(ctx echo.Context, params GetHistoryParams) error
GetRecentTariffs(ctx echo.Context) error
SendReport(ctx echo.Context) error
}
type Deps struct {
@ -152,6 +153,10 @@ 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)
}
// Wallet
func (receiver *API) RequestMoney(ctx echo.Context) error {

@ -157,6 +157,11 @@ type GetHistoryParams struct {
Type *string `form:"type,omitempty" json:"type,omitempty"`
}
// SendReportJSONBody defines parameters for SendReport.
type SendReportJSONBody struct {
Id string `json:"id"`
}
// ChangeCurrencyJSONBody defines parameters for ChangeCurrency.
type ChangeCurrencyJSONBody struct {
Currency string `json:"currency"`
@ -184,6 +189,9 @@ type SetAccountVerificationStatusJSONRequestBody SetAccountVerificationStatusJSO
// UpdateCurrenciesJSONRequestBody defines body for UpdateCurrencies for application/json ContentType.
type UpdateCurrenciesJSONRequestBody = UpdateCurrenciesJSONBody
// SendReportJSONRequestBody defines body for SendReport for application/json ContentType.
type SendReportJSONRequestBody SendReportJSONBody
// ChangeCurrencyJSONRequestBody defines body for ChangeCurrency for application/json ContentType.
type ChangeCurrencyJSONRequestBody ChangeCurrencyJSONBody

@ -31,6 +31,8 @@ type ServiceConfiguration struct {
CurrencyMicroservice CurrencyMicroserviceConfiguration
DiscountMicroservice DiscountMicroserviceConfiguration
PaymentMicroservice PaymentMicroserviceConfiguration
VerificationMicroservice VerificationMicroserviceConfiguration
TemplategenMicroserviceURL TemplategenMicroserviceConfiguration
JWT JWTConfiguration
Kafka KafkaConfiguration
}
@ -65,6 +67,14 @@ type CurrencyMicroserviceConfiguration struct {
URL CurrencyMicroserviceURL
}
type VerificationMicroserviceConfiguration struct {
URL VerificationMicroserviceURL
}
type TemplategenMicroserviceConfiguration struct {
URL TemplategenMicroserviceURL
}
type PaymentMicroserviceConfiguration struct {
HostGRPC string `env:"PAYMENT_MICROSERVICE_GRPC_HOST,required"`
}
@ -84,3 +94,11 @@ type HubadminMicroserviceURL struct {
type CurrencyMicroserviceURL struct {
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"`
}

@ -18,6 +18,23 @@ 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 {
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"`
}

@ -5,6 +5,8 @@ import (
"fmt"
"log"
"math"
"os"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.uber.org/zap"
@ -33,16 +35,36 @@ type historyRepository interface {
FindMany(context.Context, *GetHistories) ([]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)
}
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 {
Logger *zap.Logger
Repository historyRepository
AuthClient authClient
VerificationClient verificationClient
TemlategenClient temlategenClient
}
type Service struct {
logger *zap.Logger
repository historyRepository
AuthClient authClient
VerificationClient verificationClient
TemlategenClient temlategenClient
}
func New(deps Deps) *Service {
@ -54,9 +76,14 @@ func New(deps Deps) *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{
logger: deps.Logger,
repository: deps.Repository,
AuthClient: deps.AuthClient,
}
}
@ -130,3 +157,105 @@ func (receiver *Service) GetRecentTariffs(ctx context.Context, userID string) ([
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
}

BIN
report.docx Normal file

Binary file not shown.

@ -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")
// t.Log("TOKEN:", token)
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 (
"context"
"fmt"
"os"
"testing"
"github.com/stretchr/testify/assert"
@ -11,6 +12,8 @@ import (
"penahub.gitlab.yandexcloud.net/pena-services/customer/tests/helpers"
)
var customerServiceBase string = os.Getenv("CUSTOMER_SERVICE")
func TestSetAccountVerificationStatusNO(t *testing.T) {
jwtUtil := helpers.InitializeJWT()
@ -25,7 +28,7 @@ func TestSetAccountVerificationStatusNO(t *testing.T) {
}
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},
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{
URL: "http://localhost:8082/account/807f1f77bcf81cd799439077",
URL: "http://" + customerServiceBase + "/account/807f1f77bcf81cd799439077",
Body: models.SetAccountStatus{Status: models.AccountStatusOrg},
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{
URL: "http://localhost:8082/account/807f1f77bcf81cd799439077",
URL: "http://" + customerServiceBase + "/account/807f1f77bcf81cd799439077",
Body: models.SetAccountStatus{Status: models.AccountStatusNko},
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{
URL: "http://localhost:8082/account/807f1f77bcf81cd799439077",
URL: "http://" + customerServiceBase + "/account/807f1f77bcf81cd799439077",
Body: models.SetAccountStatus{Status: "radnom-status"},
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{
URL: "http://localhost:8082/account",
URL: "http://" + customerServiceBase + "/account",
Body: models.Name{
FirstName: "Ivan",
Secondname: "Ivanov",

0
tools/migrate Normal file → Executable file