Merge branch 'dev' into staging
This commit is contained in:
commit
bf7133846c
27
.env
27
.env
@ -22,6 +22,26 @@ PUBLIC_CURVE_KEY="-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAEbnIvjIMle4rqVol6K
|
|||||||
PRIVATE_CURVE_KEY="-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIKn0BKwF3vZvODgWAnUIwQhd8de5oZhY48gc23EWfrfs\n-----END PRIVATE KEY-----"
|
PRIVATE_CURVE_KEY="-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIKn0BKwF3vZvODgWAnUIwQhd8de5oZhY48gc23EWfrfs\n-----END PRIVATE KEY-----"
|
||||||
|
|
||||||
SIGN_SECRET="pena-auth-microservice-group"
|
SIGN_SECRET="pena-auth-microservice-group"
|
||||||
|
JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICWwIBAAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2B
|
||||||
|
iw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikH
|
||||||
|
oKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAEC
|
||||||
|
gYAOphnVPXbk6lpYzdkLC1Xn5EOEuNfOLLURLxBnPWozZo26r/Mtahu/9mYhrYlv
|
||||||
|
PP8r6mxta3VIil8iOdZyOLa/4d1LPd+UehgEXIJEiYXLtn7RS5eUnoPuQxssfs1k
|
||||||
|
OWjdN8p6SzppleegFTvGRX4KM3cDLfSphOk8JuBCrpSSYQJBAOdqizTSrdKMTuVe
|
||||||
|
c7Jk1JOJkyFuFs+N5zeryyeFGH7IpRdWy0rkWMxIUAi8Ap1vYVBPHv4tDOo3sy5X
|
||||||
|
VLc/knkCQQCE62pg+0TmsrhO/2Pgog6MLBkzlzXYMRp/01HbmznwYF+ejfPnzLkz
|
||||||
|
hnUlxRUNK3lhXM/7H6oAjvqF2R72u/OPAkEAterkmdbQfEZ+MwNoEiH/lie9OLdx
|
||||||
|
SSI1VGdBYcTYN7qFRW6eizYstBJYkDU0HQ0Uw+we4hMKJwk4W0KdvxxDiQJAeqlB
|
||||||
|
V1QqBneBbK10PzVuFV8QtrJhJyxRVwrtbKq38iMNuqUnI4+ijXEUpJFWVvv6nKXo
|
||||||
|
7McQvEk12dU/JNTX8wJAOlAtSNjp9tVwpMpC0w2St1eKc1L2SknjeohA5ldoBz8sGeZsPhTU3eHSD1neAZXLKN5K68z3zFBr20ubY9nyLw==
|
||||||
|
-----END RSA PRIVATE KEY-----"
|
||||||
|
|
||||||
|
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikHoKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAE=\n-----END PUBLIC KEY-----"
|
||||||
|
|
||||||
|
JWT_ISSUER="pena-auth-service"
|
||||||
|
|
||||||
|
JWT_AUDIENCE="pena"
|
||||||
|
|
||||||
SIGN_SECRET="secret"
|
SIGN_SECRET="secret"
|
||||||
|
|
||||||
@ -37,4 +57,9 @@ SMTP_SENDER="noreply@mailing.pena.digital"
|
|||||||
# URL settings
|
# URL settings
|
||||||
DEFAULT_REDIRECTION_URL = "https://shub.pena.digital/recover/"
|
DEFAULT_REDIRECTION_URL = "https://shub.pena.digital/recover/"
|
||||||
AUTH_EXCHANGE_URL = "http://10.6.0.11:59300/auth/exchange"
|
AUTH_EXCHANGE_URL = "http://10.6.0.11:59300/auth/exchange"
|
||||||
RECOVER_URL = "https://shub.pena.digital/recover/"
|
RECOVER_URL = "https://shub.pena.digital/recover/"
|
||||||
|
DISCOUNT_ADDRESS = "http://10.6.0.11:9001"
|
||||||
|
|
||||||
|
# Kafka settings
|
||||||
|
KAFKA_BROKERS="10.6.0.11:9092"
|
||||||
|
KAFKA_TOPIC_TARIFF="tariffs"
|
||||||
|
@ -6,8 +6,9 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "27020:27017"
|
- "27020:27017"
|
||||||
environment:
|
environment:
|
||||||
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USER}
|
- MONGO_INITDB_ROOT_USERNAME=test
|
||||||
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}
|
- MONGO_INITDB_ROOT_PASSWORD=test
|
||||||
|
- MONGO_INITDB_AUTH_MECHANISM=SCRAM-SHA-1
|
||||||
volumes:
|
volumes:
|
||||||
- mongo_data:/data/db
|
- mongo_data:/data/db
|
||||||
|
|
||||||
|
@ -2,66 +2,64 @@ openapi: 3.0.0
|
|||||||
info:
|
info:
|
||||||
title: Codeword Recovery Service API
|
title: Codeword Recovery Service API
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: API for handling password recovery for the Codeword service.
|
description: API для обработки восстановления паролей для сервиса Codeword.
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- name: recover
|
||||||
|
description: Операции связанные с восстановлением пароля
|
||||||
|
- name: promocode
|
||||||
|
description: Операции связанные с промокодами
|
||||||
|
- name: stats
|
||||||
|
description: Операции связанные со статистикой
|
||||||
|
|
||||||
paths:
|
paths:
|
||||||
/liveness:
|
/liveness:
|
||||||
get:
|
get:
|
||||||
|
operationId: Liveness
|
||||||
summary: Роут проверки активности
|
summary: Роут проверки активности
|
||||||
|
tags:
|
||||||
|
- recover
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Успех – сервис запущен
|
description: Успех – сервис запущен
|
||||||
|
|
||||||
/readiness:
|
/readiness:
|
||||||
get:
|
get:
|
||||||
summary: Роут проверки базы данных
|
operationId: Readiness
|
||||||
|
summary: Роут проверки баз данных
|
||||||
|
tags:
|
||||||
|
- recover
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Успех — сервис готов и соединение с БД живо
|
description: Успех — сервис готов и соединение с БД живо
|
||||||
'503':
|
'503':
|
||||||
description: Служба недоступна — не удалось выполнить проверку связи с БД
|
description: Служба недоступна — не удалось выполнить проверку связи с БД
|
||||||
|
|
||||||
/recover:
|
/recover:
|
||||||
post:
|
post:
|
||||||
summary: Запустите процесс восстановления пароля
|
operationId: Recovery
|
||||||
|
summary: Восстановления пароля
|
||||||
|
tags:
|
||||||
|
- recover
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
application/x-www-form-urlencoded:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: object
|
$ref: '#/components/schemas/RecoveryReq'
|
||||||
properties:
|
|
||||||
email:
|
|
||||||
type: string
|
|
||||||
format: email
|
|
||||||
description: Электронная почта, на которую нужно отправить инструкции по восстановлению
|
|
||||||
Referrer:
|
|
||||||
type: string
|
|
||||||
description: URL-адрес referral, если он доступен
|
|
||||||
RedirectionURL:
|
|
||||||
type: string
|
|
||||||
description: URL-адрес, на который перенаправляется пользователь после отправки электронного письма
|
|
||||||
|
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Запрос на восстановление принят, и возвращен идентификатор записи восстановления
|
description: Запрос на восстановление принят
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: string
|
|
||||||
description: Идентификатор запроса на восстановление
|
|
||||||
'404':
|
'404':
|
||||||
description: Пользователь не найден по электронной почте
|
description: Пользователь не найден по электронной почте
|
||||||
'500':
|
'500':
|
||||||
description: Внутренняя ошибка сервера – разные причины
|
description: Внутренняя ошибка сервера
|
||||||
|
|
||||||
/recover/{sign}:
|
/recover/{sign}:
|
||||||
get:
|
get:
|
||||||
summary: Обработать ссылку восстановления, в которой содержится подпись и обменять ее на токены
|
operationId: RecoveryLink
|
||||||
|
summary: Обработать ссылку восстановления и обменять ее на токены
|
||||||
|
tags:
|
||||||
|
- recover
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
name: sign
|
name: sign
|
||||||
@ -71,103 +69,67 @@ paths:
|
|||||||
description: Подпись восстановления как часть URL-адреса восстановления
|
description: Подпись восстановления как часть URL-адреса восстановления
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Восстановление успешно, информация для обмена токенов возвращена
|
description: Восстановление успешно, информация для обмена токенов возвращена в cookie
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
accessToken:
|
|
||||||
type: string
|
|
||||||
refreshToken:
|
|
||||||
type: string
|
|
||||||
'406':
|
'406':
|
||||||
description: NotAcceptable - срок действия ссылки для восстановления истек или она недействительна
|
description: NotAcceptable - срок действия ссылки для восстановления истек или она недействительна
|
||||||
'500':
|
'500':
|
||||||
description: Внутренняя ошибка сервера – разные причины
|
description: Внутренняя ошибка сервера
|
||||||
|
|
||||||
/promocode/create:
|
/promocode/create:
|
||||||
post:
|
post:
|
||||||
|
operationId: CreatePromoCode
|
||||||
summary: Создать новый промокод
|
summary: Создать новый промокод
|
||||||
|
tags:
|
||||||
|
- promocode
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/PromoCodeRequest'
|
$ref: '#/components/schemas/PromoCodeReq'
|
||||||
responses:
|
responses:
|
||||||
'201':
|
'200':
|
||||||
description: Новый промокод успешно создан
|
description: Новый промокод успешно создан
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/PromoCodeResponse'
|
$ref: '#/components/schemas/PromoCode'
|
||||||
'400':
|
'400':
|
||||||
description: Invalid request payload / Duplicate Codeword
|
description: Неверный формат запроса или дублирующийся codeword
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
error:
|
|
||||||
type: string
|
|
||||||
'500':
|
'500':
|
||||||
description: Внутренняя ошибка сервера
|
description: Внутренняя ошибка сервера
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
error:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
/promocode/edit:
|
/promocode/edit:
|
||||||
put:
|
put:
|
||||||
|
operationId: EditPromoCode
|
||||||
summary: Обновить существующий промокод
|
summary: Обновить существующий промокод
|
||||||
|
tags:
|
||||||
|
- promocode
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/ReqEditPromoCode'
|
$ref: '#/components/schemas/EditPromoCodeReq'
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Промокод успешно обновлен
|
description: Промокод успешно обновлен
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/PromoCodeResponse'
|
$ref: '#/components/schemas/PromoCode'
|
||||||
'400':
|
'400':
|
||||||
description: Неверный формат запроса
|
description: Неверный формат запроса
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
error:
|
|
||||||
type: string
|
|
||||||
'404':
|
'404':
|
||||||
description: Промокод не найден
|
description: Промокод не найден
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
error:
|
|
||||||
type: string
|
|
||||||
'500':
|
'500':
|
||||||
description: Внутренняя ошибка сервера
|
description: Внутренняя ошибка сервера
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
error:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
/promocode/getList:
|
/promocode/getList:
|
||||||
post:
|
post:
|
||||||
summary: Получить список промокодов
|
operationId: GetList
|
||||||
|
summary: Получить список промокодов с пагинацией
|
||||||
|
tags:
|
||||||
|
- promocode
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
@ -183,19 +145,15 @@ paths:
|
|||||||
$ref: '#/components/schemas/GetPromoCodesListResp'
|
$ref: '#/components/schemas/GetPromoCodesListResp'
|
||||||
'400':
|
'400':
|
||||||
description: Неверный запрос из-за невалидных данных
|
description: Неверный запрос из-за невалидных данных
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
error:
|
|
||||||
type: string
|
|
||||||
'500':
|
'500':
|
||||||
description: Внутренняя ошибка сервера
|
description: Внутренняя ошибка сервера
|
||||||
|
|
||||||
/promocode/activate:
|
/promocode/activate:
|
||||||
post:
|
post:
|
||||||
|
operationId: Activate
|
||||||
summary: Активировать промокод
|
summary: Активировать промокод
|
||||||
|
tags:
|
||||||
|
- promocode
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
@ -210,7 +168,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/ActivateResp'
|
$ref: '#/components/schemas/ActivateResp'
|
||||||
'400':
|
'400':
|
||||||
description: Невалидный запрос или отсутствует обязательное поле codeword
|
description: Невалидный запрос или отсутствует обязательное поле codeword или fastLink
|
||||||
'404':
|
'404':
|
||||||
description: Промокод не найден
|
description: Промокод не найден
|
||||||
'500':
|
'500':
|
||||||
@ -218,7 +176,10 @@ paths:
|
|||||||
|
|
||||||
/promocode/{promocodeID}:
|
/promocode/{promocodeID}:
|
||||||
delete:
|
delete:
|
||||||
|
operationId: Delete
|
||||||
summary: Мягко удалить промокод по его id
|
summary: Мягко удалить промокод по его id
|
||||||
|
tags:
|
||||||
|
- promocode
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
name: promocodeID
|
name: promocodeID
|
||||||
@ -231,39 +192,81 @@ paths:
|
|||||||
description: Промокод успешно помечен как удаленный
|
description: Промокод успешно помечен как удаленный
|
||||||
'400':
|
'400':
|
||||||
description: Неверный запрос, отсутствует идентификатор промокода
|
description: Неверный запрос, отсутствует идентификатор промокода
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
error:
|
|
||||||
type: string
|
|
||||||
'404':
|
'404':
|
||||||
description: Промокод не найден
|
description: Промокод не найден
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
error:
|
|
||||||
type: string
|
|
||||||
'500':
|
'500':
|
||||||
description: Внутренняя ошибка сервера
|
description: Внутренняя ошибка сервера
|
||||||
|
|
||||||
|
/promocode/fastlink:
|
||||||
|
post:
|
||||||
|
operationId: CreateFastLink
|
||||||
|
summary: Создать быструю ссылку для промокода
|
||||||
|
tags:
|
||||||
|
- promocode
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CreateFastLinkReq'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Быстрая ссылка для промокода успешно создана
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: object
|
$ref: '#/components/schemas/CreateFastLinkResp'
|
||||||
properties:
|
'400':
|
||||||
error:
|
description: Неверный запрос, отсутствует идентификатор промокода
|
||||||
type: string
|
'404':
|
||||||
|
description: Промокод не найден
|
||||||
|
'500':
|
||||||
|
description: Внутренняя ошибка сервера
|
||||||
|
|
||||||
|
/promocode/stats:
|
||||||
|
get:
|
||||||
|
operationId: GetStats
|
||||||
|
summary: Получить статистику промокода
|
||||||
|
tags:
|
||||||
|
- stats
|
||||||
|
description: Идентификатор промокода
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PromoCodeStatsReq'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Статистика промокода успешно получена
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PromoCodeStatsResp'
|
||||||
|
'400':
|
||||||
|
description: Неверный запрос
|
||||||
|
'500':
|
||||||
|
description: Внутренняя ошибка сервера
|
||||||
|
|
||||||
|
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
PromoCodeRequest:
|
RecoveryReq:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- email
|
||||||
|
properties:
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
description: Электронная почта, на которую нужно отправить инструкции по восстановлению
|
||||||
|
redirectionURL:
|
||||||
|
type: string
|
||||||
|
description: URL-адрес, на который перенаправляется пользователь
|
||||||
|
PromoCodeStatsReq:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
codeword:
|
promoCodeID:
|
||||||
type: string
|
type: string
|
||||||
description: Кодовое слово, которое должен ввести пользователь
|
description: Кодовое слово, которое должен ввести пользователь
|
||||||
description:
|
description:
|
||||||
@ -311,61 +314,30 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
description: ID созданного промокода
|
description: Идентификатор промокода
|
||||||
codeword:
|
usageCount:
|
||||||
type: string
|
|
||||||
description: Кодовое слово промокода
|
|
||||||
description:
|
|
||||||
type: string
|
|
||||||
description: Описание промокода
|
|
||||||
greetings:
|
|
||||||
type: string
|
|
||||||
description: Текст, который будет отправлен пользователю в ответ на активацию кода
|
|
||||||
dueTo:
|
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
description: Количество использований промокода
|
||||||
description: Временная метка окончания активации кода
|
usageMap:
|
||||||
activationCount:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
description: Лимит активации кода
|
|
||||||
bonus:
|
|
||||||
type: object
|
type: object
|
||||||
properties:
|
description: Карта использования промокода
|
||||||
privilege:
|
additionalProperties:
|
||||||
type: object
|
type: array
|
||||||
properties:
|
items:
|
||||||
privilegeID:
|
$ref: '#/components/schemas/Usage'
|
||||||
type: string
|
|
||||||
description: Идентификатор привилегии, которую необходимо предоставить
|
Usage:
|
||||||
amount:
|
type: object
|
||||||
type: integer
|
properties:
|
||||||
format: uint64
|
key:
|
||||||
description: Размер привилегии
|
type: string
|
||||||
discount:
|
description: fastlink или codeword в зависимости от того что применялось
|
||||||
type: object
|
time:
|
||||||
properties:
|
|
||||||
layer:
|
|
||||||
type: integer
|
|
||||||
factor:
|
|
||||||
type: number
|
|
||||||
target:
|
|
||||||
type: string
|
|
||||||
threshold:
|
|
||||||
type: integer
|
|
||||||
description: Информация о бонусах
|
|
||||||
outdated:
|
|
||||||
type: boolean
|
|
||||||
offLimit:
|
|
||||||
type: boolean
|
|
||||||
delete:
|
|
||||||
type: boolean
|
|
||||||
createdAt:
|
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
description: Время создания промокода
|
description: Время использования промокода
|
||||||
|
|
||||||
ReqEditPromoCode:
|
CreateFastLinkReq:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
@ -430,27 +402,12 @@ components:
|
|||||||
filter:
|
filter:
|
||||||
$ref: '#/components/schemas/GetPromoCodesListReqFilter'
|
$ref: '#/components/schemas/GetPromoCodesListReqFilter'
|
||||||
|
|
||||||
GetPromoCodesListReqFilter:
|
CreateFastLinkResp:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
text:
|
fastlink:
|
||||||
type: string
|
type: string
|
||||||
description: Полнотекстовый поиск по полям Codeword, Description, Greetings
|
description: Быстрая ссылка для активации промокода
|
||||||
active:
|
|
||||||
type: boolean
|
|
||||||
description: Если true, выбираются записи, где delete, outdated и offLimit равны false
|
|
||||||
|
|
||||||
GetPromoCodesListResp:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
count:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
description: Общее количество промокодов в выборке
|
|
||||||
items:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/PromoCodeResponse'
|
|
||||||
|
|
||||||
ActivateReq:
|
ActivateReq:
|
||||||
type: object
|
type: object
|
||||||
@ -459,11 +416,188 @@ components:
|
|||||||
properties:
|
properties:
|
||||||
codeword:
|
codeword:
|
||||||
type: string
|
type: string
|
||||||
description: Кодовое слово промокода, которое требуется активировать
|
description: Кодовое слово для активации промокода
|
||||||
|
fastLink:
|
||||||
|
type: string
|
||||||
|
description: Быстрая ссылка для активации промокода
|
||||||
|
|
||||||
ActivateResp:
|
ActivateResp:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
greetings:
|
greetings:
|
||||||
type: string
|
type: string
|
||||||
description: Поле из активированного промокода
|
description: Слово успешной активации промокода
|
||||||
|
|
||||||
|
GetPromoCodesListReq:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- page
|
||||||
|
- limit
|
||||||
|
properties:
|
||||||
|
page:
|
||||||
|
type: integer
|
||||||
|
description: Номер страницы
|
||||||
|
limit:
|
||||||
|
type: integer
|
||||||
|
description: Максимальное количество элементов на странице
|
||||||
|
filter:
|
||||||
|
$ref: '#/components/schemas/Filter'
|
||||||
|
|
||||||
|
Filter:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
text:
|
||||||
|
type: string
|
||||||
|
description: Текстовый фильтр для поиска промокодов
|
||||||
|
active:
|
||||||
|
type: boolean
|
||||||
|
description: Флаг для фильтрации активных промокодов
|
||||||
|
|
||||||
|
GetPromoCodesListResp:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
count:
|
||||||
|
type: integer
|
||||||
|
description: Общее количество промокодов
|
||||||
|
items:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/PromoCode'
|
||||||
|
|
||||||
|
PromoCode:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
description: Идентификатор промокода
|
||||||
|
codeword:
|
||||||
|
type: string
|
||||||
|
description: Кодовое слово для активации промокода
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
description: Описание промокода
|
||||||
|
greetings:
|
||||||
|
type: string
|
||||||
|
description: Приветственное сообщение после активации промокода
|
||||||
|
dueTo:
|
||||||
|
type: integer
|
||||||
|
description: Дата истечения действия промокода в формате Unix time
|
||||||
|
activationCount:
|
||||||
|
type: integer
|
||||||
|
description: Количество активаций промокода
|
||||||
|
bonus:
|
||||||
|
type: object
|
||||||
|
description: Бонус, предоставляемый с промокодом
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Bonus'
|
||||||
|
outdated:
|
||||||
|
type: boolean
|
||||||
|
description: Флаг
|
||||||
|
offLimit:
|
||||||
|
type: boolean
|
||||||
|
description: Флаг
|
||||||
|
delete:
|
||||||
|
type: boolean
|
||||||
|
description: Флаг
|
||||||
|
createdAt:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: Дата и время создания промокода
|
||||||
|
fastLinks:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
description: Список быстрых ссылок для активации промокода
|
||||||
|
|
||||||
|
EditPromoCodeReq:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
ID:
|
||||||
|
type: string
|
||||||
|
description: Идентификатор промокода, который требуется обновить
|
||||||
|
Description:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
description: Описание промокода
|
||||||
|
Greetings:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
description: Приветственное сообщение после активации промокода
|
||||||
|
DueTo:
|
||||||
|
type: integer
|
||||||
|
nullable: true
|
||||||
|
description: Дата окончания промокода в формате Unix time
|
||||||
|
ActivationCount:
|
||||||
|
type: integer
|
||||||
|
nullable: true
|
||||||
|
description: Количество активаций промокода
|
||||||
|
Delete:
|
||||||
|
type: boolean
|
||||||
|
nullable: true
|
||||||
|
description: Флаг удаления промокода
|
||||||
|
PromoCodeReq:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
codeword:
|
||||||
|
type: string
|
||||||
|
description: Кодовое слово для активации промокода
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
description: Описание промокода
|
||||||
|
greetings:
|
||||||
|
type: string
|
||||||
|
description: Приветственное сообщение после активации промокода
|
||||||
|
dueTo:
|
||||||
|
type: integer
|
||||||
|
description: Дата истечения действия промокода в формате Unix time
|
||||||
|
activationCount:
|
||||||
|
type: integer
|
||||||
|
description: Количество активаций промокода
|
||||||
|
bonus:
|
||||||
|
type: object
|
||||||
|
description: Бонус
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Bonus'
|
||||||
|
fastLinks:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
description: Список быстрых ссылок для активации промокода
|
||||||
|
|
||||||
|
Bonus:
|
||||||
|
type: object
|
||||||
|
description: Бонус
|
||||||
|
properties:
|
||||||
|
privilege:
|
||||||
|
$ref: '#/components/schemas/Privilege'
|
||||||
|
discount:
|
||||||
|
$ref: '#/components/schemas/Discount'
|
||||||
|
|
||||||
|
Privilege:
|
||||||
|
type: object
|
||||||
|
description: Привилегия
|
||||||
|
properties:
|
||||||
|
privilegeID:
|
||||||
|
type: string
|
||||||
|
description: Идентификатор привилегии
|
||||||
|
amount:
|
||||||
|
type: integer
|
||||||
|
description: Количество привилегии
|
||||||
|
|
||||||
|
Discount:
|
||||||
|
type: object
|
||||||
|
description: Скидка
|
||||||
|
properties:
|
||||||
|
layer:
|
||||||
|
type: integer
|
||||||
|
description: Уровень скидки
|
||||||
|
factor:
|
||||||
|
type: integer
|
||||||
|
description: Множитель скидки
|
||||||
|
target:
|
||||||
|
type: string
|
||||||
|
description: Цель скидки
|
||||||
|
threshold:
|
||||||
|
type: integer
|
||||||
|
description: Порог скидки
|
||||||
|
|
||||||
|
27
go.mod
27
go.mod
@ -6,27 +6,38 @@ require (
|
|||||||
github.com/caarlos0/env/v8 v8.0.0
|
github.com/caarlos0/env/v8 v8.0.0
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
github.com/gofiber/fiber/v2 v2.51.0
|
github.com/gofiber/fiber/v2 v2.51.0
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/pioz/faker v1.7.3
|
github.com/pioz/faker v1.7.3
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/rs/xid v1.5.0
|
||||||
|
github.com/stretchr/testify v1.8.4
|
||||||
|
github.com/twmb/franz-go v1.15.4
|
||||||
go.mongodb.org/mongo-driver v1.13.1
|
go.mongodb.org/mongo-driver v1.13.1
|
||||||
go.uber.org/zap v1.26.0
|
go.uber.org/zap v1.26.0
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac
|
||||||
|
google.golang.org/grpc v1.60.1
|
||||||
|
google.golang.org/protobuf v1.32.0
|
||||||
|
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240202120244-c4ef330cfe5d
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/golang/snappy v0.0.1 // indirect
|
github.com/golang/snappy v0.0.1 // indirect
|
||||||
github.com/google/uuid v1.4.0 // indirect
|
github.com/google/uuid v1.4.0 // indirect
|
||||||
github.com/klauspost/compress v1.16.7 // indirect
|
github.com/klauspost/compress v1.16.7 // indirect
|
||||||
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.19 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.7.0 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.50.0 // indirect
|
github.com/valyala/fasthttp v1.50.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
@ -35,9 +46,13 @@ require (
|
|||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||||
go.uber.org/multierr v1.10.0 // indirect
|
go.uber.org/multierr v1.10.0 // indirect
|
||||||
golang.org/x/crypto v0.7.0 // indirect
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
golang.org/x/net v0.17.0 // indirect
|
||||||
golang.org/x/sys v0.14.0 // indirect
|
golang.org/x/sync v0.4.0 // indirect
|
||||||
golang.org/x/text v0.8.0 // indirect
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
|
golang.org/x/text v0.14.0 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
72
go.sum
72
go.sum
@ -2,8 +2,8 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/
|
|||||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0=
|
github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0=
|
||||||
github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo=
|
github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -15,10 +15,17 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC
|
|||||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||||
github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ=
|
github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ=
|
||||||
github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U=
|
github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
@ -26,6 +33,12 @@ github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwA
|
|||||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
|
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
|
||||||
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
@ -41,20 +54,24 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
|||||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.19 h1:tYLzDnjDXh9qIxSTKHwXwOYmm9d887Y7Y1ZkyXYHAN4=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.19/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pioz/faker v1.7.3 h1:Tez8Emuq0UN+/d6mo3a9m/9ZZ/zdfJk0c5RtRatrceM=
|
github.com/pioz/faker v1.7.3 h1:Tez8Emuq0UN+/d6mo3a9m/9ZZ/zdfJk0c5RtRatrceM=
|
||||||
github.com/pioz/faker v1.7.3/go.mod h1:xSpay5w/oz1a6+ww0M3vfpe40pSIykeUPeWEc3TvVlc=
|
github.com/pioz/faker v1.7.3/go.mod h1:xSpay5w/oz1a6+ww0M3vfpe40pSIykeUPeWEc3TvVlc=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||||
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/twmb/franz-go v1.15.4 h1:qBCkHaiutetnrXjAUWA99D9FEcZVMt2AYwkH3vWEQTw=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/twmb/franz-go v1.15.4/go.mod h1:rC18hqNmfo8TMc1kz7CQmHL74PLNF8KVvhflxiiJZCU=
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.7.0 h1:a457IbvezYfA5UkiBvyV3zj0Is3y1i8EJgqjJYoij2E=
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.7.0/go.mod h1:se9Mjdt0Nwzc9lnjJ0HyDtLyBnaBDAd7pCje47OhSyw=
|
||||||
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/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
|
github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
|
||||||
@ -81,18 +98,19 @@ 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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||||
|
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -101,8 +119,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@ -111,16 +129,28 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg=
|
||||||
|
google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac h1:OZkkudMUu9LVQMCoRUbI/1p5VCo9BOrlvkqMvWtqa6s=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
|
||||||
|
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
|
||||||
|
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||||
|
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
@ -128,3 +158,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240202120244-c4ef330cfe5d h1:gbaDt35HMDqOK84WYmDIlXMI7rstUcRqNttaT6Kx1do=
|
||||||
|
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240202120244-c4ef330cfe5d/go.mod h1:lTmpjry+8evVkXWbEC+WMOELcFkRD1lFMc7J09mOndM=
|
||||||
|
@ -9,13 +9,28 @@ import (
|
|||||||
"codeword/internal/services"
|
"codeword/internal/services"
|
||||||
"codeword/internal/worker/purge_worker"
|
"codeword/internal/worker/purge_worker"
|
||||||
"codeword/internal/worker/recovery_worker"
|
"codeword/internal/worker/recovery_worker"
|
||||||
|
"codeword/pkg/closer"
|
||||||
|
"codeword/utils"
|
||||||
"context"
|
"context"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"errors"
|
||||||
|
"github.com/twmb/franz-go/pkg/kgo"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
|
func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
|
||||||
logger.Info("Запуск приложения", zap.String("AppName", cfg.AppName))
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
logger.Error("Recovered from a panic", zap.Any("error", r))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
logger.Info("Starting application", zap.String("AppName", cfg.AppName))
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
shutdownGroup := closer.NewCloserGroup()
|
||||||
|
|
||||||
mdb, err := initialize.MongoDB(ctx, cfg)
|
mdb, err := initialize.MongoDB(ctx, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -23,10 +38,47 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = initialize.InitDatabaseIndexes(ctx, mdb, logger); err != nil {
|
||||||
|
logger.Error("Failed to initialize db indexes", zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
kafkaTariffClient, err := kgo.NewClient(
|
||||||
|
kgo.SeedBrokers(cfg.KafkaBrokers),
|
||||||
|
kgo.ConsumeResetOffset(kgo.NewOffset().AtStart()),
|
||||||
|
kgo.DefaultProduceTopic(cfg.KafkaTopic),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = kafkaTariffClient.Ping(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
discountRpcClient, err := initialize.DiscountGRPCClient(cfg.DiscountServiceAddress)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to connect to discount service", zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
brokers := initialize.NewBrokers(initialize.BrokersDeps{
|
||||||
|
Logger: logger,
|
||||||
|
TariffClient: kafkaTariffClient,
|
||||||
|
Topic: cfg.KafkaTopic,
|
||||||
|
})
|
||||||
|
|
||||||
rdb, err := initialize.Redis(ctx, cfg)
|
rdb, err := initialize.Redis(ctx, cfg)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to connect to redis db", zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
encrypt := initialize.Encrypt(cfg)
|
encrypt := initialize.Encrypt(cfg)
|
||||||
|
|
||||||
promoCodeRepo := repository.NewPromoCodeRepository(mdb.Collection("promoCodes"))
|
promoCodeRepo := repository.NewPromoCodeRepository(mdb.Collection("promoCodes"))
|
||||||
|
statsRepo := repository.NewStatsRepository(repository.Deps{Rdb: nil, Mdb: mdb.Collection("promoStats")})
|
||||||
codewordRepo := repository.NewCodewordRepository(repository.Deps{Rdb: rdb, Mdb: mdb.Collection("codeword")})
|
codewordRepo := repository.NewCodewordRepository(repository.Deps{Rdb: rdb, Mdb: mdb.Collection("codeword")})
|
||||||
userRepo := repository.NewUserRepository(repository.Deps{Rdb: nil, Mdb: mdb.Collection("users")})
|
userRepo := repository.NewUserRepository(repository.Deps{Rdb: nil, Mdb: mdb.Collection("users")})
|
||||||
|
|
||||||
@ -42,12 +94,18 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
promoService := services.NewPromoCodeService(services.PromoDeps{
|
promoService := services.NewPromoCodeService(services.PromoDeps{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
PromoCodeRepo: promoCodeRepo,
|
PromoCodeRepo: promoCodeRepo,
|
||||||
|
StatsRepo: statsRepo,
|
||||||
|
Kafka: brokers.TariffProducer,
|
||||||
|
DiscountClient: discountRpcClient,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
jwtUtil := utils.NewJWT(&cfg)
|
||||||
|
authMiddleware := utils.NewAuthenticator(jwtUtil)
|
||||||
|
|
||||||
recoveryController := recovery.NewRecoveryController(logger, recoveryService, cfg.DefaultRedirectionURL)
|
recoveryController := recovery.NewRecoveryController(logger, recoveryService, cfg.DefaultRedirectionURL)
|
||||||
promoCodeController := promocode.NewPromoCodeController(logger, promoService)
|
promoCodeController := promocode.NewPromoCodeController(promocode.Deps{Logger: logger, PromoCodeService: promoService, AuthMiddleware: authMiddleware})
|
||||||
|
|
||||||
recoveryWC := recovery_worker.NewRecoveryWC(recovery_worker.Deps{
|
recoveryWC := recovery_worker.NewRecoveryWC(recovery_worker.Deps{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
@ -56,7 +114,7 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
|
|||||||
Mongo: mdb.Collection("codeword"),
|
Mongo: mdb.Collection("codeword"),
|
||||||
})
|
})
|
||||||
|
|
||||||
purgeWC := purge_worker.NewRecoveryWC(purge_worker.Deps{
|
purgeWC := purge_worker.NewPurgeWC(purge_worker.Deps{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
Mongo: mdb.Collection("codeword"),
|
Mongo: mdb.Collection("codeword"),
|
||||||
})
|
})
|
||||||
@ -65,52 +123,37 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
|
|||||||
go purgeWC.Start(ctx)
|
go purgeWC.Start(ctx)
|
||||||
|
|
||||||
server := httpserver.NewServer(httpserver.ServerConfig{
|
server := httpserver.NewServer(httpserver.ServerConfig{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
RecoveryController: recoveryController,
|
Controllers: []httpserver.Controller{recoveryController, promoCodeController},
|
||||||
PromoCodeController: promoCodeController,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := server.Start(cfg.HTTPHost + ":" + cfg.HTTPPort); err != nil {
|
if err := server.Start(cfg.HTTPHost + ":" + cfg.HTTPPort); err != nil {
|
||||||
logger.Error("Server startup error", zap.Error(err))
|
logger.Error("Server startup error", zap.Error(err))
|
||||||
|
cancel()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
server.ListRoutes()
|
||||||
|
|
||||||
|
shutdownGroup.Add(closer.CloserFunc(server.Shutdown))
|
||||||
|
shutdownGroup.Add(closer.CloserFunc(mdb.Client().Disconnect))
|
||||||
|
shutdownGroup.Add(closer.CloserFunc(recoveryWC.Stop))
|
||||||
|
shutdownGroup.Add(closer.CloserFunc(purgeWC.Stop))
|
||||||
|
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
|
||||||
if err := shutdownApp(ctx, server, mdb, logger); err != nil {
|
timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
return err
|
defer timeoutCancel()
|
||||||
}
|
if err := shutdownGroup.Call(timeoutCtx); err != nil {
|
||||||
logger.Info("The application has stopped")
|
if errors.Is(err, context.DeadlineExceeded) {
|
||||||
return nil
|
logger.Error("Shutdown timed out", zap.Error(err))
|
||||||
}
|
} else {
|
||||||
|
logger.Error("Failed to shutdown services gracefully", zap.Error(err))
|
||||||
// TODO возможно стоит вынести в отдельные файлы или отказаться от разделения на отдельные методы
|
}
|
||||||
|
|
||||||
func shutdownApp(ctx context.Context, server *httpserver.Server, mdb *mongo.Database, logger *zap.Logger) error {
|
|
||||||
if err := shutdownHTTPServer(ctx, server, logger); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := shutdownMongoDB(ctx, mdb, logger); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func shutdownHTTPServer(ctx context.Context, server *httpserver.Server, logger *zap.Logger) error {
|
|
||||||
if err := server.Shutdown(ctx); err != nil {
|
|
||||||
logger.Error("Error stopping HTTP server", zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func shutdownMongoDB(ctx context.Context, mdb *mongo.Database, logger *zap.Logger) error {
|
|
||||||
if err := mdb.Client().Disconnect(ctx); err != nil {
|
|
||||||
logger.Error("Error when closing MongoDB connection", zap.Error(err))
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Info("Application has stopped")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -9,15 +9,23 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Deps struct {
|
||||||
|
Logger *zap.Logger
|
||||||
|
PromoCodeService *services.PromoCodeService
|
||||||
|
AuthMiddleware func(*fiber.Ctx) error
|
||||||
|
}
|
||||||
|
|
||||||
type PromoCodeController struct {
|
type PromoCodeController struct {
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
promoCodeService *services.PromoCodeService
|
promoCodeService *services.PromoCodeService
|
||||||
|
authMiddleware func(*fiber.Ctx) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPromoCodeController(logger *zap.Logger, promoCodeService *services.PromoCodeService) *PromoCodeController {
|
func NewPromoCodeController(deps Deps) *PromoCodeController {
|
||||||
return &PromoCodeController{
|
return &PromoCodeController{
|
||||||
logger: logger,
|
logger: deps.Logger,
|
||||||
promoCodeService: promoCodeService,
|
promoCodeService: deps.PromoCodeService,
|
||||||
|
authMiddleware: deps.AuthMiddleware,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,6 +35,10 @@ func (p *PromoCodeController) CreatePromoCode(c *fiber.Ctx) error {
|
|||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if req.Codeword == "" {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "codeword is required"})
|
||||||
|
}
|
||||||
|
|
||||||
createdPromoCode, err := p.promoCodeService.CreatePromoCode(c.Context(), &req)
|
createdPromoCode, err := p.promoCodeService.CreatePromoCode(c.Context(), &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.logger.Error("Failed to create promocode", zap.Error(err))
|
p.logger.Error("Failed to create promocode", zap.Error(err))
|
||||||
@ -38,7 +50,7 @@ func (p *PromoCodeController) CreatePromoCode(c *fiber.Ctx) error {
|
|||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Status(fiber.StatusCreated).JSON(createdPromoCode)
|
return c.Status(fiber.StatusOK).JSON(createdPromoCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PromoCodeController) EditPromoCode(c *fiber.Ctx) error {
|
func (p *PromoCodeController) EditPromoCode(c *fiber.Ctx) error {
|
||||||
@ -84,30 +96,44 @@ func (p *PromoCodeController) GetList(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *PromoCodeController) Activate(c *fiber.Ctx) error {
|
func (p *PromoCodeController) Activate(c *fiber.Ctx) error {
|
||||||
|
err := p.authMiddleware(c)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err})
|
||||||
|
}
|
||||||
|
|
||||||
|
userID := c.Locals(models.AuthJWTDecodedUserIDKey).(string)
|
||||||
|
if userID == "" {
|
||||||
|
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "failed to get jwt payload"})
|
||||||
|
}
|
||||||
|
|
||||||
var req models.ActivateReq
|
var req models.ActivateReq
|
||||||
if err := c.BodyParser(&req); err != nil {
|
if err := c.BodyParser(&req); err != nil {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Codeword == "" {
|
if req.Codeword == "" && req.FastLink == "" {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "codeword is required"})
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "codeword or fastlink is required"})
|
||||||
}
|
}
|
||||||
|
|
||||||
greetings, err := p.promoCodeService.ActivatePromo(c.Context(), &req)
|
greetings, err := p.promoCodeService.ActivatePromo(c.Context(), &req, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.logger.Error("Failed to activate promocode", zap.Error(err))
|
p.logger.Error("Failed to activate promocode", zap.Error(err))
|
||||||
|
|
||||||
if errors.Is(err, repository.ErrPromoCodeNotFound) {
|
switch {
|
||||||
|
case errors.Is(err, repository.ErrPromoCodeNotFound):
|
||||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"})
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"})
|
||||||
|
case errors.Is(err, repository.ErrPromoCodeAlreadyActivated):
|
||||||
|
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "PromoCode already activated"})
|
||||||
|
case errors.Is(err, repository.ErrPromoCodeExpired):
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
|
||||||
|
case errors.Is(err, repository.ErrPromoCodeExhausted):
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode exhausted"})
|
||||||
|
default:
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := models.ActivateResp{
|
return c.Status(fiber.StatusOK).JSON(models.ActivateResp{Greetings: greetings})
|
||||||
Greetings: greetings,
|
|
||||||
}
|
|
||||||
return c.Status(fiber.StatusOK).JSON(resp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PromoCodeController) Delete(c *fiber.Ctx) error {
|
func (p *PromoCodeController) Delete(c *fiber.Ctx) error {
|
||||||
@ -130,3 +156,50 @@ func (p *PromoCodeController) Delete(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PromoCodeController) CreateFastLink(c *fiber.Ctx) error {
|
||||||
|
var req struct {
|
||||||
|
PromoCodeID string `json:"id"`
|
||||||
|
}
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.PromoCodeID == "" {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode ID is required"})
|
||||||
|
}
|
||||||
|
|
||||||
|
fastLink, err := p.promoCodeService.CreateFastLink(c.Context(), req.PromoCodeID)
|
||||||
|
if err != nil {
|
||||||
|
p.logger.Error("Failed to create fastlink", zap.Error(err))
|
||||||
|
|
||||||
|
if errors.Is(err, repository.ErrPromoCodeNotFound) {
|
||||||
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusCreated).JSON(fiber.Map{"fastlink": fastLink})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PromoCodeController) GetStats(c *fiber.Ctx) error {
|
||||||
|
var req struct {
|
||||||
|
PromoCodeID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.PromoCodeID == "" {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode ID is required"})
|
||||||
|
}
|
||||||
|
|
||||||
|
promoStats, err := p.promoCodeService.GetStats(c.Context(), req.PromoCodeID)
|
||||||
|
if err != nil {
|
||||||
|
p.logger.Error("Failed getting promo stats", zap.Error(err))
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||||
|
}
|
||||||
|
return c.Status(fiber.StatusOK).JSON(promoStats)
|
||||||
|
}
|
||||||
|
18
internal/controller/promocode/route.go
Normal file
18
internal/controller/promocode/route.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package promocode
|
||||||
|
|
||||||
|
import "github.com/gofiber/fiber/v2"
|
||||||
|
|
||||||
|
func (p *PromoCodeController) Register(router fiber.Router) {
|
||||||
|
router.Post("/create", p.CreatePromoCode)
|
||||||
|
router.Put("/edit", p.EditPromoCode)
|
||||||
|
router.Post("/getList", p.GetList)
|
||||||
|
router.Post("/activate", p.Activate)
|
||||||
|
router.Delete("/:promocodeID", p.Delete)
|
||||||
|
router.Post("/fastlink", p.CreateFastLink)
|
||||||
|
router.Get("/stats", p.GetStats)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PromoCodeController) Name() string {
|
||||||
|
return "promocode"
|
||||||
|
}
|
@ -2,8 +2,11 @@ package recovery
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"codeword/internal/models"
|
"codeword/internal/models"
|
||||||
|
"codeword/internal/repository"
|
||||||
"codeword/internal/services"
|
"codeword/internal/services"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"time"
|
"time"
|
||||||
@ -23,23 +26,44 @@ func NewRecoveryController(logger *zap.Logger, service *services.RecoveryService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RecoveryController) HandlePingDB(c *fiber.Ctx) error {
|
func (r *RecoveryController) HandleLiveness(c *fiber.Ctx) error {
|
||||||
return r.service.Ping(c.Context())
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleRecoveryRequest обрабатывает запрос на восстановление пароля
|
func (r *RecoveryController) HandlePingDB(c *fiber.Ctx) error {
|
||||||
func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
|
startTime := time.Now()
|
||||||
email := c.FormValue("email")
|
if err := r.service.Ping(c.Context()); err != nil {
|
||||||
referralURL := c.Get("Referrer")
|
r.logger.Error("Failed to ping the database", zap.Error(err))
|
||||||
redirectionURL := c.FormValue("RedirectionURL")
|
return c.Status(fiber.StatusServiceUnavailable).SendString("DB ping failed")
|
||||||
|
}
|
||||||
|
duration := time.Since(startTime)
|
||||||
|
|
||||||
if redirectionURL == "" && referralURL != "" {
|
durationMillis := duration.Milliseconds()
|
||||||
redirectionURL = referralURL
|
responseMessage := fmt.Sprintf("DB ping success - Time taken: %d ms", durationMillis)
|
||||||
} else if redirectionURL == "" {
|
|
||||||
redirectionURL = r.defaultURL
|
return c.Status(fiber.StatusOK).SendString(responseMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
|
||||||
|
var req models.RecoveryRequest
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
r.logger.Error("Failed to parse recovery request", zap.Error(err))
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Bad Request"})
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := r.service.FindUserByEmail(c.Context(), email)
|
if req.Email == "" {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "email is required"})
|
||||||
|
}
|
||||||
|
|
||||||
|
referralURL := c.Get("Referrer")
|
||||||
|
|
||||||
|
if req.RedirectionURL == "" && referralURL != "" {
|
||||||
|
req.RedirectionURL = referralURL
|
||||||
|
} else if req.RedirectionURL == "" {
|
||||||
|
req.RedirectionURL = r.defaultURL
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := r.service.FindUserByEmail(c.Context(), req.Email)
|
||||||
if err != nil || user == nil {
|
if err != nil || user == nil {
|
||||||
r.logger.Error("Failed to find user by email", zap.Error(err))
|
r.logger.Error("Failed to find user by email", zap.Error(err))
|
||||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||||
@ -51,7 +75,11 @@ func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
|
|||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
signUrl := redirectionURL
|
signUrl := redirectionURL
|
||||||
|
=======
|
||||||
|
signUrl := req.RedirectionURL
|
||||||
|
>>>>>>> dev
|
||||||
sign := base64.URLEncoding.EncodeToString(key)
|
sign := base64.URLEncoding.EncodeToString(key)
|
||||||
|
|
||||||
id, err := r.service.StoreRecoveryRecord(c.Context(), models.StoreRecDeps{
|
id, err := r.service.StoreRecoveryRecord(c.Context(), models.StoreRecDeps{
|
||||||
@ -67,22 +95,24 @@ func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
signWithID := sign + id // подпись с id записи
|
signWithID := sign + id // подпись с id записи
|
||||||
|
|
||||||
err = r.service.RecoveryEmailTask(c.Context(), models.RecEmailDeps{UserID: user.ID.Hex(), Email: email, SignWithID: signWithID, ID: id})
|
err = r.service.RecoveryEmailTask(c.Context(), models.RecEmailDeps{UserID: user.ID.Hex(), Email: req.Email, SignWithID: signWithID, ID: id})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error("Failed to send recovery email", zap.Error(err))
|
r.logger.Error("Failed to send recovery email", zap.Error(err))
|
||||||
|
|
||||||
|
if errors.Is(err, repository.ErrAlreadyReported) {
|
||||||
|
return c.Status(fiber.StatusAlreadyReported).JSON(fiber.Map{"error": "already reported"})
|
||||||
|
}
|
||||||
|
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Status(fiber.StatusOK).JSON(fiber.Map{"sent": email})
|
return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Recovery email sent successfully"})
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo тут скорее всего помимо подписи будет передаваться еще что-то, например email пользователя от фронта для поиска в бд
|
|
||||||
|
|
||||||
// HandleRecoveryLink обрабатывает ссылку восстановления и обменивает ее на токены
|
|
||||||
func (r *RecoveryController) HandleRecoveryLink(c *fiber.Ctx) error {
|
func (r *RecoveryController) HandleRecoveryLink(c *fiber.Ctx) error {
|
||||||
key := c.Params("sign")
|
sign := c.Params("sign")
|
||||||
|
|
||||||
record, err := r.service.GetRecoveryRecord(c.Context(), key)
|
record, err := r.service.GetRecoveryRecord(c.Context(), sign)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error("Recovery link expired", zap.String("signature", key))
|
r.logger.Error("Recovery link expired", zap.String("signature", key))
|
||||||
return c.Redirect("https://shub.pena.digital/recover/expired")
|
return c.Redirect("https://shub.pena.digital/recover/expired")
|
||||||
@ -107,5 +137,14 @@ func (r *RecoveryController) HandleRecoveryLink(c *fiber.Ctx) error {
|
|||||||
HTTPOnly: true,
|
HTTPOnly: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
return c.Redirect(record.SignUrl+"?auth="+tokens["accessToken"])
|
c.Cookie(&fiber.Cookie{
|
||||||
|
Name: "refreshToken",
|
||||||
|
Value: tokens["refreshToken"],
|
||||||
|
Domain: ".pena.digital",
|
||||||
|
Expires: time.Now().Add(30 * 24 * time.Hour),
|
||||||
|
Secure: true,
|
||||||
|
HTTPOnly: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return c.Redirect(record.SignUrl + "?auth=" + tokens["accessToken"])
|
||||||
}
|
}
|
||||||
|
14
internal/controller/recovery/route.go
Normal file
14
internal/controller/recovery/route.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package recovery
|
||||||
|
|
||||||
|
import "github.com/gofiber/fiber/v2"
|
||||||
|
|
||||||
|
func (r *RecoveryController) Register(router fiber.Router) {
|
||||||
|
router.Get("/liveness", r.HandleLiveness)
|
||||||
|
router.Get("/readiness", r.HandlePingDB)
|
||||||
|
router.Post("/recover", r.HandleRecoveryRequest)
|
||||||
|
router.Get("/recover/:sign", r.HandleRecoveryLink)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RecoveryController) Name() string {
|
||||||
|
return ""
|
||||||
|
}
|
@ -7,31 +7,38 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
AppName string `env:"APP_NAME" envDefault:"codeword"`
|
AppName string `env:"APP_NAME" envDefault:"codeword"`
|
||||||
HTTPHost string `env:"HTTP_HOST" envDefault:"localhost"`
|
HTTPHost string `env:"HTTP_HOST" envDefault:"localhost"`
|
||||||
HTTPPort string `env:"HTTP_PORT" envDefault:"3000"`
|
HTTPPort string `env:"HTTP_PORT" envDefault:"3000"`
|
||||||
MongoHost string `env:"MONGO_HOST" envDefault:"127.0.0.1"`
|
MongoHost string `env:"MONGO_HOST" envDefault:"127.0.0.1"`
|
||||||
MongoPort string `env:"MONGO_PORT" envDefault:"27020"`
|
MongoPort string `env:"MONGO_PORT" envDefault:"27020"`
|
||||||
MongoUser string `env:"MONGO_USER" envDefault:"test"`
|
MongoUser string `env:"MONGO_USER" envDefault:"test"`
|
||||||
MongoPassword string `env:"MONGO_PASSWORD" envDefault:"test"`
|
MongoPassword string `env:"MONGO_PASSWORD" envDefault:"test"`
|
||||||
MongoDatabase string `env:"MONGO_DB" envDefault:"admin"`
|
MongoDatabase string `env:"MONGO_DB" envDefault:"admin"`
|
||||||
MongoAuth string `env:"MONGO_AUTH" envDefault:"admin"`
|
MongoAuth string `env:"MONGO_AUTH" envDefault:"admin"`
|
||||||
PublicCurveKey string `env:"PUBLIC_CURVE_KEY"`
|
PublicCurveKey string `env:"PUBLIC_CURVE_KEY"`
|
||||||
PrivateCurveKey string `env:"PRIVATE_CURVE_KEY"`
|
PrivateCurveKey string `env:"PRIVATE_CURVE_KEY"`
|
||||||
SignSecret string `env:"SIGN_SECRET"`
|
SignSecret string `env:"SIGN_SECRET"`
|
||||||
RedisAddr string `env:"REDIS_ADDR" envDefault:"localhost:6379"`
|
RedisAddr string `env:"REDIS_ADDR" envDefault:"localhost:6379"`
|
||||||
RedisPassword string `env:"REDIS_PASS" envDefault:"admin"`
|
RedisPassword string `env:"REDIS_PASS" envDefault:"admin"`
|
||||||
RedisDB int `env:"REDIS_DB" envDefault:"2"`
|
RedisDB int `env:"REDIS_DB" envDefault:"2"`
|
||||||
SmtpApiUrl string `env:"SMTP_API_URL"`
|
SmtpApiUrl string `env:"SMTP_API_URL"`
|
||||||
SmtpHost string `env:"SMTP_HOST"`
|
SmtpHost string `env:"SMTP_HOST"`
|
||||||
SmtpPort string `env:"SMTP_PORT"`
|
SmtpPort string `env:"SMTP_PORT"`
|
||||||
SmtpUsername string `env:"SMTP_UNAME"`
|
SmtpUsername string `env:"SMTP_UNAME"`
|
||||||
SmtpPassword string `env:"SMTP_PASS"`
|
SmtpPassword string `env:"SMTP_PASS"`
|
||||||
SmtpApiKey string `env:"SMTP_API_KEY"`
|
SmtpApiKey string `env:"SMTP_API_KEY"`
|
||||||
SmtpSender string `env:"SMTP_SENDER"`
|
SmtpSender string `env:"SMTP_SENDER"`
|
||||||
DefaultRedirectionURL string `env:"DEFAULT_REDIRECTION_URL"`
|
DefaultRedirectionURL string `env:"DEFAULT_REDIRECTION_URL"`
|
||||||
AuthURL string `env:"AUTH_EXCHANGE_URL"`
|
AuthURL string `env:"AUTH_EXCHANGE_URL"`
|
||||||
RecoveryUrl string `env:"RECOVER_URL"`
|
KafkaBrokers string `env:"KAFKA_BROKERS"`
|
||||||
|
KafkaTopic string `env:"KAFKA_TOPIC_TARIFF"`
|
||||||
|
DiscountServiceAddress string `env:"DISCOUNT_ADDRESS"`
|
||||||
|
RecoveryUrl string `env:"RECOVERY_URL"`
|
||||||
|
PrivateKey string `env:"JWT_PRIVATE_KEY"`
|
||||||
|
PublicKey string `env:"JWT_PUBLIC_KEY,required"`
|
||||||
|
Issuer string `env:"JWT_ISSUER,required"`
|
||||||
|
Audience string `env:"JWT_AUDIENCE,required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig() (*Config, error) {
|
func LoadConfig() (*Config, error) {
|
||||||
|
26
internal/initialize/grpc.go
Normal file
26
internal/initialize/grpc.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeword/internal/proto/discount"
|
||||||
|
"context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DiscountGRPCClient(address string) (discount.DiscountServiceClient, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
options := []grpc.DialOption{
|
||||||
|
grpc.WithInsecure(),
|
||||||
|
//grpc.WithBlock(),
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := grpc.DialContext(ctx, address, options...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
discountClient := discount.NewDiscountServiceClient(conn)
|
||||||
|
return discountClient, nil
|
||||||
|
}
|
27
internal/initialize/kafka.go
Normal file
27
internal/initialize/kafka.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeword/internal/kafka/tariff"
|
||||||
|
"github.com/twmb/franz-go/pkg/kgo"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BrokersDeps struct {
|
||||||
|
Logger *zap.Logger
|
||||||
|
TariffClient *kgo.Client
|
||||||
|
Topic string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Brokers struct {
|
||||||
|
TariffProducer *tariff.Producer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBrokers(deps BrokersDeps) *Brokers {
|
||||||
|
return &Brokers{
|
||||||
|
TariffProducer: tariff.NewProducer(tariff.ProducerDeps{
|
||||||
|
Logger: deps.Logger,
|
||||||
|
Client: deps.TariffClient,
|
||||||
|
Topic: deps.Topic,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
@ -1,36 +1,50 @@
|
|||||||
package initialize
|
package initialize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
mdb "codeword/pkg/mongo"
|
"codeword/internal/repository"
|
||||||
"context"
|
"context"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
mdb "penahub.gitlab.yandexcloud.net/backend/penahub_common/mongo"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MongoDB(ctx context.Context, cfg Config) (*mongo.Database, error) {
|
func MongoDB(ctx context.Context, cfg Config) (*mongo.Database, error) {
|
||||||
dbConfig := &mdb.Configuration{
|
dbConfig := &mdb.Configuration{
|
||||||
MongoHost: cfg.MongoHost,
|
Host: cfg.MongoHost,
|
||||||
MongoPort: cfg.MongoPort,
|
Port: cfg.MongoPort,
|
||||||
MongoUser: cfg.MongoUser,
|
User: cfg.MongoUser,
|
||||||
MongoPassword: cfg.MongoPassword,
|
Password: cfg.MongoPassword,
|
||||||
MongoDatabase: cfg.MongoDatabase,
|
DatabaseName: cfg.MongoDatabase,
|
||||||
MongoAuth: cfg.MongoAuth,
|
Auth: cfg.MongoAuth,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
mongoDeps := &mdb.ConnectDeps{
|
mongoDeps := &mdb.ConnectDeps{
|
||||||
Configuration: dbConfig,
|
Configuration: dbConfig,
|
||||||
Timeout: 10 * time.Second,
|
Timeout: 10 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := mdb.Connect(ctx, mongoDeps)
|
db, err := mdb.Connect(newCtx, mongoDeps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.Client().Ping(ctx, nil)
|
err = db.Client().Ping(newCtx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InitDatabaseIndexes(ctx context.Context, mdb *mongo.Database, logger *zap.Logger) error {
|
||||||
|
if err := repository.InitPromoCodeIndexes(ctx, mdb.Collection("promoCodes")); err != nil {
|
||||||
|
logger.Error("Failed to initialize promoCodes indexes", zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
56
internal/kafka/tariff/producer.go
Normal file
56
internal/kafka/tariff/producer.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package tariff
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeword/internal/models"
|
||||||
|
"codeword/internal/utils/transfer"
|
||||||
|
"context"
|
||||||
|
"github.com/twmb/franz-go/pkg/kgo"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProducerDeps struct {
|
||||||
|
Logger *zap.Logger
|
||||||
|
Client *kgo.Client
|
||||||
|
Topic string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Producer struct {
|
||||||
|
logger *zap.Logger
|
||||||
|
client *kgo.Client
|
||||||
|
topic string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProducer(deps ProducerDeps) *Producer {
|
||||||
|
if deps.Logger == nil {
|
||||||
|
log.Panicln("logger is nil on <NewTariffProducer>")
|
||||||
|
}
|
||||||
|
|
||||||
|
if deps.Client == nil {
|
||||||
|
log.Panicln("Kafka client is nil on <NewTariffProducer>")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Producer{
|
||||||
|
logger: deps.Logger,
|
||||||
|
client: deps.Client,
|
||||||
|
topic: deps.Topic,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Producer) Send(ctx context.Context, userID string, tariff *models.Tariff) error {
|
||||||
|
bytes, err := proto.Marshal(transfer.TariffModelToProtoMessage(userID, tariff))
|
||||||
|
if err != nil {
|
||||||
|
p.logger.Error("failed to marshal tariff model", zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// упростил, возможно зря, но теперь возвращаем одну ошибку, просто прерываем цикл при первой встретившейся ошибке
|
||||||
|
err = p.client.ProduceSync(ctx, &kgo.Record{Topic: p.topic, Value: bytes}).FirstErr()
|
||||||
|
if err != nil {
|
||||||
|
p.logger.Error("failed to send tariff to Kafka", zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -9,3 +9,6 @@ type RefreshResponse struct {
|
|||||||
AccessToken string `json:"accessToken"`
|
AccessToken string `json:"accessToken"`
|
||||||
RefreshToken string `json:"refreshToken"`
|
RefreshToken string `json:"refreshToken"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AuthJWTDecodedUserIDKey = "userID"
|
||||||
|
const AuthJWTDecodedAccessTokenKey = "access-token"
|
||||||
|
@ -18,7 +18,7 @@ type PromoCode struct {
|
|||||||
Amount uint64 `json:"amount" bson:"amount"` // количество
|
Amount uint64 `json:"amount" bson:"amount"` // количество
|
||||||
} `json:"privilege" bson:"privilege"`
|
} `json:"privilege" bson:"privilege"`
|
||||||
Discount struct {
|
Discount struct {
|
||||||
Layer int `json:"layer" bson:"layer"` // 1|2
|
Layer uint32 `json:"layer" bson:"layer"` // 1|2
|
||||||
Factor float64 `json:"factor" bson:"factor"` // процент скидки, вернее множитель, при котором достигается этот процент скидки
|
Factor float64 `json:"factor" bson:"factor"` // процент скидки, вернее множитель, при котором достигается этот процент скидки
|
||||||
Target string `json:"target" bson:"target"` // PrivilegeID или ServiceKey в зависимости от слоя
|
Target string `json:"target" bson:"target"` // PrivilegeID или ServiceKey в зависимости от слоя
|
||||||
Threshold int64 `json:"threshold" bson:"threshold"` // граничное значение, при пересечении которого применяется эта скидка
|
Threshold int64 `json:"threshold" bson:"threshold"` // граничное значение, при пересечении которого применяется эта скидка
|
||||||
@ -28,6 +28,7 @@ type PromoCode struct {
|
|||||||
OffLimit bool `json:"offLimit" bson:"offLimit"`
|
OffLimit bool `json:"offLimit" bson:"offLimit"`
|
||||||
Delete bool `json:"delete" bson:"delete"`
|
Delete bool `json:"delete" bson:"delete"`
|
||||||
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
|
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
|
||||||
|
FastLinks []string `json:"fastLinks" bson:"fastLinks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReqEditPromoCode struct {
|
type ReqEditPromoCode struct {
|
||||||
@ -73,8 +74,20 @@ type GetPromoCodesListResp struct {
|
|||||||
|
|
||||||
type ActivateReq struct {
|
type ActivateReq struct {
|
||||||
Codeword string `json:"codeword"`
|
Codeword string `json:"codeword"`
|
||||||
|
FastLink string `json:"fastLink"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActivateResp struct {
|
type ActivateResp struct {
|
||||||
Greetings string `json:"greetings"` // поле из активированного промокода
|
Greetings string `json:"greetings"` // поле из активированного промокода
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PromoCodeStats struct {
|
||||||
|
ID string `bson:"_id,omitempty" json:"id,omitempty"`
|
||||||
|
UsageCount int `bson:"usageCount" json:"usageCount"`
|
||||||
|
UsageMap map[string][]Usage `bson:"usageMap" json:"usageMap"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Usage struct {
|
||||||
|
Key string `bson:"key" json:"key"`
|
||||||
|
Time time.Time `bson:"time" json:"time"`
|
||||||
|
}
|
||||||
|
46
internal/models/tariff.go
Normal file
46
internal/models/tariff.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeword/internal/proto/broker"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Tariff struct {
|
||||||
|
ID string `json:"_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Price uint64 `json:"price,omitempty"`
|
||||||
|
IsCustom bool `json:"isCustom"`
|
||||||
|
Privileges []Privilege `json:"privileges"`
|
||||||
|
Deleted bool `json:"isDeleted"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Privilege struct {
|
||||||
|
ID string `json:"_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
PrivilegeID string `json:"privilegeId"`
|
||||||
|
ServiceKey string `json:"serviceKey"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Amount uint64 `json:"amount"`
|
||||||
|
Type PrivilegeType `json:"type"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
Price uint64 `json:"price"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrivilegeType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PrivilegeTypeCount = "count"
|
||||||
|
PrivilegeTypeDay = "day"
|
||||||
|
PrivilegeTypeFull = "full"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
PrivilegeBrokerTypeMap = map[PrivilegeType]broker.PrivilegeType{
|
||||||
|
PrivilegeTypeFull: broker.PrivilegeType_Full,
|
||||||
|
PrivilegeTypeDay: broker.PrivilegeType_Day,
|
||||||
|
PrivilegeTypeCount: broker.PrivilegeType_Count,
|
||||||
|
}
|
||||||
|
)
|
@ -35,3 +35,12 @@ type RecoveryRecord struct {
|
|||||||
Email string
|
Email string
|
||||||
Key string
|
Key string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RecoveryRequest struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
RedirectionURL string `json:"redirectionURL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RecoveryLinkRequest struct {
|
||||||
|
Sign string `json:"sign"`
|
||||||
|
}
|
||||||
|
314
internal/proto/broker/models.pb.go
Normal file
314
internal/proto/broker/models.pb.go
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.31.0
|
||||||
|
// protoc (unknown)
|
||||||
|
// source: kafka/models.proto
|
||||||
|
|
||||||
|
package broker
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type PrivilegeType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
PrivilegeType_Full PrivilegeType = 0
|
||||||
|
PrivilegeType_Day PrivilegeType = 1
|
||||||
|
PrivilegeType_Count PrivilegeType = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for PrivilegeType.
|
||||||
|
var (
|
||||||
|
PrivilegeType_name = map[int32]string{
|
||||||
|
0: "Full",
|
||||||
|
1: "Day",
|
||||||
|
2: "Count",
|
||||||
|
}
|
||||||
|
PrivilegeType_value = map[string]int32{
|
||||||
|
"Full": 0,
|
||||||
|
"Day": 1,
|
||||||
|
"Count": 2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x PrivilegeType) Enum() *PrivilegeType {
|
||||||
|
p := new(PrivilegeType)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x PrivilegeType) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PrivilegeType) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_broker_models_proto_enumTypes[0].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PrivilegeType) Type() protoreflect.EnumType {
|
||||||
|
return &file_broker_models_proto_enumTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x PrivilegeType) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use PrivilegeType.Descriptor instead.
|
||||||
|
func (PrivilegeType) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_broker_models_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrivilegeMessage struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
PrivilegeID string `protobuf:"bytes,1,opt,name=PrivilegeID,proto3" json:"PrivilegeID,omitempty"`
|
||||||
|
ServiceKey string `protobuf:"bytes,2,opt,name=ServiceKey,proto3" json:"ServiceKey,omitempty"`
|
||||||
|
Type PrivilegeType `protobuf:"varint,3,opt,name=Type,proto3,enum=kafka.PrivilegeType" json:"Type,omitempty"`
|
||||||
|
Value string `protobuf:"bytes,4,opt,name=Value,proto3" json:"Value,omitempty"`
|
||||||
|
Amount uint64 `protobuf:"varint,5,opt,name=Amount,proto3" json:"Amount,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrivilegeMessage) Reset() {
|
||||||
|
*x = PrivilegeMessage{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_broker_models_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrivilegeMessage) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PrivilegeMessage) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *PrivilegeMessage) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_broker_models_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use PrivilegeMessage.ProtoReflect.Descriptor instead.
|
||||||
|
func (*PrivilegeMessage) Descriptor() ([]byte, []int) {
|
||||||
|
return file_broker_models_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrivilegeMessage) GetPrivilegeID() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PrivilegeID
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrivilegeMessage) GetServiceKey() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ServiceKey
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrivilegeMessage) GetType() PrivilegeType {
|
||||||
|
if x != nil {
|
||||||
|
return x.Type
|
||||||
|
}
|
||||||
|
return PrivilegeType_Full
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrivilegeMessage) GetValue() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Value
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrivilegeMessage) GetAmount() uint64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Amount
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type TariffMessage struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Privileges []*PrivilegeMessage `protobuf:"bytes,1,rep,name=Privileges,proto3" json:"Privileges,omitempty"`
|
||||||
|
UserID string `protobuf:"bytes,2,opt,name=UserID,proto3" json:"UserID,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *TariffMessage) Reset() {
|
||||||
|
*x = TariffMessage{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_broker_models_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *TariffMessage) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*TariffMessage) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *TariffMessage) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_broker_models_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use TariffMessage.ProtoReflect.Descriptor instead.
|
||||||
|
func (*TariffMessage) Descriptor() ([]byte, []int) {
|
||||||
|
return file_broker_models_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *TariffMessage) GetPrivileges() []*PrivilegeMessage {
|
||||||
|
if x != nil {
|
||||||
|
return x.Privileges
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *TariffMessage) GetUserID() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.UserID
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_broker_models_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_broker_models_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x13, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2e,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x22, 0xad, 0x01,
|
||||||
|
0x0a, 0x10, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
||||||
|
0x67, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x49,
|
||||||
|
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65,
|
||||||
|
0x67, 0x65, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4b,
|
||||||
|
0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||||
|
0x65, 0x4b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01,
|
||||||
|
0x28, 0x0e, 0x32, 0x15, 0x2e, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x69, 0x76,
|
||||||
|
0x69, 0x6c, 0x65, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
|
||||||
|
0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||||
|
0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18,
|
||||||
|
0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x61, 0x0a,
|
||||||
|
0x0d, 0x54, 0x61, 0x72, 0x69, 0x66, 0x66, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38,
|
||||||
|
0x0a, 0x0a, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||||
|
0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x69, 0x76,
|
||||||
|
0x69, 0x6c, 0x65, 0x67, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0a, 0x50, 0x72,
|
||||||
|
0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x73, 0x65, 0x72,
|
||||||
|
0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44,
|
||||||
|
0x2a, 0x2d, 0x0a, 0x0d, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x54, 0x79, 0x70,
|
||||||
|
0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x44,
|
||||||
|
0x61, 0x79, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x10, 0x02, 0x42,
|
||||||
|
0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_broker_models_proto_rawDescOnce sync.Once
|
||||||
|
file_broker_models_proto_rawDescData = file_broker_models_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_broker_models_proto_rawDescGZIP() []byte {
|
||||||
|
file_broker_models_proto_rawDescOnce.Do(func() {
|
||||||
|
file_broker_models_proto_rawDescData = protoimpl.X.CompressGZIP(file_broker_models_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_broker_models_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_broker_models_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
|
var file_broker_models_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
|
var file_broker_models_proto_goTypes = []interface{}{
|
||||||
|
(PrivilegeType)(0), // 0: kafka.PrivilegeType
|
||||||
|
(*PrivilegeMessage)(nil), // 1: kafka.PrivilegeMessage
|
||||||
|
(*TariffMessage)(nil), // 2: kafka.TariffMessage
|
||||||
|
}
|
||||||
|
var file_broker_models_proto_depIdxs = []int32{
|
||||||
|
0, // 0: kafka.PrivilegeMessage.Type:type_name -> kafka.PrivilegeType
|
||||||
|
1, // 1: kafka.TariffMessage.Privileges:type_name -> kafka.PrivilegeMessage
|
||||||
|
2, // [2:2] is the sub-list for method output_type
|
||||||
|
2, // [2:2] is the sub-list for method input_type
|
||||||
|
2, // [2:2] is the sub-list for extension type_name
|
||||||
|
2, // [2:2] is the sub-list for extension extendee
|
||||||
|
0, // [0:2] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_broker_models_proto_init() }
|
||||||
|
func file_broker_models_proto_init() {
|
||||||
|
if File_broker_models_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_broker_models_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*PrivilegeMessage); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_broker_models_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*TariffMessage); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_broker_models_proto_rawDesc,
|
||||||
|
NumEnums: 1,
|
||||||
|
NumMessages: 2,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_broker_models_proto_goTypes,
|
||||||
|
DependencyIndexes: file_broker_models_proto_depIdxs,
|
||||||
|
EnumInfos: file_broker_models_proto_enumTypes,
|
||||||
|
MessageInfos: file_broker_models_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_broker_models_proto = out.File
|
||||||
|
file_broker_models_proto_rawDesc = nil
|
||||||
|
file_broker_models_proto_goTypes = nil
|
||||||
|
file_broker_models_proto_depIdxs = nil
|
||||||
|
}
|
192
internal/proto/discount/audit.model.pb.go
Normal file
192
internal/proto/discount/audit.model.pb.go
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.31.0
|
||||||
|
// protoc (unknown)
|
||||||
|
// source: discount/audit.model.proto
|
||||||
|
|
||||||
|
package discount
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "google.golang.org/genproto/googleapis/api/annotations"
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
_ "google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Audit struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=UpdatedAt,proto3" json:"UpdatedAt,omitempty"`
|
||||||
|
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"`
|
||||||
|
DeletedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=DeletedAt,proto3,oneof" json:"DeletedAt,omitempty"`
|
||||||
|
Deleted bool `protobuf:"varint,4,opt,name=Deleted,proto3" json:"Deleted,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Audit) Reset() {
|
||||||
|
*x = Audit{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_discount_audit_model_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Audit) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Audit) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Audit) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_discount_audit_model_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Audit.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Audit) Descriptor() ([]byte, []int) {
|
||||||
|
return file_discount_audit_model_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Audit) GetUpdatedAt() *timestamppb.Timestamp {
|
||||||
|
if x != nil {
|
||||||
|
return x.UpdatedAt
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Audit) GetCreatedAt() *timestamppb.Timestamp {
|
||||||
|
if x != nil {
|
||||||
|
return x.CreatedAt
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Audit) GetDeletedAt() *timestamppb.Timestamp {
|
||||||
|
if x != nil {
|
||||||
|
return x.DeletedAt
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Audit) GetDeleted() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Deleted
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_discount_audit_model_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_discount_audit_model_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x1a, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x61, 0x75, 0x64, 0x69, 0x74,
|
||||||
|
0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x64, 0x69,
|
||||||
|
0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61,
|
||||||
|
0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x22, 0xe2, 0x01, 0x0a, 0x05, 0x41, 0x75, 0x64, 0x69, 0x74, 0x12, 0x38, 0x0a, 0x09,
|
||||||
|
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||||
|
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||||
|
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x55, 0x70, 0x64,
|
||||||
|
0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
|
||||||
|
0x64, 0x41, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||||
|
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,
|
||||||
|
0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74,
|
||||||
|
0x12, 0x3d, 0x0a, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x03, 0x20,
|
||||||
|
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48,
|
||||||
|
0x00, 0x52, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x88, 0x01, 0x01, 0x12,
|
||||||
|
0x18, 0x0a, 0x07, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
|
||||||
|
0x52, 0x07, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x44, 0x65,
|
||||||
|
0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x42, 0x0c, 0x5a, 0x0a, 0x2e, 0x2f, 0x64, 0x69, 0x73,
|
||||||
|
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_discount_audit_model_proto_rawDescOnce sync.Once
|
||||||
|
file_discount_audit_model_proto_rawDescData = file_discount_audit_model_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_discount_audit_model_proto_rawDescGZIP() []byte {
|
||||||
|
file_discount_audit_model_proto_rawDescOnce.Do(func() {
|
||||||
|
file_discount_audit_model_proto_rawDescData = protoimpl.X.CompressGZIP(file_discount_audit_model_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_discount_audit_model_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_discount_audit_model_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_discount_audit_model_proto_goTypes = []interface{}{
|
||||||
|
(*Audit)(nil), // 0: discount.Audit
|
||||||
|
(*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp
|
||||||
|
}
|
||||||
|
var file_discount_audit_model_proto_depIdxs = []int32{
|
||||||
|
1, // 0: discount.Audit.UpdatedAt:type_name -> google.protobuf.Timestamp
|
||||||
|
1, // 1: discount.Audit.CreatedAt:type_name -> google.protobuf.Timestamp
|
||||||
|
1, // 2: discount.Audit.DeletedAt:type_name -> google.protobuf.Timestamp
|
||||||
|
3, // [3:3] is the sub-list for method output_type
|
||||||
|
3, // [3:3] is the sub-list for method input_type
|
||||||
|
3, // [3:3] is the sub-list for extension type_name
|
||||||
|
3, // [3:3] is the sub-list for extension extendee
|
||||||
|
0, // [0:3] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_discount_audit_model_proto_init() }
|
||||||
|
func file_discount_audit_model_proto_init() {
|
||||||
|
if File_discount_audit_model_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_discount_audit_model_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Audit); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_discount_audit_model_proto_msgTypes[0].OneofWrappers = []interface{}{}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_discount_audit_model_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_discount_audit_model_proto_goTypes,
|
||||||
|
DependencyIndexes: file_discount_audit_model_proto_depIdxs,
|
||||||
|
MessageInfos: file_discount_audit_model_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_discount_audit_model_proto = out.File
|
||||||
|
file_discount_audit_model_proto_rawDesc = nil
|
||||||
|
file_discount_audit_model_proto_goTypes = nil
|
||||||
|
file_discount_audit_model_proto_depIdxs = nil
|
||||||
|
}
|
1132
internal/proto/discount/discount.model.pb.go
Normal file
1132
internal/proto/discount/discount.model.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
524
internal/proto/discount/service.pb.go
Normal file
524
internal/proto/discount/service.pb.go
Normal file
@ -0,0 +1,524 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.31.0
|
||||||
|
// protoc (unknown)
|
||||||
|
// source: discount/service.proto
|
||||||
|
|
||||||
|
package discount
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "google.golang.org/genproto/googleapis/api/annotations"
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetDiscountByIDRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetDiscountByIDRequest) Reset() {
|
||||||
|
*x = GetDiscountByIDRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_discount_service_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetDiscountByIDRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GetDiscountByIDRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *GetDiscountByIDRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_discount_service_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use GetDiscountByIDRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*GetDiscountByIDRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_discount_service_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GetDiscountByIDRequest) GetID() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ID
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApplyDiscountRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
UserInformation *UserInformation `protobuf:"bytes,1,opt,name=UserInformation,proto3" json:"UserInformation,omitempty"`
|
||||||
|
Products []*ProductInformation `protobuf:"bytes,2,rep,name=Products,proto3" json:"Products,omitempty"`
|
||||||
|
Date *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=Date,proto3" json:"Date,omitempty"`
|
||||||
|
Coupon *string `protobuf:"bytes,4,opt,name=Coupon,proto3,oneof" json:"Coupon,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ApplyDiscountRequest) Reset() {
|
||||||
|
*x = ApplyDiscountRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_discount_service_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ApplyDiscountRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ApplyDiscountRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ApplyDiscountRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_discount_service_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ApplyDiscountRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ApplyDiscountRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_discount_service_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ApplyDiscountRequest) GetUserInformation() *UserInformation {
|
||||||
|
if x != nil {
|
||||||
|
return x.UserInformation
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ApplyDiscountRequest) GetProducts() []*ProductInformation {
|
||||||
|
if x != nil {
|
||||||
|
return x.Products
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ApplyDiscountRequest) GetDate() *timestamppb.Timestamp {
|
||||||
|
if x != nil {
|
||||||
|
return x.Date
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ApplyDiscountRequest) GetCoupon() string {
|
||||||
|
if x != nil && x.Coupon != nil {
|
||||||
|
return *x.Coupon
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApplyDiscountResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Price uint64 `protobuf:"varint,1,opt,name=Price,proto3" json:"Price,omitempty"`
|
||||||
|
AppliedDiscounts []*Discount `protobuf:"bytes,2,rep,name=AppliedDiscounts,proto3" json:"AppliedDiscounts,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ApplyDiscountResponse) Reset() {
|
||||||
|
*x = ApplyDiscountResponse{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_discount_service_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ApplyDiscountResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ApplyDiscountResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ApplyDiscountResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_discount_service_proto_msgTypes[2]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ApplyDiscountResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ApplyDiscountResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_discount_service_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ApplyDiscountResponse) GetPrice() uint64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Price
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ApplyDiscountResponse) GetAppliedDiscounts() []*Discount {
|
||||||
|
if x != nil {
|
||||||
|
return x.AppliedDiscounts
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateDiscountRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"`
|
||||||
|
Layer uint32 `protobuf:"varint,2,opt,name=Layer,proto3" json:"Layer,omitempty"`
|
||||||
|
Description string `protobuf:"bytes,3,opt,name=Description,proto3" json:"Description,omitempty"`
|
||||||
|
Condition *DiscountCondition `protobuf:"bytes,4,opt,name=Condition,proto3" json:"Condition,omitempty"`
|
||||||
|
Target *DiscountCalculationTarget `protobuf:"bytes,5,opt,name=Target,proto3" json:"Target,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateDiscountRequest) Reset() {
|
||||||
|
*x = CreateDiscountRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_discount_service_proto_msgTypes[3]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateDiscountRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CreateDiscountRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CreateDiscountRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_discount_service_proto_msgTypes[3]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CreateDiscountRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CreateDiscountRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_discount_service_proto_rawDescGZIP(), []int{3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateDiscountRequest) GetName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateDiscountRequest) GetLayer() uint32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Layer
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateDiscountRequest) GetDescription() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Description
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateDiscountRequest) GetCondition() *DiscountCondition {
|
||||||
|
if x != nil {
|
||||||
|
return x.Condition
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateDiscountRequest) GetTarget() *DiscountCalculationTarget {
|
||||||
|
if x != nil {
|
||||||
|
return x.Target
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_discount_service_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_discount_service_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x16, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69,
|
||||||
|
0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75,
|
||||||
|
0x6e, 0x74, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61,
|
||||||
|
0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
|
0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||||
|
0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
|
0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d,
|
||||||
|
0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e,
|
||||||
|
0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x28, 0x0a,
|
||||||
|
0x16, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44,
|
||||||
|
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20,
|
||||||
|
0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x22, 0xed, 0x01, 0x0a, 0x14, 0x41, 0x70, 0x70, 0x6c,
|
||||||
|
0x79, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
|
0x12, 0x43, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74,
|
||||||
|
0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x63,
|
||||||
|
0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d,
|
||||||
|
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74,
|
||||||
|
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75,
|
||||||
|
0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d,
|
||||||
|
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, 0x12,
|
||||||
|
0x2e, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
|
||||||
|
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||||
|
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x44, 0x61, 0x74, 0x65, 0x12,
|
||||||
|
0x1b, 0x0a, 0x06, 0x43, 0x6f, 0x75, 0x70, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48,
|
||||||
|
0x00, 0x52, 0x06, 0x43, 0x6f, 0x75, 0x70, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07,
|
||||||
|
0x5f, 0x43, 0x6f, 0x75, 0x70, 0x6f, 0x6e, 0x22, 0x6d, 0x0a, 0x15, 0x41, 0x70, 0x70, 0x6c, 0x79,
|
||||||
|
0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
|
0x12, 0x14, 0x0a, 0x05, 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
|
||||||
|
0x05, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x10, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65,
|
||||||
|
0x64, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
|
||||||
|
0x32, 0x12, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63,
|
||||||
|
0x6f, 0x75, 0x6e, 0x74, 0x52, 0x10, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x69, 0x73,
|
||||||
|
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22, 0xdb, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74,
|
||||||
|
0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
|
0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
||||||
|
0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x02, 0x20,
|
||||||
|
0x01, 0x28, 0x0d, 0x52, 0x05, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x44, 0x65,
|
||||||
|
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
|
0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x09,
|
||||||
|
0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||||
|
0x1b, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f,
|
||||||
|
0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x43, 0x6f,
|
||||||
|
0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x06, 0x54, 0x61, 0x72, 0x67, 0x65,
|
||||||
|
0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75,
|
||||||
|
0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x61, 0x6c, 0x63, 0x75,
|
||||||
|
0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x06, 0x54, 0x61,
|
||||||
|
0x72, 0x67, 0x65, 0x74, 0x32, 0x80, 0x07, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e,
|
||||||
|
0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x52, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x41,
|
||||||
|
0x6c, 0x6c, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f,
|
||||||
|
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
|
||||||
|
0x70, 0x74, 0x79, 0x1a, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44,
|
||||||
|
0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c,
|
||||||
|
0x12, 0x0a, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x66, 0x0a, 0x10,
|
||||||
|
0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73,
|
||||||
|
0x12, 0x20, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x44,
|
||||||
|
0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
|
0x73, 0x74, 0x1a, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69,
|
||||||
|
0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12,
|
||||||
|
0x13, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f,
|
||||||
|
0x7b, 0x49, 0x44, 0x7d, 0x12, 0x69, 0x0a, 0x12, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e,
|
||||||
|
0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x64, 0x69, 0x73,
|
||||||
|
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x44, 0x69, 0x73, 0x63, 0x6f,
|
||||||
|
0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x64, 0x69, 0x73,
|
||||||
|
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22,
|
||||||
|
0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x3a, 0x01, 0x2a, 0x22, 0x13, 0x2f, 0x64, 0x69, 0x73,
|
||||||
|
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x12,
|
||||||
|
0x6d, 0x0a, 0x0e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||||
|
0x73, 0x12, 0x1e, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x41, 0x70, 0x70,
|
||||||
|
0x6c, 0x79, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
|
0x74, 0x1a, 0x1f, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x41, 0x70, 0x70,
|
||||||
|
0x6c, 0x79, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||||
|
0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a, 0x22, 0x0f, 0x2f,
|
||||||
|
0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x5f,
|
||||||
|
0x0a, 0x0f, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x49,
|
||||||
|
0x44, 0x12, 0x20, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74,
|
||||||
|
0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75,
|
||||||
|
0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44,
|
||||||
|
0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12,
|
||||||
|
0x0e, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x7b, 0x49, 0x44, 0x7d, 0x12,
|
||||||
|
0x5b, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e,
|
||||||
|
0x74, 0x12, 0x1f, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x43, 0x72, 0x65,
|
||||||
|
0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
|
0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69,
|
||||||
|
0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x3a, 0x01,
|
||||||
|
0x2a, 0x22, 0x09, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x5c, 0x0a, 0x0f,
|
||||||
|
0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12,
|
||||||
|
0x1a, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f,
|
||||||
|
0x75, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x1a, 0x12, 0x2e, 0x64, 0x69,
|
||||||
|
0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22,
|
||||||
|
0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x1a, 0x0e, 0x2f, 0x64, 0x69, 0x73,
|
||||||
|
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x7b, 0x49, 0x44, 0x7d, 0x12, 0x5b, 0x0a, 0x0e, 0x55, 0x70,
|
||||||
|
0x64, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x2e, 0x64,
|
||||||
|
0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||||
|
0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x1a, 0x12, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f,
|
||||||
|
0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x19, 0x82, 0xd3,
|
||||||
|
0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x32, 0x0e, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75,
|
||||||
|
0x6e, 0x74, 0x2f, 0x7b, 0x49, 0x44, 0x7d, 0x12, 0x5e, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74,
|
||||||
|
0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x20, 0x2e, 0x64, 0x69, 0x73, 0x63,
|
||||||
|
0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||||
|
0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x69,
|
||||||
|
0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22,
|
||||||
|
0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x2a, 0x0e, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75,
|
||||||
|
0x6e, 0x74, 0x2f, 0x7b, 0x49, 0x44, 0x7d, 0x42, 0x0c, 0x5a, 0x0a, 0x2e, 0x2f, 0x64, 0x69, 0x73,
|
||||||
|
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_discount_service_proto_rawDescOnce sync.Once
|
||||||
|
file_discount_service_proto_rawDescData = file_discount_service_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_discount_service_proto_rawDescGZIP() []byte {
|
||||||
|
file_discount_service_proto_rawDescOnce.Do(func() {
|
||||||
|
file_discount_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_discount_service_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_discount_service_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_discount_service_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||||
|
var file_discount_service_proto_goTypes = []interface{}{
|
||||||
|
(*GetDiscountByIDRequest)(nil), // 0: discount.GetDiscountByIDRequest
|
||||||
|
(*ApplyDiscountRequest)(nil), // 1: discount.ApplyDiscountRequest
|
||||||
|
(*ApplyDiscountResponse)(nil), // 2: discount.ApplyDiscountResponse
|
||||||
|
(*CreateDiscountRequest)(nil), // 3: discount.CreateDiscountRequest
|
||||||
|
(*UserInformation)(nil), // 4: discount.UserInformation
|
||||||
|
(*ProductInformation)(nil), // 5: discount.ProductInformation
|
||||||
|
(*timestamppb.Timestamp)(nil), // 6: google.protobuf.Timestamp
|
||||||
|
(*Discount)(nil), // 7: discount.Discount
|
||||||
|
(*DiscountCondition)(nil), // 8: discount.DiscountCondition
|
||||||
|
(*DiscountCalculationTarget)(nil), // 9: discount.DiscountCalculationTarget
|
||||||
|
(*emptypb.Empty)(nil), // 10: google.protobuf.Empty
|
||||||
|
(*DiscountOptional)(nil), // 11: discount.DiscountOptional
|
||||||
|
(*Discounts)(nil), // 12: discount.Discounts
|
||||||
|
}
|
||||||
|
var file_discount_service_proto_depIdxs = []int32{
|
||||||
|
4, // 0: discount.ApplyDiscountRequest.UserInformation:type_name -> discount.UserInformation
|
||||||
|
5, // 1: discount.ApplyDiscountRequest.Products:type_name -> discount.ProductInformation
|
||||||
|
6, // 2: discount.ApplyDiscountRequest.Date:type_name -> google.protobuf.Timestamp
|
||||||
|
7, // 3: discount.ApplyDiscountResponse.AppliedDiscounts:type_name -> discount.Discount
|
||||||
|
8, // 4: discount.CreateDiscountRequest.Condition:type_name -> discount.DiscountCondition
|
||||||
|
9, // 5: discount.CreateDiscountRequest.Target:type_name -> discount.DiscountCalculationTarget
|
||||||
|
10, // 6: discount.DiscountService.GetAllDiscounts:input_type -> google.protobuf.Empty
|
||||||
|
0, // 7: discount.DiscountService.GetUserDiscounts:input_type -> discount.GetDiscountByIDRequest
|
||||||
|
1, // 8: discount.DiscountService.DetermineDiscounts:input_type -> discount.ApplyDiscountRequest
|
||||||
|
1, // 9: discount.DiscountService.ApplyDiscounts:input_type -> discount.ApplyDiscountRequest
|
||||||
|
0, // 10: discount.DiscountService.GetDiscountByID:input_type -> discount.GetDiscountByIDRequest
|
||||||
|
3, // 11: discount.DiscountService.CreateDiscount:input_type -> discount.CreateDiscountRequest
|
||||||
|
11, // 12: discount.DiscountService.ReplaceDiscount:input_type -> discount.DiscountOptional
|
||||||
|
11, // 13: discount.DiscountService.UpdateDiscount:input_type -> discount.DiscountOptional
|
||||||
|
0, // 14: discount.DiscountService.DeleteDiscount:input_type -> discount.GetDiscountByIDRequest
|
||||||
|
12, // 15: discount.DiscountService.GetAllDiscounts:output_type -> discount.Discounts
|
||||||
|
12, // 16: discount.DiscountService.GetUserDiscounts:output_type -> discount.Discounts
|
||||||
|
12, // 17: discount.DiscountService.DetermineDiscounts:output_type -> discount.Discounts
|
||||||
|
2, // 18: discount.DiscountService.ApplyDiscounts:output_type -> discount.ApplyDiscountResponse
|
||||||
|
7, // 19: discount.DiscountService.GetDiscountByID:output_type -> discount.Discount
|
||||||
|
7, // 20: discount.DiscountService.CreateDiscount:output_type -> discount.Discount
|
||||||
|
7, // 21: discount.DiscountService.ReplaceDiscount:output_type -> discount.Discount
|
||||||
|
7, // 22: discount.DiscountService.UpdateDiscount:output_type -> discount.Discount
|
||||||
|
7, // 23: discount.DiscountService.DeleteDiscount:output_type -> discount.Discount
|
||||||
|
15, // [15:24] is the sub-list for method output_type
|
||||||
|
6, // [6:15] is the sub-list for method input_type
|
||||||
|
6, // [6:6] is the sub-list for extension type_name
|
||||||
|
6, // [6:6] is the sub-list for extension extendee
|
||||||
|
0, // [0:6] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_discount_service_proto_init() }
|
||||||
|
func file_discount_service_proto_init() {
|
||||||
|
if File_discount_service_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file_discount_discount_model_proto_init()
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_discount_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*GetDiscountByIDRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_discount_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*ApplyDiscountRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_discount_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*ApplyDiscountResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_discount_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*CreateDiscountRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_discount_service_proto_msgTypes[1].OneofWrappers = []interface{}{}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_discount_service_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 4,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 1,
|
||||||
|
},
|
||||||
|
GoTypes: file_discount_service_proto_goTypes,
|
||||||
|
DependencyIndexes: file_discount_service_proto_depIdxs,
|
||||||
|
MessageInfos: file_discount_service_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_discount_service_proto = out.File
|
||||||
|
file_discount_service_proto_rawDesc = nil
|
||||||
|
file_discount_service_proto_goTypes = nil
|
||||||
|
file_discount_service_proto_depIdxs = nil
|
||||||
|
}
|
404
internal/proto/discount/service_grpc.pb.go
Normal file
404
internal/proto/discount/service_grpc.pb.go
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// - protoc-gen-go-grpc v1.3.0
|
||||||
|
// - protoc (unknown)
|
||||||
|
// source: discount/service.proto
|
||||||
|
|
||||||
|
package discount
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
const (
|
||||||
|
DiscountService_GetAllDiscounts_FullMethodName = "/discount.DiscountService/GetAllDiscounts"
|
||||||
|
DiscountService_GetUserDiscounts_FullMethodName = "/discount.DiscountService/GetUserDiscounts"
|
||||||
|
DiscountService_DetermineDiscounts_FullMethodName = "/discount.DiscountService/DetermineDiscounts"
|
||||||
|
DiscountService_ApplyDiscounts_FullMethodName = "/discount.DiscountService/ApplyDiscounts"
|
||||||
|
DiscountService_GetDiscountByID_FullMethodName = "/discount.DiscountService/GetDiscountByID"
|
||||||
|
DiscountService_CreateDiscount_FullMethodName = "/discount.DiscountService/CreateDiscount"
|
||||||
|
DiscountService_ReplaceDiscount_FullMethodName = "/discount.DiscountService/ReplaceDiscount"
|
||||||
|
DiscountService_UpdateDiscount_FullMethodName = "/discount.DiscountService/UpdateDiscount"
|
||||||
|
DiscountService_DeleteDiscount_FullMethodName = "/discount.DiscountService/DeleteDiscount"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DiscountServiceClient is the client API for DiscountService service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type DiscountServiceClient interface {
|
||||||
|
GetAllDiscounts(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*Discounts, error)
|
||||||
|
GetUserDiscounts(ctx context.Context, in *GetDiscountByIDRequest, opts ...grpc.CallOption) (*Discounts, error)
|
||||||
|
DetermineDiscounts(ctx context.Context, in *ApplyDiscountRequest, opts ...grpc.CallOption) (*Discounts, error)
|
||||||
|
ApplyDiscounts(ctx context.Context, in *ApplyDiscountRequest, opts ...grpc.CallOption) (*ApplyDiscountResponse, error)
|
||||||
|
GetDiscountByID(ctx context.Context, in *GetDiscountByIDRequest, opts ...grpc.CallOption) (*Discount, error)
|
||||||
|
CreateDiscount(ctx context.Context, in *CreateDiscountRequest, opts ...grpc.CallOption) (*Discount, error)
|
||||||
|
ReplaceDiscount(ctx context.Context, in *DiscountOptional, opts ...grpc.CallOption) (*Discount, error)
|
||||||
|
UpdateDiscount(ctx context.Context, in *DiscountOptional, opts ...grpc.CallOption) (*Discount, error)
|
||||||
|
DeleteDiscount(ctx context.Context, in *GetDiscountByIDRequest, opts ...grpc.CallOption) (*Discount, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type discountServiceClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDiscountServiceClient(cc grpc.ClientConnInterface) DiscountServiceClient {
|
||||||
|
return &discountServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *discountServiceClient) GetAllDiscounts(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*Discounts, error) {
|
||||||
|
out := new(Discounts)
|
||||||
|
err := c.cc.Invoke(ctx, DiscountService_GetAllDiscounts_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *discountServiceClient) GetUserDiscounts(ctx context.Context, in *GetDiscountByIDRequest, opts ...grpc.CallOption) (*Discounts, error) {
|
||||||
|
out := new(Discounts)
|
||||||
|
err := c.cc.Invoke(ctx, DiscountService_GetUserDiscounts_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *discountServiceClient) DetermineDiscounts(ctx context.Context, in *ApplyDiscountRequest, opts ...grpc.CallOption) (*Discounts, error) {
|
||||||
|
out := new(Discounts)
|
||||||
|
err := c.cc.Invoke(ctx, DiscountService_DetermineDiscounts_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *discountServiceClient) ApplyDiscounts(ctx context.Context, in *ApplyDiscountRequest, opts ...grpc.CallOption) (*ApplyDiscountResponse, error) {
|
||||||
|
out := new(ApplyDiscountResponse)
|
||||||
|
err := c.cc.Invoke(ctx, DiscountService_ApplyDiscounts_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *discountServiceClient) GetDiscountByID(ctx context.Context, in *GetDiscountByIDRequest, opts ...grpc.CallOption) (*Discount, error) {
|
||||||
|
out := new(Discount)
|
||||||
|
err := c.cc.Invoke(ctx, DiscountService_GetDiscountByID_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *discountServiceClient) CreateDiscount(ctx context.Context, in *CreateDiscountRequest, opts ...grpc.CallOption) (*Discount, error) {
|
||||||
|
out := new(Discount)
|
||||||
|
err := c.cc.Invoke(ctx, DiscountService_CreateDiscount_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *discountServiceClient) ReplaceDiscount(ctx context.Context, in *DiscountOptional, opts ...grpc.CallOption) (*Discount, error) {
|
||||||
|
out := new(Discount)
|
||||||
|
err := c.cc.Invoke(ctx, DiscountService_ReplaceDiscount_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *discountServiceClient) UpdateDiscount(ctx context.Context, in *DiscountOptional, opts ...grpc.CallOption) (*Discount, error) {
|
||||||
|
out := new(Discount)
|
||||||
|
err := c.cc.Invoke(ctx, DiscountService_UpdateDiscount_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *discountServiceClient) DeleteDiscount(ctx context.Context, in *GetDiscountByIDRequest, opts ...grpc.CallOption) (*Discount, error) {
|
||||||
|
out := new(Discount)
|
||||||
|
err := c.cc.Invoke(ctx, DiscountService_DeleteDiscount_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscountServiceServer is the server API for DiscountService service.
|
||||||
|
// All implementations should embed UnimplementedDiscountServiceServer
|
||||||
|
// for forward compatibility
|
||||||
|
type DiscountServiceServer interface {
|
||||||
|
GetAllDiscounts(context.Context, *emptypb.Empty) (*Discounts, error)
|
||||||
|
GetUserDiscounts(context.Context, *GetDiscountByIDRequest) (*Discounts, error)
|
||||||
|
DetermineDiscounts(context.Context, *ApplyDiscountRequest) (*Discounts, error)
|
||||||
|
ApplyDiscounts(context.Context, *ApplyDiscountRequest) (*ApplyDiscountResponse, error)
|
||||||
|
GetDiscountByID(context.Context, *GetDiscountByIDRequest) (*Discount, error)
|
||||||
|
CreateDiscount(context.Context, *CreateDiscountRequest) (*Discount, error)
|
||||||
|
ReplaceDiscount(context.Context, *DiscountOptional) (*Discount, error)
|
||||||
|
UpdateDiscount(context.Context, *DiscountOptional) (*Discount, error)
|
||||||
|
DeleteDiscount(context.Context, *GetDiscountByIDRequest) (*Discount, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedDiscountServiceServer should be embedded to have forward compatible implementations.
|
||||||
|
type UnimplementedDiscountServiceServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedDiscountServiceServer) GetAllDiscounts(context.Context, *emptypb.Empty) (*Discounts, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetAllDiscounts not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDiscountServiceServer) GetUserDiscounts(context.Context, *GetDiscountByIDRequest) (*Discounts, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetUserDiscounts not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDiscountServiceServer) DetermineDiscounts(context.Context, *ApplyDiscountRequest) (*Discounts, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method DetermineDiscounts not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDiscountServiceServer) ApplyDiscounts(context.Context, *ApplyDiscountRequest) (*ApplyDiscountResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method ApplyDiscounts not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDiscountServiceServer) GetDiscountByID(context.Context, *GetDiscountByIDRequest) (*Discount, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetDiscountByID not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDiscountServiceServer) CreateDiscount(context.Context, *CreateDiscountRequest) (*Discount, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method CreateDiscount not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDiscountServiceServer) ReplaceDiscount(context.Context, *DiscountOptional) (*Discount, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method ReplaceDiscount not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDiscountServiceServer) UpdateDiscount(context.Context, *DiscountOptional) (*Discount, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method UpdateDiscount not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDiscountServiceServer) DeleteDiscount(context.Context, *GetDiscountByIDRequest) (*Discount, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method DeleteDiscount not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsafeDiscountServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to DiscountServiceServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeDiscountServiceServer interface {
|
||||||
|
mustEmbedUnimplementedDiscountServiceServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterDiscountServiceServer(s grpc.ServiceRegistrar, srv DiscountServiceServer) {
|
||||||
|
s.RegisterService(&DiscountService_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _DiscountService_GetAllDiscounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(emptypb.Empty)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DiscountServiceServer).GetAllDiscounts(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: DiscountService_GetAllDiscounts_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DiscountServiceServer).GetAllDiscounts(ctx, req.(*emptypb.Empty))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _DiscountService_GetUserDiscounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetDiscountByIDRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DiscountServiceServer).GetUserDiscounts(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: DiscountService_GetUserDiscounts_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DiscountServiceServer).GetUserDiscounts(ctx, req.(*GetDiscountByIDRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _DiscountService_DetermineDiscounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(ApplyDiscountRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DiscountServiceServer).DetermineDiscounts(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: DiscountService_DetermineDiscounts_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DiscountServiceServer).DetermineDiscounts(ctx, req.(*ApplyDiscountRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _DiscountService_ApplyDiscounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(ApplyDiscountRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DiscountServiceServer).ApplyDiscounts(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: DiscountService_ApplyDiscounts_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DiscountServiceServer).ApplyDiscounts(ctx, req.(*ApplyDiscountRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _DiscountService_GetDiscountByID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetDiscountByIDRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DiscountServiceServer).GetDiscountByID(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: DiscountService_GetDiscountByID_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DiscountServiceServer).GetDiscountByID(ctx, req.(*GetDiscountByIDRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _DiscountService_CreateDiscount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(CreateDiscountRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DiscountServiceServer).CreateDiscount(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: DiscountService_CreateDiscount_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DiscountServiceServer).CreateDiscount(ctx, req.(*CreateDiscountRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _DiscountService_ReplaceDiscount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(DiscountOptional)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DiscountServiceServer).ReplaceDiscount(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: DiscountService_ReplaceDiscount_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DiscountServiceServer).ReplaceDiscount(ctx, req.(*DiscountOptional))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _DiscountService_UpdateDiscount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(DiscountOptional)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DiscountServiceServer).UpdateDiscount(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: DiscountService_UpdateDiscount_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DiscountServiceServer).UpdateDiscount(ctx, req.(*DiscountOptional))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _DiscountService_DeleteDiscount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetDiscountByIDRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DiscountServiceServer).DeleteDiscount(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: DiscountService_DeleteDiscount_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DiscountServiceServer).DeleteDiscount(ctx, req.(*GetDiscountByIDRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscountService_ServiceDesc is the grpc.ServiceDesc for DiscountService service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var DiscountService_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "discount.DiscountService",
|
||||||
|
HandlerType: (*DiscountServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "GetAllDiscounts",
|
||||||
|
Handler: _DiscountService_GetAllDiscounts_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetUserDiscounts",
|
||||||
|
Handler: _DiscountService_GetUserDiscounts_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "DetermineDiscounts",
|
||||||
|
Handler: _DiscountService_DetermineDiscounts_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "ApplyDiscounts",
|
||||||
|
Handler: _DiscountService_ApplyDiscounts_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetDiscountByID",
|
||||||
|
Handler: _DiscountService_GetDiscountByID_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "CreateDiscount",
|
||||||
|
Handler: _DiscountService_CreateDiscount_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "ReplaceDiscount",
|
||||||
|
Handler: _DiscountService_ReplaceDiscount_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "UpdateDiscount",
|
||||||
|
Handler: _DiscountService_UpdateDiscount_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "DeleteDiscount",
|
||||||
|
Handler: _DiscountService_DeleteDiscount_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "discount/service.proto",
|
||||||
|
}
|
@ -46,6 +46,17 @@ func (r *CodewordRepository) StoreRecoveryRecord(ctx context.Context, deps model
|
|||||||
|
|
||||||
// добавляем в очередь данные для отправки на почту в редис
|
// добавляем в очередь данные для отправки на почту в редис
|
||||||
func (r *CodewordRepository) InsertToQueue(ctx context.Context, deps models.RecEmailDeps) error {
|
func (r *CodewordRepository) InsertToQueue(ctx context.Context, deps models.RecEmailDeps) error {
|
||||||
|
sendLockKey := "email:sendLock:" + deps.Email
|
||||||
|
ttl := 5 * time.Minute
|
||||||
|
|
||||||
|
lockSuccess, err := r.rdb.SetNX(ctx, sendLockKey, "1", ttl).Result()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !lockSuccess {
|
||||||
|
return ErrAlreadyReported
|
||||||
|
}
|
||||||
|
|
||||||
task := models.RecoveryRecord{
|
task := models.RecoveryRecord{
|
||||||
ID: deps.ID,
|
ID: deps.ID,
|
||||||
UserID: deps.UserID,
|
UserID: deps.UserID,
|
||||||
@ -58,11 +69,7 @@ func (r *CodewordRepository) InsertToQueue(ctx context.Context, deps models.RecE
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.rdb.LPush(ctx, "recoveryQueue", taskBytes).Err(); err != nil {
|
return r.rdb.Set(ctx, "email:task:"+deps.Email, taskBytes, ttl).Err()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// получаем данные юзера по подписи
|
// получаем данные юзера по подписи
|
||||||
@ -81,6 +88,8 @@ func (r *CodewordRepository) GetRecoveryRecord(ctx context.Context, key string)
|
|||||||
|
|
||||||
// пингует в монгу чтобы проверить подключение
|
// пингует в монгу чтобы проверить подключение
|
||||||
func (r *CodewordRepository) Ping(ctx context.Context) error {
|
func (r *CodewordRepository) Ping(ctx context.Context) error {
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
if err := r.mdb.Database().Client().Ping(ctx, readpref.Primary()); err != nil {
|
if err := r.mdb.Database().Client().Ping(ctx, readpref.Primary()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
13
internal/repository/errors.go
Normal file
13
internal/repository/errors.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrPromoUserNotFound = errors.New("user not found")
|
||||||
|
ErrAlreadyReported = errors.New("already reported")
|
||||||
|
ErrDuplicateCodeword = errors.New("duplicate codeword")
|
||||||
|
ErrPromoCodeNotFound = errors.New("promo code not found")
|
||||||
|
ErrPromoCodeExpired = errors.New("promo code is expired")
|
||||||
|
ErrPromoCodeExhausted = errors.New("promo code is exhausted")
|
||||||
|
ErrPromoCodeAlreadyActivated = errors.New("promo code is already activated")
|
||||||
|
)
|
@ -3,7 +3,6 @@ package repository
|
|||||||
import (
|
import (
|
||||||
"codeword/internal/models"
|
"codeword/internal/models"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"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"
|
||||||
@ -11,11 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
ErrDuplicateCodeword = errors.New("duplicate codeword")
|
|
||||||
ErrPromoCodeNotFound = errors.New("promo code not found")
|
|
||||||
)
|
|
||||||
|
|
||||||
// структура для горутины чтобы ошибки не пропускать
|
// структура для горутины чтобы ошибки не пропускать
|
||||||
type countResult struct {
|
type countResult struct {
|
||||||
count int64
|
count int64
|
||||||
@ -27,7 +21,10 @@ type PromoCodeRepository struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewPromoCodeRepository(mdb *mongo.Collection) *PromoCodeRepository {
|
func NewPromoCodeRepository(mdb *mongo.Collection) *PromoCodeRepository {
|
||||||
// todo заменить паники вроде как в роде не круто их юзать
|
return &PromoCodeRepository{mdb: mdb}
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitPromoCodeIndexes(ctx context.Context, mdb *mongo.Collection) error {
|
||||||
uniqueIndexModel := mongo.IndexModel{
|
uniqueIndexModel := mongo.IndexModel{
|
||||||
Keys: bson.D{
|
Keys: bson.D{
|
||||||
{Key: "codeword", Value: 1},
|
{Key: "codeword", Value: 1},
|
||||||
@ -35,9 +32,9 @@ func NewPromoCodeRepository(mdb *mongo.Collection) *PromoCodeRepository {
|
|||||||
},
|
},
|
||||||
Options: options.Index().SetUnique(true).SetPartialFilterExpression(bson.M{"delete": false}),
|
Options: options.Index().SetUnique(true).SetPartialFilterExpression(bson.M{"delete": false}),
|
||||||
}
|
}
|
||||||
_, err := mdb.Indexes().CreateOne(context.Background(), uniqueIndexModel)
|
_, err := mdb.Indexes().CreateOne(ctx, uniqueIndexModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
textIndexModel := mongo.IndexModel{
|
textIndexModel := mongo.IndexModel{
|
||||||
@ -48,17 +45,20 @@ func NewPromoCodeRepository(mdb *mongo.Collection) *PromoCodeRepository {
|
|||||||
},
|
},
|
||||||
Options: options.Index().SetName("TextIndex"),
|
Options: options.Index().SetName("TextIndex"),
|
||||||
}
|
}
|
||||||
_, err = mdb.Indexes().CreateOne(context.Background(), textIndexModel)
|
_, err = mdb.Indexes().CreateOne(ctx, textIndexModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &PromoCodeRepository{mdb: mdb}
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PromoCodeRepository) CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error) {
|
func (r *PromoCodeRepository) CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error) {
|
||||||
req.CreatedAt = time.Now()
|
req.CreatedAt = time.Now()
|
||||||
req.ID = primitive.NewObjectID()
|
req.ID = primitive.NewObjectID()
|
||||||
|
if req.FastLinks == nil {
|
||||||
|
req.FastLinks = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
_, err := r.mdb.InsertOne(ctx, req)
|
_, err := r.mdb.InsertOne(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -214,48 +214,51 @@ func (r *PromoCodeRepository) GetPromoCodesList(ctx context.Context, req *models
|
|||||||
return promoCodes, count, nil
|
return promoCodes, count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PromoCodeRepository) ActivatePromo(ctx context.Context, req *models.ActivateReq) (string, error) {
|
func (r *PromoCodeRepository) ActivatePromo(ctx context.Context, req *models.ActivateReq) (*models.PromoCode, error) {
|
||||||
session, err := r.mdb.Database().Client().StartSession()
|
var promoCode models.PromoCode
|
||||||
|
|
||||||
|
var filter bson.M
|
||||||
|
if req.Codeword != "" {
|
||||||
|
filter = bson.M{
|
||||||
|
"codeword": req.Codeword,
|
||||||
|
}
|
||||||
|
} else if req.FastLink != "" {
|
||||||
|
filter = bson.M{
|
||||||
|
"fastLinks": req.FastLink,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := options.FindOneAndUpdate().SetReturnDocument(options.After)
|
||||||
|
|
||||||
|
err := r.mdb.FindOneAndUpdate(ctx, filter, bson.M{"$inc": bson.M{"activationCount": -1}}, opts).Decode(&promoCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
if err == mongo.ErrNoDocuments {
|
||||||
|
return nil, ErrPromoCodeNotFound
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
defer session.EndSession(ctx)
|
|
||||||
|
|
||||||
var greetings string
|
if promoCode.ActivationCount <= 0 && promoCode.DueTo > time.Now().Unix() {
|
||||||
|
if !promoCode.OffLimit {
|
||||||
transactionErr := mongo.WithSession(ctx, session, func(sc mongo.SessionContext) error {
|
update := bson.M{"$set": bson.M{"offLimit": true}}
|
||||||
filter := bson.M{
|
_, err := r.mdb.UpdateOne(ctx, filter, update)
|
||||||
"codeword": req.Codeword,
|
if err != nil {
|
||||||
"delete": false,
|
return nil, err
|
||||||
"outdated": false,
|
|
||||||
"offLimit": false,
|
|
||||||
"activationCount": bson.M{"$gt": 0},
|
|
||||||
"dueTo": bson.M{"$gt": time.Now().Unix()},
|
|
||||||
}
|
|
||||||
update := bson.M{
|
|
||||||
"$inc": bson.M{"activationCount": -1},
|
|
||||||
}
|
|
||||||
opts := options.FindOneAndUpdate().SetReturnDocument(options.After)
|
|
||||||
|
|
||||||
var updatedPromoCode models.PromoCode
|
|
||||||
err := r.mdb.FindOneAndUpdate(sc, filter, update, opts).Decode(&updatedPromoCode)
|
|
||||||
if err != nil {
|
|
||||||
if err == mongo.ErrNoDocuments {
|
|
||||||
return ErrPromoCodeNotFound
|
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return &promoCode, nil
|
||||||
|
}
|
||||||
|
|
||||||
greetings = updatedPromoCode.Greetings
|
func (r *PromoCodeRepository) IncreaseActivationCount(ctx context.Context, promoCodeID primitive.ObjectID) error {
|
||||||
|
filter := bson.M{"_id": promoCodeID}
|
||||||
return nil
|
update := bson.M{"$inc": bson.M{"activationCount": 1}}
|
||||||
})
|
_, err := r.mdb.UpdateOne(ctx, filter, update)
|
||||||
|
if err != nil {
|
||||||
if transactionErr != nil {
|
return err
|
||||||
return "", transactionErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return greetings, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PromoCodeRepository) DeletePromoCode(ctx context.Context, promoCodeID string) error {
|
func (r *PromoCodeRepository) DeletePromoCode(ctx context.Context, promoCodeID string) error {
|
||||||
@ -279,3 +282,19 @@ func (r *PromoCodeRepository) DeletePromoCode(ctx context.Context, promoCodeID s
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *PromoCodeRepository) AddFastLink(ctx context.Context, promoCodeID primitive.ObjectID, xid string) error {
|
||||||
|
filter := bson.M{"_id": promoCodeID, "delete": false}
|
||||||
|
update := bson.M{"$push": bson.M{"fastLinks": xid}}
|
||||||
|
|
||||||
|
result, err := r.mdb.UpdateOne(ctx, filter, update)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.MatchedCount == 0 {
|
||||||
|
return ErrPromoCodeNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
72
internal/repository/promocode_stats.go
Normal file
72
internal/repository/promocode_stats.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeword/internal/models"
|
||||||
|
"context"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StatsRepository struct {
|
||||||
|
mdb *mongo.Collection
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStatsRepository(deps Deps) *StatsRepository {
|
||||||
|
|
||||||
|
return &StatsRepository{mdb: deps.Mdb}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *StatsRepository) UpdateStatistics(ctx context.Context, req *models.ActivateReq, promoCode *models.PromoCode, userID string) error {
|
||||||
|
filter := bson.M{"_id": promoCode.ID, "usageMap." + userID: bson.M{"$exists": true}}
|
||||||
|
count, err := r.mdb.CountDocuments(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if count >= 1 {
|
||||||
|
return ErrPromoCodeAlreadyActivated
|
||||||
|
}
|
||||||
|
|
||||||
|
var key string
|
||||||
|
if req.FastLink != "" {
|
||||||
|
key = req.FastLink
|
||||||
|
} else {
|
||||||
|
key = req.Codeword
|
||||||
|
}
|
||||||
|
|
||||||
|
usage := models.Usage{
|
||||||
|
Key: key,
|
||||||
|
Time: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
update := bson.M{
|
||||||
|
"$inc": bson.M{"usageCount": 1},
|
||||||
|
"$push": bson.M{
|
||||||
|
"usageMap." + userID: usage,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := options.Update().SetUpsert(true)
|
||||||
|
_, err = r.mdb.UpdateOne(ctx, bson.M{"_id": promoCode.ID}, update, opts)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *StatsRepository) GetStatistics(ctx context.Context, promoCodeID string) (*models.PromoCodeStats, error) {
|
||||||
|
objID, err := primitive.ObjectIDFromHex(promoCodeID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
filter := bson.M{"_id": objID}
|
||||||
|
|
||||||
|
var promoCodeStats models.PromoCodeStats
|
||||||
|
err = r.mdb.FindOne(ctx, filter).Decode(&promoCodeStats)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &promoCodeStats, nil
|
||||||
|
}
|
@ -31,7 +31,7 @@ func (r *UserRepository) FindByEmail(ctx context.Context, email string) (*models
|
|||||||
if err == mongo.ErrNoDocuments {
|
if err == mongo.ErrNoDocuments {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, ErrPromoUserNotFound
|
||||||
}
|
}
|
||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,30 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"codeword/internal/controller/promocode"
|
|
||||||
"codeword/internal/controller/recovery"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
RecoveryController *recovery.RecoveryController
|
Controllers []Controller
|
||||||
PromoCodeController *promocode.PromoCodeController
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
RecoveryController *recovery.RecoveryController
|
Controllers []Controller
|
||||||
PromoCodeController *promocode.PromoCodeController
|
app *fiber.App
|
||||||
app *fiber.App
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(config ServerConfig) *Server {
|
func NewServer(config ServerConfig) *Server {
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
Logger: config.Logger,
|
Logger: config.Logger,
|
||||||
RecoveryController: config.RecoveryController,
|
Controllers: config.Controllers,
|
||||||
PromoCodeController: config.PromoCodeController,
|
app: app,
|
||||||
app: app,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.registerRoutes()
|
s.registerRoutes()
|
||||||
@ -51,33 +45,22 @@ func (s *Server) Shutdown(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) registerRoutes() {
|
func (s *Server) registerRoutes() {
|
||||||
s.app.Get("/liveness", s.handleLiveness)
|
for _, c := range s.Controllers {
|
||||||
s.app.Get("/readiness", s.handleReadiness)
|
router := s.app.Group(c.Name())
|
||||||
|
c.Register(router)
|
||||||
s.app.Post("/recover", s.RecoveryController.HandleRecoveryRequest)
|
}
|
||||||
s.app.Get("/recover/:sign", s.RecoveryController.HandleRecoveryLink)
|
}
|
||||||
|
|
||||||
s.app.Post("/promocode/create", s.PromoCodeController.CreatePromoCode)
|
type Controller interface {
|
||||||
s.app.Put("/promocode/edit", s.PromoCodeController.EditPromoCode)
|
Register(router fiber.Router)
|
||||||
s.app.Post("/promocode/getList", s.PromoCodeController.GetList)
|
Name() string
|
||||||
s.app.Post("/promocode/activate", s.PromoCodeController.Activate)
|
}
|
||||||
s.app.Delete("/promocode/:promocodeID", s.PromoCodeController.Delete)
|
|
||||||
//... other
|
func (s *Server) ListRoutes() {
|
||||||
}
|
fmt.Println("Registered routes:")
|
||||||
|
for _, stack := range s.app.Stack() {
|
||||||
func (s *Server) handleLiveness(c *fiber.Ctx) error {
|
for _, route := range stack {
|
||||||
return c.SendStatus(fiber.StatusOK)
|
fmt.Printf("%s %s\n", route.Method, route.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleReadiness(c *fiber.Ctx) error {
|
|
||||||
startTime := time.Now()
|
|
||||||
if err := s.RecoveryController.HandlePingDB(c); err != nil {
|
|
||||||
s.Logger.Error("Failed to ping the database", zap.Error(err))
|
|
||||||
return c.Status(fiber.StatusServiceUnavailable).SendString("DB ping failed")
|
|
||||||
}
|
}
|
||||||
duration := time.Since(startTime)
|
|
||||||
durationMillis := duration.Milliseconds()
|
|
||||||
responseMessage := fmt.Sprintf("DB ping success - Time taken: %d ms", durationMillis)
|
|
||||||
|
|
||||||
return c.Status(fiber.StatusOK).SendString(responseMessage)
|
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,58 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"codeword/internal/kafka/tariff"
|
||||||
"codeword/internal/models"
|
"codeword/internal/models"
|
||||||
|
"codeword/internal/proto/discount"
|
||||||
|
"codeword/internal/repository"
|
||||||
|
"codeword/internal/utils/genID"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PromoCodeRepository interface {
|
type PromoCodeRepository interface {
|
||||||
CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error)
|
CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error)
|
||||||
EditPromoCode(ctx context.Context, req *models.ReqEditPromoCode) (*models.PromoCode, error)
|
EditPromoCode(ctx context.Context, req *models.ReqEditPromoCode) (*models.PromoCode, error)
|
||||||
GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error)
|
GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error)
|
||||||
ActivatePromo(ctx context.Context, req *models.ActivateReq) (string, error)
|
ActivatePromo(ctx context.Context, req *models.ActivateReq) (*models.PromoCode, error)
|
||||||
DeletePromoCode(ctx context.Context, promoCodeID string) error
|
DeletePromoCode(ctx context.Context, promoCodeID string) error
|
||||||
|
GetPromoCodeByID(ctx context.Context, promoCodeID primitive.ObjectID) (*models.PromoCode, error)
|
||||||
|
AddFastLink(ctx context.Context, promoCodeID primitive.ObjectID, xid string) error
|
||||||
|
IncreaseActivationCount(ctx context.Context, promoCodeID primitive.ObjectID) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type PromoStatsRepository interface {
|
||||||
|
UpdateStatistics(ctx context.Context, req *models.ActivateReq, promoCode *models.PromoCode, userID string) error
|
||||||
|
GetStatistics(ctx context.Context, promoCodeID string) (*models.PromoCodeStats, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PromoDeps struct {
|
type PromoDeps struct {
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
PromoCodeRepo PromoCodeRepository
|
PromoCodeRepo PromoCodeRepository
|
||||||
|
StatsRepo PromoStatsRepository
|
||||||
|
Kafka *tariff.Producer
|
||||||
|
DiscountClient discount.DiscountServiceClient
|
||||||
}
|
}
|
||||||
|
|
||||||
type PromoCodeService struct {
|
type PromoCodeService struct {
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
promoCodeRepo PromoCodeRepository
|
promoCodeRepo PromoCodeRepository
|
||||||
|
statsRepo PromoStatsRepository
|
||||||
|
kafka *tariff.Producer
|
||||||
|
discountClient discount.DiscountServiceClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPromoCodeService(deps PromoDeps) *PromoCodeService {
|
func NewPromoCodeService(deps PromoDeps) *PromoCodeService {
|
||||||
return &PromoCodeService{
|
return &PromoCodeService{
|
||||||
logger: deps.Logger,
|
logger: deps.Logger,
|
||||||
promoCodeRepo: deps.PromoCodeRepo,
|
promoCodeRepo: deps.PromoCodeRepo,
|
||||||
|
statsRepo: deps.StatsRepo,
|
||||||
|
kafka: deps.Kafka,
|
||||||
|
discountClient: deps.DiscountClient,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,14 +86,92 @@ func (s *PromoCodeService) GetPromoCodesList(ctx context.Context, req *models.Ge
|
|||||||
return promoCodes, count, nil
|
return promoCodes, count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.ActivateReq) (string, error) {
|
// todo одумать еще реализацию этого дела, надо уточнить как разделяется ответственность в бонусе между привилегией и скидкой
|
||||||
greetings, err := s.promoCodeRepo.ActivatePromo(ctx, req)
|
// разделяется ли она или они всегда вместе, если разделяются то что-то из этого может быть пустым либо все заполеннное,
|
||||||
|
// соответсвенно надо сделать соответствующие проверки до записи в кафку и до отправки в дискаунт сервис
|
||||||
|
|
||||||
|
func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.ActivateReq, userID string) (string, error) {
|
||||||
|
promoCode, err := s.promoCodeRepo.ActivatePromo(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to activate promocode", zap.Error(err))
|
s.logger.Error("Failed to activate promocode", zap.Error(err))
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
//todo такая реализация проверок кажется довольно массивной, думаю как то это стоит сделать параллельно обхаживая все условия
|
||||||
|
if promoCode.DueTo < time.Now().Unix() && promoCode.DueTo > 0 {
|
||||||
|
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("%w: expired on %s", repository.ErrPromoCodeExpired, time.Unix(promoCode.DueTo, 0).Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
|
||||||
return greetings, nil
|
if promoCode.DueTo == 0 && promoCode.ActivationCount < 0 {
|
||||||
|
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return "", repository.ErrPromoCodeExhausted
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.statsRepo.UpdateStatistics(ctx, req, promoCode, userID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, repository.ErrPromoCodeAlreadyActivated) {
|
||||||
|
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return "", repository.ErrPromoCodeAlreadyActivated
|
||||||
|
}
|
||||||
|
s.logger.Error("Failed add in stats", zap.Error(err))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var postfix string
|
||||||
|
|
||||||
|
if req.FastLink != "" {
|
||||||
|
postfix = fmt.Sprintf(":(%s)", req.FastLink)
|
||||||
|
}
|
||||||
|
|
||||||
|
var privileges []models.Privilege
|
||||||
|
privilege := models.Privilege{
|
||||||
|
PrivilegeID: promoCode.Bonus.Privilege.PrivilegeID,
|
||||||
|
Amount: promoCode.Bonus.Privilege.Amount,
|
||||||
|
}
|
||||||
|
privileges = append(privileges, privilege)
|
||||||
|
|
||||||
|
fakeTariff := &models.Tariff{
|
||||||
|
Name: promoCode.Codeword + postfix,
|
||||||
|
Privileges: privileges,
|
||||||
|
Deleted: promoCode.Delete,
|
||||||
|
CreatedAt: promoCode.CreatedAt,
|
||||||
|
}
|
||||||
|
if err := s.kafka.Send(ctx, userID, fakeTariff); err != nil {
|
||||||
|
s.logger.Error("Failed to send fake tariff to Kafka", zap.Error(err))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
disOverHelm := true
|
||||||
|
discountRequest := &discount.CreateDiscountRequest{
|
||||||
|
Name: promoCode.Codeword + postfix,
|
||||||
|
Layer: promoCode.Bonus.Discount.Layer,
|
||||||
|
Description: "",
|
||||||
|
Condition: &discount.DiscountCondition{
|
||||||
|
Coupon: &promoCode.Codeword,
|
||||||
|
User: &userID,
|
||||||
|
},
|
||||||
|
Target: &discount.DiscountCalculationTarget{
|
||||||
|
Factor: promoCode.Bonus.Discount.Factor,
|
||||||
|
Overhelm: &disOverHelm,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.discountClient.CreateDiscount(ctx, discountRequest)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to create discount", zap.Error(err))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return promoCode.Greetings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PromoCodeService) DeletePromoCode(ctx context.Context, promoCodeID string) error {
|
func (s *PromoCodeService) DeletePromoCode(ctx context.Context, promoCodeID string) error {
|
||||||
@ -80,3 +183,29 @@ func (s *PromoCodeService) DeletePromoCode(ctx context.Context, promoCodeID stri
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PromoCodeService) CreateFastLink(ctx context.Context, promoCodeID string) (string, error) {
|
||||||
|
xid := genID.GenerateXID()
|
||||||
|
promoID, err := primitive.ObjectIDFromHex(promoCodeID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed conversion promoCodeID to ObjectID", zap.Error(err))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.promoCodeRepo.AddFastLink(ctx, promoID, xid)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to add fastlink for promocode by promocode id", zap.Error(err))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return xid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PromoCodeService) GetStats(ctx context.Context, promoCodeID string) (*models.PromoCodeStats, error) {
|
||||||
|
promoStats, err := s.statsRepo.GetStatistics(ctx, promoCodeID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed getting promo stats", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return promoStats, nil
|
||||||
|
}
|
||||||
|
@ -73,10 +73,6 @@ func (s *RecoveryService) FindUserByEmail(ctx context.Context, email string) (*m
|
|||||||
s.logger.Error("Failed to find user by email", zap.String("email", email), zap.Error(err))
|
s.logger.Error("Failed to find user by email", zap.String("email", email), zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if user == nil {
|
|
||||||
s.logger.Info("No user found with email", zap.String("email", email))
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
8
internal/utils/genID/gen_id.go
Normal file
8
internal/utils/genID/gen_id.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package genID
|
||||||
|
|
||||||
|
import "github.com/rs/xid"
|
||||||
|
|
||||||
|
func GenerateXID() string {
|
||||||
|
id := xid.New()
|
||||||
|
return id.String()
|
||||||
|
}
|
31
internal/utils/transfer/privilege.go
Normal file
31
internal/utils/transfer/privilege.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package transfer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeword/internal/models"
|
||||||
|
"codeword/internal/proto/broker"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PrivilegeModelToProto(privilege *models.Privilege) *broker.PrivilegeMessage {
|
||||||
|
if privilege == nil {
|
||||||
|
return &broker.PrivilegeMessage{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &broker.PrivilegeMessage{
|
||||||
|
PrivilegeID: privilege.PrivilegeID,
|
||||||
|
ServiceKey: privilege.ServiceKey,
|
||||||
|
Type: models.PrivilegeBrokerTypeMap[privilege.Type],
|
||||||
|
Value: privilege.Value,
|
||||||
|
Amount: privilege.Amount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrivilegeArrayModelToProto(privileges []models.Privilege) []*broker.PrivilegeMessage {
|
||||||
|
privilegesProto := make([]*broker.PrivilegeMessage, len(privileges))
|
||||||
|
|
||||||
|
for index, privilege := range privileges {
|
||||||
|
privilegeCopy := privilege
|
||||||
|
privilegesProto[index] = PrivilegeModelToProto(&privilegeCopy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return privilegesProto
|
||||||
|
}
|
17
internal/utils/transfer/tariff.go
Normal file
17
internal/utils/transfer/tariff.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package transfer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeword/internal/models"
|
||||||
|
"codeword/internal/proto/broker"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TariffModelToProtoMessage(userID string, tariffModel *models.Tariff) *broker.TariffMessage {
|
||||||
|
if tariffModel == nil {
|
||||||
|
return &broker.TariffMessage{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &broker.TariffMessage{
|
||||||
|
UserID: userID,
|
||||||
|
Privileges: PrivilegeArrayModelToProto(tariffModel.Privileges),
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ type PurgeWorker struct {
|
|||||||
mongo *mongo.Collection
|
mongo *mongo.Collection
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRecoveryWC(deps Deps) *PurgeWorker {
|
func NewPurgeWC(deps Deps) *PurgeWorker {
|
||||||
return &PurgeWorker{
|
return &PurgeWorker{
|
||||||
logger: deps.Logger,
|
logger: deps.Logger,
|
||||||
mongo: deps.Mongo,
|
mongo: deps.Mongo,
|
||||||
@ -54,3 +54,7 @@ func (wc *PurgeWorker) processTasks(ctx context.Context) {
|
|||||||
wc.logger.Info("Deleted documents", zap.Int64("count", result.DeletedCount))
|
wc.logger.Info("Deleted documents", zap.Int64("count", result.DeletedCount))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wc *PurgeWorker) Stop(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -37,14 +37,13 @@ func NewRecoveryWC(deps Deps) *RecoveryWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (wc *RecoveryWorker) Start(ctx context.Context) {
|
func (wc *RecoveryWorker) Start(ctx context.Context) {
|
||||||
ticker := time.NewTicker(1 * time.Second)
|
ticker := time.NewTicker(10 * time.Second)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
wc.processTasks(ctx)
|
wc.processTasks(ctx)
|
||||||
|
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -52,29 +51,39 @@ func (wc *RecoveryWorker) Start(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (wc *RecoveryWorker) processTasks(ctx context.Context) {
|
func (wc *RecoveryWorker) processTasks(ctx context.Context) {
|
||||||
result, err := wc.redis.BRPop(ctx, 1*time.Second, "recoveryQueue").Result()
|
var cursor uint64
|
||||||
if err != nil {
|
for {
|
||||||
if err != redis.Nil {
|
var keys []string
|
||||||
wc.logger.Error("Failed to BRPop from the recovery queue", zap.Error(err))
|
var err error
|
||||||
|
keys, cursor, err = wc.redis.Scan(ctx, cursor, "email:task:*", 0).Result()
|
||||||
|
if err != nil {
|
||||||
|
wc.logger.Error("Failed to scan for email tasks", zap.Error(err))
|
||||||
|
break
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(result) < 2 {
|
for _, key := range keys {
|
||||||
wc.logger.Error("Received unexpected number of elements from BRPop", zap.Strings("result", result))
|
taskBytes, err := wc.redis.GetDel(ctx, key).Result()
|
||||||
return
|
if err == redis.Nil {
|
||||||
}
|
continue
|
||||||
|
} else if err != nil {
|
||||||
|
wc.logger.Error("Failed to getdel recovery task", zap.String("key", key), zap.Error(err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
var task models.RecoveryRecord
|
var task models.RecoveryRecord
|
||||||
if err = json.Unmarshal([]byte(result[1]), &task); err != nil {
|
if json.Unmarshal([]byte(taskBytes), &task) != nil {
|
||||||
wc.logger.Error("Failed to unmarshal recovery task", zap.String("key", result[0]), zap.Error(err))
|
wc.logger.Error("Failed to unmarshal recovery task", zap.String("key", key), zap.String("task", taskBytes))
|
||||||
return
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err = wc.sendRecoveryTask(ctx, task)
|
err = wc.sendRecoveryTask(ctx, task)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wc.logger.Error("Failed to send recovery task", zap.String("key", result[0]), zap.Error(err))
|
wc.logger.Error("Failed to send recovery task", zap.String("key", key), zap.Error(err))
|
||||||
return
|
}
|
||||||
|
}
|
||||||
|
if cursor == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,3 +123,7 @@ func (wc *RecoveryWorker) sendRecoveryTask(ctx context.Context, task models.Reco
|
|||||||
//wc.logger.Info("Recovery email sent and restore request updated successfully", zap.String("email", task.Email))
|
//wc.logger.Info("Recovery email sent and restore request updated successfully", zap.String("email", task.Email))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wc *RecoveryWorker) Stop(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
37
pkg/closer/closer.go
Normal file
37
pkg/closer/closer.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package closer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Closer interface {
|
||||||
|
Close(ctx context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloserFunc func(ctx context.Context) error
|
||||||
|
|
||||||
|
func (cf CloserFunc) Close(ctx context.Context) error {
|
||||||
|
return cf(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloserGroup struct {
|
||||||
|
closers []Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCloserGroup() *CloserGroup {
|
||||||
|
return &CloserGroup{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cg *CloserGroup) Add(c Closer) {
|
||||||
|
cg.closers = append(cg.closers, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cg *CloserGroup) Call(ctx context.Context) error {
|
||||||
|
var closeErr error
|
||||||
|
for i := len(cg.closers) - 1; i >= 0; i-- {
|
||||||
|
if err := cg.closers[i].Close(ctx); err != nil && closeErr == nil {
|
||||||
|
closeErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closeErr
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
package mongo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Configuration struct {
|
|
||||||
MongoHost string `env:"MONGO_HOST" envDefault:"127.0.0.1"`
|
|
||||||
MongoPort string `env:"MONGO_PORT" envDefault:"27020"`
|
|
||||||
MongoUser string `env:"MONGO_USER" envDefault:"test"`
|
|
||||||
MongoPassword string `env:"MONGO_PASSWORD" envDefault:"test"`
|
|
||||||
MongoDatabase string `env:"MONGO_DB" envDefault:"admin"`
|
|
||||||
MongoAuth string `env:"MONGO_AUTH" envDefault:"admin"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RequestSettings struct {
|
|
||||||
Driver *mongo.Collection
|
|
||||||
Options *options.FindOptions
|
|
||||||
Filter primitive.M
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
package mongo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConnectDeps struct {
|
|
||||||
Configuration *Configuration
|
|
||||||
Timeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func Connect(ctx context.Context, deps *ConnectDeps) (*mongo.Database, error) {
|
|
||||||
if deps == nil {
|
|
||||||
return nil, ErrEmptyArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
mongoURI := &url.URL{
|
|
||||||
Scheme: "mongodb",
|
|
||||||
Host: net.JoinHostPort(deps.Configuration.MongoHost, deps.Configuration.MongoPort),
|
|
||||||
}
|
|
||||||
|
|
||||||
connectionOptions := options.Client().
|
|
||||||
ApplyURI(mongoURI.String()).
|
|
||||||
SetAuth(options.Credential{
|
|
||||||
AuthMechanism: "SCRAM-SHA-256",
|
|
||||||
AuthSource: deps.Configuration.MongoAuth,
|
|
||||||
Username: deps.Configuration.MongoUser,
|
|
||||||
Password: deps.Configuration.MongoPassword,
|
|
||||||
})
|
|
||||||
|
|
||||||
ticker := time.NewTicker(1 * time.Second)
|
|
||||||
timeoutExceeded := time.After(deps.Timeout)
|
|
||||||
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ticker.C:
|
|
||||||
connection, err := mongo.Connect(ctx, connectionOptions)
|
|
||||||
if err == nil {
|
|
||||||
return connection.Database(deps.Configuration.MongoDatabase), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("failed to connect to db <%s>: %s", mongoURI.String(), err.Error())
|
|
||||||
case <-timeoutExceeded:
|
|
||||||
return nil, fmt.Errorf("db connection <%s> failed after %d timeout", mongoURI.String(), deps.Timeout)
|
|
||||||
default:
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package mongo
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrEmptyArgs = errors.New("arguments are empty")
|
|
||||||
)
|
|
599
tests/e2e/promo_test.go
Normal file
599
tests/e2e/promo_test.go
Normal file
@ -0,0 +1,599 @@
|
|||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeword/internal/models"
|
||||||
|
"codeword/tests/helpers"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/pioz/faker"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var promoID string
|
||||||
|
var fastLink string
|
||||||
|
|
||||||
|
// CreatePromoCode
|
||||||
|
func TestCreatePromoCode(t *testing.T) {
|
||||||
|
client := fiber.AcquireClient()
|
||||||
|
|
||||||
|
t.Run("CreatePromoCode-success", func(t *testing.T) {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
jsonString := `{
|
||||||
|
"codeword": "example",
|
||||||
|
"description": "Example description",
|
||||||
|
"greetings": "Example greetings",
|
||||||
|
"dueTo": 1734429225,
|
||||||
|
"activationCount": 100,
|
||||||
|
"bonus": {
|
||||||
|
"privilege": {
|
||||||
|
"privilegeID": "examplePrivilegeID",
|
||||||
|
"amount": 50
|
||||||
|
},
|
||||||
|
"discount": {
|
||||||
|
"layer": 1,
|
||||||
|
"factor": 0.2,
|
||||||
|
"target": "exampleTarget",
|
||||||
|
"threshold": 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outdated": false,
|
||||||
|
"offLimit": false,
|
||||||
|
"delete": false
|
||||||
|
}`
|
||||||
|
|
||||||
|
var reqBody models.PromoCode
|
||||||
|
err := json.Unmarshal([]byte(jsonString), &reqBody)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if i != 0 {
|
||||||
|
reqBody.Codeword = reqBody.Codeword + faker.String() + strconv.Itoa(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/promocode/create").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusCreated, statusCode)
|
||||||
|
|
||||||
|
var response models.PromoCode
|
||||||
|
err = json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
promoID = response.ID.Hex()
|
||||||
|
fmt.Println(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
t.Run("CreatePromoCode-duplicate", func(t *testing.T) {
|
||||||
|
jsonString := `{
|
||||||
|
"codeword": "example",
|
||||||
|
"description": "Example description",
|
||||||
|
"greetings": "Example greetings",
|
||||||
|
"dueTo": 1734429225,
|
||||||
|
"activationCount": 100,
|
||||||
|
"bonus": {
|
||||||
|
"privilege": {
|
||||||
|
"privilegeID": "examplePrivilegeID",
|
||||||
|
"amount": 50
|
||||||
|
},
|
||||||
|
"discount": {
|
||||||
|
"layer": 1,
|
||||||
|
"factor": 0.2,
|
||||||
|
"target": "exampleTarget",
|
||||||
|
"threshold": 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outdated": false,
|
||||||
|
"offLimit": false,
|
||||||
|
"delete": false
|
||||||
|
}`
|
||||||
|
|
||||||
|
var reqBody models.PromoCode
|
||||||
|
err := json.Unmarshal([]byte(jsonString), &reqBody)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/promocode/create").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.Error(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
err = json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response["error"])
|
||||||
|
})
|
||||||
|
t.Run("CreatePromoCode-invalid request payload", func(t *testing.T) {
|
||||||
|
jsonString := `{
|
||||||
|
"example": "example",
|
||||||
|
"description": "Example description",
|
||||||
|
"greetings": "Example greetings",
|
||||||
|
"dueTo": 1734429225,
|
||||||
|
"activationCount": 100,
|
||||||
|
"bonus": {
|
||||||
|
"privilege": {
|
||||||
|
"privilegeID": "examplePrivilegeID",
|
||||||
|
"amount": 50
|
||||||
|
},
|
||||||
|
"discount": {
|
||||||
|
"layer": 1,
|
||||||
|
"factor": 0.2,
|
||||||
|
"target": "exampleTarget",
|
||||||
|
"threshold": 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outdated": false,
|
||||||
|
"offLimit": false,
|
||||||
|
"delete": false
|
||||||
|
}`
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/promocode/create").Set("Content-Type", "application/json").Body([]byte(jsonString))
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.Error(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response["error"])
|
||||||
|
})
|
||||||
|
t.Run("CreatePromoCode-nil codeword", func(t *testing.T) {
|
||||||
|
jsonString := `{
|
||||||
|
"description": "Example description",
|
||||||
|
"greetings": "Example greetings",
|
||||||
|
"dueTo": 1734429225,
|
||||||
|
"activationCount": 100,
|
||||||
|
"bonus": {
|
||||||
|
"privilege": {
|
||||||
|
"privilegeID": "examplePrivilegeID",
|
||||||
|
"amount": 50
|
||||||
|
},
|
||||||
|
"discount": {
|
||||||
|
"layer": 1,
|
||||||
|
"factor": 0.2,
|
||||||
|
"target": "exampleTarget",
|
||||||
|
"threshold": 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outdated": false,
|
||||||
|
"offLimit": false,
|
||||||
|
"delete": false
|
||||||
|
}`
|
||||||
|
|
||||||
|
var reqBody models.PromoCode
|
||||||
|
err := json.Unmarshal([]byte(jsonString), &reqBody)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/promocode/create").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.Error(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
err = json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response["error"])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditPromoCode
|
||||||
|
func TestEditPromoCode(t *testing.T) {
|
||||||
|
client := fiber.AcquireClient()
|
||||||
|
|
||||||
|
t.Run("EditPromoCode-success", func(t *testing.T) {
|
||||||
|
reqBody := models.ReqEditPromoCode{
|
||||||
|
ID: promoID,
|
||||||
|
Description: toString("Updated description"),
|
||||||
|
Greetings: toString("Updated greetings"),
|
||||||
|
DueTo: toInt64(1734429225),
|
||||||
|
ActivationCount: toInt64(150),
|
||||||
|
Delete: toBool(false),
|
||||||
|
}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
req := client.Put(BaseUrl+"/promocode/edit").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||||
|
|
||||||
|
var response models.PromoCode
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("EditPromoCode-success one column", func(t *testing.T) {
|
||||||
|
reqBody := models.ReqEditPromoCode{
|
||||||
|
ID: promoID,
|
||||||
|
Greetings: toString("Updated greetings one"),
|
||||||
|
}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
req := client.Put(BaseUrl+"/promocode/edit").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||||
|
|
||||||
|
var response models.PromoCode
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("EditPromoCode-promocod not found", func(t *testing.T) {
|
||||||
|
reqBody := models.ReqEditPromoCode{
|
||||||
|
ID: primitive.NewObjectID().Hex(),
|
||||||
|
Description: toString("Updated description"),
|
||||||
|
Greetings: toString("Updated greetings"),
|
||||||
|
DueTo: toInt64(1734429225),
|
||||||
|
ActivationCount: toInt64(150),
|
||||||
|
Delete: toBool(false),
|
||||||
|
}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
req := client.Put(BaseUrl+"/promocode/edit").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
assert.Equal(t, fiber.StatusNotFound, statusCode)
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response["error"])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("EditPromoCode-invalid request payload", func(t *testing.T) {
|
||||||
|
reqBody := map[string]interface{}{
|
||||||
|
"invalid_field": "example",
|
||||||
|
"description": "Updated description",
|
||||||
|
"greetings": "Updated greetings",
|
||||||
|
"dueTo": 1734429225,
|
||||||
|
"activationCount": 150,
|
||||||
|
"delete": false,
|
||||||
|
}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
req := client.Put(BaseUrl+"/promocode/edit").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response["error"])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func toString(s string) *string {
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
func toInt64(i int64) *int64 {
|
||||||
|
return &i
|
||||||
|
}
|
||||||
|
|
||||||
|
func toBool(b bool) *bool {
|
||||||
|
return &b
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFastLink
|
||||||
|
func TestCreateFastLink(t *testing.T) {
|
||||||
|
client := fiber.AcquireClient()
|
||||||
|
|
||||||
|
t.Run("CreateFastLink-success", func(t *testing.T) {
|
||||||
|
reqBody := struct {
|
||||||
|
PromoCodeID string `json:"id"`
|
||||||
|
}{
|
||||||
|
PromoCodeID: promoID,
|
||||||
|
}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/promocode/fastlink").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
fmt.Println(string(resBody))
|
||||||
|
assert.Equal(t, fiber.StatusCreated, statusCode)
|
||||||
|
|
||||||
|
var response map[string]string
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fastLink = response["fastlink"]
|
||||||
|
fmt.Println(response["fastlink"])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("CreateFastLink-missing promoCodeID", func(t *testing.T) {
|
||||||
|
req := client.Post(BaseUrl+"/promocode/fastlink").Set("Content-Type", "application/json").Body([]byte(`{}`))
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.Error(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response["error"])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("CreateFastLink-promocode not found", func(t *testing.T) {
|
||||||
|
reqBody := map[string]string{"id": primitive.NewObjectID().Hex()}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/promocode/fastlink").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.Error(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusNotFound, statusCode)
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response["error"])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPromoCodesList
|
||||||
|
func TestGetPromoCodesList(t *testing.T) {
|
||||||
|
client := fiber.AcquireClient()
|
||||||
|
|
||||||
|
t.Run("GetPromoCodesList-success", func(t *testing.T) {
|
||||||
|
reqBody := models.GetPromoCodesListReq{
|
||||||
|
Page: 0,
|
||||||
|
Limit: 10,
|
||||||
|
Filter: models.GetPromoCodesListReqFilter{
|
||||||
|
Text: "example",
|
||||||
|
Active: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
req := client.Post(BaseUrl+"/promocode/getList").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||||
|
|
||||||
|
var response models.GetPromoCodesListResp
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("GetPromoCodesList-invalid request payload", func(t *testing.T) {
|
||||||
|
req := client.Post(BaseUrl+"/promocode/getList").Set("Content-Type", "application/json").Body([]byte("invalid json"))
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response["error"])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActivatePromoCode
|
||||||
|
func TestActivatePromoCode(t *testing.T) {
|
||||||
|
client := fiber.AcquireClient()
|
||||||
|
jwtUtil := helpers.InitializeJWT()
|
||||||
|
token, tokenErr := jwtUtil.Create(ExampleUserID)
|
||||||
|
fmt.Println(token)
|
||||||
|
if isNoError := assert.NoError(t, tokenErr); !isNoError {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Run("ActivatePromoCode-success codeword", func(t *testing.T) {
|
||||||
|
reqBody := models.ActivateReq{
|
||||||
|
Codeword: "example",
|
||||||
|
}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(resBody))
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||||
|
fmt.Println(statusCode)
|
||||||
|
var response models.ActivateResp
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ActivatePromoCode-success fastLink", func(t *testing.T) {
|
||||||
|
reqBody := models.ActivateReq{
|
||||||
|
FastLink: fastLink,
|
||||||
|
}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||||
|
var response models.ActivateResp
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ActivatePromoCode-missing userid", func(t *testing.T) {
|
||||||
|
reqBody := models.ActivateReq{
|
||||||
|
Codeword: "example",
|
||||||
|
}
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.Error(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response["error"])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ActivatePromoCode-missing codeword and fastlink", func(t *testing.T) {
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(nil)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.Error(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response["error"])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ActivatePromoCode-promocode not found", func(t *testing.T) {
|
||||||
|
reqBody := models.ActivateReq{
|
||||||
|
Codeword: "none",
|
||||||
|
}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(reqJSON)
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.Error(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusNotFound, statusCode)
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response["error"])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPromoStats
|
||||||
|
func TestGetPromoStats(t *testing.T) {
|
||||||
|
client := fiber.AcquireClient()
|
||||||
|
|
||||||
|
t.Run("GetAllStats", func(t *testing.T) {
|
||||||
|
|
||||||
|
reqBody := struct {
|
||||||
|
PromoCodeID string `json:"id"`
|
||||||
|
}{
|
||||||
|
PromoCodeID: promoID,
|
||||||
|
}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
req := client.Get(BaseUrl+"/promocode/stats").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||||
|
|
||||||
|
var response []models.PromoCodeStats
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePromoCode
|
||||||
|
func TestDeletePromoCode(t *testing.T) {
|
||||||
|
client := fiber.AcquireClient()
|
||||||
|
|
||||||
|
t.Run("DeletePromoCode-success", func(t *testing.T) {
|
||||||
|
|
||||||
|
req := client.Delete(BaseUrl + "/promocode/" + promoID)
|
||||||
|
|
||||||
|
statusCode, _, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("DeletePromoCode-promocode not found", func(t *testing.T) {
|
||||||
|
|
||||||
|
req := client.Delete(BaseUrl + "/promocode/" + primitive.NewObjectID().Hex())
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.Error(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusNotFound, statusCode)
|
||||||
|
|
||||||
|
var response map[string]interface{}
|
||||||
|
err := json.Unmarshal(resBody, &response)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(response["error"])
|
||||||
|
})
|
||||||
|
}
|
122
tests/e2e/recover_test.go
Normal file
122
tests/e2e/recover_test.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeword/internal/models"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// todo добавить другие константы такие как exampleUserID
|
||||||
|
const (
|
||||||
|
BaseUrl = "http://localhost:8080"
|
||||||
|
ValidSign = "GSiyv5zBITGshqnvYLHKtXE3e4yZjKGvruOVFWuUuj9Nvaps28-Zt6RDq9n47eaNUlay1-nUVld61I3xoAAgCA==65b286c2f13095d96792079d"
|
||||||
|
ExampleUserID = "6597babdd1ba7e2dbd32d7e3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// post handler
|
||||||
|
func TestRecoveryHandler(t *testing.T) {
|
||||||
|
client := fiber.AcquireClient()
|
||||||
|
|
||||||
|
t.Run("HandleRecoveryRequest", func(t *testing.T) {
|
||||||
|
reqBody := models.RecoveryRequest{
|
||||||
|
Email: "adminSOLO",
|
||||||
|
RedirectionURL: "http://redirect.com",
|
||||||
|
}
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/recover").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, resBody, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||||
|
|
||||||
|
var responseMap map[string]interface{}
|
||||||
|
err := json.Unmarshal(resBody, &responseMap)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
fmt.Println(responseMap)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("HandleRecoveryRequest-AlreadyReported", func(t *testing.T) {
|
||||||
|
reqBody := models.RecoveryRequest{
|
||||||
|
Email: "adminSOLO",
|
||||||
|
RedirectionURL: "http://redirect.com",
|
||||||
|
}
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/recover").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, _, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusAlreadyReported, statusCode)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("HandleRecoveryRequest_MissingEmail", func(t *testing.T) {
|
||||||
|
reqBody := models.RecoveryRequest{
|
||||||
|
RedirectionURL: "http://redirect.com",
|
||||||
|
}
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/recover").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, _, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("HandleRecoveryRequest_UserNotFound", func(t *testing.T) {
|
||||||
|
reqBody := models.RecoveryRequest{
|
||||||
|
Email: "nonexistent@example.com",
|
||||||
|
RedirectionURL: "http://redirect.com",
|
||||||
|
}
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
|
req := client.Post(BaseUrl+"/recover").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
|
statusCode, _, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusNotFound, statusCode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// get handler
|
||||||
|
func TestRecoveryLinkHandler(t *testing.T) {
|
||||||
|
client := fiber.AcquireClient()
|
||||||
|
|
||||||
|
t.Run("HandleRecoveryLink_ValidSign", func(t *testing.T) {
|
||||||
|
req := client.Get(BaseUrl + "/recover/" + ValidSign)
|
||||||
|
statusCode, _, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||||
|
|
||||||
|
fmt.Println("Recovery link handled successfully")
|
||||||
|
})
|
||||||
|
time.Sleep(15 * time.Minute)
|
||||||
|
t.Run("HandleRecoveryLink_ExpiredSign", func(t *testing.T) {
|
||||||
|
req := client.Get(BaseUrl + "/recover/" + ValidSign)
|
||||||
|
statusCode, _, errs := req.Bytes()
|
||||||
|
if len(errs) != 0 {
|
||||||
|
assert.NoError(t, errs[0])
|
||||||
|
}
|
||||||
|
assert.Equal(t, fiber.StatusNotAcceptable, statusCode)
|
||||||
|
fmt.Println("Recovery link with expired sign handled correctly")
|
||||||
|
})
|
||||||
|
}
|
38
tests/helpers/jwt.go
Normal file
38
tests/helpers/jwt.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeword/internal/initialize"
|
||||||
|
"codeword/utils"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitializeJWT() *utils.JWT {
|
||||||
|
publicKey := strings.Replace(`-----BEGIN PUBLIC KEY-----
|
||||||
|
MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm69
|
||||||
|
80fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6B
|
||||||
|
dA4TS2kB9Kf0wn0+7wSlyikHoKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y
|
||||||
|
+3GyaOY536H47qyXAgMBAAE=
|
||||||
|
-----END PUBLIC KEY-----`, "\t", "", -1)
|
||||||
|
|
||||||
|
privateKey := strings.Replace(`-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICWwIBAAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2B
|
||||||
|
iw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikH
|
||||||
|
oKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAEC
|
||||||
|
gYAOphnVPXbk6lpYzdkLC1Xn5EOEuNfOLLURLxBnPWozZo26r/Mtahu/9mYhrYlv
|
||||||
|
PP8r6mxta3VIil8iOdZyOLa/4d1LPd+UehgEXIJEiYXLtn7RS5eUnoPuQxssfs1k
|
||||||
|
OWjdN8p6SzppleegFTvGRX4KM3cDLfSphOk8JuBCrpSSYQJBAOdqizTSrdKMTuVe
|
||||||
|
c7Jk1JOJkyFuFs+N5zeryyeFGH7IpRdWy0rkWMxIUAi8Ap1vYVBPHv4tDOo3sy5X
|
||||||
|
VLc/knkCQQCE62pg+0TmsrhO/2Pgog6MLBkzlzXYMRp/01HbmznwYF+ejfPnzLkz
|
||||||
|
hnUlxRUNK3lhXM/7H6oAjvqF2R72u/OPAkEAterkmdbQfEZ+MwNoEiH/lie9OLdx
|
||||||
|
SSI1VGdBYcTYN7qFRW6eizYstBJYkDU0HQ0Uw+we4hMKJwk4W0KdvxxDiQJAeqlB
|
||||||
|
V1QqBneBbK10PzVuFV8QtrJhJyxRVwrtbKq38iMNuqUnI4+ijXEUpJFWVvv6nKXo
|
||||||
|
7McQvEk12dU/JNTX8wJAOlAtSNjp9tVwpMpC0w2St1eKc1L2SknjeohA5ldoBz8sGeZsPhTU3eHSD1neAZXLKN5K68z3zFBr20ubY9nyLw==
|
||||||
|
-----END RSA PRIVATE KEY-----`, "\t", "", -1)
|
||||||
|
|
||||||
|
return utils.NewJWT(&initialize.Config{
|
||||||
|
PrivateKey: privateKey,
|
||||||
|
PublicKey: publicKey,
|
||||||
|
Audience: "pena",
|
||||||
|
Issuer: "pena-auth-service",
|
||||||
|
})
|
||||||
|
}
|
@ -7,9 +7,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"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"
|
||||||
"log"
|
"log"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -17,10 +19,9 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// todo add another tests
|
|
||||||
|
|
||||||
const mongoURI = "mongodb://test:test@127.0.0.1:27020/?authMechanism=SCRAM-SHA-256&authSource=admin&directConnection=true"
|
const mongoURI = "mongodb://test:test@127.0.0.1:27020/?authMechanism=SCRAM-SHA-256&authSource=admin&directConnection=true"
|
||||||
|
|
||||||
|
// codeword unit tests
|
||||||
func TestFindByEmail(t *testing.T) {
|
func TestFindByEmail(t *testing.T) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@ -45,14 +46,15 @@ func TestFindByEmail(t *testing.T) {
|
|||||||
userRepo := repository.NewUserRepository(repository.Deps{Rdb: nil, Mdb: db.Collection("users")})
|
userRepo := repository.NewUserRepository(repository.Deps{Rdb: nil, Mdb: db.Collection("users")})
|
||||||
|
|
||||||
t.Run("FindByEmail - existing user", func(t *testing.T) {
|
t.Run("FindByEmail - existing user", func(t *testing.T) {
|
||||||
user, err := userRepo.FindByEmail(ctx, "email@mail.ru")
|
user, err := userRepo.FindByEmail(ctx, "admin")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, user)
|
assert.NotNil(t, user)
|
||||||
assert.Equal(t, "email@mail.ru", user.Email)
|
fmt.Println(user.Email)
|
||||||
|
assert.Equal(t, "admin", user.Login)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("FindByEmail - non-existing user", func(t *testing.T) {
|
t.Run("FindByEmail - non-existing user", func(t *testing.T) {
|
||||||
user, err := userRepo.FindByEmail(ctx, "nonexisting@example.com")
|
user, err := userRepo.FindByEmail(ctx, "neadmin")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Nil(t, user)
|
assert.Nil(t, user)
|
||||||
})
|
})
|
||||||
@ -60,7 +62,8 @@ func TestFindByEmail(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStoreRecoveryRecord(t *testing.T) {
|
func TestStoreRecoveryRecord(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -93,3 +96,423 @@ func TestStoreRecoveryRecord(t *testing.T) {
|
|||||||
|
|
||||||
_ = database.Drop(ctx)
|
_ = database.Drop(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetRecoveryRecord(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = mongoClient.Disconnect(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
database := mongoClient.Database("admin")
|
||||||
|
codeword := database.Collection("codeword")
|
||||||
|
_ = codeword.Drop(ctx)
|
||||||
|
|
||||||
|
userRepo := repository.NewCodewordRepository(repository.Deps{Rdb: nil, Mdb: codeword})
|
||||||
|
|
||||||
|
ID := primitive.NewObjectID()
|
||||||
|
userID := "6597babdd1ba7e2dbd32d7e3"
|
||||||
|
email := "test@mail.ru"
|
||||||
|
key := "test_recovery_key"
|
||||||
|
|
||||||
|
record := models.RestoreRequest{
|
||||||
|
ID: ID,
|
||||||
|
UserID: userID,
|
||||||
|
Email: email,
|
||||||
|
Sign: key,
|
||||||
|
SignUrl: "def.url",
|
||||||
|
SignID: key + userID,
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = codeword.InsertOne(ctx, record)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
result, err := userRepo.GetRecoveryRecord(ctx, key+userID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, userID, result.UserID)
|
||||||
|
assert.Equal(t, email, result.Email)
|
||||||
|
assert.Equal(t, key, result.Sign)
|
||||||
|
|
||||||
|
_ = database.Drop(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// promoCode unit tests
|
||||||
|
|
||||||
|
func TestInitPromoCodeIndexes(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = mongoClient.Disconnect(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
database := mongoClient.Database("admin")
|
||||||
|
promoCode := database.Collection("promoCode")
|
||||||
|
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreatePromoCode(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer func() {
|
||||||
|
_ = mongoClient.Disconnect(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
database := mongoClient.Database("admin")
|
||||||
|
promoCode := database.Collection("promoCode")
|
||||||
|
_ = promoCode.Drop(ctx)
|
||||||
|
|
||||||
|
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||||
|
|
||||||
|
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("CreatePromoCode - success", func(t *testing.T) {
|
||||||
|
req := &models.PromoCode{
|
||||||
|
Codeword: "test_codeword",
|
||||||
|
Description: faker.String(),
|
||||||
|
Greetings: faker.String(),
|
||||||
|
DueTo: 1737280065,
|
||||||
|
ActivationCount: 100,
|
||||||
|
}
|
||||||
|
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, createdPromoCode)
|
||||||
|
assert.Equal(t, "test_codeword", createdPromoCode.Codeword)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("CreatePromoCode - duplicate codeword", func(t *testing.T) {
|
||||||
|
req := &models.PromoCode{
|
||||||
|
Codeword: "test_codeword",
|
||||||
|
Description: faker.String(),
|
||||||
|
Greetings: faker.String(),
|
||||||
|
DueTo: 1737280065,
|
||||||
|
ActivationCount: 100,
|
||||||
|
}
|
||||||
|
_, err := userRepo.CreatePromoCode(ctx, req)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEditPromoCode(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer func() {
|
||||||
|
_ = mongoClient.Disconnect(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
database := mongoClient.Database("admin")
|
||||||
|
promoCode := database.Collection("promoCode")
|
||||||
|
_ = promoCode.Drop(ctx)
|
||||||
|
|
||||||
|
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||||
|
|
||||||
|
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
req := &models.PromoCode{
|
||||||
|
Codeword: "test_codeword",
|
||||||
|
Description: faker.String(),
|
||||||
|
Greetings: faker.String(),
|
||||||
|
DueTo: 1737280065,
|
||||||
|
ActivationCount: 100,
|
||||||
|
}
|
||||||
|
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
newDescription := "New Description"
|
||||||
|
|
||||||
|
t.Run("EditPromoCode - success", func(t *testing.T) {
|
||||||
|
editReq := &models.ReqEditPromoCode{
|
||||||
|
ID: createdPromoCode.ID.Hex(),
|
||||||
|
Description: &newDescription,
|
||||||
|
}
|
||||||
|
editedPromoCode, err := userRepo.EditPromoCode(ctx, editReq)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, editedPromoCode)
|
||||||
|
assert.Equal(t, "New Description", editedPromoCode.Description)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("EditPromoCode - promo code not found", func(t *testing.T) {
|
||||||
|
nonExistingID := primitive.NewObjectID().Hex()
|
||||||
|
editReq := &models.ReqEditPromoCode{
|
||||||
|
ID: nonExistingID,
|
||||||
|
}
|
||||||
|
_, err := userRepo.EditPromoCode(ctx, editReq)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPromoCodeByID(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer func() {
|
||||||
|
_ = mongoClient.Disconnect(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
database := mongoClient.Database("admin")
|
||||||
|
promoCode := database.Collection("promoCode")
|
||||||
|
_ = promoCode.Drop(ctx)
|
||||||
|
|
||||||
|
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||||
|
|
||||||
|
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
req := &models.PromoCode{
|
||||||
|
Codeword: "test_codeword",
|
||||||
|
Description: faker.String(),
|
||||||
|
Greetings: faker.String(),
|
||||||
|
DueTo: 1737280065,
|
||||||
|
ActivationCount: 100,
|
||||||
|
}
|
||||||
|
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("GetPromoCodeByID - success", func(t *testing.T) {
|
||||||
|
result, err := userRepo.GetPromoCodeByID(ctx, createdPromoCode.ID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, result)
|
||||||
|
assert.Equal(t, createdPromoCode.Codeword, result.Codeword)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("GetPromoCodeByID - promo code not found", func(t *testing.T) {
|
||||||
|
nonExistingID := primitive.NewObjectID()
|
||||||
|
_, err := userRepo.GetPromoCodeByID(ctx, nonExistingID)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPromoCodesList(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer func() {
|
||||||
|
_ = mongoClient.Disconnect(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
database := mongoClient.Database("admin")
|
||||||
|
promoCode := database.Collection("promoCode")
|
||||||
|
_ = promoCode.Drop(ctx)
|
||||||
|
|
||||||
|
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||||
|
|
||||||
|
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
for i := 0; i < 1111; i++ {
|
||||||
|
|
||||||
|
req := &models.PromoCode{
|
||||||
|
Codeword: "test" + faker.String() + strconv.Itoa(i),
|
||||||
|
Description: faker.String(),
|
||||||
|
Greetings: faker.String(),
|
||||||
|
DueTo: 1737280065,
|
||||||
|
ActivationCount: 100,
|
||||||
|
Delete: faker.Bool(),
|
||||||
|
Outdated: faker.Bool(),
|
||||||
|
OffLimit: faker.Bool(),
|
||||||
|
}
|
||||||
|
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, createdPromoCode)
|
||||||
|
}
|
||||||
|
t.Run("GetPromoCodesList - true", func(t *testing.T) {
|
||||||
|
filter := models.GetPromoCodesListReqFilter{
|
||||||
|
Text: "test",
|
||||||
|
Active: true,
|
||||||
|
}
|
||||||
|
req := &models.GetPromoCodesListReq{
|
||||||
|
Page: 0,
|
||||||
|
Limit: 10,
|
||||||
|
Filter: filter,
|
||||||
|
}
|
||||||
|
promoCodes, count, err := userRepo.GetPromoCodesList(ctx, req)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, promoCodes)
|
||||||
|
assert.True(t, count >= 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("GetPromoCodesList - false", func(t *testing.T) {
|
||||||
|
filter := models.GetPromoCodesListReqFilter{
|
||||||
|
Text: "test",
|
||||||
|
Active: false,
|
||||||
|
}
|
||||||
|
req := &models.GetPromoCodesListReq{
|
||||||
|
Page: 0,
|
||||||
|
Limit: 10,
|
||||||
|
Filter: filter,
|
||||||
|
}
|
||||||
|
promoCodes, count, err := userRepo.GetPromoCodesList(ctx, req)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, promoCodes)
|
||||||
|
assert.True(t, count >= 0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestActivatePromo(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer func() {
|
||||||
|
_ = mongoClient.Disconnect(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
database := mongoClient.Database("admin")
|
||||||
|
promoCode := database.Collection("promoCode")
|
||||||
|
_ = promoCode.Drop(ctx)
|
||||||
|
|
||||||
|
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||||
|
|
||||||
|
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
req := &models.PromoCode{
|
||||||
|
Codeword: "test_codeword",
|
||||||
|
Description: faker.String(),
|
||||||
|
Greetings: faker.String(),
|
||||||
|
DueTo: 1737280065,
|
||||||
|
ActivationCount: 100,
|
||||||
|
}
|
||||||
|
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, createdPromoCode)
|
||||||
|
|
||||||
|
xid := "test_xid"
|
||||||
|
err = userRepo.AddFastLink(ctx, createdPromoCode.ID, xid)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("ActivatePromo Codeword - success", func(t *testing.T) {
|
||||||
|
req := &models.ActivateReq{
|
||||||
|
UserID: "6597babdd1ba7e2dbd32d7e3",
|
||||||
|
Codeword: "test_codeword",
|
||||||
|
}
|
||||||
|
activatedPromoCode, err := userRepo.ActivatePromo(ctx, req)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, activatedPromoCode)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ActivatePromo FastLink - success", func(t *testing.T) {
|
||||||
|
req := &models.ActivateReq{
|
||||||
|
UserID: "6597babdd1ba7e2dbd32d7e3",
|
||||||
|
FastLink: "test_xid",
|
||||||
|
}
|
||||||
|
activatedPromoCode, err := userRepo.ActivatePromo(ctx, req)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, activatedPromoCode)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ActivatePromo - promo code not found", func(t *testing.T) {
|
||||||
|
req := &models.ActivateReq{
|
||||||
|
UserID: "6597babdd1ba7e2dbd32d7e3",
|
||||||
|
Codeword: "non_existing_codeword",
|
||||||
|
}
|
||||||
|
_, err := userRepo.ActivatePromo(ctx, req)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeletePromoCode(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer func() {
|
||||||
|
_ = mongoClient.Disconnect(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
database := mongoClient.Database("admin")
|
||||||
|
promoCode := database.Collection("promoCode")
|
||||||
|
_ = promoCode.Drop(ctx)
|
||||||
|
|
||||||
|
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||||
|
|
||||||
|
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
req := &models.PromoCode{
|
||||||
|
Codeword: "test_codeword",
|
||||||
|
Description: faker.String(),
|
||||||
|
Greetings: faker.String(),
|
||||||
|
DueTo: 1737280065,
|
||||||
|
ActivationCount: 100,
|
||||||
|
}
|
||||||
|
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, createdPromoCode)
|
||||||
|
|
||||||
|
t.Run("DeletePromoCode - success", func(t *testing.T) {
|
||||||
|
err := userRepo.DeletePromoCode(ctx, createdPromoCode.ID.Hex())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("DeletePromoCode - promo code not found", func(t *testing.T) {
|
||||||
|
nonExistingID := primitive.NewObjectID().Hex()
|
||||||
|
err := userRepo.DeletePromoCode(ctx, nonExistingID)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddFastLink(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer func() {
|
||||||
|
_ = mongoClient.Disconnect(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
database := mongoClient.Database("admin")
|
||||||
|
promoCode := database.Collection("promoCode")
|
||||||
|
_ = promoCode.Drop(ctx)
|
||||||
|
|
||||||
|
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||||
|
|
||||||
|
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
req := &models.PromoCode{
|
||||||
|
Codeword: "test_codeword",
|
||||||
|
Description: faker.String(),
|
||||||
|
Greetings: faker.String(),
|
||||||
|
DueTo: 1737280065,
|
||||||
|
ActivationCount: 100,
|
||||||
|
}
|
||||||
|
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, createdPromoCode)
|
||||||
|
|
||||||
|
t.Run("AddFastLink - success", func(t *testing.T) {
|
||||||
|
xid := "test_xid"
|
||||||
|
err := userRepo.AddFastLink(ctx, createdPromoCode.ID, xid)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("AddFastLink - promo code not found", func(t *testing.T) {
|
||||||
|
nonExistingID := primitive.NewObjectID()
|
||||||
|
xid := "test_xid"
|
||||||
|
err := userRepo.AddFastLink(ctx, nonExistingID, xid)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
46
utils/authenticator.go
Normal file
46
utils/authenticator.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeword/internal/models"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
prefix = "Bearer "
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewAuthenticator(jwtUtil *JWT) func(*fiber.Ctx) error {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
if jwtUtil == nil {
|
||||||
|
return fmt.Errorf("jwt util is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := authenticate(c, jwtUtil); err != nil {
|
||||||
|
return fmt.Errorf("authentication error:%d", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func authenticate(c *fiber.Ctx, jwtUtil *JWT) error {
|
||||||
|
authHeader := c.Get("Authorization")
|
||||||
|
if authHeader == "" || !strings.HasPrefix(authHeader, prefix) {
|
||||||
|
return fmt.Errorf("failed to parse jws from request header: %s", authHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
jws := strings.TrimPrefix(authHeader, prefix)
|
||||||
|
|
||||||
|
userID, validateErr := jwtUtil.Validate(jws)
|
||||||
|
if validateErr != nil {
|
||||||
|
return validateErr
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Locals(models.AuthJWTDecodedUserIDKey, userID)
|
||||||
|
c.Locals(models.AuthJWTDecodedAccessTokenKey, jws)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
89
utils/jwt.go
Normal file
89
utils/jwt.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"codeword/internal/initialize"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JWT struct {
|
||||||
|
privateKey []byte
|
||||||
|
publicKey []byte
|
||||||
|
algorithm *jwt.SigningMethodRSA
|
||||||
|
expiresIn time.Duration
|
||||||
|
issuer string
|
||||||
|
audience string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJWT(configuration *initialize.Config) *JWT {
|
||||||
|
return &JWT{
|
||||||
|
privateKey: []byte(configuration.PrivateKey),
|
||||||
|
publicKey: []byte(configuration.PublicKey),
|
||||||
|
issuer: configuration.Issuer,
|
||||||
|
audience: configuration.Audience,
|
||||||
|
algorithm: jwt.SigningMethodRS256,
|
||||||
|
expiresIn: 15 * time.Minute,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JWT) Create(id string) (string, error) {
|
||||||
|
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(j.privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse private key on <Create> of <JWT>: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().UTC()
|
||||||
|
|
||||||
|
claims := jwt.MapClaims{
|
||||||
|
"id": id,
|
||||||
|
"exp": now.Add(j.expiresIn).Unix(),
|
||||||
|
"aud": j.audience,
|
||||||
|
"iss": j.issuer,
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := jwt.NewWithClaims(j.algorithm, claims).SignedString(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to sing on <Create> of <JWT>: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JWT) Validate(tokenString string) (string, error) {
|
||||||
|
key, err := jwt.ParseRSAPublicKeyFromPEM(j.publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse rsa public key on <Validate> of <JWT>: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
parseCallback := func(token *jwt.Token) (any, error) {
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected signing method: %s", token.Header["alg"])
|
||||||
|
}
|
||||||
|
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := jwt.Parse(
|
||||||
|
tokenString,
|
||||||
|
parseCallback,
|
||||||
|
jwt.WithAudience(j.audience),
|
||||||
|
jwt.WithIssuer(j.issuer),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse jwt token on <Validate> of <JWT>: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, ok := token.Claims.(jwt.MapClaims)
|
||||||
|
if !ok || !token.Valid {
|
||||||
|
return "", errors.New("token is invalid on <Validate> of <JWT>")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, ok := claims["id"].(string)
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("data is empty or not a string on <Validate> of <JWT>")
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user