From f1b18775d01a270a0daae5c31217c2584949e6f9 Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 22 Jun 2023 09:36:43 +0000 Subject: [PATCH] ci: fix env hosts --- .dockerignore | 8 + .env.test | 27 + .gitignore | 6 + .gitlab-ci.yml | 100 ++ .golangci.yaml | 163 +++ .mockery.yaml | 6 + Dockerfile | 73 ++ Makefile | 47 + README.md | 115 +- buf.gen.yaml | 8 + buf.work.yaml | 3 + buf.yaml | 4 + cmd/app/main.go | 34 + deployments/dev/.env.dev | 27 + deployments/dev/docker-compose.yaml | 43 + deployments/staging/docker-compose.yaml | 42 + deployments/test/.env.test | 27 + deployments/test/docker-compose.yaml | 68 + go.mod | 62 + go.sum | 273 ++++ internal/app/app.go | 116 ++ internal/errors/errors.go | 62 + internal/errors/grpc.go | 27 + internal/errors/http.go | 33 + internal/fields/account.go | 25 + internal/fields/currency.go | 19 + internal/fields/history.go | 23 + internal/initialize/api.go | 15 + internal/initialize/api_test.go | 51 + internal/initialize/clients.go | 49 + internal/initialize/clients_test.go | 34 + internal/initialize/config.go | 30 + internal/initialize/config_test.go | 131 ++ internal/initialize/controllers.go | 61 + internal/initialize/controllers_test.go | 54 + internal/initialize/repositories.go | 37 + internal/initialize/repositories_test.go | 31 + internal/initialize/services.go | 78 ++ internal/initialize/services_test.go | 51 + internal/interface/client/auth.go | 78 ++ internal/interface/client/currency.go | 79 ++ internal/interface/client/discount.go | 62 + internal/interface/client/hubadmin.go | 94 ++ internal/interface/client/payment.go | 271 ++++ .../controller/grpc/customer/customer.go | 55 + .../controller/grpc/payment/payment.go | 74 ++ .../controller/rest/account/account.go | 177 +++ .../interface/controller/rest/cart/cart.go | 133 ++ .../controller/rest/currency/currency.go | 87 ++ .../controller/rest/history/history.go | 55 + .../controller/rest/wallet/wallet.go | 131 ++ internal/interface/repository/account.go | 418 ++++++ internal/interface/repository/currency.go | 129 ++ internal/interface/repository/health.go | 19 + internal/interface/repository/history.go | 108 ++ internal/interface/swagger/api.gen.go | 508 ++++++++ internal/interface/swagger/api.go | 161 +++ internal/interface/swagger/api.yaml | 6 + internal/interface/swagger/middleware.go | 20 + internal/interface/swagger/models.gen.go | 188 +++ internal/interface/swagger/models.yaml | 4 + internal/models/account.go | 52 + internal/models/auth.go | 16 + internal/models/cart.go | 5 + internal/models/common.go | 27 + internal/models/config.go | 76 ++ internal/models/currency.go | 47 + internal/models/discount.go | 66 + internal/models/history.go | 34 + internal/models/payment.go | 54 + internal/models/tariff.go | 26 + internal/models/wallet.go | 33 + internal/proto/customer/service.pb.go | 182 +++ internal/proto/customer/service_grpc.pb.go | 100 ++ internal/proto/discount/audit.model.pb.go | 192 +++ internal/proto/discount/discount.model.pb.go | 1121 +++++++++++++++++ internal/proto/discount/service.pb.go | 524 ++++++++ internal/proto/discount/service_grpc.pb.go | 388 ++++++ .../api/annotations/annotations.pb.go | 118 ++ .../googleapis/api/annotations/http.pb.go | 777 ++++++++++++ internal/proto/payment_callback/service.pb.go | 325 +++++ .../proto/payment_callback/service_grpc.pb.go | 136 ++ internal/proto/treasurer/payment.model.pb.go | 299 +++++ internal/proto/treasurer/service.pb.go | 633 ++++++++++ internal/proto/treasurer/service_grpc.pb.go | 459 +++++++ internal/server/grpc.go | 82 ++ internal/server/http.go | 93 ++ internal/service/account/account.go | 237 ++++ internal/service/callback/payment.go | 107 ++ internal/service/cart/cart.go | 188 +++ internal/service/currency/currency.go | 93 ++ internal/service/history/history.go | 93 ++ internal/service/payment/payment.go | 261 ++++ internal/service/wallet/wallet.go | 249 ++++ internal/utils/authenticator.go | 71 ++ internal/utils/client_response.go | 24 + internal/utils/jwt.go | 90 ++ internal/utils/jwt_test.go | 129 ++ internal/utils/pagination.go | 38 + internal/utils/payment.go | 62 + internal/utils/payment_test.go | 192 +++ internal/utils/tariff.go | 13 + internal/utils/tariff_test.go | 18 + internal/utils/transfer/tariff.go | 23 + internal/utils/transfer/tariff_test.go | 25 + internal/utils/validate_configuration_urls.go | 20 + migrations/test/001_accounts_insert.down.json | 1 + migrations/test/001_accounts_insert.up.json | 28 + openapi.yaml | 743 +++++++++++ pkg/client/client.go | 37 + pkg/client/request.go | 56 + pkg/client/response.go | 28 + pkg/closer/closer.go | 50 + pkg/echotools/bind.go | 13 + pkg/env/parse.go | 23 + pkg/json/encode.go | 16 + pkg/json/parse.go | 26 + pkg/mongo/config.go | 22 + pkg/mongo/connection.go | 75 ++ pkg/mongo/errors.go | 7 + pkg/mongo/find.go | 55 + pkg/validate/string.go | 7 + pkg/validate/string_test.go | 16 + pkg/validate/url.go | 28 + pkg/validate/url_test.go | 25 + proto/callback/service.proto | 31 + proto/customer/service.proto | 18 + proto/discount/audit.model.proto | 17 + proto/discount/discount.model.proto | 88 ++ proto/discount/service.proto | 94 ++ proto/google/api/annotations.proto | 31 + proto/google/api/http.proto | 375 ++++++ proto/treasurer/payment.model.proto | 22 + proto/treasurer/service.proto | 51 + tests/helpers/jwt.go | 88 ++ ...t_account_verification_status_test copy.go | 133 ++ ...update_account_verification_status_test.go | 51 + 137 files changed, 14888 insertions(+), 85 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.test create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 .golangci.yaml create mode 100644 .mockery.yaml create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 buf.gen.yaml create mode 100644 buf.work.yaml create mode 100644 buf.yaml create mode 100644 cmd/app/main.go create mode 100644 deployments/dev/.env.dev create mode 100644 deployments/dev/docker-compose.yaml create mode 100644 deployments/staging/docker-compose.yaml create mode 100644 deployments/test/.env.test create mode 100644 deployments/test/docker-compose.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/app/app.go create mode 100644 internal/errors/errors.go create mode 100644 internal/errors/grpc.go create mode 100644 internal/errors/http.go create mode 100644 internal/fields/account.go create mode 100644 internal/fields/currency.go create mode 100644 internal/fields/history.go create mode 100644 internal/initialize/api.go create mode 100644 internal/initialize/api_test.go create mode 100644 internal/initialize/clients.go create mode 100644 internal/initialize/clients_test.go create mode 100644 internal/initialize/config.go create mode 100644 internal/initialize/config_test.go create mode 100644 internal/initialize/controllers.go create mode 100644 internal/initialize/controllers_test.go create mode 100644 internal/initialize/repositories.go create mode 100644 internal/initialize/repositories_test.go create mode 100644 internal/initialize/services.go create mode 100644 internal/initialize/services_test.go create mode 100644 internal/interface/client/auth.go create mode 100644 internal/interface/client/currency.go create mode 100644 internal/interface/client/discount.go create mode 100644 internal/interface/client/hubadmin.go create mode 100644 internal/interface/client/payment.go create mode 100644 internal/interface/controller/grpc/customer/customer.go create mode 100644 internal/interface/controller/grpc/payment/payment.go create mode 100644 internal/interface/controller/rest/account/account.go create mode 100644 internal/interface/controller/rest/cart/cart.go create mode 100644 internal/interface/controller/rest/currency/currency.go create mode 100644 internal/interface/controller/rest/history/history.go create mode 100644 internal/interface/controller/rest/wallet/wallet.go create mode 100644 internal/interface/repository/account.go create mode 100644 internal/interface/repository/currency.go create mode 100644 internal/interface/repository/health.go create mode 100644 internal/interface/repository/history.go create mode 100644 internal/interface/swagger/api.gen.go create mode 100644 internal/interface/swagger/api.go create mode 100644 internal/interface/swagger/api.yaml create mode 100644 internal/interface/swagger/middleware.go create mode 100644 internal/interface/swagger/models.gen.go create mode 100644 internal/interface/swagger/models.yaml create mode 100644 internal/models/account.go create mode 100644 internal/models/auth.go create mode 100644 internal/models/cart.go create mode 100644 internal/models/common.go create mode 100644 internal/models/config.go create mode 100644 internal/models/currency.go create mode 100644 internal/models/discount.go create mode 100644 internal/models/history.go create mode 100644 internal/models/payment.go create mode 100644 internal/models/tariff.go create mode 100644 internal/models/wallet.go create mode 100644 internal/proto/customer/service.pb.go create mode 100644 internal/proto/customer/service_grpc.pb.go create mode 100644 internal/proto/discount/audit.model.pb.go create mode 100644 internal/proto/discount/discount.model.pb.go create mode 100644 internal/proto/discount/service.pb.go create mode 100644 internal/proto/discount/service_grpc.pb.go create mode 100644 internal/proto/google.golang.org/genproto/googleapis/api/annotations/annotations.pb.go create mode 100644 internal/proto/google.golang.org/genproto/googleapis/api/annotations/http.pb.go create mode 100644 internal/proto/payment_callback/service.pb.go create mode 100644 internal/proto/payment_callback/service_grpc.pb.go create mode 100644 internal/proto/treasurer/payment.model.pb.go create mode 100644 internal/proto/treasurer/service.pb.go create mode 100644 internal/proto/treasurer/service_grpc.pb.go create mode 100644 internal/server/grpc.go create mode 100644 internal/server/http.go create mode 100644 internal/service/account/account.go create mode 100644 internal/service/callback/payment.go create mode 100644 internal/service/cart/cart.go create mode 100644 internal/service/currency/currency.go create mode 100644 internal/service/history/history.go create mode 100644 internal/service/payment/payment.go create mode 100644 internal/service/wallet/wallet.go create mode 100644 internal/utils/authenticator.go create mode 100644 internal/utils/client_response.go create mode 100644 internal/utils/jwt.go create mode 100644 internal/utils/jwt_test.go create mode 100644 internal/utils/pagination.go create mode 100644 internal/utils/payment.go create mode 100644 internal/utils/payment_test.go create mode 100644 internal/utils/tariff.go create mode 100644 internal/utils/tariff_test.go create mode 100644 internal/utils/transfer/tariff.go create mode 100644 internal/utils/transfer/tariff_test.go create mode 100644 internal/utils/validate_configuration_urls.go create mode 100644 migrations/test/001_accounts_insert.down.json create mode 100644 migrations/test/001_accounts_insert.up.json create mode 100644 openapi.yaml create mode 100644 pkg/client/client.go create mode 100644 pkg/client/request.go create mode 100644 pkg/client/response.go create mode 100644 pkg/closer/closer.go create mode 100644 pkg/echotools/bind.go create mode 100644 pkg/env/parse.go create mode 100644 pkg/json/encode.go create mode 100644 pkg/json/parse.go create mode 100644 pkg/mongo/config.go create mode 100644 pkg/mongo/connection.go create mode 100644 pkg/mongo/errors.go create mode 100644 pkg/mongo/find.go create mode 100644 pkg/validate/string.go create mode 100644 pkg/validate/string_test.go create mode 100644 pkg/validate/url.go create mode 100644 pkg/validate/url_test.go create mode 100644 proto/callback/service.proto create mode 100644 proto/customer/service.proto create mode 100644 proto/discount/audit.model.proto create mode 100644 proto/discount/discount.model.proto create mode 100644 proto/discount/service.proto create mode 100644 proto/google/api/annotations.proto create mode 100644 proto/google/api/http.proto create mode 100644 proto/treasurer/payment.model.proto create mode 100644 proto/treasurer/service.proto create mode 100644 tests/helpers/jwt.go create mode 100644 tests/integration/set_account_verification_status_test copy.go create mode 100644 tests/integration/update_account_verification_status_test.go diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7a61806 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.mockery.yaml +.golangci.yaml +.gitlab-ci.yaml +.gitingore +.Makefile +.README.md +deployments +tests \ No newline at end of file diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..1cb9f58 --- /dev/null +++ b/.env.test @@ -0,0 +1,27 @@ +# HTTP settings +HTTP_HOST=0.0.0.0 +HTTP_PORT=8080 + +# MONGO settings +MONGO_HOST=localhost +MONGO_PORT=27017 +MONGO_USER=test +MONGO_PASSWORD=test +MONGO_AUTH=admin +MONGO_DB_NAME=admin + +# Auth Microservice settings +AUTH_MICROSERVICE_USER_URL=http://localhost:8000/user + +# Hub Admin Microservice settings +HUBADMIN_MICROSERVICE_TARIFF_URL=http://localhost:8001/tariff + +# Currency Microservice settings +CURRENCY_MICROSERVICE_TRANSLATE_URL=http://localhost:8002/change + +# Admin + +# JWT settings +JWT_ISSUER="pena-auth-service" +JWT_AUDIENCE="pena" +JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikHoKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAE=\n-----END PUBLIC KEY-----" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..52d12d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Dependency directories (remove the comment below to include it) +# vendor/ +.idea/ +.vscode +.env + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..48faa39 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,100 @@ +stages: + - lint + - test + - clean + - build + - deploy + +lint: + image: golangci/golangci-lint:v1.52-alpine + stage: lint + before_script: + - go install github.com/vektra/mockery/v2@v2.26.0 + - go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4 + script: + - go generate ./internal/... + - golangci-lint version + - golangci-lint run ./internal/... + +test: + image: golang:1.20.3-alpine + stage: test + coverage: /\(statements\)(?:\s+)?(\d+(?:\.\d+)?%)/ + script: + - CGO_ENABLED=0 go test ./internal/... -coverprofile=coverage.out + - go tool cover -html=coverage.out -o coverage.html + - go tool cover -func coverage.out + artifacts: + expire_in: "3 days" + paths: + - coverage.html + +clean-old: + stage: clean + image: + name: docker/compose:1.28.0 + entrypoint: [""] + allow_failure: true + variables: + PRODUCTION_BRANCH: main + STAGING_BRANCH: "staging" + DEPLOY_TO: "staging" + rules: + - if: $CI_COMMIT_BRANCH == $PRODUCTION_BRANCH || $CI_COMMIT_BRANCH == $STAGING_BRANCH + when: on_success + before_script: + - echo DEPLOY_TO = $DEPLOY_TO + script: + - docker-compose -f deployments/$DEPLOY_TO/docker-compose.yaml down --volumes --rmi local + +build-app: + stage: build + image: + name: gcr.io/kaniko-project/executor:debug + entrypoint: [""] + variables: + DOCKER_BUILD_PATH: "./Dockerfile" + STAGING_BRANCH: "staging" + PRODUCTION_BRANCH: "main" + rules: + - if: $CI_COMMIT_BRANCH == $PRODUCTION_BRANCH || $CI_COMMIT_BRANCH == $STAGING_BRANCH + when: on_success + before_script: + - echo PRODUCTION_BRANCH = $PRODUCTION_BRANCH + - echo STAGING_BRANCH = $STAGING_BRANCH + - echo CI_REGISTRY = $CI_REGISTRY + - echo CI_REGISTRY_USER = $CI_REGISTRY_USER + - echo CI_PROJECT_DIR = $CI_PROJECT_DIR + - echo CI_REGISTRY_IMAGE = $CI_REGISTRY_IMAGE + - echo CI_COMMIT_REF_SLUG = $CI_COMMIT_REF_SLUG + - echo DOCKER_BUILD_PATH = $DOCKER_BUILD_PATH + - echo CI_PIPELINE_ID = $CI_PIPELINE_ID + script: + - mkdir -p /kaniko/.docker + - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json + - | + /kaniko/executor --context $CI_PROJECT_DIR \ + --cache=true --cache-repo=$CI_REGISTRY_IMAGE \ + --dockerfile $CI_PROJECT_DIR/$DOCKER_BUILD_PATH --target production \ + --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID + +deploy-to-staging: + stage: deploy + image: + name: docker/compose:1.28.0 + entrypoint: [""] + variables: + DEPLOY_TO: "staging" + BRANCH: "staging" + rules: + - if: $CI_COMMIT_BRANCH == $BRANCH + before_script: + - echo CI_PROJECT_NAME = $CI_PROJECT_NAME + - echo CI_REGISTRY = $CI_REGISTRY + - echo REGISTRY_USER = $REGISTRY_USER + - echo REGISTRY_TOKEN = $REGISTRY_TOKEN + - echo DEPLOY_TO = $DEPLOY_TO + - echo BRANCH = $BRANCH + script: + - docker login -u $REGISTRY_USER -p $REGISTRY_TOKEN $CI_REGISTRY + - docker-compose -f deployments/$DEPLOY_TO/docker-compose.yaml up -d diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..13cf4aa --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,163 @@ +run: + timeout: 5m + skip-files: + - \.pb\.go$ + - \.pb\.validate\.go$ + - \.pb\.gw\.go$ + - \.gen\.go$ + skip-dirs: + - mocks + +linters: + disable-all: true + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - containedctx + - depguard + - dogsled + - dupword + - durationcheck + - errcheck + - errchkjson + - exportloopref + - goconst + - gocritic + - godot + - gofmt + - gci + - goprintffuncname + - gosec + - gosimple + - govet + - importas + - ineffassign + - misspell + - nakedret + - nilerr + - noctx + - nolintlint + - nosprintfhostport + - prealloc + - predeclared + - revive + - rowserrcheck + - staticcheck + - stylecheck + - thelper + - typecheck + - unconvert + - unparam + - unused + - usestdlibvars + - whitespace + +linters-settings: + errcheck: + exclude-functions: + - (io.Closer).Close + govet: + check-shadowing: true + gci: + custom-order: false + section-separators: + - newLine + sections: + - standard # Standard section: captures all standard packages. + - default # Default section: contains all imports that could not be matched to another section type. + - blank # Blank section: contains all blank imports. This section is not present unless explicitly enabled. + - dot # Dot section: contains all dot imports. This section is not present unless explicitly enabled. + importas: + no-unaliased: true + alias: + # Foundation libraries + - pkg: git.sbercloud.tech/products/paas/shared/foundation/management-server + alias: mgmtserver + maligned: + suggest-new: true + goconst: + min-len: 2 + min-occurrences: 2 + lll: + line-length: 140 + revive: + rules: + # The following rules are recommended https://github.com/mgechev/revive#recommended-configuration + - name: blank-imports + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: error-return + - name: error-strings + - name: error-naming + # - name: exported + - name: if-return + - name: increment-decrement + - name: var-naming + - name: var-declaration + # - name: package-comments + - name: range + - name: receiver-naming + - name: time-naming + - name: unexported-return + - name: indent-error-flow + - name: errorf + - name: empty-block + - name: superfluous-else + - name: unused-parameter + - name: unreachable-code + - name: redefines-builtin-id + # + # Rules in addition to the recommended configuration above. + # + - name: bool-literal-in-expr + - name: constant-logical-expr + gosec: + excludes: + - G307 # Deferring unsafe method "Close" on type "\*os.File" + - G108 # Profiling endpoint is automatically exposed on /debug/pprof + gocritic: + enabled-tags: + - diagnostic + - experimental + - performance + disabled-checks: + - appendAssign + - dupImport # https://github.com/go-critic/go-critic/issues/845 + - evalOrder + - ifElseChain + - octalLiteral + - regexpSimplify + - sloppyReassign + - truncateCmp + - typeDefFirst + - unnamedResult + - unnecessaryDefer + - whyNoLint + - wrapperFunc + - rangeValCopy + - hugeParam + +issues: + exclude-rules: + - text: "at least one file in a package should have a package comment" + linters: + - stylecheck + - text: "should have a package comment, unless it's in another file for this package" + linters: + - golint + - text: "should have comment or be unexported" + linters: + - golint + - path: _test\.go + linters: + - gosec + - dupl + exclude-use-default: false + +output: + # colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number" + format: colored-line-number + print-linter-name: true diff --git a/.mockery.yaml b/.mockery.yaml new file mode 100644 index 0000000..8365809 --- /dev/null +++ b/.mockery.yaml @@ -0,0 +1,6 @@ +exported: True +inpackage: False +keeptree: True +case: underscore +with-expecter: True +inpackage-suffix: True \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..827843a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,73 @@ +# BUILD +FROM golang:1.20.3-alpine AS build + +# Update depences +RUN apk update && apk add --no-cache curl +# Create build directory +RUN mkdir /app/bin -p +RUN mkdir /bin/golang-migrate -p +# Download migrate app +RUN GOLANG_MIGRATE_VERSION=v4.15.1 && \ + curl -L https://github.com/golang-migrate/migrate/releases/download/${GOLANG_MIGRATE_VERSION}/migrate.linux-amd64.tar.gz |\ + tar xvz migrate -C /bin/golang-migrate +# Download health check utility +RUN GRPC_HEALTH_PROBE_VERSION=v0.4.6 && \ + wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ + chmod +x /bin/grpc_health_probe +# Download debugger +# Set home directory +WORKDIR /app +# Copy go.mod +ADD go.mod go.sum /app/ +# Download go depences +RUN go mod download +# Copy all local files +ADD . /app +# Build app +RUN GOOS=linux go build -o bin ./... + + + +# TEST +FROM alpine:latest AS test + +# Install packages +RUN apk --no-cache add ca-certificates +ENV GO111MODULE=off +# Create home directory +WORKDIR /app +# Copy build file +COPY --from=build /app/bin/app ./app +# CMD +CMD [ "./app" ] + + +# MIGRATION +FROM alpine:latest AS migration + +# Install packages +RUN apk --no-cache add ca-certificates +# Create home directory +WORKDIR /app +# Copy migration dir +COPY --from=build /app/migrations/test ./migrations +# Install migrate tool +COPY --from=build /bin/golang-migrate /usr/local/bin + + + +# PRODUCTION +FROM alpine:latest AS production + +# Install packages +RUN apk --no-cache add ca-certificates +# Create home directory +WORKDIR /app +# Copy build file +COPY --from=build /app/bin/app ./app +# Copy grpc health probe dir +COPY --from=build /bin/grpc_health_probe /bin/grpc_health_probe +# Install migrate tool +COPY --from=build /bin/golang-migrate /usr/local/bin +# CMD +CMD ["./app"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..02d812b --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +help: ## show this help + @echo 'usage: make [target] ...' + @echo '' + @echo 'targets:' + @egrep '^(.+)\:\ .*##\ (.+)' ${MAKEFILE_LIST} | sed 's/:.*##/#/' | column -t -c 2 -s '#' + +install: ## install all go dependencies + go get \ + github.com/bufbuild/buf/cmd/buf \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \ + google.golang.org/grpc/cmd/protoc-gen-go-grpc \ + google.golang.org/protobuf/cmd/protoc-gen-go + +generate: ## generate grpc proto for golang + buf generate + go generate ./internal/interface/swagger + +test: ## run all layers tests + @make test.unit + @make test.integration + +test.unit: ## run unit tests + go test ./... + +test.integration: ## run integration tests + @make test.integration.up + @make test.integration.start + @make test.integration.down + +test.integration.up: ## build integration test environment + docker-compose -f deployments/test/docker-compose.yaml --env-file ./.env.test up -d + +test.integration.start: ## run integration test + go test -tags integration ./tests/integration/... + +test.integration.down: ## shutting down integration environment + docker-compose -f deployments/test/docker-compose.yaml --env-file ./.env.test down --volumes --rmi local + +run: ## run app + go run ./cmd/app/main.go + +dev.up: ## run dev environment + docker-compose -f deployments/dev/docker-compose.yaml up -d + +dev.down: ## shutting down dev environment + docker-compose -f deployments/dev/docker-compose.yaml down --volumes --rmi local \ No newline at end of file diff --git a/README.md b/README.md index 26c6209..45ddc35 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,37 @@ # customer +Сервис customer +| Branch | Pipeline | Code coverage | +| ------------- |:-----------------:| --------------:| +| main | [![pipeline status](https://penahub.gitlab.yandexcloud.net/pena-services/customer/badges/main/pipeline.svg)](https://penahub.gitlab.yandexcloud.net/pena-services/customer/-/pipelines) | [![coverage report](https://penahub.gitlab.yandexcloud.net/pena-services/customer/badges/main/coverage.svg?job=test)](https://penahub.gitlab.yandexcloud.net/pena-services/customer/-/pipelines) | +| staging | [![pipeline status](https://penahub.gitlab.yandexcloud.net/pena-services/customer/badges/staging/pipeline.svg)](https://penahub.gitlab.yandexcloud.net/pena-services/customer/-/pipelines) | [![coverage report](https://penahub.gitlab.yandexcloud.net/pena-services/customer/badges/staging/coverage.svg?job=test)](https://penahub.gitlab.yandexcloud.net/pena-services/customer/-/pipelines) | +| dev | [![pipeline status](https://penahub.gitlab.yandexcloud.net/pena-services/customer/badges/dev/pipeline.svg)](https://penahub.gitlab.yandexcloud.net/pena-services/customer/-/pipelines) | [![coverage report](https://penahub.gitlab.yandexcloud.net/pena-services/customer/badges/dev/coverage.svg?job=test)](https://penahub.gitlab.yandexcloud.net/pena-services/customer/-/pipelines) | -## Getting started - -To make it easy for you to get started with GitLab, here's a list of recommended next steps. - -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: +## Переменные окружения приложения ``` -cd existing_repo -git remote add origin https://penahub.gitlab.yandexcloud.net/pena-services/customer.git -git branch -M main -git push -uf origin main +HTTP_HOST - хост приложения (HTTP) +HTTP_PORT - порт приложения (HTTP) + +GRPC_HOST - хост приложения (GRPC) +GRPC_PORT - порт приложения (GRPC) +GRPC_DOMEN - домен приложения (GRPC) + +MONGO_HOST - хост MongoDB +MONGO_PORT - порт MongoDB +MONGO_USER - пользователь MongoDB +MONGO_DB_NAME - название базы данных для подключения +MONGO_PASSWORD - пароль пользователя MongoDB +MONGO_AUTH - имя базы данных Mongo, по которой будет производится авторизация + +AUTH_MICROSERVICE_USER_URL - ссылка на получение пользователя микросервиса авторизации +HUBADMIN_MICROSERVICE_TARIFF_URL - ссылка на получение тарифов (hub admin) +CURRENCY_MICROSERVICE_TRANSLATE_URL - ссылка на перевод с одной валюты на другую +DISCOUNT_MICROSERVICE_GRPC_HOST - хост микросервиса discount (GRPC) +PAYMENT_MICROSERVICE_GRPC_HOST - хост микросервиса payment (GRPC) + +JWT_PUBLIC_KEY - публичный ключ для верификации jwt токена +JWT_ISSUER - издатель токена +JWT_AUDIENCE - аудитория, которая может верифицировать токен ``` - -## Integrate with your tools - -- [ ] [Set up project integrations](https://penahub.gitlab.yandexcloud.net/pena-services/customer/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000..ccda8e0 --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,8 @@ +version: v1 +plugins: + - name: go + out: internal/proto + - name: go-grpc + out: internal/proto + opt: + - require_unimplemented_servers=false \ No newline at end of file diff --git a/buf.work.yaml b/buf.work.yaml new file mode 100644 index 0000000..fd67f23 --- /dev/null +++ b/buf.work.yaml @@ -0,0 +1,3 @@ +version: v1 +directories: + - proto \ No newline at end of file diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000..85468c9 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,4 @@ +version: v1 +lint: + use: + - DEFAULT \ No newline at end of file diff --git a/cmd/app/main.go b/cmd/app/main.go new file mode 100644 index 0000000..13a61ca --- /dev/null +++ b/cmd/app/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/app" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize" +) + +func main() { + logger, err := zap.NewProduction(zap.AddStacktrace(zap.DPanicLevel)) + if err != nil { + log.Fatalf("failed to init zap logger: %v", err) + } + + config, err := initialize.Configuration(".env.test") + if err != nil { + logger.Fatal("failed to init config: %v", zap.Error(err)) + } + + configJSON, err := json.MarshalIndent(config, "", "\t") + if err != nil { + logger.Error("failed to translate config struct to json for print inromation: %v", zap.Error(err)) + } + + fmt.Println("env configuration: \n", string(configJSON)) + + if err := app.Run(config, logger); err != nil { + logger.Fatal("failed to run app: %v", zap.Error(err)) + } +} diff --git a/deployments/dev/.env.dev b/deployments/dev/.env.dev new file mode 100644 index 0000000..1cb9f58 --- /dev/null +++ b/deployments/dev/.env.dev @@ -0,0 +1,27 @@ +# HTTP settings +HTTP_HOST=0.0.0.0 +HTTP_PORT=8080 + +# MONGO settings +MONGO_HOST=localhost +MONGO_PORT=27017 +MONGO_USER=test +MONGO_PASSWORD=test +MONGO_AUTH=admin +MONGO_DB_NAME=admin + +# Auth Microservice settings +AUTH_MICROSERVICE_USER_URL=http://localhost:8000/user + +# Hub Admin Microservice settings +HUBADMIN_MICROSERVICE_TARIFF_URL=http://localhost:8001/tariff + +# Currency Microservice settings +CURRENCY_MICROSERVICE_TRANSLATE_URL=http://localhost:8002/change + +# Admin + +# JWT settings +JWT_ISSUER="pena-auth-service" +JWT_AUDIENCE="pena" +JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikHoKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAE=\n-----END PUBLIC KEY-----" diff --git a/deployments/dev/docker-compose.yaml b/deployments/dev/docker-compose.yaml new file mode 100644 index 0000000..bb66281 --- /dev/null +++ b/deployments/dev/docker-compose.yaml @@ -0,0 +1,43 @@ +version: "3" + +services: + customer-app: + container_name: customer-service + tty: true + build: + context: ../../. + dockerfile: Dockerfile + target: test + env_file: + - .env.dev + environment: + - HTTP_HOST=0.0.0.0 + - HTTP_PORT=8082 + + - GRPC_HOST=0.0.0.0 + - GRPC_PORT=9082 + - GRPC_DOMEN=customer-app:9082 + + - MONGO_HOST=mongo + - MONGO_PORT=27017 + - MONGO_USER=test + - MONGO_PASSWORD=test + - MONGO_DB_NAME=admin + - MONGO_AUTH=admin + + - JWT_ISSUER=pena-auth-service + - JWT_AUDIENCE=pena + + - AUTH_MICROSERVICE_USER_URL=http://pena-auth-service:8000/user + - HUBADMIN_MICROSERVICE_TARIFF_URL=http://hub-admin-service:8010/tariff + - CURRENCY_MICROSERVICE_TRANSLATE_URL=http://cbrf-service:8020/translate + - DISCOUNT_MICROSERVICE_GRPC_HOST=discount-service:9040 + - PAYMENT_MICROSERVICE_GRPC_HOST=treasurer-service:9085 + ports: + - 8082:8082 + - 9082:9082 + networks: + - dev + +networks: + dev: \ No newline at end of file diff --git a/deployments/staging/docker-compose.yaml b/deployments/staging/docker-compose.yaml new file mode 100644 index 0000000..209aba3 --- /dev/null +++ b/deployments/staging/docker-compose.yaml @@ -0,0 +1,42 @@ +version: "3.3" + +services: + customer-app-staging: + hostname: customer-service-staging + container_name: customer-service-staging + image: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID + tty: true + environment: + - HTTP_HOST=0.0.0.0 + - HTTP_PORT=8065 + + - GRPC_HOST=0.0.0.0 + - GRPC_PORT=9085 + - GRPC_DOMEN=http://customer-service:9085 + + - MONGO_HOST=10.6.0.11 + - MONGO_PORT=27017 + - MONGO_USER=$MONGO_USER + - MONGO_PASSWORD=$MONGO_PASSWORD + - MONGO_DB_NAME=customer + - MONGO_AUTH=customer + + - AUTH_MICROSERVICE_USER_URL=https://admin.pena.digital/user + - HUBADMIN_MICROSERVICE_TARIFF_URL=https://admin.pena.digital/strator/tariff + - CURRENCY_MICROSERVICE_TRANSLATE_URL=http://10.6.0.11:3131/change + - DISCOUNT_MICROSERVICE_GRPC_HOST=10.6.0.11:9001 + - PAYMENT_MICROSERVICE_GRPC_HOST=10.6.0.11:9085 + + - JWT_PUBLIC_KEY=$JWT_PUBLIC_KEY + - JWT_ISSUER=pena-auth-service + - JWT_AUDIENCE=pena + ports: + - 8065:8065 + - 9065:9065 + networks: + - marketplace_penahub_frontend + - default + +networks: + marketplace_penahub_frontend: + external: true \ No newline at end of file diff --git a/deployments/test/.env.test b/deployments/test/.env.test new file mode 100644 index 0000000..c27b275 --- /dev/null +++ b/deployments/test/.env.test @@ -0,0 +1,27 @@ +# HTTP settings +HTTP_HOST=0.0.0.0 +HTTP_PORT=8080 + +# MONGO settings +MONGO_HOST=localhost +MONGO_PORT=27017 +MONGO_USER=test +MONGO_PASSWORD=test +MONGO_AUTH=admin +MONGO_DB_NAME=admin + +# Auth Microservice settings +AUTH_MICROSERVICE_USER_URL=http://localhost:8000/user + +# Hub Admin Microservice settings +HUBADMIN_MICROSERVICE_TARIFF_URL=http://localhost:8001/tariff + +# Currency Microservice settings +CURRENCY_MICROSERVICE_TRANSLATE_URL=http://localhost:8002/change + +# Admin + +# JWT settings +JWT_ISSUER="pena-auth-service" +JWT_AUDIENCE="pena" +JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyt4XuLovUY7i12K2PIMbQZOKn+wFFKUvxvKQDel049/+VMpHMx1FLolUKuyGp9zi6gOwjHsBPgc9oqr/eaXGQSh7Ult7i9f+Ht563Y0er5UU9Zc5ZPSxf9O75KYD48ruGkqiFoncDqPENK4dtUa7w0OqlN4bwVBbmIsP8B3EDC5Dof+vtiNTSHSXPx+zifKeZGyknp+nyOHVrRDhPjOhzQzCom0MSZA/sJYmps8QZgiPA0k4Z6jTupDymPOIwYeD2C57zSxnAv0AfC3/pZYJbZYH/0TszRzmy052DME3zMnhMK0ikdN4nzYqU0dkkA5kb5GtKDymspHIJ9eWbUuwgtg8Rq/LrVBj1I3UFgs0ibio40k6gqinLKslc5Y1I5mro7J3OSEP5eO/XeDLOLlOJjEqkrx4fviI1cL3m5L6QV905xmcoNZG1+RmOg7D7cZQUf27TXqM381jkbNdktm1JLTcMScxuo3vaRftnIVw70V8P8sIkaKY8S8HU1sQgE2LB9t04oog5u59htx2FHv4B13NEm8tt8Tv1PexpB4UVh7PIualF6SxdFBrKbraYej72wgjXVPQ0eGXtGGD57j8DUEzk7DK2OvIWhehlVqtiRnFdAvdBj2ynHT2/5FJ/Zpd4n5dKGJcQvy1U1qWMs+8M7AHfWyt2+nZ04s48+bK3yMCAwEAAQ==\n-----END PUBLIC KEY-----" diff --git a/deployments/test/docker-compose.yaml b/deployments/test/docker-compose.yaml new file mode 100644 index 0000000..bf7e447 --- /dev/null +++ b/deployments/test/docker-compose.yaml @@ -0,0 +1,68 @@ +version: "3" + +services: + app: + build: + context: ../../. + dockerfile: Dockerfile + target: test + env_file: + - .env.test + environment: + - HTTP_HOST=0.0.0.0 + - HTTP_PORT=8082 + + - GRPC_HOST=0.0.0.0 + - GRPC_PORT=9082 + - GRPC_DOMEN=customer-app:9082 + + - MONGO_HOST=mongo + - MONGO_PORT=27017 + - MONGO_USER=test + - MONGO_PASSWORD=test + - MONGO_DB_NAME=admin + - MONGO_AUTH=admin + + - JWT_ISSUER=issuer + - JWT_AUDIENCE=audience + + - AUTH_MICROSERVICE_USER_URL=http://pena-auth-service:8000/user + - HUBADMIN_MICROSERVICE_TARIFF_URL=http://hub-admin-service:8010/tariff + - CURRENCY_MICROSERVICE_TRANSLATE_URL=http://cbrf-service:8020/translate + - DISCOUNT_MICROSERVICE_GRPC_HOST=discount-service:9040 + - PAYMENT_MICROSERVICE_GRPC_HOST=treasurer-service:9085 + ports: + - 8082:8082 + depends_on: + - migration + networks: + - integration_test + + migration: + build: + context: ../../. + dockerfile: Dockerfile + target: migration + command: + [ + "sh", + "-c", + 'migrate -source file://migrations -database "mongodb://test:test@mongo:27017/admin?authSource=admin" up', + ] + depends_on: + - mongo + networks: + - integration_test + + mongo: + image: 'mongo:6.0.3' + environment: + MONGO_INITDB_ROOT_USERNAME: test + MONGO_INITDB_ROOT_PASSWORD: test + ports: + - '27017:27017' + networks: + - integration_test + +networks: + integration_test: \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f1c8b7a --- /dev/null +++ b/go.mod @@ -0,0 +1,62 @@ +module penahub.gitlab.yandexcloud.net/pena-services/customer + +go 1.20 + +require ( + github.com/deepmap/oapi-codegen v1.12.4 + github.com/getkin/kin-openapi v0.116.0 + github.com/go-resty/resty/v2 v2.7.0 + github.com/golang-jwt/jwt/v5 v5.0.0 + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 + github.com/joho/godotenv v1.5.1 + github.com/labstack/echo/v4 v4.10.2 + github.com/sethvargo/go-envconfig v0.9.0 + github.com/stretchr/testify v1.8.2 + go.mongodb.org/mongo-driver v1.11.4 + go.uber.org/zap v1.24.0 + google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 + google.golang.org/grpc v1.53.0 + google.golang.org/protobuf v1.30.0 +) + +require ( + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/benbjohnson/clock v1.3.3 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/invopop/yaml v0.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/klauspost/compress v1.16.5 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/labstack/gommon v0.4.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect + github.com/perimeterx/marshmallow v1.1.4 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.1 // indirect + github.com/xdg-go/stringprep v1.0.3 // indirect + github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sync v0.2.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + golang.org/x/time v0.3.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..01c1d0f --- /dev/null +++ b/go.sum @@ -0,0 +1,273 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.3 h1:g+rSsSaAzhHJYcIQE78hJ3AhyjjtQvleKDjlhdBnIhc= +github.com/benbjohnson/clock v1.3.3/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP7g5Q2s= +github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/getkin/kin-openapi v0.116.0 h1:o986hwgMzR972JzOG5j6+WTwWqllZLs1EJKMKCivs2E= +github.com/getkin/kin-openapi v0.116.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.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/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +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.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= +github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= +github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= +github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= +github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +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-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= +github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sethvargo/go-envconfig v0.9.0 h1:Q6FQ6hVEeTECULvkJZakq3dZMeBQ3JUpcKMfPQbKMDE= +github.com/sethvargo/go-envconfig v0.9.0/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/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.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +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/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas= +go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/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.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/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.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 h1:znp6mq/drrY+6khTAlJUDNFFcDGV2ENLYKpMq8SyCds= +google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +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.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/app/app.go b/internal/app/app.go new file mode 100644 index 0000000..8d63084 --- /dev/null +++ b/internal/app/app.go @@ -0,0 +1,116 @@ +package app + +import ( + "context" + "errors" + "fmt" + "os/signal" + "syscall" + "time" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/server" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/closer" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo" +) + +const ( + shutdownTimeout = 5 * time.Second +) + +func Run(config *models.Config, logger *zap.Logger) (appErr error) { + defer func() { + if recovered := recover(); recovered != nil { + appErr = errors.New("recovered panic on application run") + logger.Error("recovered panic on application run", zap.Any("recovered", recovered)) + } + }() + + ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer cancel() + + closer := closer.New() + + mongoDB, err := mongo.Connect(ctx, &mongo.ConnectDeps{ + Configuration: &config.Database, + Timeout: 10 * time.Second, + }) + if err != nil { + return fmt.Errorf("failed connection to db: %w", err) + } + + clients := initialize.NewClients(initialize.ClientsDeps{ + Logger: logger, + AuthURL: &config.Service.AuthMicroservice.URL, + HubadminURL: &config.Service.HubadminMicroservice.URL, + CurrencyURL: &config.Service.CurrencyMicroservice.URL, + DiscountServiceConfiguration: &config.Service.DiscountMicroservice, + PaymentServiceConfiguration: &config.Service.PaymentMicroservice, + }) + + repositories := initialize.NewRepositories(initialize.RepositoriesDeps{ + Logger: logger, + MongoDB: mongoDB, + }) + + services := initialize.NewServices(initialize.ServicesDeps{ + Logger: logger, + Repositories: repositories, + Clients: clients, + ConfigurationGRPC: &config.GRPC, + }) + + controllers := initialize.NewControllers(initialize.ControllersDeps{ + Logger: logger, + Services: services, + }) + + openapi, err := swagger.GetSwagger() + if err != nil { + return fmt.Errorf("failed to loading openapi spec: %w", err) + } + + api := initialize.NewAPI(*controllers) + + serverHTTP, httpErr := server.NewHTTP(server.DepsHTTP{ + Logger: logger, + Swagger: openapi, + AuthenticationFunc: utils.NewAuthenticator(utils.NewJWT(&config.Service.JWT)), + }) + if httpErr != nil { + return httpErr.Wrap("failed to init http server") + } + + serverGRPC, grpcErr := server.NewGRPC(server.DepsGRPC{Logger: logger}) + if grpcErr != nil { + return httpErr.Wrap("failed to init grpc server") + } + + serverHTTP.Register(api) + serverGRPC.Register(controllers) + + go serverHTTP.Run(&config.HTTP) + go serverGRPC.Run(&config.GRPC) + + closer.Add(mongoDB.Client().Disconnect) + closer.Add(serverHTTP.Stop) + closer.Add(serverGRPC.Stop) + + <-ctx.Done() + + logger.Info("shutting down app gracefully") + + shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout) + + defer cancel() + + if err := closer.Close(shutdownCtx); err != nil { + return fmt.Errorf("closer: %w", err) + } + + return nil +} diff --git a/internal/errors/errors.go b/internal/errors/errors.go new file mode 100644 index 0000000..803bb87 --- /dev/null +++ b/internal/errors/errors.go @@ -0,0 +1,62 @@ +package errors + +import ( + "errors" + "fmt" +) + +type ErrorType error + +var ( + ErrInternalError ErrorType = errors.New("internal error") + ErrInvalidArgs ErrorType = errors.New("invalid arguments") + ErrMethodNotImplemented ErrorType = errors.New("method is not implemented") + ErrNotFound ErrorType = errors.New("record not found") + ErrNoAccess ErrorType = errors.New("no access") + ErrConflict ErrorType = errors.New("record already exist") + ErrInsufficientFunds ErrorType = errors.New("insufficient funds") +) + +type Error interface { + Error() string + Type() ErrorType + Wrap(message string) Error + SetType(errorType ErrorType) +} + +type customError struct { + errorType ErrorType + err error +} + +func New(err error, errorType ErrorType) Error { + return &customError{ + errorType: errorType, + err: err, + } +} + +func NewWithMessage(message string, errorType ErrorType) Error { + return &customError{ + errorType: errorType, + err: errors.New(message), + } +} + +func (receiver *customError) Error() string { + return receiver.err.Error() +} + +func (receiver *customError) Type() ErrorType { + return receiver.errorType +} + +func (receiver *customError) Wrap(message string) Error { + receiver.err = fmt.Errorf("%s: %w", message, receiver.err) + + return receiver +} + +func (receiver *customError) SetType(errorType ErrorType) { + receiver.errorType = errorType +} diff --git a/internal/errors/grpc.go b/internal/errors/grpc.go new file mode 100644 index 0000000..a89bab0 --- /dev/null +++ b/internal/errors/grpc.go @@ -0,0 +1,27 @@ +package errors + +import ( + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const defaultGRPCCode = codes.Internal + +var grpcCodes = map[error]codes.Code{ + ErrInvalidArgs: codes.InvalidArgument, + ErrConflict: codes.AlreadyExists, + ErrInsufficientFunds: codes.Unavailable, + ErrInternalError: codes.Internal, + ErrMethodNotImplemented: codes.Unimplemented, + ErrNoAccess: codes.Unavailable, + ErrNotFound: codes.NotFound, +} + +func GRPC(message string, err Error) error { + currentStatus, ok := grpcCodes[err] + if !ok { + return status.Errorf(defaultGRPCCode, message, err) + } + + return status.Errorf(currentStatus, message, err) +} diff --git a/internal/errors/http.go b/internal/errors/http.go new file mode 100644 index 0000000..f2f8fde --- /dev/null +++ b/internal/errors/http.go @@ -0,0 +1,33 @@ +package errors + +import ( + "net/http" + + "github.com/labstack/echo/v4" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +var httpStatuses = map[ErrorType]int{ + ErrInternalError: http.StatusInternalServerError, + ErrInvalidArgs: http.StatusBadRequest, + ErrNoAccess: http.StatusForbidden, + ErrNotFound: http.StatusNotFound, + ErrMethodNotImplemented: http.StatusNotImplemented, + ErrConflict: http.StatusConflict, + ErrInsufficientFunds: http.StatusPaymentRequired, +} + +func HTTP(ctx echo.Context, err Error) error { + status, ok := httpStatuses[err.Type()] + if !ok { + return ctx.JSON(http.StatusInternalServerError, models.ResponseErrorHTTP{ + StatusCode: http.StatusInternalServerError, + Message: err.Error(), + }) + } + + return ctx.JSON(status, models.ResponseErrorHTTP{ + StatusCode: status, + Message: err.Error(), + }) +} diff --git a/internal/fields/account.go b/internal/fields/account.go new file mode 100644 index 0000000..977eb5a --- /dev/null +++ b/internal/fields/account.go @@ -0,0 +1,25 @@ +package fields + +var Account = struct { + ID string + UserID string + Cart string + Wallet string + Name string + Status string + IsDeleted string + CreatedAt string + UpdatedAt string + DeletedAt string +}{ + ID: "_id", + UserID: "userId", + Cart: "cart", + Wallet: "wallet", + Name: "name", + Status: "status", + IsDeleted: "isDeleted", + CreatedAt: "createdAt", + UpdatedAt: "updatedAt", + DeletedAt: "deletedAt", +} diff --git a/internal/fields/currency.go b/internal/fields/currency.go new file mode 100644 index 0000000..3987b47 --- /dev/null +++ b/internal/fields/currency.go @@ -0,0 +1,19 @@ +package fields + +var Currency = struct { + ID string + Name string + Currencies string + IsDeleted string + CreatedAt string + UpdatedAt string + DeletedAt string +}{ + ID: "_id", + Name: "name", + Currencies: "currencies", + IsDeleted: "isDeleted", + CreatedAt: "createdAt", + UpdatedAt: "updatedAt", + DeletedAt: "deletedAt", +} diff --git a/internal/fields/history.go b/internal/fields/history.go new file mode 100644 index 0000000..6ee43c1 --- /dev/null +++ b/internal/fields/history.go @@ -0,0 +1,23 @@ +package fields + +var History = struct { + ID string + UserID string + Comment string + Subject string + Type string + IsDeleted string + CreatedAt string + UpdatedAt string + DeletedAt string +}{ + ID: "_id", + UserID: "userId", + Comment: "comment", + Subject: "subject", + Type: "type", + IsDeleted: "isDeleted", + CreatedAt: "createdAt", + UpdatedAt: "updatedAt", + DeletedAt: "deletedAt", +} diff --git a/internal/initialize/api.go b/internal/initialize/api.go new file mode 100644 index 0000000..514ee01 --- /dev/null +++ b/internal/initialize/api.go @@ -0,0 +1,15 @@ +package initialize + +import ( + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger" +) + +func NewAPI(controllers Controllers) *swagger.API { + return swagger.New(swagger.Deps{ + AccountController: controllers.AccountController, + CurrencyController: controllers.CurrencyController, + CartController: controllers.CartController, + WalletController: controllers.WalletController, + HistoryController: controllers.HistoryController, + }) +} diff --git a/internal/initialize/api_test.go b/internal/initialize/api_test.go new file mode 100644 index 0000000..a2b4bcd --- /dev/null +++ b/internal/initialize/api_test.go @@ -0,0 +1,51 @@ +package initialize_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/mongo/integration/mtest" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +func TestNewAPI(t *testing.T) { + mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) + + mt.Run("API сваггера должен успешно инициализироваться", func(t *mtest.T) { + assert.NotPanics(t, func() { + logger := zap.New(zap.L().Core()) + + repositories := initialize.NewRepositories(initialize.RepositoriesDeps{ + Logger: logger, + MongoDB: t.Client.Database("test"), + }) + + clients := initialize.NewClients(initialize.ClientsDeps{ + Logger: logger, + AuthURL: &models.AuthMicroserviceURL{User: ""}, + HubadminURL: &models.HubadminMicroserviceURL{Tariff: ""}, + CurrencyURL: &models.CurrencyMicroserviceURL{}, + DiscountServiceConfiguration: &models.DiscountMicroserviceConfiguration{HostGRPC: "host"}, + PaymentServiceConfiguration: &models.PaymentMicroserviceConfiguration{HostGRPC: "host"}, + }) + + services := initialize.NewServices(initialize.ServicesDeps{ + Logger: logger, + Repositories: repositories, + Clients: clients, + ConfigurationGRPC: &models.ConfigurationGRPC{Domen: "domen"}, + }) + + controllers := initialize.NewControllers(initialize.ControllersDeps{ + Logger: logger, + Services: services, + }) + + api := initialize.NewAPI(*controllers) + + assert.NotNil(t, api) + }) + }) +} diff --git a/internal/initialize/clients.go b/internal/initialize/clients.go new file mode 100644 index 0000000..dbe0f04 --- /dev/null +++ b/internal/initialize/clients.go @@ -0,0 +1,49 @@ +package initialize + +import ( + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/client" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +type ClientsDeps struct { + Logger *zap.Logger + AuthURL *models.AuthMicroserviceURL + HubadminURL *models.HubadminMicroserviceURL + CurrencyURL *models.CurrencyMicroserviceURL + DiscountServiceConfiguration *models.DiscountMicroserviceConfiguration + PaymentServiceConfiguration *models.PaymentMicroserviceConfiguration +} + +type Clients struct { + AuthClient *client.AuthClient + HubadminClient *client.HubadminClient + CurrencyClient *client.CurrencyClient + DiscountClient *client.DiscountClient + PaymentClient *client.PaymentClient +} + +func NewClients(deps ClientsDeps) *Clients { + return &Clients{ + AuthClient: client.NewAuthClient(client.AuthClientDeps{ + Logger: deps.Logger, + URLs: deps.AuthURL, + }), + HubadminClient: client.NewHubadminClient(client.HubadminClientDeps{ + Logger: deps.Logger, + URLs: deps.HubadminURL, + }), + CurrencyClient: client.NewCurrencyClient(client.CurrencyClientDeps{ + Logger: deps.Logger, + URLs: deps.CurrencyURL, + }), + DiscountClient: client.NewDiscountClient(client.DiscountClientDeps{ + Logger: deps.Logger, + DiscountServiceHost: deps.DiscountServiceConfiguration.HostGRPC, + }), + PaymentClient: client.NewPaymentClient(client.PaymentClientDeps{ + Logger: deps.Logger, + PaymentServiceHost: deps.PaymentServiceConfiguration.HostGRPC, + }), + } +} diff --git a/internal/initialize/clients_test.go b/internal/initialize/clients_test.go new file mode 100644 index 0000000..bfa925c --- /dev/null +++ b/internal/initialize/clients_test.go @@ -0,0 +1,34 @@ +package initialize_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +func TestNewClients(t *testing.T) { + t.Run("Клиенты должны успешно инициализироваться", func(t *testing.T) { + logger := zap.New(zap.L().Core()) + + assert.NotPanics(t, func() { + clients := initialize.NewClients(initialize.ClientsDeps{ + Logger: logger, + AuthURL: &models.AuthMicroserviceURL{}, + HubadminURL: &models.HubadminMicroserviceURL{}, + CurrencyURL: &models.CurrencyMicroserviceURL{}, + DiscountServiceConfiguration: &models.DiscountMicroserviceConfiguration{HostGRPC: "host"}, + PaymentServiceConfiguration: &models.PaymentMicroserviceConfiguration{HostGRPC: "host"}, + }) + + assert.NotNil(t, clients) + assert.NotNil(t, clients.AuthClient) + assert.NotNil(t, clients.HubadminClient) + assert.NotNil(t, clients.CurrencyClient) + assert.NotNil(t, clients.DiscountClient) + assert.NotNil(t, clients.PaymentClient) + }) + }) +} diff --git a/internal/initialize/config.go b/internal/initialize/config.go new file mode 100644 index 0000000..c021baf --- /dev/null +++ b/internal/initialize/config.go @@ -0,0 +1,30 @@ +package initialize + +import ( + "time" + + "github.com/golang-jwt/jwt/v5" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/env" +) + +func Configuration(path string) (*models.Config, error) { + config, err := env.Parse[models.Config](path) + if err != nil { + return nil, err + } + + if err := utils.ValidateConfigurationURLs(&config.Service); err != nil { + return nil, err + } + + iniJWTConfiguration(&config.Service.JWT) + + return config, nil +} + +func iniJWTConfiguration(config *models.JWTConfiguration) { + config.Algorithm = *jwt.SigningMethodRS256 + config.ExpiresIn = 15 * time.Minute +} diff --git a/internal/initialize/config_test.go b/internal/initialize/config_test.go new file mode 100644 index 0000000..1345ed2 --- /dev/null +++ b/internal/initialize/config_test.go @@ -0,0 +1,131 @@ +package initialize_test + +import ( + "testing" + "time" + + "github.com/golang-jwt/jwt/v5" + "github.com/stretchr/testify/assert" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo" +) + +func TestConfiguration(t *testing.T) { + t.Run("Успешная инициализация конфигурации", func(t *testing.T) { + defaultConfiguration := setDefaultTestingENV(t) + + assert.NotPanics(t, func() { + configuration, err := initialize.Configuration("") + + assert.NoError(t, err) + assert.Equal(t, defaultConfiguration, configuration) + }) + }) + + t.Run("Ошибка при наличии кривого url", func(t *testing.T) { + setDefaultTestingENV(t) + t.Setenv("AUTH_MICROSERVICE_USER_URL", "url") + + assert.NotPanics(t, func() { + configuration, err := initialize.Configuration("") + + assert.Error(t, err) + assert.Nil(t, configuration) + }) + }) + + t.Run("Ошибка при отсутствии обязательного env", func(t *testing.T) { + assert.NotPanics(t, func() { + configuration, err := initialize.Configuration("") + + assert.Error(t, err) + assert.Nil(t, configuration) + }) + }) +} + +func setDefaultTestingENV(t *testing.T) *models.Config { + t.Helper() + + defaultAuthURL := models.AuthMicroserviceURL{ + User: "http://www.auth.ru/user", + } + + defaultHubAdminURL := models.HubadminMicroserviceURL{ + Tariff: "http://www.hubadmin.ru/tariff", + } + + defaultCurrencyURL := models.CurrencyMicroserviceURL{ + Translate: "http://www.currency.ru/change", + } + + defaultConfiguration := models.Config{ + HTTP: models.ConfigurationHTTP{ + Host: "localhost", + Port: "8080", + }, + GRPC: models.ConfigurationGRPC{ + Host: "localhost", + Port: "8081", + Domen: "domen", + }, + Service: models.ServiceConfiguration{ + AuthMicroservice: models.AuthMicroserviceConfiguration{ + URL: defaultAuthURL, + }, + HubadminMicroservice: models.HubadminMicroserviceConfiguration{ + URL: defaultHubAdminURL, + }, + CurrencyMicroservice: models.CurrencyMicroserviceConfiguration{ + URL: defaultCurrencyURL, + }, + DiscountMicroservice: models.DiscountMicroserviceConfiguration{ + HostGRPC: "domen", + }, + PaymentMicroservice: models.PaymentMicroserviceConfiguration{ + HostGRPC: "domen", + }, + JWT: models.JWTConfiguration{ + PrivateKey: "jwt private key", + PublicKey: "jwt public key", + Issuer: "issuer", + Audience: "audience", + Algorithm: *jwt.SigningMethodRS256, + ExpiresIn: 15 * time.Minute, + }, + }, + Database: mongo.Configuration{ + Host: "localhost", + Port: "27017", + User: "user", + Password: "pass", + Auth: "db", + DatabaseName: "db", + }, + } + + t.Setenv("GRPC_HOST", defaultConfiguration.GRPC.Host) + t.Setenv("GRPC_PORT", defaultConfiguration.GRPC.Port) + t.Setenv("GRPC_DOMEN", defaultConfiguration.GRPC.Domen) + + t.Setenv("JWT_PUBLIC_KEY", defaultConfiguration.Service.JWT.PublicKey) + t.Setenv("JWT_PRIVATE_KEY", defaultConfiguration.Service.JWT.PrivateKey) + t.Setenv("JWT_ISSUER", defaultConfiguration.Service.JWT.Issuer) + t.Setenv("JWT_AUDIENCE", defaultConfiguration.Service.JWT.Audience) + + t.Setenv("AUTH_MICROSERVICE_USER_URL", defaultConfiguration.Service.AuthMicroservice.URL.User) + t.Setenv("HUBADMIN_MICROSERVICE_TARIFF_URL", defaultConfiguration.Service.HubadminMicroservice.URL.Tariff) + t.Setenv("CURRENCY_MICROSERVICE_TRANSLATE_URL", defaultConfiguration.Service.CurrencyMicroservice.URL.Translate) + t.Setenv("DISCOUNT_MICROSERVICE_GRPC_HOST", defaultConfiguration.Service.DiscountMicroservice.HostGRPC) + t.Setenv("PAYMENT_MICROSERVICE_GRPC_HOST", defaultConfiguration.Service.PaymentMicroservice.HostGRPC) + + t.Setenv("MONGO_HOST", defaultConfiguration.Database.Host) + t.Setenv("MONGO_PORT", defaultConfiguration.Database.Port) + t.Setenv("MONGO_USER", defaultConfiguration.Database.User) + t.Setenv("MONGO_PASSWORD", defaultConfiguration.Database.Password) + t.Setenv("MONGO_AUTH", defaultConfiguration.Database.Auth) + t.Setenv("MONGO_DB_NAME", defaultConfiguration.Database.DatabaseName) + + return &defaultConfiguration +} diff --git a/internal/initialize/controllers.go b/internal/initialize/controllers.go new file mode 100644 index 0000000..2ae5dab --- /dev/null +++ b/internal/initialize/controllers.go @@ -0,0 +1,61 @@ +package initialize + +import ( + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/grpc/customer" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/grpc/payment" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/rest/account" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/rest/cart" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/rest/currency" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/rest/history" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/rest/wallet" +) + +type ControllersDeps struct { + Logger *zap.Logger + Services *Services +} + +type Controllers struct { + AccountController *account.Controller + CurrencyController *currency.Controller + CartController *cart.Controller + HistoryController *history.Controller + WalletController *wallet.Controller + PaymentController *payment.Controller + CustomerController *customer.Controller +} + +func NewControllers(deps ControllersDeps) *Controllers { + return &Controllers{ + AccountController: account.New(account.Deps{ + Logger: deps.Logger, + Service: deps.Services.AccountService, + }), + CurrencyController: currency.New(currency.Deps{ + Logger: deps.Logger, + CurrencyService: deps.Services.CurrencyService, + }), + CartController: cart.New(cart.Deps{ + Logger: deps.Logger, + CartService: deps.Services.CartService, + }), + HistoryController: history.New(history.Deps{ + Logger: deps.Logger, + HistoryService: deps.Services.HistoryService, + }), + WalletController: wallet.New(wallet.Deps{ + Logger: deps.Logger, + WalletService: deps.Services.WalletService, + PaymentService: deps.Services.PaymentService, + }), + PaymentController: payment.New(payment.Deps{ + Logger: deps.Logger, + PaymentCallbackService: deps.Services.PaymentCallbackService, + }), + CustomerController: customer.New(customer.Deps{ + Logger: deps.Logger, + HistoryService: deps.Services.HistoryService, + }), + } +} diff --git a/internal/initialize/controllers_test.go b/internal/initialize/controllers_test.go new file mode 100644 index 0000000..3d299e9 --- /dev/null +++ b/internal/initialize/controllers_test.go @@ -0,0 +1,54 @@ +package initialize_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/mongo/integration/mtest" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +func TestNewControllers(t *testing.T) { + mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) + + mt.Run("Контроллеры должны успешно инициализироваться", func(t *mtest.T) { + logger := zap.New(zap.L().Core()) + + assert.NotPanics(t, func() { + clients := initialize.NewClients(initialize.ClientsDeps{ + Logger: logger, + AuthURL: &models.AuthMicroserviceURL{}, + HubadminURL: &models.HubadminMicroserviceURL{}, + CurrencyURL: &models.CurrencyMicroserviceURL{}, + DiscountServiceConfiguration: &models.DiscountMicroserviceConfiguration{HostGRPC: "host"}, + PaymentServiceConfiguration: &models.PaymentMicroserviceConfiguration{HostGRPC: "host"}, + }) + + repositories := initialize.NewRepositories(initialize.RepositoriesDeps{ + Logger: logger, + MongoDB: t.Client.Database("test"), + }) + + services := initialize.NewServices(initialize.ServicesDeps{ + Logger: logger, + Clients: clients, + Repositories: repositories, + ConfigurationGRPC: &models.ConfigurationGRPC{Domen: "http://test:8080"}, + }) + + controllers := initialize.NewControllers(initialize.ControllersDeps{ + Logger: logger, + Services: services, + }) + + assert.NotNil(t, controllers) + assert.NotNil(t, controllers.AccountController) + assert.NotNil(t, controllers.CurrencyController) + assert.NotNil(t, controllers.CartController) + assert.NotNil(t, controllers.HistoryController) + assert.NotNil(t, controllers.WalletController) + }) + }) +} diff --git a/internal/initialize/repositories.go b/internal/initialize/repositories.go new file mode 100644 index 0000000..01b0810 --- /dev/null +++ b/internal/initialize/repositories.go @@ -0,0 +1,37 @@ +package initialize + +import ( + "go.mongodb.org/mongo-driver/mongo" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/repository" +) + +type RepositoriesDeps struct { + Logger *zap.Logger + MongoDB *mongo.Database +} + +type Repositories struct { + HealthRepository *repository.HealthRepository + AccountRepository *repository.AccountRepository + CurrencyRepository *repository.CurrencyRepository + HistoryRepository *repository.HistoryRepository +} + +func NewRepositories(deps RepositoriesDeps) *Repositories { + return &Repositories{ + HealthRepository: repository.NewHealthRepository(deps.MongoDB), + AccountRepository: repository.NewAccountRepository(repository.AccountRepositoryDeps{ + Logger: deps.Logger, + MongoDB: deps.MongoDB.Collection("accounts"), + }), + CurrencyRepository: repository.NewCurrencyRepository(repository.CurrencyRepositoryDeps{ + Logger: deps.Logger, + MongoDB: deps.MongoDB.Collection("currency_lists"), + }), + HistoryRepository: repository.NewHistoryRepository(repository.HistoryRepositoryDeps{ + Logger: deps.Logger, + MongoDB: deps.MongoDB.Collection("histories"), + }), + } +} diff --git a/internal/initialize/repositories_test.go b/internal/initialize/repositories_test.go new file mode 100644 index 0000000..a57e31e --- /dev/null +++ b/internal/initialize/repositories_test.go @@ -0,0 +1,31 @@ +package initialize_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/mongo/integration/mtest" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize" +) + +func TestNewRepositories(t *testing.T) { + mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) + + mt.Run("Репозитории должны успешно инициализироваться", func(t *mtest.T) { + logger := zap.New(zap.L().Core()) + + assert.NotPanics(t, func() { + repositories := initialize.NewRepositories(initialize.RepositoriesDeps{ + Logger: logger, + MongoDB: t.Client.Database("test"), + }) + + assert.NotNil(t, repositories) + assert.NotNil(t, repositories.AccountRepository) + assert.NotNil(t, repositories.HealthRepository) + assert.NotNil(t, repositories.CurrencyRepository) + assert.NotNil(t, repositories.HistoryRepository) + }) + }) +} diff --git a/internal/initialize/services.go b/internal/initialize/services.go new file mode 100644 index 0000000..e8f6162 --- /dev/null +++ b/internal/initialize/services.go @@ -0,0 +1,78 @@ +package initialize + +import ( + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/account" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/callback" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/cart" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/currency" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/history" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/payment" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/wallet" +) + +type ServicesDeps struct { + Logger *zap.Logger + Repositories *Repositories + Clients *Clients + ConfigurationGRPC *models.ConfigurationGRPC +} + +type Services struct { + AccountService *account.Service + CurrencyService *currency.Service + CartService *cart.Service + HistoryService *history.Service + WalletService *wallet.Service + PaymentService *payment.Service + PaymentCallbackService *callback.PaymentCallbackService +} + +func NewServices(deps ServicesDeps) *Services { + historyService := history.New(history.Deps{ + Logger: deps.Logger, + Repository: deps.Repositories.HistoryRepository, + }) + + walletService := wallet.New(wallet.Deps{ + Logger: deps.Logger, + Repository: deps.Repositories.AccountRepository, + CurrencyClient: deps.Clients.CurrencyClient, + HistoryService: historyService, + }) + + return &Services{ + WalletService: walletService, + HistoryService: historyService, + AccountService: account.New(account.Deps{ + Logger: deps.Logger, + Repository: deps.Repositories.AccountRepository, + AuthClient: deps.Clients.AuthClient, + }), + CurrencyService: currency.New(currency.Deps{ + Logger: deps.Logger, + Repository: deps.Repositories.CurrencyRepository, + }), + CartService: cart.New(cart.Deps{ + Logger: deps.Logger, + Repository: deps.Repositories.AccountRepository, + HubadminClient: deps.Clients.HubadminClient, + DiscountClient: deps.Clients.DiscountClient, + WalletService: walletService, + HistoryService: historyService, + }), + PaymentService: payment.New(payment.Deps{ + Logger: deps.Logger, + ConfigurationGRPC: deps.ConfigurationGRPC, + PaymentClient: deps.Clients.PaymentClient, + AuthClient: deps.Clients.AuthClient, + }), + PaymentCallbackService: callback.NewPaymentCallbackService(callback.PaymentCallbackServiceDeps{ + Logger: deps.Logger, + AccountRepository: deps.Repositories.AccountRepository, + WalletService: walletService, + HistoryService: historyService, + }), + } +} diff --git a/internal/initialize/services_test.go b/internal/initialize/services_test.go new file mode 100644 index 0000000..8de4726 --- /dev/null +++ b/internal/initialize/services_test.go @@ -0,0 +1,51 @@ +package initialize_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/mongo/integration/mtest" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +func TestNewServices(t *testing.T) { + mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock)) + + mt.Run("Сервисы должны успешно инициализироваться", func(t *mtest.T) { + logger := zap.New(zap.L().Core()) + + assert.NotPanics(t, func() { + clients := initialize.NewClients(initialize.ClientsDeps{ + Logger: logger, + AuthURL: &models.AuthMicroserviceURL{}, + HubadminURL: &models.HubadminMicroserviceURL{}, + CurrencyURL: &models.CurrencyMicroserviceURL{}, + DiscountServiceConfiguration: &models.DiscountMicroserviceConfiguration{HostGRPC: "host"}, + PaymentServiceConfiguration: &models.PaymentMicroserviceConfiguration{HostGRPC: "host"}, + }) + + repositories := initialize.NewRepositories(initialize.RepositoriesDeps{ + Logger: logger, + MongoDB: t.Client.Database("test"), + }) + + services := initialize.NewServices(initialize.ServicesDeps{ + Logger: logger, + Clients: clients, + Repositories: repositories, + ConfigurationGRPC: &models.ConfigurationGRPC{Domen: "http://test:8080"}, + }) + + assert.NotNil(t, services) + assert.NotNil(t, services.AccountService) + assert.NotNil(t, services.CartService) + assert.NotNil(t, services.CurrencyService) + assert.NotNil(t, services.HistoryService) + assert.NotNil(t, services.WalletService) + assert.NotNil(t, services.PaymentService) + assert.NotNil(t, services.PaymentCallbackService) + }) + }) +} diff --git a/internal/interface/client/auth.go b/internal/interface/client/auth.go new file mode 100644 index 0000000..24a6b7e --- /dev/null +++ b/internal/interface/client/auth.go @@ -0,0 +1,78 @@ +package client + +import ( + "context" + "fmt" + "log" + "net/url" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/client" +) + +type AuthClientDeps struct { + Logger *zap.Logger + URLs *models.AuthMicroserviceURL +} + +type AuthClient struct { + logger *zap.Logger + urls *models.AuthMicroserviceURL +} + +func NewAuthClient(deps AuthClientDeps) *AuthClient { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.URLs == nil { + log.Panicln("urls is nil on ") + } + + return &AuthClient{ + logger: deps.Logger, + urls: deps.URLs, + } +} + +func (receiver *AuthClient) GetUser(ctx context.Context, userID string) (*models.User, errors.Error) { + userURL, err := url.JoinPath(receiver.urls.User, userID) + if err != nil { + return nil, errors.New( + fmt.Errorf("failed to join path on of : %w", err), + errors.ErrInternalError, + ) + } + + response, err := client.Get[models.User, models.FastifyError](ctx, &client.RequestSettings{ + URL: userURL, + Headers: map[string]string{"Content-Type": "application/json"}, + }) + if err != nil { + receiver.logger.Error("failed to request get user on of ", + zap.Error(err), + zap.String("userID", userID), + ) + + return nil, errors.New( + fmt.Errorf("failed to request get user with <%s> on of : %w", userID, err), + errors.ErrInternalError, + ) + } + if response.Error != nil { + receiver.logger.Error("failed request on of ", + zap.String("error", response.Error.Message), + zap.String("userID", userID), + ) + + return nil, errors.New( + fmt.Errorf("failed request with <%s> on of : %s", userID, response.Error.Message), + utils.DetermineClientErrorResponse(response.StatusCode), + ) + } + + return response.Body, nil +} diff --git a/internal/interface/client/currency.go b/internal/interface/client/currency.go new file mode 100644 index 0000000..14a1292 --- /dev/null +++ b/internal/interface/client/currency.go @@ -0,0 +1,79 @@ +package client + +import ( + "context" + "fmt" + "log" + "strconv" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/client" +) + +type CurrencyClientDeps struct { + Logger *zap.Logger + URLs *models.CurrencyMicroserviceURL +} + +type CurrencyClient struct { + logger *zap.Logger + urls *models.CurrencyMicroserviceURL +} + +func NewCurrencyClient(deps CurrencyClientDeps) *CurrencyClient { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.URLs == nil { + log.Panicln("urls is nil on ") + } + + return &CurrencyClient{ + logger: deps.Logger, + urls: deps.URLs, + } +} + +func (receiver *CurrencyClient) Translate(ctx context.Context, request *models.TranslateCurrency) (int64, errors.Error) { + response, err := client.Get[models.CurrencyClientResponse[int64], models.CurrencyClientResponse[string]](ctx, &client.RequestSettings{ + URL: receiver.urls.Translate, + Headers: map[string]string{"Content-Type": "application/json"}, + QueryParams: map[string]string{ + "currencyFrom": request.From, + "currencyTo": request.To, + "value": strconv.FormatInt(request.Money, 10), + }, + }) + if err != nil { + receiver.logger.Error("failed to request on of ", + zap.Error(err), + zap.String("from", request.From), + zap.String("to", request.To), + zap.Int64("money", request.Money), + ) + + return 0, errors.New( + fmt.Errorf("failed to request on of : %w", err), + errors.ErrInternalError, + ) + } + if response.Error != nil { + receiver.logger.Error("failed translate currency on of ", + zap.String("error", response.Error.Message), + zap.String("from", request.From), + zap.String("to", request.To), + zap.Int64("money", request.Money), + ) + + return 0, errors.New( + fmt.Errorf("failed translate currency on of : %s", response.Error.Message), + utils.DetermineClientErrorResponse(response.StatusCode), + ) + } + + return response.Body.Message, nil +} diff --git a/internal/interface/client/discount.go b/internal/interface/client/discount.go new file mode 100644 index 0000000..76ae52c --- /dev/null +++ b/internal/interface/client/discount.go @@ -0,0 +1,62 @@ +package client + +import ( + "context" + "fmt" + "log" + + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/discount" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate" +) + +type DiscountClientDeps struct { + Logger *zap.Logger + DiscountServiceHost string +} + +type DiscountClient struct { + logger *zap.Logger + discountServiceHost string +} + +func NewDiscountClient(deps DiscountClientDeps) *DiscountClient { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if validate.IsStringEmpty(deps.DiscountServiceHost) { + log.Panicln("discount host is empty on ") + } + + return &DiscountClient{ + logger: deps.Logger, + discountServiceHost: deps.DiscountServiceHost, + } +} + +func (receiver *DiscountClient) Apply(ctx context.Context, request *discount.ApplyDiscountRequest) (*discount.ApplyDiscountResponse, errors.Error) { + connection, err := grpc.Dial(receiver.discountServiceHost, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + receiver.logger.Error("failed to connect on of ", zap.Error(err), zap.String("discount host", receiver.discountServiceHost)) + return nil, errors.New(fmt.Errorf("failed connect to discount service: %w", err), errors.ErrInternalError) + } + defer func() { + if closeErr := connection.Close(); closeErr != nil { + receiver.logger.Error("failed to close connection on of ", zap.Error(closeErr)) + } + }() + + client := discount.NewDiscountServiceClient(connection) + + response, err := client.ApplyDiscounts(ctx, request) + if err != nil { + receiver.logger.Error("failed to apply discounts on of ", zap.Error(err), zap.Any("request", request)) + return nil, errors.New(fmt.Errorf("failed to apply discounts: %w", err), errors.ErrInternalError) + } + + return response, nil +} diff --git a/internal/interface/client/hubadmin.go b/internal/interface/client/hubadmin.go new file mode 100644 index 0000000..e4e21eb --- /dev/null +++ b/internal/interface/client/hubadmin.go @@ -0,0 +1,94 @@ +package client + +import ( + "context" + "fmt" + "log" + "net/url" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/client" +) + +type HubadminClientDeps struct { + Logger *zap.Logger + URLs *models.HubadminMicroserviceURL +} + +type HubadminClient struct { + logger *zap.Logger + urls *models.HubadminMicroserviceURL +} + +func NewHubadminClient(deps HubadminClientDeps) *HubadminClient { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.URLs == nil { + log.Panicln("urls is nil on ") + } + + return &HubadminClient{ + logger: deps.Logger, + urls: deps.URLs, + } +} + +func (receiver *HubadminClient) GetTariff(ctx context.Context, tariffID string) (*models.Tariff, errors.Error) { + tariffURL, err := url.JoinPath(receiver.urls.Tariff, tariffID) + if err != nil { + return nil, errors.New( + fmt.Errorf("failed to join path on of : %w", err), + errors.ErrInternalError, + ) + } + + response, err := client.Get[models.Tariff, models.FastifyError](ctx, &client.RequestSettings{ + URL: tariffURL, + Headers: map[string]string{"Content-Type": "application/json"}, + }) + if err != nil { + receiver.logger.Error("failed to request get tariff on of ", + zap.Error(err), + zap.String("tariffID", tariffID), + ) + + return nil, errors.New( + fmt.Errorf("failed to request get tariff with <%s> on of : %w", tariffID, err), + errors.ErrInternalError, + ) + } + if response.Error != nil { + receiver.logger.Error("failed request on of ", + zap.String("error", response.Error.Message), + zap.String("tariffID", tariffID), + ) + + return nil, errors.New( + fmt.Errorf("failed request with <%s> on of : %s", tariffID, response.Error.Message), + utils.DetermineClientErrorResponse(response.StatusCode), + ) + } + + return response.Body, nil +} + +func (receiver *HubadminClient) GetTariffs(ctx context.Context, tarriffIDs []string) ([]models.Tariff, errors.Error) { + tariffs := make([]models.Tariff, len(tarriffIDs)) + + for index, tariffID := range tarriffIDs { + tariff, err := receiver.GetTariff(ctx, tariffID) + if err != nil { + receiver.logger.Error("failed to get tariff on of ", zap.Error(err), zap.String("tariffID", tariffID)) + return []models.Tariff{}, err + } + + tariffs[index] = *tariff + } + + return tariffs, nil +} diff --git a/internal/interface/client/payment.go b/internal/interface/client/payment.go new file mode 100644 index 0000000..9f7b6a3 --- /dev/null +++ b/internal/interface/client/payment.go @@ -0,0 +1,271 @@ +package client + +import ( + "context" + "fmt" + "log" + + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/treasurer" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate" +) + +type PaymentClientDeps struct { + Logger *zap.Logger + PaymentServiceHost string +} + +type PaymentClient struct { + logger *zap.Logger + paymentServiceHost string +} + +func NewPaymentClient(deps PaymentClientDeps) *PaymentClient { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if validate.IsStringEmpty(deps.PaymentServiceHost) { + log.Panicln("payment host is empty on ") + } + + return &PaymentClient{ + logger: deps.Logger, + paymentServiceHost: deps.PaymentServiceHost, + } +} + +func (receiver *PaymentClient) GetPaymentLinkBankCard(ctx context.Context, request *treasurer.GetBankCardPaymentLinkRequest) (string, errors.Error) { + client, closeConnection, connectErr := receiver.connect(ctx) + if connectErr != nil { + receiver.logger.Error("failed to connect on of ", zap.Error(connectErr)) + return "", errors.New(fmt.Errorf("failed connect to payment service: %w", connectErr), errors.ErrInternalError) + } + defer closeConnection() + + response, err := client.GetPaymentLinkBankCard(ctx, request) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", + zap.Error(err), + zap.String("host", receiver.paymentServiceHost), + ) + + return "", errors.New(fmt.Errorf("failed to get payment link: %w", err), errors.ErrInternalError) + } + + return response.RedirectURL, nil +} + +func (receiver *PaymentClient) GetPaymentLinkYooMoney(ctx context.Context, request *treasurer.GetPaymentLinkBody) (string, errors.Error) { + client, closeConnection, connectErr := receiver.connect(ctx) + if connectErr != nil { + receiver.logger.Error("failed to connect on of ", zap.Error(connectErr)) + return "", errors.New(fmt.Errorf("failed connect to payment service: %w", connectErr), errors.ErrInternalError) + } + defer closeConnection() + + response, err := client.GetPaymentLinkYooMoney(ctx, request) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", + zap.Error(err), + zap.String("host", receiver.paymentServiceHost), + ) + + return "", errors.New(fmt.Errorf("failed to get payment link: %w", err), errors.ErrInternalError) + } + + return response.RedirectURL, nil +} + +func (receiver *PaymentClient) GetPaymentLinkQIWI(ctx context.Context, request *treasurer.GetPhonePaymentLinkRequest) (string, errors.Error) { + client, closeConnection, connectErr := receiver.connect(ctx) + if connectErr != nil { + receiver.logger.Error("failed to connect on of ", zap.Error(connectErr)) + return "", errors.New(fmt.Errorf("failed connect to payment service: %w", connectErr), errors.ErrInternalError) + } + defer closeConnection() + + response, err := client.GetPaymentLinkQIWI(ctx, request) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", + zap.Error(err), + zap.String("host", receiver.paymentServiceHost), + ) + + return "", errors.New(fmt.Errorf("failed to get payment link: %w", err), errors.ErrInternalError) + } + + return response.RedirectURL, nil +} + +func (receiver *PaymentClient) GetPaymentLinkSberPay(ctx context.Context, request *treasurer.GetPhonePaymentLinkRequest) (string, errors.Error) { + client, closeConnection, connectErr := receiver.connect(ctx) + if connectErr != nil { + receiver.logger.Error("failed to connect on of ", zap.Error(connectErr)) + return "", errors.New(fmt.Errorf("failed connect to payment service: %w", connectErr), errors.ErrInternalError) + } + defer closeConnection() + + response, err := client.GetPaymentLinkSberPay(ctx, request) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", + zap.Error(err), + zap.String("host", receiver.paymentServiceHost), + ) + + return "", errors.New(fmt.Errorf("failed to get payment link: %w", err), errors.ErrInternalError) + } + + return response.RedirectURL, nil +} + +func (receiver *PaymentClient) GetPaymentLinkAlfaClick(ctx context.Context, request *treasurer.GetLoginPaymentLinkRequest) (string, errors.Error) { + client, closeConnection, connectErr := receiver.connect(ctx) + if connectErr != nil { + receiver.logger.Error("failed to connect on of ", zap.Error(connectErr)) + return "", errors.New(fmt.Errorf("failed connect to payment service: %w", connectErr), errors.ErrInternalError) + } + defer closeConnection() + + response, err := client.GetPaymentLinkAlfaClick(ctx, request) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", + zap.Error(err), + zap.String("host", receiver.paymentServiceHost), + ) + + return "", errors.New(fmt.Errorf("failed to get payment link: %w", err), errors.ErrInternalError) + } + + return response.RedirectURL, nil +} + +func (receiver *PaymentClient) GetPaymentLinkTinkoff(ctx context.Context, request *treasurer.GetPaymentLinkBody) (string, errors.Error) { + client, closeConnection, connectErr := receiver.connect(ctx) + if connectErr != nil { + receiver.logger.Error("failed to connect on of ", zap.Error(connectErr)) + return "", errors.New(fmt.Errorf("failed connect to payment service: %w", connectErr), errors.ErrInternalError) + } + defer closeConnection() + + response, err := client.GetPaymentLinkTinkoff(ctx, request) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", + zap.Error(err), + zap.String("host", receiver.paymentServiceHost), + ) + + return "", errors.New(fmt.Errorf("failed to get payment link: %w", err), errors.ErrInternalError) + } + + return response.RedirectURL, nil +} + +func (receiver *PaymentClient) GetPaymentLinkSberbankB2B(ctx context.Context, request *treasurer.GetB2BPaymentLinkRequest) (string, errors.Error) { + client, closeConnection, connectErr := receiver.connect(ctx) + if connectErr != nil { + receiver.logger.Error("failed to connect on of ", zap.Error(connectErr)) + return "", errors.New(fmt.Errorf("failed connect to payment service: %w", connectErr), errors.ErrInternalError) + } + defer closeConnection() + + response, err := client.GetPaymentLinkSberbankB2B(ctx, request) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", + zap.Error(err), + zap.String("host", receiver.paymentServiceHost), + ) + + return "", errors.New(fmt.Errorf("failed to get payment link: %w", err), errors.ErrInternalError) + } + + return response.RedirectURL, nil +} + +func (receiver *PaymentClient) GetPaymentLinkMobile(ctx context.Context, request *treasurer.GetPhonePaymentLinkRequest) (string, errors.Error) { + client, closeConnection, connectErr := receiver.connect(ctx) + if connectErr != nil { + receiver.logger.Error("failed to connect on of ", zap.Error(connectErr)) + return "", errors.New(fmt.Errorf("failed connect to payment service: %w", connectErr), errors.ErrInternalError) + } + defer closeConnection() + + response, err := client.GetPaymentLinkMobile(ctx, request) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", + zap.Error(err), + zap.String("host", receiver.paymentServiceHost), + ) + + return "", errors.New(fmt.Errorf("failed to get payment link: %w", err), errors.ErrInternalError) + } + + return response.RedirectURL, nil +} + +func (receiver *PaymentClient) GetPaymentLinkCash(ctx context.Context, request *treasurer.GetPhonePaymentLinkRequest) (string, errors.Error) { + client, closeConnection, connectErr := receiver.connect(ctx) + if connectErr != nil { + receiver.logger.Error("failed to connect on of ", zap.Error(connectErr)) + return "", errors.New(fmt.Errorf("failed connect to payment service: %w", connectErr), errors.ErrInternalError) + } + defer closeConnection() + + response, err := client.GetPaymentLinkCash(ctx, request) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", + zap.Error(err), + zap.String("host", receiver.paymentServiceHost), + ) + + return "", errors.New(fmt.Errorf("failed to get payment link: %w", err), errors.ErrInternalError) + } + + return response.RedirectURL, nil +} + +func (receiver *PaymentClient) GetPaymentLinkInstallments(ctx context.Context, request *treasurer.GetPaymentLinkBody) (string, errors.Error) { + client, closeConnection, connectErr := receiver.connect(ctx) + if connectErr != nil { + receiver.logger.Error("failed to connect on of ", zap.Error(connectErr)) + return "", errors.New(fmt.Errorf("failed connect to payment service: %w", connectErr), errors.ErrInternalError) + } + defer closeConnection() + + response, err := client.GetPaymentLinkInstallments(ctx, request) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", + zap.Error(err), + zap.String("host", receiver.paymentServiceHost), + ) + + return "", errors.New(fmt.Errorf("failed to get payment link: %w", err), errors.ErrInternalError) + } + + return response.RedirectURL, nil +} + +func (receiver *PaymentClient) connect(ctx context.Context) (treasurer.TreasurerServiceClient, func(), errors.Error) { + connection, err := grpc.DialContext(ctx, receiver.paymentServiceHost, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + receiver.logger.Error("failed connect to payment service no of ", + zap.Error(err), + zap.String("host", receiver.paymentServiceHost), + ) + + return nil, nil, errors.New(fmt.Errorf("failed connect to payment service: %w", err), errors.ErrInternalError) + } + + closeConnect := func() { + if closeErr := connection.Close(); closeErr != nil { + receiver.logger.Error("failed to close connection on of ", zap.Error(closeErr)) + } + } + + client := treasurer.NewTreasurerServiceClient(connection) + + return client, closeConnect, nil +} diff --git a/internal/interface/controller/grpc/customer/customer.go b/internal/interface/controller/grpc/customer/customer.go new file mode 100644 index 0000000..ffa75a1 --- /dev/null +++ b/internal/interface/controller/grpc/customer/customer.go @@ -0,0 +1,55 @@ +package customer + +import ( + "context" + "log" + + "go.uber.org/zap" + "google.golang.org/protobuf/types/known/emptypb" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/customer" +) + +type historyService interface { + CreateHistory(ctx context.Context, history *models.History) (*models.History, errors.Error) +} + +type Deps struct { + Logger *zap.Logger + HistoryService historyService +} + +type Controller struct { + logger *zap.Logger + historyService historyService +} + +func New(deps Deps) *Controller { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.HistoryService == nil { + log.Panicln("HistoryService is nil on ") + } + + return &Controller{ + logger: deps.Logger, + historyService: deps.HistoryService, + } +} + +func (receiver *Controller) InsertHistory(ctx context.Context, in *customer.History) (*emptypb.Empty, error) { + if _, err := receiver.historyService.CreateHistory(ctx, &models.History{ + UserID: in.UserID, + Comment: in.Comment, + Key: in.Key, + RawDetails: in.RawDetails, + }); err != nil { + receiver.logger.Error("failed to insert history on of ", zap.Error(err)) + return nil, errors.GRPC("failed to insert history", err) + } + + return &emptypb.Empty{}, nil +} diff --git a/internal/interface/controller/grpc/payment/payment.go b/internal/interface/controller/grpc/payment/payment.go new file mode 100644 index 0000000..3c22940 --- /dev/null +++ b/internal/interface/controller/grpc/payment/payment.go @@ -0,0 +1,74 @@ +package payment + +import ( + "context" + "log" + + "go.uber.org/zap" + "google.golang.org/protobuf/types/known/emptypb" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/payment_callback" +) + +type paymentCallbackService interface { + SuccessEvent(context.Context, *models.PaymentEvent) errors.Error + FailureEvent(context.Context, *models.PaymentEvent) errors.Error +} + +type Deps struct { + Logger *zap.Logger + PaymentCallbackService paymentCallbackService +} + +type Controller struct { + logger *zap.Logger + paymentCallbackService paymentCallbackService +} + +func New(deps Deps) *Controller { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.PaymentCallbackService == nil { + log.Panicln("PaymentCallbackService is nil on ") + } + + return &Controller{ + logger: deps.Logger, + paymentCallbackService: deps.PaymentCallbackService, + } +} + +func (receiver *Controller) OnSuccess(ctx context.Context, in *payment_callback.Event) (*emptypb.Empty, error) { + if err := receiver.paymentCallbackService.SuccessEvent(ctx, &models.PaymentEvent{ + Key: in.Key, + Message: in.Message, + PaymentID: in.Payment.PaymentID, + Currency: in.Payment.Currency, + Amount: in.Payment.Amount, + UserID: in.Payment.UserID, + }); err != nil { + receiver.logger.Error("failed to send success event on of ", zap.Error(err)) + return nil, errors.GRPC("failed to send success event", err) + } + + return &emptypb.Empty{}, nil +} + +func (receiver *Controller) OnFailure(ctx context.Context, in *payment_callback.Event) (*emptypb.Empty, error) { + if err := receiver.paymentCallbackService.FailureEvent(ctx, &models.PaymentEvent{ + Key: in.Key, + Message: in.Message, + PaymentID: in.Payment.PaymentID, + Currency: in.Payment.Currency, + Amount: in.Payment.Amount, + UserID: in.Payment.UserID, + }); err != nil { + receiver.logger.Error("failed to send failure event on of ", zap.Error(err)) + return nil, errors.GRPC("failed to send failure event", err) + } + + return &emptypb.Empty{}, nil +} diff --git a/internal/interface/controller/rest/account/account.go b/internal/interface/controller/rest/account/account.go new file mode 100644 index 0000000..9492b7f --- /dev/null +++ b/internal/interface/controller/rest/account/account.go @@ -0,0 +1,177 @@ +package account + +import ( + "context" + "fmt" + "log" + "net/http" + + "github.com/labstack/echo/v4" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/echotools" +) + +type accountService interface { + GetAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error) + GetAccountsList(ctx context.Context, pagination *models.Pagination) (*models.PaginationResponse[models.Account], errors.Error) + CreateAccount(ctx context.Context, account *models.Account) (*models.Account, errors.Error) + CreateAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error) + RemoveAccount(ctx context.Context, userID string) (*models.Account, errors.Error) + DeleteAccount(ctx context.Context, userID string) (*models.Account, errors.Error) + SetVerificationStatus(ctx context.Context, userID string, status models.AccountStatus) (*models.Account, errors.Error) + UpdateAccountName(ctx context.Context, userID string, name *models.Name) (*models.Account, errors.Error) +} + +type Deps struct { + Logger *zap.Logger + Service accountService +} + +type Controller struct { + logger *zap.Logger + service accountService +} + +func New(deps Deps) *Controller { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.Service == nil { + log.Panicln("account service is nil on ") + } + + return &Controller{ + logger: deps.Logger, + service: deps.Service, + } +} + +func (receiver *Controller) GetAccount(ctx echo.Context) error { + userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) + if !ok { + receiver.logger.Error("failed to convert jwt payload to string on of ") + + return errors.HTTP(ctx, errors.New( + fmt.Errorf("failed to convert jwt payload to string: %s", userID), + errors.ErrInvalidArgs, + )) + } + + account, err := receiver.service.GetAccountByUserID(ctx.Request().Context(), userID) + if err != nil { + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, account) +} + +func (receiver *Controller) GetDirectAccount(ctx echo.Context, userID string) error { + account, err := receiver.service.GetAccountByUserID(ctx.Request().Context(), userID) + if err != nil { + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, account) +} + +func (receiver *Controller) GetAccounts(ctx echo.Context, params swagger.PaginationAccountsParams) error { + response, err := receiver.service.GetAccountsList( + ctx.Request().Context(), + utils.DeterminePagination(params.Page, params.Limit), + ) + if err != nil { + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, response) +} + +func (receiver *Controller) CreateAccount(ctx echo.Context) error { + userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) + if !ok { + receiver.logger.Error("failed to convert jwt payload to string on of ") + + return errors.HTTP(ctx, errors.New( + fmt.Errorf("failed to convert jwt payload to string: %s", userID), + errors.ErrInvalidArgs, + )) + } + + account, err := receiver.service.CreateAccountByUserID(ctx.Request().Context(), userID) + if err != nil { + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, account) +} + +func (receiver *Controller) RemoveAccount(ctx echo.Context) error { + userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) + if !ok { + receiver.logger.Error("failed to convert jwt payload to string on of ") + + return errors.HTTP(ctx, errors.New( + fmt.Errorf("failed to convert jwt payload to string: %s", userID), + errors.ErrInvalidArgs, + )) + } + + account, err := receiver.service.RemoveAccount(ctx.Request().Context(), userID) + if err != nil { + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, account) +} + +func (receiver *Controller) RemoveDirectAccount(ctx echo.Context, userID string) error { + account, err := receiver.service.RemoveAccount(ctx.Request().Context(), userID) + if err != nil { + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, account) +} + +func (receiver *Controller) SetVerificationStatus(ctx echo.Context, userID string) error { + request, bindErr := echotools.Bind[models.SetAccountStatus](ctx) + if bindErr != nil { + receiver.logger.Error("failed to bind json request on of ", zap.Error(bindErr)) + return errors.HTTP(ctx, errors.New(fmt.Errorf("failed to bind json: %w", bindErr), errors.ErrInternalError)) + } + + account, err := receiver.service.SetVerificationStatus(ctx.Request().Context(), userID, request.Status) + if err != nil { + receiver.logger.Error("failed set status on of ", zap.Error(err)) + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, account) +} + +func (receiver *Controller) UpdateAccountName(ctx echo.Context) error { + userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) + if !ok { + receiver.logger.Error("failed to convert jwt payload to string on of ") + return errors.HTTP(ctx, errors.New(fmt.Errorf("failed to convert jwt payload to string: %s", userID), errors.ErrInvalidArgs)) + } + + request, bindErr := echotools.Bind[models.Name](ctx) + if bindErr != nil { + receiver.logger.Error("failed to bind json request on of ", zap.Error(bindErr)) + return errors.HTTP(ctx, errors.New(fmt.Errorf("failed to bind json: %w", bindErr), errors.ErrInternalError)) + } + + account, err := receiver.service.UpdateAccountName(ctx.Request().Context(), userID, request) + if err != nil { + receiver.logger.Error("failed to update account name on of ", zap.Error(err)) + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, account) +} diff --git a/internal/interface/controller/rest/cart/cart.go b/internal/interface/controller/rest/cart/cart.go new file mode 100644 index 0000000..23e1d33 --- /dev/null +++ b/internal/interface/controller/rest/cart/cart.go @@ -0,0 +1,133 @@ +package cart + +import ( + "context" + "fmt" + "log" + "net/http" + + "github.com/labstack/echo/v4" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate" +) + +type cartService interface { + Remove(ctx context.Context, userID, itemID string) ([]string, errors.Error) + Add(ctx context.Context, userID, itemID string) ([]string, errors.Error) + Pay(ctx context.Context, userID string) (link string, err errors.Error) +} + +type Deps struct { + Logger *zap.Logger + CartService cartService +} + +type Controller struct { + logger *zap.Logger + cartService cartService +} + +func New(deps Deps) *Controller { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.CartService == nil { + log.Panicln("cart service is nil on ") + } + + return &Controller{ + logger: deps.Logger, + cartService: deps.CartService, + } +} + +func (receiver *Controller) Remove(ctx echo.Context, params swagger.RemoveFromCartParams) error { + userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) + if !ok { + receiver.logger.Error("failed to convert jwt payload to string on of ") + + return errors.HTTP(ctx, errors.New( + fmt.Errorf("failed to convert jwt payload to string: %s", userID), + errors.ErrInvalidArgs, + )) + } + + if validate.IsStringEmpty(params.Id) { + return errors.HTTP(ctx, errors.New( + fmt.Errorf("failed to remove cart item from user <%s>: empty item id", userID), + errors.ErrInvalidArgs, + )) + } + + cartItems, err := receiver.cartService.Remove(ctx.Request().Context(), userID, params.Id) + if err != nil { + receiver.logger.Error( + "failed to remove item from cart on of ", + zap.Error(err), + ) + + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, cartItems) +} + +func (receiver *Controller) Add(ctx echo.Context, params swagger.Add2cartParams) error { + userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) + if !ok { + receiver.logger.Error("failed to convert jwt payload to string on of ") + + return errors.HTTP(ctx, errors.New( + fmt.Errorf("failed to convert jwt payload to string: %s", userID), + errors.ErrInvalidArgs, + )) + } + + if validate.IsStringEmpty(params.Id) { + return errors.HTTP(ctx, errors.New( + fmt.Errorf("failed to add cart item to user <%s>: empty item id", userID), + errors.ErrInvalidArgs, + )) + } + + cartItems, err := receiver.cartService.Add(ctx.Request().Context(), userID, params.Id) + if err != nil { + receiver.logger.Error( + "failed to add item to cart on of ", + zap.Error(err), + ) + + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, cartItems) +} + +func (receiver *Controller) Pay(ctx echo.Context) error { + userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) + if !ok { + receiver.logger.Error("failed to convert jwt payload to string on of ") + + return errors.HTTP(ctx, errors.New( + fmt.Errorf("failed to convert jwt payload to string: %s", userID), + errors.ErrInvalidArgs, + )) + } + + link, err := receiver.cartService.Pay(ctx.Request().Context(), userID) + if err != nil { + receiver.logger.Error("failed to pay cart on of ", zap.Error(err)) + + return errors.HTTP(ctx, err) + } + + if !validate.IsStringEmpty(link) { + return ctx.Redirect(http.StatusTemporaryRedirect, link) + } + + return ctx.JSON(http.StatusOK, true) +} diff --git a/internal/interface/controller/rest/currency/currency.go b/internal/interface/controller/rest/currency/currency.go new file mode 100644 index 0000000..110abb6 --- /dev/null +++ b/internal/interface/controller/rest/currency/currency.go @@ -0,0 +1,87 @@ +package currency + +import ( + "context" + "fmt" + "log" + "net/http" + + "github.com/labstack/echo/v4" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/echotools" +) + +type currencyService interface { + GetCurrencies(context.Context) ([]string, errors.Error) + PutCurrencies(context.Context, []string) ([]string, errors.Error) +} + +type Deps struct { + Logger *zap.Logger + CurrencyService currencyService +} + +type Controller struct { + logger *zap.Logger + currencyService currencyService +} + +func New(deps Deps) *Controller { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.CurrencyService == nil { + log.Panicln("currency service is nil on ") + } + + return &Controller{ + logger: deps.Logger, + currencyService: deps.CurrencyService, + } +} + +func (receiver *Controller) GetCurrencies(ctx echo.Context) error { + currencies, err := receiver.currencyService.GetCurrencies(ctx.Request().Context()) + if err != nil { + receiver.logger.Error( + "failed to get currencies on of ", + zap.Error(err), + ) + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, currencies) +} + +func (receiver *Controller) PutCurrencies(ctx echo.Context) error { + currencies, bindErr := echotools.Bind[[]string](ctx) + if bindErr != nil { + receiver.logger.Error( + "failed to parse body on of ", + zap.Error(bindErr), + ) + return errors.HTTP(ctx, errors.New( + fmt.Errorf("failed to parse body: %w", bindErr), + errors.ErrInvalidArgs, + )) + } + + currenciesCopy := *currencies + + if len(currenciesCopy) < 1 { + currenciesCopy = make([]string, 0) + } + + updatedCurrencies, err := receiver.currencyService.PutCurrencies(ctx.Request().Context(), currenciesCopy) + if err != nil { + receiver.logger.Error( + "failed to put currencies on of ", + zap.Error(err), + ) + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, updatedCurrencies) +} diff --git a/internal/interface/controller/rest/history/history.go b/internal/interface/controller/rest/history/history.go new file mode 100644 index 0000000..6396a99 --- /dev/null +++ b/internal/interface/controller/rest/history/history.go @@ -0,0 +1,55 @@ +package history + +import ( + "context" + "log" + "net/http" + + "github.com/labstack/echo/v4" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +type historyService interface { + GetHistoryList(context.Context, *models.Pagination) (*models.PaginationResponse[models.History], errors.Error) +} + +type Deps struct { + Logger *zap.Logger + HistoryService historyService +} + +type Controller struct { + logger *zap.Logger + historyService historyService +} + +func New(deps Deps) *Controller { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.HistoryService == nil { + log.Panicln("HistoryService is nil on ") + } + + return &Controller{ + logger: deps.Logger, + historyService: deps.HistoryService, + } +} + +func (receiver *Controller) GetHistoryList(ctx echo.Context, params swagger.GetHistoryParams) error { + histories, err := receiver.historyService.GetHistoryList(ctx.Request().Context(), &models.Pagination{ + Page: int64(*params.Page), + Limit: int64(*params.Limit), + }) + if err != nil { + receiver.logger.Error("failed to get histories on of ", zap.Error(err)) + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, histories) +} diff --git a/internal/interface/controller/rest/wallet/wallet.go b/internal/interface/controller/rest/wallet/wallet.go new file mode 100644 index 0000000..e2d5841 --- /dev/null +++ b/internal/interface/controller/rest/wallet/wallet.go @@ -0,0 +1,131 @@ +package wallet + +import ( + "context" + "fmt" + "log" + "net/http" + + "github.com/labstack/echo/v4" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/echotools" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate" +) + +type walletService interface { + ReplenishAccountWallet(context.Context, *models.ReplenishAccountWallet) (*models.Account, errors.Error) + ChangeCurrency(ctx context.Context, userID string, currency models.CurrencyKey) (*models.Account, errors.Error) +} + +type paymentService interface { + GetPaymentLink(context.Context, *models.GetPaymentLinkRequest) (string, errors.Error) +} + +type Deps struct { + Logger *zap.Logger + WalletService walletService + PaymentService paymentService +} + +type Controller struct { + logger *zap.Logger + walletService walletService + paymentService paymentService +} + +func New(deps Deps) *Controller { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.WalletService == nil { + log.Panicln("wallet service is nil on ") + } + + if deps.PaymentService == nil { + log.Panicln("payment service is nil on ") + } + + return &Controller{ + logger: deps.Logger, + walletService: deps.WalletService, + paymentService: deps.PaymentService, + } +} + +func (receiver *Controller) ChangeCurrency(ctx echo.Context) error { + userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) + if !ok { + receiver.logger.Error("failed to convert jwt payload to string on of ") + + return errors.HTTP(ctx, errors.New( + fmt.Errorf("failed to convert jwt payload to string: %s", userID), + errors.ErrInvalidArgs, + )) + } + + request, bindErr := echotools.Bind[models.ChangeCurrency](ctx) + if bindErr != nil { + receiver.logger.Error("failed to bind body on of ", zap.Error(bindErr)) + return errors.HTTP(ctx, errors.New( + fmt.Errorf("failed to parse body on of : %w", bindErr), + errors.ErrInvalidArgs, + )) + } + + if validate.IsStringEmpty(request.Currency) { + return errors.HTTP(ctx, errors.New( + fmt.Errorf("empty currency key on of : %w", errors.ErrInvalidArgs), + errors.ErrInvalidArgs, + )) + } + + account, err := receiver.walletService.ChangeCurrency(ctx.Request().Context(), userID, request.Currency) + if err != nil { + receiver.logger.Error("failed to put money on of ", zap.Error(err)) + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, account) +} + +func (receiver *Controller) GetPaymentLink(ctx echo.Context) error { + userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) + if !ok { + receiver.logger.Error("failed to convert jwt payload to string on of ") + + return errors.HTTP(ctx, errors.New( + fmt.Errorf("failed to convert jwt payload to string: %s", userID), + errors.ErrInvalidArgs, + )) + } + + request, bindErr := echotools.Bind[models.GetPaymentLinkBody](ctx) + if bindErr != nil { + receiver.logger.Error("failed to bind body on of ", zap.Error(bindErr)) + return errors.HTTP(ctx, errors.New( + fmt.Errorf("failed to parse body on of : %w", bindErr), + errors.ErrInvalidArgs, + )) + } + + if validateErr := utils.ValidateGetPaymentLinkBody(request); validateErr != nil { + receiver.logger.Error("failed to validate body on of ", zap.Error(validateErr)) + return errors.HTTP(ctx, validateErr) + } + + link, err := receiver.paymentService.GetPaymentLink(ctx.Request().Context(), &models.GetPaymentLinkRequest{ + Body: request, + UserID: userID, + ClientIP: ctx.RealIP(), + }) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", zap.Error(err)) + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, &models.GetPaymentLinkResponse{Link: link}) +} diff --git a/internal/interface/repository/account.go b/internal/interface/repository/account.go new file mode 100644 index 0000000..74042a4 --- /dev/null +++ b/internal/interface/repository/account.go @@ -0,0 +1,418 @@ +package repository + +import ( + "context" + "fmt" + "log" + "time" + + "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" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/fields" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + mongoWrapper "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo" +) + +type AccountRepositoryDeps struct { + Logger *zap.Logger + MongoDB *mongo.Collection +} + +type AccountRepository struct { + logger *zap.Logger + mongoDB *mongo.Collection +} + +func NewAccountRepository(deps AccountRepositoryDeps) *AccountRepository { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.MongoDB == nil { + log.Panicln("mongodb is nil on ") + } + + return &AccountRepository{ + logger: deps.Logger, + mongoDB: deps.MongoDB, + } +} + +func (receiver *AccountRepository) FindByUserID(ctx context.Context, id string) (*models.Account, errors.Error) { + filter := bson.M{ + fields.Account.UserID: id, + fields.Account.IsDeleted: false, + } + + account, err := mongoWrapper.FindOne[models.Account](ctx, &mongoWrapper.RequestSettings{ + Driver: receiver.mongoDB, + Filter: filter, + }) + if err != nil { + receiver.logger.Error("failed to find account by userID on of ", + zap.String("id", id), + zap.Error(err), + ) + + findError := errors.New( + fmt.Errorf("failed to find account with <%s> on of : %w", id, err), + errors.ErrInternalError, + ) + + if err == mongo.ErrNoDocuments { + findError.SetType(errors.ErrNotFound) + } + + return nil, findError + } + + return account, nil +} + +func (receiver *AccountRepository) FindMany(ctx context.Context, page, limit int64) ([]models.Account, errors.Error) { + filter := bson.M{fields.Account.IsDeleted: false} + findOptions := options.Find() + + skip := (page - 1) * limit + + findOptions.SetSkip(skip) + findOptions.SetLimit(limit) + + accounts, err := mongoWrapper.Find[models.Account](ctx, &mongoWrapper.RequestSettings{ + Driver: receiver.mongoDB, + Options: findOptions, + Filter: filter, + }) + if err != nil { + receiver.logger.Error("failed to find many accounts on of ", + zap.Int64("page", page), + zap.Int64("limit", limit), + zap.Int64("skip", skip), + zap.Error(err), + ) + + return nil, errors.New( + fmt.Errorf("failed to find many accounts on of : %w", err), + errors.ErrInternalError, + ) + } + + return accounts, nil +} + +func (receiver *AccountRepository) Insert(ctx context.Context, account *models.Account) (*models.Account, errors.Error) { + result, err := receiver.mongoDB.InsertOne(ctx, account.Sanitize()) + if err != nil { + receiver.logger.Error("failed to insert account on of ", + zap.Any("account", account), + zap.Error(err), + ) + + return nil, errors.New( + fmt.Errorf("failed to insert account on of : %w", err), + errors.ErrInternalError, + ) + } + + insertedID := result.InsertedID.(primitive.ObjectID).Hex() + account.ID = insertedID + + return account, nil +} + +func (receiver *AccountRepository) Remove(ctx context.Context, id string) (*models.Account, errors.Error) { + account := models.Account{} + options := options.FindOneAndUpdate().SetReturnDocument(options.After) + + filter := bson.M{ + fields.Account.UserID: id, + fields.Account.IsDeleted: false, + } + + update := bson.M{"$set": bson.M{ + fields.Account.IsDeleted: true, + fields.Account.DeletedAt: time.Now(), + }} + + if err := receiver.mongoDB.FindOneAndUpdate(ctx, filter, update, options).Decode(&account); err != nil { + receiver.logger.Error("failed to set 'deleted=true' on of ", + zap.String("id", id), + zap.Error(err), + ) + + removeErr := errors.New( + fmt.Errorf("failed to remove account with <%s> on of : %w", id, err), + errors.ErrInternalError, + ) + + if err == mongo.ErrNoDocuments { + removeErr.SetType(errors.ErrNotFound) + } + + return nil, removeErr + } + + return &account, nil +} + +func (receiver *AccountRepository) Delete(ctx context.Context, id string) (*models.Account, errors.Error) { + account := models.Account{} + + filter := bson.M{ + fields.Account.UserID: id, + fields.Account.IsDeleted: false, + } + + if err := receiver.mongoDB.FindOneAndDelete(ctx, filter).Decode(&account); err != nil { + receiver.logger.Error("failed delete account on of ", + zap.String("id", id), + zap.Error(err), + ) + + removeErr := errors.New( + fmt.Errorf("failed to remove account with <%s> on of : %w", id, err), + errors.ErrInternalError, + ) + + if err == mongo.ErrNoDocuments { + removeErr.SetType(errors.ErrNotFound) + } + + return nil, removeErr + } + + return &account, nil +} + +func (receiver *AccountRepository) CountAll(ctx context.Context) (int64, errors.Error) { + count, err := receiver.mongoDB.CountDocuments(ctx, bson.M{fields.Account.IsDeleted: false}) + if err != nil { + receiver.logger.Error("failed to count all documents on of ", zap.Error(err)) + + return 0, errors.New( + fmt.Errorf("failed to count all documents on of : %w", err), + errors.ErrInternalError, + ) + } + + return count, nil +} + +func (receiver *AccountRepository) AddItemToCart(ctx context.Context, userID, itemID string) (*models.Account, errors.Error) { + account := models.Account{} + options := options.FindOneAndUpdate().SetReturnDocument(options.After) + + filter := bson.M{ + fields.Account.UserID: userID, + fields.Account.IsDeleted: false, + } + + update := bson.M{ + "$addToSet": bson.M{fields.Account.Cart: itemID}, + "$set": bson.M{fields.Account.UpdatedAt: time.Now()}, + } + + if err := receiver.mongoDB.FindOneAndUpdate(ctx, filter, update, options).Decode(&account); err != nil { + receiver.logger.Error("failed to add item on of ", + zap.String("userID", userID), + zap.String("itemID", itemID), + zap.Error(err), + ) + + removeErr := errors.New( + fmt.Errorf("failed to add item <%s> account with <%s> on of : %w", itemID, userID, err), + errors.ErrInternalError, + ) + + if err == mongo.ErrNoDocuments { + removeErr.SetType(errors.ErrNotFound) + } + + return nil, removeErr + } + + return &account, nil +} + +func (receiver *AccountRepository) RemoveItemFromCart(ctx context.Context, userID, itemID string) (*models.Account, errors.Error) { + account := models.Account{} + options := options.FindOneAndUpdate().SetReturnDocument(options.After) + + filter := bson.M{ + fields.Account.UserID: userID, + fields.Account.IsDeleted: false, + } + + update := bson.M{ + "$pull": bson.M{fields.Account.Cart: itemID}, + "$set": bson.M{fields.Account.UpdatedAt: time.Now()}, + } + + if err := receiver.mongoDB.FindOneAndUpdate(ctx, filter, update, options).Decode(&account); err != nil { + receiver.logger.Error("failed to add item on of ", + zap.String("userID", userID), + zap.String("itemID", itemID), + zap.Error(err), + ) + + removeErr := errors.New( + fmt.Errorf("failed to add item <%s> account with <%s> on of : %w", itemID, userID, err), + errors.ErrInternalError, + ) + + if err == mongo.ErrNoDocuments { + removeErr.SetType(errors.ErrNotFound) + } + + return nil, removeErr + } + + return &account, nil +} + +func (receiver *AccountRepository) ChangeWallet(ctx context.Context, userID string, wallet *models.Wallet) (*models.Account, errors.Error) { + account := models.Account{} + options := options.FindOneAndUpdate().SetReturnDocument(options.After) + + filter := bson.M{ + fields.Account.UserID: userID, + fields.Account.IsDeleted: false, + } + + update := bson.M{"$set": bson.M{ + fields.Account.Wallet: wallet, + fields.Account.UpdatedAt: time.Now(), + }} + + if err := receiver.mongoDB.FindOneAndUpdate(ctx, filter, update, options).Decode(&account); err != nil { + receiver.logger.Error("failed to change wallet on of ", + zap.Error(err), + zap.String("userID", userID), + zap.Any("wallet", wallet), + ) + + removeErr := errors.New( + fmt.Errorf("failed to change wallet of account <%s> on of : %w", userID, err), + errors.ErrInternalError, + ) + + if err == mongo.ErrNoDocuments { + removeErr.SetType(errors.ErrNotFound) + } + + return nil, removeErr + } + + return &account, nil +} + +func (receiver *AccountRepository) ClearCart(ctx context.Context, userID string) (*models.Account, errors.Error) { + account := models.Account{} + options := options.FindOneAndUpdate().SetReturnDocument(options.After) + + filter := bson.M{ + fields.Account.UserID: userID, + fields.Account.IsDeleted: false, + } + + update := bson.M{"$set": bson.M{ + fields.Account.Cart: []string{}, + fields.Account.UpdatedAt: time.Now(), + }} + + if err := receiver.mongoDB.FindOneAndUpdate(ctx, filter, update, options).Decode(&account); err != nil { + receiver.logger.Error("failed to clear cart on of ", + zap.String("userID", userID), + zap.Error(err), + ) + + removeErr := errors.New( + fmt.Errorf("failed to clear cart of account <%s> on of : %w", userID, err), + errors.ErrInternalError, + ) + + if err == mongo.ErrNoDocuments { + removeErr.SetType(errors.ErrNotFound) + } + + return nil, removeErr + } + + return &account, nil +} + +func (receiver *AccountRepository) SetStatus(ctx context.Context, userID string, status models.AccountStatus) (*models.Account, errors.Error) { + account := models.Account{} + options := options.FindOneAndUpdate().SetReturnDocument(options.After) + + filter := bson.M{ + fields.Account.UserID: userID, + fields.Account.IsDeleted: false, + } + + update := bson.M{"$set": bson.M{ + fields.Account.Status: status, + fields.Account.UpdatedAt: time.Now(), + }} + + if err := receiver.mongoDB.FindOneAndUpdate(ctx, filter, update, options).Decode(&account); err != nil { + receiver.logger.Error("failed to set status on of ", + zap.Error(err), + zap.String("userID", userID), + zap.String("status", string(status)), + ) + + removeErr := errors.New( + fmt.Errorf("failed to set status <%s> to account <%s> on of : %w", status, userID, err), + errors.ErrInternalError, + ) + + if err == mongo.ErrNoDocuments { + removeErr.SetType(errors.ErrNotFound) + } + + return nil, removeErr + } + + return &account, nil +} + +func (receiver *AccountRepository) UpdateName(ctx context.Context, userID string, name *models.Name) (*models.Account, errors.Error) { + account := models.Account{} + options := options.FindOneAndUpdate().SetReturnDocument(options.After) + + filter := bson.M{ + fields.Account.UserID: userID, + fields.Account.IsDeleted: false, + } + + update := bson.M{"$set": bson.M{ + fields.Account.Name: name, + fields.Account.UpdatedAt: time.Now(), + }} + + if err := receiver.mongoDB.FindOneAndUpdate(ctx, filter, update, options).Decode(&account); err != nil { + receiver.logger.Error("failed to change name on of ", + zap.Error(err), + zap.String("userID", userID), + zap.Any("name", name), + ) + + removeErr := errors.New( + fmt.Errorf("failed to change name of account <%s> on of : %w", userID, err), + errors.ErrInternalError, + ) + + if err == mongo.ErrNoDocuments { + removeErr.SetType(errors.ErrNotFound) + } + + return nil, removeErr + } + + return &account, nil +} diff --git a/internal/interface/repository/currency.go b/internal/interface/repository/currency.go new file mode 100644 index 0000000..6b989b4 --- /dev/null +++ b/internal/interface/repository/currency.go @@ -0,0 +1,129 @@ +package repository + +import ( + "context" + "fmt" + "log" + "time" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/fields" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + mongoWrapper "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo" +) + +type CurrencyRepositoryDeps struct { + Logger *zap.Logger + MongoDB *mongo.Collection +} + +type CurrencyRepository struct { + logger *zap.Logger + mongoDB *mongo.Collection +} + +func NewCurrencyRepository(deps CurrencyRepositoryDeps) *CurrencyRepository { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.MongoDB == nil { + log.Panicln("mongodb is nil on ") + } + + return &CurrencyRepository{ + mongoDB: deps.MongoDB, + logger: deps.Logger, + } +} + +func (receiver *CurrencyRepository) FindCurrenciesList(ctx context.Context, name string) (*models.CurrencyList, errors.Error) { + filter := bson.M{ + fields.Currency.Name: name, + fields.Currency.IsDeleted: false, + } + + currencyList, err := mongoWrapper.FindOne[models.CurrencyList](ctx, &mongoWrapper.RequestSettings{ + Driver: receiver.mongoDB, + Filter: filter, + }) + if err != nil { + receiver.logger.Error("failed to find list on of ", + zap.String("name", name), + zap.Error(err), + ) + + findError := errors.New( + fmt.Errorf("failed to find list with <%s> on of : %w", name, err), + errors.ErrInternalError, + ) + + if err == mongo.ErrNoDocuments { + findError.SetType(errors.ErrNotFound) + } + + return nil, findError + } + + return currencyList, nil +} + +func (receiver *CurrencyRepository) ReplaceCurrencies(ctx context.Context, list *models.CurrencyList) (*models.CurrencyList, errors.Error) { + currencyList := models.CurrencyList{} + + filter := bson.M{ + fields.Currency.Name: list.Name, + fields.Currency.IsDeleted: false, + } + + update := bson.M{ + "$set": bson.M{ + fields.Currency.UpdatedAt: time.Now(), + fields.Currency.Currencies: list.Currencies, + }, + } + + if err := receiver.mongoDB.FindOneAndUpdate(ctx, filter, update).Decode(¤cyList); err != nil { + receiver.logger.Error("failed to insert currencies on of ", + zap.String("name", list.Name), + zap.Error(err), + ) + + removeErr := errors.New( + fmt.Errorf("failed to insert currencies <%s> on of : %w", list.Name, err), + errors.ErrInternalError, + ) + + if err == mongo.ErrNoDocuments { + removeErr.SetType(errors.ErrNotFound) + } + + return nil, removeErr + } + + return ¤cyList, nil +} + +func (receiver *CurrencyRepository) Insert(ctx context.Context, list *models.CurrencyList) (*models.CurrencyList, errors.Error) { + result, err := receiver.mongoDB.InsertOne(ctx, list.Sanitize()) + if err != nil { + receiver.logger.Error("failed to insert currency list on of ", + zap.Any("list", list), + zap.Error(err), + ) + + return nil, errors.New( + fmt.Errorf("failed to insert currency list on of : %w", err), + errors.ErrInternalError, + ) + } + + insertedID := result.InsertedID.(primitive.ObjectID).Hex() + list.ID = insertedID + + return list, nil +} diff --git a/internal/interface/repository/health.go b/internal/interface/repository/health.go new file mode 100644 index 0000000..0870b3b --- /dev/null +++ b/internal/interface/repository/health.go @@ -0,0 +1,19 @@ +package repository + +import ( + "context" + + "go.mongodb.org/mongo-driver/mongo" +) + +type HealthRepository struct { + MongoDB *mongo.Database +} + +func NewHealthRepository(database *mongo.Database) *HealthRepository { + return &HealthRepository{MongoDB: database} +} + +func (receiver *HealthRepository) Check(ctx context.Context) error { + return receiver.MongoDB.Client().Ping(ctx, nil) +} diff --git a/internal/interface/repository/history.go b/internal/interface/repository/history.go new file mode 100644 index 0000000..29b5400 --- /dev/null +++ b/internal/interface/repository/history.go @@ -0,0 +1,108 @@ +package repository + +import ( + "context" + "fmt" + "log" + + "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" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/fields" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + mongoWrapper "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo" +) + +type HistoryRepositoryDeps struct { + Logger *zap.Logger + MongoDB *mongo.Collection +} + +type HistoryRepository struct { + logger *zap.Logger + mongoDB *mongo.Collection +} + +func NewHistoryRepository(deps HistoryRepositoryDeps) *HistoryRepository { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.MongoDB == nil { + log.Panicln("mongodb is nil on ") + } + + return &HistoryRepository{ + logger: deps.Logger, + mongoDB: deps.MongoDB, + } +} + +func (receiver *HistoryRepository) Insert(ctx context.Context, history *models.History) (*models.History, errors.Error) { + result, err := receiver.mongoDB.InsertOne(ctx, history.Sanitize()) + if err != nil { + receiver.logger.Error("failed to insert history on of ", + zap.Any("history", history), + zap.Error(err), + ) + + return nil, errors.New( + fmt.Errorf("failed to insert history on of : %w", err), + errors.ErrInternalError, + ) + } + + insertedID := result.InsertedID.(primitive.ObjectID).Hex() + history.ID = insertedID + + return history, nil +} + +func (receiver *HistoryRepository) FindMany(ctx context.Context, page, limit int64) ([]models.History, errors.Error) { + filter := bson.M{fields.Account.IsDeleted: false} + findOptions := options.Find() + skip := (page - 1) * limit + + findOptions.SetSkip(skip) + findOptions.SetLimit(limit) + + histories, err := mongoWrapper.Find[models.History](ctx, &mongoWrapper.RequestSettings{ + Driver: receiver.mongoDB, + Options: findOptions, + Filter: filter, + }) + if err != nil { + receiver.logger.Error("failed to find many histories on of ", + zap.Int64("page", page), + zap.Int64("limit", limit), + zap.Int64("skip", skip), + zap.Error(err), + ) + + return nil, errors.New( + fmt.Errorf("failed to find many histories on of : %w", err), + errors.ErrInternalError, + ) + } + + return histories, nil +} + +func (receiver *HistoryRepository) CountAll(ctx context.Context) (int64, errors.Error) { + count, err := receiver.mongoDB.CountDocuments(ctx, bson.M{fields.History.IsDeleted: false}) + if err != nil { + receiver.logger.Error("failed to count all documents on of ", + zap.Error(err), + ) + + return 0, errors.New( + fmt.Errorf("failed to count all documents on of : %w", err), + errors.ErrInternalError, + ) + } + + return count, nil +} diff --git a/internal/interface/swagger/api.gen.go b/internal/interface/swagger/api.gen.go new file mode 100644 index 0000000..8f439c6 --- /dev/null +++ b/internal/interface/swagger/api.gen.go @@ -0,0 +1,508 @@ +// Package swagger provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen version v1.12.4 DO NOT EDIT. +package swagger + +import ( + "bytes" + "compress/gzip" + "encoding/base64" + "fmt" + "net/http" + "net/url" + "path" + "strings" + + "github.com/deepmap/oapi-codegen/pkg/runtime" + "github.com/getkin/kin-openapi/openapi3" + "github.com/labstack/echo/v4" +) + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // удалить собственный аккаунт + // (DELETE /account) + DeleteAccount(ctx echo.Context) error + // Получение текущего аккаунта юзера + // (GET /account) + GetAccount(ctx echo.Context) error + // Отредактировать аккаунт + // (PATCH /account) + ChangeAccount(ctx echo.Context) error + // Создать новый аккаунт + // (POST /account) + AddAccount(ctx echo.Context) error + // Удалить аккаунт по айди + // (DELETE /account/{userId}) + DeleteDirectAccount(ctx echo.Context, userId string) error + // Получить аккаунт по ID пользователя системы единой авторизации + // (GET /account/{userId}) + GetDirectAccount(ctx echo.Context, userId string) error + // Выставление статуса верификации + // (PATCH /account/{userId}) + SetAccountVerificationStatus(ctx echo.Context, userId string) error + // списко аккаунтов с пагинацией + // (GET /accounts) + PaginationAccounts(ctx echo.Context, params PaginationAccountsParams) error + // Удаляем из корзины тариф + // (DELETE /cart) + RemoveFromCart(ctx echo.Context, params RemoveFromCartParams) error + // Добавляем в корзину тариф + // (PATCH /cart) + Add2cart(ctx echo.Context, params Add2cartParams) error + // оплатить козину + // (POST /cart/pay) + PayCart(ctx echo.Context) error + // получить список одобренных валют + // (GET /currencies) + GetCurrencies(ctx echo.Context) error + // обновляет список одобренных валют + // (PUT /currencies) + UpdateCurrencies(ctx echo.Context) error + // Получение лога событий связанных с аккаунтом + // (GET /history) + GetHistory(ctx echo.Context, params GetHistoryParams) error + // Изменить валюту кошелька + // (PATCH /wallet) + ChangeCurrency(ctx echo.Context) error + // Запрос на получение ссылки на оплату + // (POST /wallet) + RequestMoney(ctx echo.Context) error +} + +// ServerInterfaceWrapper converts echo contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface +} + +// DeleteAccount converts echo context to params. +func (w *ServerInterfaceWrapper) DeleteAccount(ctx echo.Context) error { + var err error + + ctx.Set(BearerScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.DeleteAccount(ctx) + return err +} + +// GetAccount converts echo context to params. +func (w *ServerInterfaceWrapper) GetAccount(ctx echo.Context) error { + var err error + + ctx.Set(BearerScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetAccount(ctx) + return err +} + +// ChangeAccount converts echo context to params. +func (w *ServerInterfaceWrapper) ChangeAccount(ctx echo.Context) error { + var err error + + ctx.Set(BearerScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.ChangeAccount(ctx) + return err +} + +// AddAccount converts echo context to params. +func (w *ServerInterfaceWrapper) AddAccount(ctx echo.Context) error { + var err error + + ctx.Set(BearerScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.AddAccount(ctx) + return err +} + +// DeleteDirectAccount converts echo context to params. +func (w *ServerInterfaceWrapper) DeleteDirectAccount(ctx echo.Context) error { + var err error + // ------------- Path parameter "userId" ------------- + var userId string + + err = runtime.BindStyledParameterWithLocation("simple", false, "userId", runtime.ParamLocationPath, ctx.Param("userId"), &userId) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter userId: %s", err)) + } + + ctx.Set(BearerScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.DeleteDirectAccount(ctx, userId) + return err +} + +// GetDirectAccount converts echo context to params. +func (w *ServerInterfaceWrapper) GetDirectAccount(ctx echo.Context) error { + var err error + // ------------- Path parameter "userId" ------------- + var userId string + + err = runtime.BindStyledParameterWithLocation("simple", false, "userId", runtime.ParamLocationPath, ctx.Param("userId"), &userId) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter userId: %s", err)) + } + + ctx.Set(BearerScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetDirectAccount(ctx, userId) + return err +} + +// SetAccountVerificationStatus converts echo context to params. +func (w *ServerInterfaceWrapper) SetAccountVerificationStatus(ctx echo.Context) error { + var err error + // ------------- Path parameter "userId" ------------- + var userId string + + err = runtime.BindStyledParameterWithLocation("simple", false, "userId", runtime.ParamLocationPath, ctx.Param("userId"), &userId) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter userId: %s", err)) + } + + ctx.Set(BearerScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.SetAccountVerificationStatus(ctx, userId) + return err +} + +// PaginationAccounts converts echo context to params. +func (w *ServerInterfaceWrapper) PaginationAccounts(ctx echo.Context) error { + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params PaginationAccountsParams + // ------------- Optional query parameter "page" ------------- + + err = runtime.BindQueryParameter("form", false, false, "page", ctx.QueryParams(), ¶ms.Page) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter page: %s", err)) + } + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", false, false, "limit", ctx.QueryParams(), ¶ms.Limit) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) + } + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.PaginationAccounts(ctx, params) + return err +} + +// RemoveFromCart converts echo context to params. +func (w *ServerInterfaceWrapper) RemoveFromCart(ctx echo.Context) error { + var err error + + ctx.Set(BearerScopes, []string{""}) + + // Parameter object where we will unmarshal all parameters from the context + var params RemoveFromCartParams + // ------------- Required query parameter "id" ------------- + + err = runtime.BindQueryParameter("form", true, true, "id", ctx.QueryParams(), ¶ms.Id) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) + } + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.RemoveFromCart(ctx, params) + return err +} + +// Add2cart converts echo context to params. +func (w *ServerInterfaceWrapper) Add2cart(ctx echo.Context) error { + var err error + + ctx.Set(BearerScopes, []string{""}) + + // Parameter object where we will unmarshal all parameters from the context + var params Add2cartParams + // ------------- Required query parameter "id" ------------- + + err = runtime.BindQueryParameter("form", true, true, "id", ctx.QueryParams(), ¶ms.Id) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) + } + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.Add2cart(ctx, params) + return err +} + +// PayCart converts echo context to params. +func (w *ServerInterfaceWrapper) PayCart(ctx echo.Context) error { + var err error + + ctx.Set(BearerScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.PayCart(ctx) + return err +} + +// GetCurrencies converts echo context to params. +func (w *ServerInterfaceWrapper) GetCurrencies(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetCurrencies(ctx) + return err +} + +// UpdateCurrencies converts echo context to params. +func (w *ServerInterfaceWrapper) UpdateCurrencies(ctx echo.Context) error { + var err error + + ctx.Set(BearerScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.UpdateCurrencies(ctx) + return err +} + +// GetHistory converts echo context to params. +func (w *ServerInterfaceWrapper) GetHistory(ctx echo.Context) error { + var err error + + ctx.Set(BearerScopes, []string{""}) + + // Parameter object where we will unmarshal all parameters from the context + var params GetHistoryParams + // ------------- Optional query parameter "page" ------------- + + err = runtime.BindQueryParameter("form", false, false, "page", ctx.QueryParams(), ¶ms.Page) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter page: %s", err)) + } + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", false, false, "limit", ctx.QueryParams(), ¶ms.Limit) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) + } + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetHistory(ctx, params) + return err +} + +// ChangeCurrency converts echo context to params. +func (w *ServerInterfaceWrapper) ChangeCurrency(ctx echo.Context) error { + var err error + + ctx.Set(BearerScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.ChangeCurrency(ctx) + return err +} + +// RequestMoney converts echo context to params. +func (w *ServerInterfaceWrapper) RequestMoney(ctx echo.Context) error { + var err error + + ctx.Set(BearerScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.RequestMoney(ctx) + return err +} + +// This is a simple interface which specifies echo.Route addition functions which +// are present on both echo.Echo and echo.Group, since we want to allow using +// either of them for path registration +type EchoRouter interface { + CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route +} + +// RegisterHandlers adds each server route to the EchoRouter. +func RegisterHandlers(router EchoRouter, si ServerInterface) { + RegisterHandlersWithBaseURL(router, si, "") +} + +// Registers handlers, and prepends BaseURL to the paths, so that the paths +// can be served under a prefix. +func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + + wrapper := ServerInterfaceWrapper{ + Handler: si, + } + + router.DELETE(baseURL+"/account", wrapper.DeleteAccount) + router.GET(baseURL+"/account", wrapper.GetAccount) + router.PATCH(baseURL+"/account", wrapper.ChangeAccount) + router.POST(baseURL+"/account", wrapper.AddAccount) + router.DELETE(baseURL+"/account/:userId", wrapper.DeleteDirectAccount) + router.GET(baseURL+"/account/:userId", wrapper.GetDirectAccount) + router.PATCH(baseURL+"/account/:userId", wrapper.SetAccountVerificationStatus) + router.GET(baseURL+"/accounts", wrapper.PaginationAccounts) + router.DELETE(baseURL+"/cart", wrapper.RemoveFromCart) + router.PATCH(baseURL+"/cart", wrapper.Add2cart) + router.POST(baseURL+"/cart/pay", wrapper.PayCart) + router.GET(baseURL+"/currencies", wrapper.GetCurrencies) + router.PUT(baseURL+"/currencies", wrapper.UpdateCurrencies) + router.GET(baseURL+"/history", wrapper.GetHistory) + router.PATCH(baseURL+"/wallet", wrapper.ChangeCurrency) + router.POST(baseURL+"/wallet", wrapper.RequestMoney) + +} + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "H4sIAAAAAAAC/+xb/3MTx5L/V6b27oekbrFlY47g38Akd1wVJBUCKQq7krU0sjdIu2J3FeKjVGVZCYQy", + "geMuVUflArl8qXq/vSfLFpZtSf4Xev6jV90z+31ky4YAfsl7VUSWdnZ6ero//emenrtG0a3WXIc7gW/M", + "3jX84jKvWvTxfLHo1p0AP9Y8t8a9wOb0w2d2Cf/Dv7KqtQo3Zo33CmfLU+WzZxeL5femiqWz587NnD5X", + "mJoyTCNYqeETfuDZzpLRMI2i5QWp0TcPGj7ip2ljwTTsgFdJntwc6gvL86wVmtPjVsBL52nisutVrcCY", + "NUpWwE8FdpXrxCzxCj/iENu/KAellle2Kj6Pnl503Qq3HHzcsaocn/xnj5eNWeOfJuONmFS7MHkFn2mY", + "hh9YQd0/7Gm1YVflww3TqNdKR1133efepZfY3jtWpcKDwyT9VD7VaJiGx2/XbQ+VdpMMKxJBmUr0SqWx", + "SBnRHhnJDU4ueiGSz138ghcDlC+tI1ymU6/i3I6LM9zCf11vKTE2XtsFy7k1Z3mlvEcULa+07FZK3MO/", + "StwvenYtsF3HmDXgKfTFYwYd2IM2bEEX9sRDcQ/aDHahLVbFmlg3zIS6L10/f4XhPx9e1zqQX9RM8gMM", + "YYvNXZ+bZtCDPeixuevXp012Ovxzhokm9KAPHRiiJCaDfeiK+9AWa9CGrlgTTRRzgIINYUOs0i8DGMIO", + "E02xBkOxCkMYQHeU4IUzOnn5VzXbW7nsOsGyRu4foYvzinsMejQLitSFAfTEY5wWp9xN6Yq9c/nyu2PP", + "e4Nbuj35H1LX+FPeuHHjRnrS6cK01gGcenVRawbPYAh96IrVUer7+NqF/AszHqLenlpdWsU6o3/f81wv", + "b7VV7vvWEk87O3ofc9yAld26U9KtUPrfnFtKj5wpzJgxyNhO8K8z8WjbCfgS93LrKeJbzEgSnfD/bvuB", + "661onM6tVrmTDiYGbuF3aKxo7uRs0IY9tPUhfgt90WLiPj0wM611r9cTK14CYo8YZzzrzkUeWHbF1xjl", + "30gz0tzJs/swhE3RYrAvVqELW/TzHgzhBfTEmnhokvXCLvTw6Q3Rgi3REmtMfI04Ix6KNbEq1umtoXsh", + "bvSgZ8pN+C7ahmgK3KJNaItHDDrh7jE1cVc+NpRvwsn3oI1/iIeEStJbcVOb7AvfdSYYg6ewgY9uwR4C", + "Gor7ArZwXWgVa9CDfVzuNrRhH0WEHiNca6PJdGAoHk+k/PLuvBFYnl0u+/PG7M35kXs1b5gH/bjQ0G2m", + "/CJpCsW6H7hV7k3ISd93Slzrhq89uGdcNx2r6dnDQ7IZea1p+HXp4zqnv6LIUdrjy7bnByFvipcAT6ED", + "bRjolly1S6UKHz0GhtCBnrivG+t6S5qBz/H/bH5+HoPXEC3sOdqbCo5t/EGLmrzoOqWDBdHqPKebj6wV", + "1OAnoeUoArMYshPTCGznllsuI18xTOO2fcdGdS9yb1F+s+K6VdfhK4i87qJdwZ2zHT+wKpUqJQLIvfxl", + "GlQzTGNxevFqPNqqlC36qONIn0b8L8uQfF34/1m0oA99aDMSSHISRIUOIoN4AgPUachLMFajs4sH0IMd", + "Rh9XRTPprVOFs4WpMeKQaRTrnsed4opGql+00zDaqD3xaLzIjbpFHedf/z2yDfEQNhF5JO4gAxFrCIod", + "RKwh6gF2iCh8M8HgLwSaGwSsiKaRnkQTx4onyOHENwrDkeFtI58h7NuTWIgEpwubSAflbxgPdxG/oT0h", + "4XsXsVCsihZ0oS/Z4C5C45ZigfH6oWuGmCljAEqPvw8wGNAGDmi+HZMpQRTmd8UTJaWKO/ACZzveFtbq", + "XnHZ8rl/vhpmqhlVP4cN8YAMSDQjU4u1QdFsKGOUWJe6G4gnuNcUfsRD2CaEaJPp7YmHxliC+TV+BHGI", + "mxDfljx0INZxLxN7to2PdUQTldch9fbVO8QDMoI12nQJJEhjKUSj9bRggLZhHIOahf4RgUGIGVm1h+vN", + "A7nEvbpnBytXMfWTWHCBW56kyIv06YNQsv/49BOKIUmdve8E3GPBMmeBe4s77I4dLNOfn8vXzLLPWc3j", + "Zfsrk/GJpQk2r97PrMViiU9Nn545M29gRKfkk8iRnD+SdjkIakYDhbWdsqvftgTtIIPpqIyJPpGxS4rS", + "QxKBrtkhatFmp2KeI59oUyZI+UboBnk7e8TE17h3sCvuoa+iq6EI0l5XYRt6aCYMoQC/+VZaJ275BAWA", + "gIBpTnEJdiolVsiNRItkS0gkCdMeiiYNB/rQM0zjS+75UhlTE4WJAkXHGnesmm3MGqcnChPIF2pWsEwb", + "PGnFpSPJCDRK3Vf50H2ZfWZMlklkgj0ZA8Q6YQRGFAtfgCzGkDQ4LFOh9fo11/GlkU0XCjJJcALli1at", + "VrGLNHwSqWJc7hqzsCJtJL0M0RJNAuNvCSS7kdzxDmddsWEaM4WpVyacTO40osEz6OLeRhR8O4SIlGMa", + "szdjl7y50FhAZlatWphyGdFqeopxUxhKmL1Yx8CQWiLan7XkI4aEdrDQMI0lrkPEp6Q+5QIYecI6hIpz", + "2xjXKAZ1mXgE22TEbUoUmmH6jpFlgsF/wQ5sQY92oge7qcflFK3Y2HqwzSio7tI62plIsBMzkS1oY3yV", + "UnVk3rCpkqIODmdWPVh2Pfs/afdyVvpvPHjLTDShDWWkb4VNoggzr0GEX8hcZTo4An6Jb3bF2viOAj9l", + "dZpkq105WQYJEhY6wmdqVlBc1pYUtxE7YSAeh+ZMNUbJJ2E7ZAK42ajnTfUnehPGk97IhefMd27ZcpZS", + "IHu7zv3ggltaeWV7Jcvcmq36UXo+CkprlLqlqsZbgfVNYtpbsbJPMtbDc0XdcT27yGUopyXjQP2PA/I1", + "19eh/M+hnhBJmUx2NZEj4iWRX5iYMTSpgE1sJPLbrngQZRIbYh3ROGe650ult40c/GMZTLytZCCjtlVr", + "Kw0zoomTd2UZqXEgX/wRkU6W7TErQh4iiQFiH03aQbBrqpoO/tuXIV4S4yFT+IEG/kKaF/TNBBdO8LYc", + "Dx5BPi/aHi8m4nvN8qwqD7jnk+bSS7h0UZeX2fgTUufwhGs2rqrF+Vjg1bmZ2PRsnWjhzVg4/Hoi6e/r", + "ohrPKDqnkWtU4B3f7X5NcfIshO5LpkFc+EhU/Hd2sCzrxK8kEKp6xzDtcl0dk/7T3w7l8n963Kv3uJjc", + "j/Y5tLZR2UQqV11n5CG9sKqa1pgi6EdNCEak0aliFPorFa5WoScLvnE6oHy0I9azRSpZaFyjIN+ikpb+", + "HSYTj3GUTElkuky8TZE5Bs/gB3huMvgr/vF/0BP3pEiyQQF+o6Q8/iHn/1ejTPo69+yyMp2rYTvIm8WC", + "42VF6UOSY7X55I+IGo3G24JOGnsiGpy0p5MODv89xhpH+MxhzJg2Tx+tsxUt0rz08t2sqQ8xctNBU4uO", + "XLZl2I1aeyhSD8jvuuxfmOz/eQBd2eozJKpxPz5n0Lw+56sfWUu2Q3+eD5dyiIcmGmTSMol1OkRqI/qq", + "00DRZFOy76VCbSiq+YHc+XadeyuxP9esJW4kvbfEy1a9EhizU7ojkKxUJMb2CLnGFKFiV+1ghAyFgkaK", + "lyUXaVRJ2lLUMzmWh+c7KQM3sCofWUvyzfGRnVaXeVw6Rmkyp3Sd9ZGzxkXr0BV2tbbK5PFeGzaVPd0j", + "zrkz0hnDltU4NU3b+se86n7JP/Dc6pzsWMzYuc4m7INDzLH6M94YJ02kfENZWk8fU/3RGKiswu7IE9w4", + "3uzQRxkFjp7uIa+SbCmr3uRrYxsmqz2IMz7LJnkUDlXNTwYravhS7XLaZWTOoCXz1BXipot/SNf4HjWc", + "YAbDsMEj2r3Wn87xMs6RUHDoIFkFH+geIb5P1izZYqovYP8vtMM+RNUiHTmK6tOQZy2wT40Caxim0hCY", + "J0grKlq88co09VdFoueaDE5WTTpeh0rVd6lILV1Nv/2y0UWxJcW2c5Wnufipl9yyxM0X2Th27epFwzTm", + "zl88yq2W45KpiBq1E/1cGQKVGBid+yeSiyF1026EbV6yehe/K6HksIWIwlBdo9hr1Bma0e3xUulXpdbG", + "idleRD5Zi907fINfkw9fsxzVDcFLR/LaaCnqSPvlTQ59ezm+PDDKscP7BScyPYX/P/HpqceLrlcaPzsN", + "9+v3z07HKbarjqh1ulewc7JCZb5lhZqaNuVNiMSy6GKEeBzWjcj7kAhlU+t+whFDz5N+GF8LHJWN/BT1", + "e4VES1LFXI4yO++cYvCcSlF0CkZNX4lOhWzG0pa378LW5XYWTdr0wlyJPzqbQjmoXSzd2Flc9MrsHTrw", + "xkf7UvYtBALRUiRxoDp/8Cv51n11ZI6/dN9VM0ct76qFNNFo3sr1dzJZzBtitkVXZuJnJxj8ED38BHFz", + "QMx3KO5T7iHu0YWoIS2yqaIGtfCFOpM95smuCtW+SnXRARGpHQa7dO4n3xq1vTLJvGgbHlH5cCd8jAh+", + "9gKQhE3Z6BfejNuiauOelFo1E4Qd7rJNvpe+rtiRixrIBvgR3UtzcSvzqynUJ+8OxPmoDPQH35qJ49Nb", + "WraX8Uud5LZTdx5ypijhrvDaevbynjyQPCjlzn+ohPon/TUFpZlEpn2EuBB1F4ZQeBAcJSBfgfzoPrBT", + "DH4jVfXjeyYpyCdkScAszpfIqU0m0+98c0JftTQSGG2I7+iYow+9DLS35YxRAz99zAJ7KosXTUZ3bBAL", + "ZZkivCEdPtNKtQ4TJA8oEAzTPcQR/Gu6/XO49bGEqcvqxsWrQS0ruiUTU6MzhemC7gbLYuKu+0GWGd2J", + "P/BG1aWrH56amZ46y8TXav/bxNoPvUJVcZdsJ42y9NVnAfcD3YDasuvwK9Ed7HjY2XOF8H+6cR4P6p5z", + "zaukRy0HQc2fnZz07YBPePVJVcUceY3zIFUl7+1lw4K6Ppm4c6M26/cIE2mrqNjOLf2il1x3qYLLHude", + "4sFhRfYPpTw9ut6E+jvzWqLIz8krMKlanSx4D0QL9hjhWw82YFe0joCamhKh7iwrhJJeHkh0UEqall/m", + "KHO2yVLlc1Z0fJcboe4giRbsh8w2fadRvSKyQ807ErVBQv9wCHrGiMcjQho/rhaoGZC4NE7t6GpAmE80", + "Fhp/DwAA//8+Bz17tEYAAA==", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %s", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %s", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %s", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.T, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} diff --git a/internal/interface/swagger/api.go b/internal/interface/swagger/api.go new file mode 100644 index 0000000..822a219 --- /dev/null +++ b/internal/interface/swagger/api.go @@ -0,0 +1,161 @@ +package swagger + +import ( + "log" + + "github.com/labstack/echo/v4" +) + +//go:generate oapi-codegen --config api.yaml ../../../openapi.yaml +//go:generate oapi-codegen --config models.yaml ../../../openapi.yaml + +type accountController interface { + RemoveAccount(ctx echo.Context) error + GetAccount(ctx echo.Context) error + CreateAccount(ctx echo.Context) error + RemoveDirectAccount(ctx echo.Context, userID string) error + GetDirectAccount(ctx echo.Context, userID string) error + GetAccounts(echo.Context, PaginationAccountsParams) error + SetVerificationStatus(ctx echo.Context, userID string) error + UpdateAccountName(ctx echo.Context) error +} + +type currencyController interface { + GetCurrencies(ctx echo.Context) error + PutCurrencies(ctx echo.Context) error +} + +type cartController interface { + Remove(echo.Context, RemoveFromCartParams) error + Add(echo.Context, Add2cartParams) error + Pay(echo.Context) error +} + +type walletController interface { + ChangeCurrency(ctx echo.Context) error + GetPaymentLink(ctx echo.Context) error +} + +type historyController interface { + GetHistoryList(ctx echo.Context, params GetHistoryParams) error +} + +type Deps struct { + AccountController accountController + CurrencyController currencyController + CartController cartController + WalletController walletController + HistoryController historyController +} + +type API struct { + accountController accountController + currencyController currencyController + cartController cartController + walletController walletController + historyController historyController +} + +func New(deps Deps) *API { + if deps.AccountController == nil { + log.Panicln("AccountController is nil on ") + } + + if deps.CurrencyController == nil { + log.Panicln("currencyController is nil on ") + } + + if deps.CartController == nil { + log.Panicln("cartController is nil on ") + } + + if deps.WalletController == nil { + log.Panicln("walletController is nil on ") + } + + if deps.HistoryController == nil { + log.Panicln("historyController is nil on ") + } + + return &API{ + accountController: deps.AccountController, + currencyController: deps.CurrencyController, + cartController: deps.CartController, + walletController: deps.WalletController, + historyController: deps.HistoryController, + } +} + +// Account + +func (receiver *API) DeleteAccount(ctx echo.Context) error { + return receiver.accountController.RemoveAccount(ctx) +} + +func (receiver *API) ChangeAccount(ctx echo.Context) error { + return receiver.accountController.UpdateAccountName(ctx) +} + +func (receiver *API) SetAccountVerificationStatus(ctx echo.Context, userID string) error { + return receiver.accountController.SetVerificationStatus(ctx, userID) +} + +func (receiver *API) GetAccount(ctx echo.Context) error { + return receiver.accountController.GetAccount(ctx) +} + +func (receiver *API) AddAccount(ctx echo.Context) error { + return receiver.accountController.CreateAccount(ctx) +} + +func (receiver *API) DeleteDirectAccount(ctx echo.Context, userID string) error { + return receiver.accountController.RemoveDirectAccount(ctx, userID) +} + +func (receiver *API) GetDirectAccount(ctx echo.Context, userID string) error { + return receiver.accountController.GetDirectAccount(ctx, userID) +} + +func (receiver *API) PaginationAccounts(ctx echo.Context, params PaginationAccountsParams) error { + return receiver.accountController.GetAccounts(ctx, params) +} + +// Cart + +func (receiver *API) RemoveFromCart(ctx echo.Context, params RemoveFromCartParams) error { + return receiver.cartController.Remove(ctx, params) +} + +func (receiver *API) Add2cart(ctx echo.Context, params Add2cartParams) error { + return receiver.cartController.Add(ctx, params) +} + +func (receiver *API) PayCart(ctx echo.Context) error { + return receiver.cartController.Pay(ctx) +} + +// Currency + +func (receiver *API) GetCurrencies(ctx echo.Context) error { + return receiver.currencyController.GetCurrencies(ctx) +} + +func (receiver *API) UpdateCurrencies(ctx echo.Context) error { + return receiver.currencyController.PutCurrencies(ctx) +} + +// History + +func (receiver *API) GetHistory(ctx echo.Context, params GetHistoryParams) error { + return receiver.historyController.GetHistoryList(ctx, params) +} + +// Wallet + +func (receiver *API) RequestMoney(ctx echo.Context) error { + return receiver.walletController.GetPaymentLink(ctx) +} + +func (receiver *API) ChangeCurrency(ctx echo.Context) error { + return receiver.walletController.ChangeCurrency(ctx) +} diff --git a/internal/interface/swagger/api.yaml b/internal/interface/swagger/api.yaml new file mode 100644 index 0000000..a2634dd --- /dev/null +++ b/internal/interface/swagger/api.yaml @@ -0,0 +1,6 @@ +output: api.gen.go +package: swagger +generate: + echo-server: true + models: false + embedded-spec: true diff --git a/internal/interface/swagger/middleware.go b/internal/interface/swagger/middleware.go new file mode 100644 index 0000000..b049a4c --- /dev/null +++ b/internal/interface/swagger/middleware.go @@ -0,0 +1,20 @@ +package swagger + +import ( + "github.com/deepmap/oapi-codegen/pkg/middleware" + "github.com/getkin/kin-openapi/openapi3" + "github.com/getkin/kin-openapi/openapi3filter" + "github.com/labstack/echo/v4" +) + +func CreateMiddleware(swagger *openapi3.T, authenticationFunc openapi3filter.AuthenticationFunc) echo.MiddlewareFunc { + validator := middleware.OapiRequestValidatorWithOptions(swagger, + &middleware.Options{ + Options: openapi3filter.Options{ + AuthenticationFunc: authenticationFunc, + }, + }, + ) + + return validator +} diff --git a/internal/interface/swagger/models.gen.go b/internal/interface/swagger/models.gen.go new file mode 100644 index 0000000..1f96932 --- /dev/null +++ b/internal/interface/swagger/models.gen.go @@ -0,0 +1,188 @@ +// Package swagger provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen version v1.12.4 DO NOT EDIT. +package swagger + +import ( + "time" +) + +const ( + BearerScopes = "Bearer.Scopes" +) + +// Defines values for AccountStatus. +const ( + Nko AccountStatus = "nko" + No AccountStatus = "no" + Org AccountStatus = "org" +) + +// Defines values for PaymentType. +const ( + PaymentTypeAlfabank PaymentType = "alfabank" + PaymentTypeB2bSberbank PaymentType = "b2bSberbank" + PaymentTypeBankCard PaymentType = "bankCard" + PaymentTypeCash PaymentType = "cash" + PaymentTypeInstallments PaymentType = "installments" + PaymentTypeMobile PaymentType = "mobile" + PaymentTypeQiwi PaymentType = "qiwi" + PaymentTypeSberbank PaymentType = "sberbank" + PaymentTypeSbp PaymentType = "sbp" + PaymentTypeTinkoffBank PaymentType = "tinkoffBank" + PaymentTypeYoomoney PaymentType = "yoomoney" +) + +// Account defines model for Account. +type Account struct { + Id string `json:"_id"` + Cart []string `json:"cart"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt *time.Time `json:"deletedAt,omitempty"` + IsDeleted *bool `json:"isDeleted,omitempty"` + Name Name `json:"name"` + Status AccountStatus `json:"status"` + UpdatedAt time.Time `json:"updatedAt"` + UserId string `json:"userId"` + Wallet Wallet `json:"wallet"` +} + +// AccountStatus defines model for AccountStatus. +type AccountStatus string + +// BankCard defines model for BankCard. +type BankCard struct { + // Cardholder Имя владельца карты + Cardholder *string `json:"cardholder,omitempty"` + + // Csc Код CVC2 или CVV2, 3 или 4 символа, печатается на обратной стороне карты + Csc *string `json:"csc,omitempty"` + + // ExpiryMonth Месяц истечения срока карты (MM) + ExpiryMonth string `json:"expiryMonth"` + + // ExpiryYear Год истечения срока карты (YYYY) + ExpiryYear string `json:"expiryYear"` + + // Number Номер карты + Number string `json:"number"` +} + +// Error defines model for Error. +type Error struct { + Message string `json:"message"` + StatusCode *int64 `json:"statusCode,omitempty"` +} + +// History defines model for History. +type History struct { + Comment string `json:"comment"` + CreatedAt time.Time `json:"createdAt"` + DeletedAt *time.Time `json:"deletedAt,omitempty"` + Id string `json:"id"` + IsDeleted *bool `json:"isDeleted,omitempty"` + + // RawDetails Я пока не могу предположить, какие будут фильтры по истории, поэтому предлагаю в это поле просто класть строку с json. Ибо для каждого типа записи она своя. + RawDetails *string `json:"rawDetails,omitempty"` + Type string `json:"type"` + UpdatedAt time.Time `json:"updatedAt"` + UserId string `json:"userId"` +} + +// Name defines model for Name. +type Name struct { + Firstname *string `json:"firstname,omitempty"` + Middlename *string `json:"middlename,omitempty"` + Orgname *string `json:"orgname,omitempty"` + Secondname *string `json:"secondname,omitempty"` +} + +// PaymentType defines model for PaymentType. +type PaymentType string + +// Wallet defines model for Wallet. +type Wallet struct { + // Cash Сумма money переведённая на текущий курс + Cash int64 `json:"cash"` + + // Currency Текущий курс валюты + Currency string `json:"currency"` + + // Money Деньги на счету в копейках. Чтобы при перессчётах не возникало денег изниоткуда. фиксируемся к одной валюте, она будет внутренней, никому её не покажем + Money int64 `json:"money"` + + // PurchasesAmount Общая сумма денег, которые внёс пользователь + PurchasesAmount int64 `json:"purchasesAmount"` + + // Spent Общая сумма потраченных денег за всё время существования аккаунта + Spent int64 `json:"spent"` +} + +// SetAccountVerificationStatusJSONBody defines parameters for SetAccountVerificationStatus. +type SetAccountVerificationStatusJSONBody struct { + Status *AccountStatus `json:"status,omitempty"` +} + +// PaginationAccountsParams defines parameters for PaginationAccounts. +type PaginationAccountsParams struct { + // Page Номер страницы, начиная с 1 + Page *int `form:"page,omitempty" json:"page,omitempty"` + + // Limit размер страницы + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` +} + +// RemoveFromCartParams defines parameters for RemoveFromCart. +type RemoveFromCartParams struct { + Id string `form:"id" json:"id"` +} + +// Add2cartParams defines parameters for Add2cart. +type Add2cartParams struct { + Id string `form:"id" json:"id"` +} + +// UpdateCurrenciesJSONBody defines parameters for UpdateCurrencies. +type UpdateCurrenciesJSONBody = []string + +// GetHistoryParams defines parameters for GetHistory. +type GetHistoryParams struct { + // Page Номер страницы, начиная с 1 + Page *int `form:"page,omitempty" json:"page,omitempty"` + + // Limit Размер страницы + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` +} + +// ChangeCurrencyJSONBody defines parameters for ChangeCurrency. +type ChangeCurrencyJSONBody struct { + Currency string `json:"currency"` +} + +// RequestMoneyJSONBody defines parameters for RequestMoney. +type RequestMoneyJSONBody struct { + Amount int `json:"amount"` + BankCard *BankCard `json:"bankCard,omitempty"` + + // Currency ISO-4217 формат + Currency string `json:"currency"` + Login *string `json:"login,omitempty"` + PhoneNumber *string `json:"phoneNumber,omitempty"` + ReturnUrl *string `json:"returnUrl,omitempty"` + Type PaymentType `json:"type"` +} + +// ChangeAccountJSONRequestBody defines body for ChangeAccount for application/json ContentType. +type ChangeAccountJSONRequestBody = Name + +// SetAccountVerificationStatusJSONRequestBody defines body for SetAccountVerificationStatus for application/json ContentType. +type SetAccountVerificationStatusJSONRequestBody SetAccountVerificationStatusJSONBody + +// UpdateCurrenciesJSONRequestBody defines body for UpdateCurrencies for application/json ContentType. +type UpdateCurrenciesJSONRequestBody = UpdateCurrenciesJSONBody + +// ChangeCurrencyJSONRequestBody defines body for ChangeCurrency for application/json ContentType. +type ChangeCurrencyJSONRequestBody ChangeCurrencyJSONBody + +// RequestMoneyJSONRequestBody defines body for RequestMoney for application/json ContentType. +type RequestMoneyJSONRequestBody RequestMoneyJSONBody diff --git a/internal/interface/swagger/models.yaml b/internal/interface/swagger/models.yaml new file mode 100644 index 0000000..9ef06be --- /dev/null +++ b/internal/interface/swagger/models.yaml @@ -0,0 +1,4 @@ +output: models.gen.go +package: swagger +generate: + models: true diff --git a/internal/models/account.go b/internal/models/account.go new file mode 100644 index 0000000..1aa5c61 --- /dev/null +++ b/internal/models/account.go @@ -0,0 +1,52 @@ +package models + +import "time" + +type Account struct { + ID string `json:"id" bson:"_id,omitempty"` + UserID string `json:"userId" bson:"userId"` + Cart []string `json:"cart" bson:"cart"` + Wallet Wallet `json:"wallet" bson:"wallet"` + Name Name `json:"name" bson:"name"` + Status AccountStatus `json:"status" bson:"status"` + Deleted bool `json:"isDeleted" bson:"isDeleted"` + CreatedAt time.Time `json:"createdAt" bson:"createdAt"` + UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt,omitempty" bson:"deletedAt,omitempty"` +} + +func (receiver *Account) Sanitize() *Account { + now := time.Now() + + receiver.ID = "" + receiver.Cart = []string{} + receiver.Wallet = Wallet{} + receiver.Name = Name{} + receiver.Status = DefaultAccountStatus + receiver.CreatedAt = now + receiver.UpdatedAt = now + receiver.DeletedAt = nil + receiver.Deleted = false + + return receiver +} + +type Name struct { + Middlename string `json:"middlename,omitempty"` + FirstName string `json:"firstname,omitempty"` + Orgname string `json:"orgname,omitempty"` + Secondname string `json:"secondname,omitempty"` +} + +type SetAccountStatus struct { + Status AccountStatus `json:"status"` +} + +type AccountStatus string + +const ( + AccountStatusNko AccountStatus = "nko" + AccountStatusNo AccountStatus = "no" + AccountStatusOrg AccountStatus = "org" + DefaultAccountStatus AccountStatus = AccountStatusNo +) diff --git a/internal/models/auth.go b/internal/models/auth.go new file mode 100644 index 0000000..9322913 --- /dev/null +++ b/internal/models/auth.go @@ -0,0 +1,16 @@ +package models + +import "time" + +type User struct { + ID string `json:"_id"` + Login string `json:"login"` + Email string `json:"email"` + PhoneNumber string `json:"phoneNumber"` + IsDeleted bool `json:"isDeleted"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt,omitempty"` +} + +const AuthJWTDecodedUserIDKey = "userID" diff --git a/internal/models/cart.go b/internal/models/cart.go new file mode 100644 index 0000000..1dcec30 --- /dev/null +++ b/internal/models/cart.go @@ -0,0 +1,5 @@ +package models + +type AddItemToCart struct { + ID string `json:"id"` +} diff --git a/internal/models/common.go b/internal/models/common.go new file mode 100644 index 0000000..98b3977 --- /dev/null +++ b/internal/models/common.go @@ -0,0 +1,27 @@ +package models + +type FastifyError struct { + StatusCode int `json:"statusCode"` + Error string `json:"error"` + Message string `json:"message"` +} + +type ResponseErrorHTTP struct { + StatusCode int `json:"statusCode"` + Message string `json:"message"` +} + +type PaginationResponse[T any] struct { + TotalPages int64 `json:"totalPages"` + Records []T `json:"records"` +} + +type Pagination struct { + Page int64 + Limit int64 +} + +const ( + DefaultPageNumber int64 = 1 + DefaultLimit int64 = 100 +) diff --git a/internal/models/config.go b/internal/models/config.go new file mode 100644 index 0000000..19ceec0 --- /dev/null +++ b/internal/models/config.go @@ -0,0 +1,76 @@ +package models + +import ( + "time" + + "github.com/golang-jwt/jwt/v5" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo" +) + +type Config struct { + HTTP ConfigurationHTTP + GRPC ConfigurationGRPC + Service ServiceConfiguration + Database mongo.Configuration +} + +type ConfigurationHTTP struct { + Host string `env:"HTTP_HOST,default=localhost"` + Port string `env:"HTTP_PORT,default=8080"` +} + +type ConfigurationGRPC struct { + Host string `env:"GRPC_HOST,default=localhost"` + Port string `env:"GRPC_PORT,default=8081"` + Domen string `env:"GRPC_DOMEN,default=https://domen.ru"` +} + +type ServiceConfiguration struct { + AuthMicroservice AuthMicroserviceConfiguration + HubadminMicroservice HubadminMicroserviceConfiguration + CurrencyMicroservice CurrencyMicroserviceConfiguration + DiscountMicroservice DiscountMicroserviceConfiguration + PaymentMicroservice PaymentMicroserviceConfiguration + JWT JWTConfiguration +} + +type JWTConfiguration struct { + 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"` + Algorithm jwt.SigningMethodRSA + ExpiresIn time.Duration +} + +type AuthMicroserviceConfiguration struct { + URL AuthMicroserviceURL +} + +type HubadminMicroserviceConfiguration struct { + URL HubadminMicroserviceURL +} + +type CurrencyMicroserviceConfiguration struct { + URL CurrencyMicroserviceURL +} + +type PaymentMicroserviceConfiguration struct { + HostGRPC string `env:"PAYMENT_MICROSERVICE_GRPC_HOST,required"` +} + +type DiscountMicroserviceConfiguration struct { + HostGRPC string `env:"DISCOUNT_MICROSERVICE_GRPC_HOST,required"` +} + +type AuthMicroserviceURL struct { + User string `env:"AUTH_MICROSERVICE_USER_URL,required"` +} + +type HubadminMicroserviceURL struct { + Tariff string `env:"HUBADMIN_MICROSERVICE_TARIFF_URL,required"` +} + +type CurrencyMicroserviceURL struct { + Translate string `env:"CURRENCY_MICROSERVICE_TRANSLATE_URL,required"` +} diff --git a/internal/models/currency.go b/internal/models/currency.go new file mode 100644 index 0000000..082c84b --- /dev/null +++ b/internal/models/currency.go @@ -0,0 +1,47 @@ +package models + +import "time" + +type CurrencyKey = string + +type CurrencyList struct { + ID string `json:"id" bson:"_id,omitempty"` + Name string `json:"name" bson:"name"` + Currencies []CurrencyKey `json:"currencies" bson:"currencies"` + Deleted bool `json:"isDeleted" bson:"isDeleted"` + CreatedAt time.Time `json:"createdAt" bson:"createdAt"` + UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt,omitempty" bson:"deletedAt,omitempty"` +} + +func (receiver *CurrencyList) Sanitize() *CurrencyList { + now := time.Now() + + receiver.ID = "" + receiver.CreatedAt = now + receiver.UpdatedAt = now + receiver.DeletedAt = nil + receiver.Deleted = false + + return receiver +} + +const ( + DefaultCurrencyListName = "currency_list" + InternalCurrencyKey CurrencyKey = "RUB" +) + +type TranslateCurrency struct { + Money int64 `json:"value"` + From CurrencyKey `json:"currencyFrom"` + To CurrencyKey `json:"currencyTo"` +} + +type CurrencyClientResponse[T any] struct { + Success bool `json:"success"` + Message T `json:"message"` +} + +type ChangeCurrency struct { + Currency CurrencyKey `json:"currency"` +} diff --git a/internal/models/discount.go b/internal/models/discount.go new file mode 100644 index 0000000..5c95946 --- /dev/null +++ b/internal/models/discount.go @@ -0,0 +1,66 @@ +package models + +import "time" + +type Discount struct { + Target DiscountCalculationTarget `json:"target"` + Condition DiscountCondition `json:"condition"` + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Layer uint32 `json:"layer"` + Audit Audit `json:"audit"` + Deprecated bool `json:"deprecated"` +} + +type Audit struct { + UpdatedAt time.Time `json:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt,omitempty"` + CreatedAt time.Time `json:"createdAt"` + Deleted bool `json:"deleted"` +} + +type DiscountCalculationTarget struct { + Products []ProductTarget `json:"products"` + Factor float64 `json:"factor"` + TargetScope TargetScope `json:"scope"` + TargetGroup string `json:"group"` + Overhelm bool `json:"overhelm"` +} + +type DiscountCondition struct { + Period *PeriodCondition `json:"period,omitempty"` + Product *string `json:"product,omitempty"` + PriceFrom *float64 `json:"priceFrom,omitempty"` + Group *string `json:"group,omitempty"` + User *string `json:"user,omitempty"` + UserType *string `json:"userType,omitempty"` + Coupon *string `json:"coupon,omitempty"` + PurchasesAmount *float64 `json:"purchasesAmount,omitempty"` + CartPurchasesAmount *float64 `json:"cartPurchasesAmount,omitempty"` + + /* Срок использования (количество дней использования) */ + Term *uint64 `json:"term"` + + /* Количество использований (количество попыток) */ + Usage *uint64 `json:"usage"` +} + +type ProductTarget struct { + ID string `json:"productId"` + Factor float64 `json:"factor"` + Overhelm bool `json:"overhelm"` +} + +type PeriodCondition struct { + From time.Time `json:"from"` + To time.Time `json:"to"` +} + +type TargetScope string + +const ( + TargetSum TargetScope = "sum" + TargetGroup TargetScope = "group" + TargetEach TargetScope = "each" +) diff --git a/internal/models/history.go b/internal/models/history.go new file mode 100644 index 0000000..9fd7f2f --- /dev/null +++ b/internal/models/history.go @@ -0,0 +1,34 @@ +package models + +import "time" + +type History struct { + ID string `json:"id" bson:"_id,omitempty"` + UserID string `json:"userId" bson:"userId"` + Comment string `json:"comment" bson:"comment"` + Key string `json:"key" bson:"key"` + RawDetails string `json:"rawDetails" bson:"rawDetails"` + Deleted bool `json:"isDeleted" bson:"isDeleted"` + CreatedAt time.Time `json:"createdAt" bson:"createdAt"` + UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt,omitempty" bson:"deletedAt,omitempty"` +} + +func (receiver *History) Sanitize() *History { + now := time.Now() + + receiver.ID = "" + receiver.CreatedAt = now + receiver.UpdatedAt = now + receiver.DeletedAt = nil + receiver.Deleted = false + + return receiver +} + +type CustomerHistoryKey string + +const ( + CustomerHistoryKeyReplenish = "customer.replenishWallet" + CustomerHistoryKeyPayCart = "customer.payCart" +) diff --git a/internal/models/payment.go b/internal/models/payment.go new file mode 100644 index 0000000..eda0aad --- /dev/null +++ b/internal/models/payment.go @@ -0,0 +1,54 @@ +package models + +type GetPaymentLinkBody struct { + Type PaymentType `json:"type"` + Currency string `json:"currency"` + Amount int64 `json:"amount"` + ReturnURL string `json:"returnUrl,omitempty"` + PhoneNumber string `json:"phoneNumber,omitempty"` + Login string `json:"login,omitempty"` + BankCard *BankCard `json:"bankCard,omitempty"` +} + +type GetPaymentLinkRequest struct { + Body *GetPaymentLinkBody + ClientIP string + UserID string +} + +type GetPaymentLinkResponse struct { + Link string `json:"link"` +} + +type BankCard struct { + Number string `json:"number"` + ExpiryYear string `json:"expiryYear"` + ExpiryMonth string `json:"expiryMonth"` + CSC *string `json:"csc,omitempty"` + CardHolderName *string `json:"cardholder,omitempty"` +} + +type PaymentType string + +const ( + PaymentTypeBankCard PaymentType = "bankCard" + PaymentTypeTinkoff PaymentType = "tinkoffBank" + PaymentTypeQiwi PaymentType = "qiwi" + PaymentTypeSberPay PaymentType = "sberbank" + PaymentTypeYoomoney PaymentType = "yoomoney" + PaymentTypeMobile PaymentType = "mobile" + PaymentTypeInstallments PaymentType = "installments" + PaymentTypeCash PaymentType = "cash" + PaymentTypeSBP PaymentType = "sbp" + PaymentTypeSberB2B PaymentType = "b2bSberbank" + PaymentTypeAlfabank PaymentType = "alfabank" +) + +type PaymentEvent struct { + Key string + Message string + PaymentID string + UserID string + Currency string + Amount int64 +} diff --git a/internal/models/tariff.go b/internal/models/tariff.go new file mode 100644 index 0000000..12e86a4 --- /dev/null +++ b/internal/models/tariff.go @@ -0,0 +1,26 @@ +package models + +import "time" + +type Tariff struct { + ID string `json:"_id"` + Name string `json:"name"` + Price int64 `json:"price"` + IsCustom bool `json:"isCustom"` + Privileges map[string]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"` + Type string `json:"type"` + Value string `json:"value"` + Price int64 `json:"price"` +} diff --git a/internal/models/wallet.go b/internal/models/wallet.go new file mode 100644 index 0000000..1d76bc7 --- /dev/null +++ b/internal/models/wallet.go @@ -0,0 +1,33 @@ +package models + +type Wallet struct { + Cash int64 `json:"cash" bson:"cash"` + Currency string `json:"currency" bson:"currency"` + + /* Общая сумма потраченных денег за всё время существования аккаунта */ + Spent int64 `json:"spent" bson:"spent"` + + /* Общая сумма денег, которые внёс пользователь */ + PurchasesAmount int64 `json:"purchasesAmount" bson:"purchasesAmount"` + + /* + Money деньги на счету в копейках. Чтобы при перессчётах не возникало денег из ни откуда. + Фиксируемся к одной валюте, она будет внутренней, никому её не покажем. + */ + Money int64 `json:"money" bson:"money"` + + /* Последний ID платежа, по которому было произведено пополнение средств (кошелька) */ + LastPaymentID string `json:"lastPaymentId" bson:"lastPaymentId"` +} + +type ReplenishAccountWallet struct { + Cash int64 + Currency string + PaymentID string + Account *Account +} + +type WithdrawAccountWallet struct { + Money int64 + Account *Account +} diff --git a/internal/proto/customer/service.pb.go b/internal/proto/customer/service.pb.go new file mode 100644 index 0000000..09f8f8a --- /dev/null +++ b/internal/proto/customer/service.pb.go @@ -0,0 +1,182 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc (unknown) +// source: customer/service.proto + +package customer + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + 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 History struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserID string `protobuf:"bytes,1,opt,name=UserID,proto3" json:"UserID,omitempty"` + Comment string `protobuf:"bytes,2,opt,name=Comment,proto3" json:"Comment,omitempty"` + Key string `protobuf:"bytes,3,opt,name=Key,proto3" json:"Key,omitempty"` + RawDetails string `protobuf:"bytes,4,opt,name=RawDetails,proto3" json:"RawDetails,omitempty"` +} + +func (x *History) Reset() { + *x = History{} + if protoimpl.UnsafeEnabled { + mi := &file_customer_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *History) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*History) ProtoMessage() {} + +func (x *History) ProtoReflect() protoreflect.Message { + mi := &file_customer_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 History.ProtoReflect.Descriptor instead. +func (*History) Descriptor() ([]byte, []int) { + return file_customer_service_proto_rawDescGZIP(), []int{0} +} + +func (x *History) GetUserID() string { + if x != nil { + return x.UserID + } + return "" +} + +func (x *History) GetComment() string { + if x != nil { + return x.Comment + } + return "" +} + +func (x *History) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *History) GetRawDetails() string { + if x != nil { + return x.RawDetails + } + return "" +} + +var File_customer_service_proto protoreflect.FileDescriptor + +var file_customer_service_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x65, 0x72, 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, + 0x6d, 0x0a, 0x07, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x73, + 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x55, 0x73, 0x65, 0x72, + 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x1e, + 0x0a, 0x0a, 0x52, 0x61, 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x52, 0x61, 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x32, 0x4f, + 0x0a, 0x0f, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x49, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x12, 0x11, 0x2e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x2e, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, + 0x0c, 0x5a, 0x0a, 0x2e, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_customer_service_proto_rawDescOnce sync.Once + file_customer_service_proto_rawDescData = file_customer_service_proto_rawDesc +) + +func file_customer_service_proto_rawDescGZIP() []byte { + file_customer_service_proto_rawDescOnce.Do(func() { + file_customer_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_customer_service_proto_rawDescData) + }) + return file_customer_service_proto_rawDescData +} + +var file_customer_service_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_customer_service_proto_goTypes = []interface{}{ + (*History)(nil), // 0: customer.History + (*emptypb.Empty)(nil), // 1: google.protobuf.Empty +} +var file_customer_service_proto_depIdxs = []int32{ + 0, // 0: customer.CustomerService.InsertHistory:input_type -> customer.History + 1, // 1: customer.CustomerService.InsertHistory:output_type -> google.protobuf.Empty + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_customer_service_proto_init() } +func file_customer_service_proto_init() { + if File_customer_service_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_customer_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*History); 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_customer_service_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_customer_service_proto_goTypes, + DependencyIndexes: file_customer_service_proto_depIdxs, + MessageInfos: file_customer_service_proto_msgTypes, + }.Build() + File_customer_service_proto = out.File + file_customer_service_proto_rawDesc = nil + file_customer_service_proto_goTypes = nil + file_customer_service_proto_depIdxs = nil +} diff --git a/internal/proto/customer/service_grpc.pb.go b/internal/proto/customer/service_grpc.pb.go new file mode 100644 index 0000000..53e1376 --- /dev/null +++ b/internal/proto/customer/service_grpc.pb.go @@ -0,0 +1,100 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package customer + +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 + +// CustomerServiceClient is the client API for CustomerService 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 CustomerServiceClient interface { + InsertHistory(ctx context.Context, in *History, opts ...grpc.CallOption) (*emptypb.Empty, error) +} + +type customerServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewCustomerServiceClient(cc grpc.ClientConnInterface) CustomerServiceClient { + return &customerServiceClient{cc} +} + +func (c *customerServiceClient) InsertHistory(ctx context.Context, in *History, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, "/customer.CustomerService/InsertHistory", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CustomerServiceServer is the server API for CustomerService service. +// All implementations should embed UnimplementedCustomerServiceServer +// for forward compatibility +type CustomerServiceServer interface { + InsertHistory(context.Context, *History) (*emptypb.Empty, error) +} + +// UnimplementedCustomerServiceServer should be embedded to have forward compatible implementations. +type UnimplementedCustomerServiceServer struct { +} + +func (UnimplementedCustomerServiceServer) InsertHistory(context.Context, *History) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method InsertHistory not implemented") +} + +// UnsafeCustomerServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to CustomerServiceServer will +// result in compilation errors. +type UnsafeCustomerServiceServer interface { + mustEmbedUnimplementedCustomerServiceServer() +} + +func RegisterCustomerServiceServer(s grpc.ServiceRegistrar, srv CustomerServiceServer) { + s.RegisterService(&CustomerService_ServiceDesc, srv) +} + +func _CustomerService_InsertHistory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(History) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CustomerServiceServer).InsertHistory(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/customer.CustomerService/InsertHistory", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CustomerServiceServer).InsertHistory(ctx, req.(*History)) + } + return interceptor(ctx, in, info, handler) +} + +// CustomerService_ServiceDesc is the grpc.ServiceDesc for CustomerService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var CustomerService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "customer.CustomerService", + HandlerType: (*CustomerServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "InsertHistory", + Handler: _CustomerService_InsertHistory_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "customer/service.proto", +} diff --git a/internal/proto/discount/audit.model.pb.go b/internal/proto/discount/audit.model.pb.go new file mode 100644 index 0000000..32dafb0 --- /dev/null +++ b/internal/proto/discount/audit.model.pb.go @@ -0,0 +1,192 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.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 +} diff --git a/internal/proto/discount/discount.model.pb.go b/internal/proto/discount/discount.model.pb.go new file mode 100644 index 0000000..5a1ba58 --- /dev/null +++ b/internal/proto/discount/discount.model.pb.go @@ -0,0 +1,1121 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc (unknown) +// source: discount/discount.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 TargetScope int32 + +const ( + TargetScope_Sum TargetScope = 0 + TargetScope_Group TargetScope = 1 + TargetScope_Each TargetScope = 2 +) + +// Enum value maps for TargetScope. +var ( + TargetScope_name = map[int32]string{ + 0: "Sum", + 1: "Group", + 2: "Each", + } + TargetScope_value = map[string]int32{ + "Sum": 0, + "Group": 1, + "Each": 2, + } +) + +func (x TargetScope) Enum() *TargetScope { + p := new(TargetScope) + *p = x + return p +} + +func (x TargetScope) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TargetScope) Descriptor() protoreflect.EnumDescriptor { + return file_discount_discount_model_proto_enumTypes[0].Descriptor() +} + +func (TargetScope) Type() protoreflect.EnumType { + return &file_discount_discount_model_proto_enumTypes[0] +} + +func (x TargetScope) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TargetScope.Descriptor instead. +func (TargetScope) EnumDescriptor() ([]byte, []int) { + return file_discount_discount_model_proto_rawDescGZIP(), []int{0} +} + +type DiscountOptional struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + Name *string `protobuf:"bytes,2,opt,name=Name,proto3,oneof" json:"Name,omitempty"` + Layer *uint32 `protobuf:"varint,3,opt,name=Layer,proto3,oneof" json:"Layer,omitempty"` + Description *string `protobuf:"bytes,4,opt,name=Description,proto3,oneof" json:"Description,omitempty"` + Condition *DiscountCondition `protobuf:"bytes,5,opt,name=Condition,proto3,oneof" json:"Condition,omitempty"` + Target *DiscountCalculationTarget `protobuf:"bytes,6,opt,name=Target,proto3,oneof" json:"Target,omitempty"` +} + +func (x *DiscountOptional) Reset() { + *x = DiscountOptional{} + if protoimpl.UnsafeEnabled { + mi := &file_discount_discount_model_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DiscountOptional) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DiscountOptional) ProtoMessage() {} + +func (x *DiscountOptional) ProtoReflect() protoreflect.Message { + mi := &file_discount_discount_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 DiscountOptional.ProtoReflect.Descriptor instead. +func (*DiscountOptional) Descriptor() ([]byte, []int) { + return file_discount_discount_model_proto_rawDescGZIP(), []int{0} +} + +func (x *DiscountOptional) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *DiscountOptional) GetName() string { + if x != nil && x.Name != nil { + return *x.Name + } + return "" +} + +func (x *DiscountOptional) GetLayer() uint32 { + if x != nil && x.Layer != nil { + return *x.Layer + } + return 0 +} + +func (x *DiscountOptional) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *DiscountOptional) GetCondition() *DiscountCondition { + if x != nil { + return x.Condition + } + return nil +} + +func (x *DiscountOptional) GetTarget() *DiscountCalculationTarget { + if x != nil { + return x.Target + } + return nil +} + +type Discounts struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Discounts []*Discount `protobuf:"bytes,1,rep,name=Discounts,proto3" json:"Discounts,omitempty"` +} + +func (x *Discounts) Reset() { + *x = Discounts{} + if protoimpl.UnsafeEnabled { + mi := &file_discount_discount_model_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Discounts) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Discounts) ProtoMessage() {} + +func (x *Discounts) ProtoReflect() protoreflect.Message { + mi := &file_discount_discount_model_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 Discounts.ProtoReflect.Descriptor instead. +func (*Discounts) Descriptor() ([]byte, []int) { + return file_discount_discount_model_proto_rawDescGZIP(), []int{1} +} + +func (x *Discounts) GetDiscounts() []*Discount { + if x != nil { + return x.Discounts + } + return nil +} + +type Discount struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"` + Layer uint32 `protobuf:"varint,3,opt,name=Layer,proto3" json:"Layer,omitempty"` + Description string `protobuf:"bytes,4,opt,name=Description,proto3" json:"Description,omitempty"` + Condition *DiscountCondition `protobuf:"bytes,5,opt,name=Condition,proto3" json:"Condition,omitempty"` + Target *DiscountCalculationTarget `protobuf:"bytes,6,opt,name=Target,proto3" json:"Target,omitempty"` + Audit *Audit `protobuf:"bytes,7,opt,name=Audit,proto3" json:"Audit,omitempty"` + Deprecated bool `protobuf:"varint,8,opt,name=Deprecated,proto3" json:"Deprecated,omitempty"` +} + +func (x *Discount) Reset() { + *x = Discount{} + if protoimpl.UnsafeEnabled { + mi := &file_discount_discount_model_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Discount) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Discount) ProtoMessage() {} + +func (x *Discount) ProtoReflect() protoreflect.Message { + mi := &file_discount_discount_model_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 Discount.ProtoReflect.Descriptor instead. +func (*Discount) Descriptor() ([]byte, []int) { + return file_discount_discount_model_proto_rawDescGZIP(), []int{2} +} + +func (x *Discount) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *Discount) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Discount) GetLayer() uint32 { + if x != nil { + return x.Layer + } + return 0 +} + +func (x *Discount) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Discount) GetCondition() *DiscountCondition { + if x != nil { + return x.Condition + } + return nil +} + +func (x *Discount) GetTarget() *DiscountCalculationTarget { + if x != nil { + return x.Target + } + return nil +} + +func (x *Discount) GetAudit() *Audit { + if x != nil { + return x.Audit + } + return nil +} + +func (x *Discount) GetDeprecated() bool { + if x != nil { + return x.Deprecated + } + return false +} + +type DiscountCalculationTarget struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Products []*ProductTarget `protobuf:"bytes,1,rep,name=Products,proto3" json:"Products,omitempty"` + Factor float64 `protobuf:"fixed64,2,opt,name=Factor,proto3" json:"Factor,omitempty"` + TargetScope *TargetScope `protobuf:"varint,3,opt,name=TargetScope,proto3,enum=discount.TargetScope,oneof" json:"TargetScope,omitempty"` + TargetGroup *string `protobuf:"bytes,4,opt,name=TargetGroup,proto3,oneof" json:"TargetGroup,omitempty"` + Overhelm *bool `protobuf:"varint,5,opt,name=Overhelm,proto3,oneof" json:"Overhelm,omitempty"` +} + +func (x *DiscountCalculationTarget) Reset() { + *x = DiscountCalculationTarget{} + if protoimpl.UnsafeEnabled { + mi := &file_discount_discount_model_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DiscountCalculationTarget) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DiscountCalculationTarget) ProtoMessage() {} + +func (x *DiscountCalculationTarget) ProtoReflect() protoreflect.Message { + mi := &file_discount_discount_model_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 DiscountCalculationTarget.ProtoReflect.Descriptor instead. +func (*DiscountCalculationTarget) Descriptor() ([]byte, []int) { + return file_discount_discount_model_proto_rawDescGZIP(), []int{3} +} + +func (x *DiscountCalculationTarget) GetProducts() []*ProductTarget { + if x != nil { + return x.Products + } + return nil +} + +func (x *DiscountCalculationTarget) GetFactor() float64 { + if x != nil { + return x.Factor + } + return 0 +} + +func (x *DiscountCalculationTarget) GetTargetScope() TargetScope { + if x != nil && x.TargetScope != nil { + return *x.TargetScope + } + return TargetScope_Sum +} + +func (x *DiscountCalculationTarget) GetTargetGroup() string { + if x != nil && x.TargetGroup != nil { + return *x.TargetGroup + } + return "" +} + +func (x *DiscountCalculationTarget) GetOverhelm() bool { + if x != nil && x.Overhelm != nil { + return *x.Overhelm + } + return false +} + +type DiscountCondition struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Period *PeriodCondition `protobuf:"bytes,1,opt,name=Period,proto3,oneof" json:"Period,omitempty"` + User *string `protobuf:"bytes,2,opt,name=User,proto3,oneof" json:"User,omitempty"` + UserType *string `protobuf:"bytes,3,opt,name=UserType,proto3,oneof" json:"UserType,omitempty"` + Coupon *string `protobuf:"bytes,4,opt,name=Coupon,proto3,oneof" json:"Coupon,omitempty"` + PurchasesAmount *float64 `protobuf:"fixed64,5,opt,name=PurchasesAmount,proto3,oneof" json:"PurchasesAmount,omitempty"` + CartPurchasesAmount *float64 `protobuf:"fixed64,6,opt,name=CartPurchasesAmount,proto3,oneof" json:"CartPurchasesAmount,omitempty"` + Product *string `protobuf:"bytes,7,opt,name=Product,proto3,oneof" json:"Product,omitempty"` + Term *uint64 `protobuf:"varint,8,opt,name=Term,proto3,oneof" json:"Term,omitempty"` + Usage *uint64 `protobuf:"varint,9,opt,name=Usage,proto3,oneof" json:"Usage,omitempty"` + PriceFrom *float64 `protobuf:"fixed64,10,opt,name=PriceFrom,proto3,oneof" json:"PriceFrom,omitempty"` + Group *string `protobuf:"bytes,11,opt,name=Group,proto3,oneof" json:"Group,omitempty"` +} + +func (x *DiscountCondition) Reset() { + *x = DiscountCondition{} + if protoimpl.UnsafeEnabled { + mi := &file_discount_discount_model_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DiscountCondition) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DiscountCondition) ProtoMessage() {} + +func (x *DiscountCondition) ProtoReflect() protoreflect.Message { + mi := &file_discount_discount_model_proto_msgTypes[4] + 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 DiscountCondition.ProtoReflect.Descriptor instead. +func (*DiscountCondition) Descriptor() ([]byte, []int) { + return file_discount_discount_model_proto_rawDescGZIP(), []int{4} +} + +func (x *DiscountCondition) GetPeriod() *PeriodCondition { + if x != nil { + return x.Period + } + return nil +} + +func (x *DiscountCondition) GetUser() string { + if x != nil && x.User != nil { + return *x.User + } + return "" +} + +func (x *DiscountCondition) GetUserType() string { + if x != nil && x.UserType != nil { + return *x.UserType + } + return "" +} + +func (x *DiscountCondition) GetCoupon() string { + if x != nil && x.Coupon != nil { + return *x.Coupon + } + return "" +} + +func (x *DiscountCondition) GetPurchasesAmount() float64 { + if x != nil && x.PurchasesAmount != nil { + return *x.PurchasesAmount + } + return 0 +} + +func (x *DiscountCondition) GetCartPurchasesAmount() float64 { + if x != nil && x.CartPurchasesAmount != nil { + return *x.CartPurchasesAmount + } + return 0 +} + +func (x *DiscountCondition) GetProduct() string { + if x != nil && x.Product != nil { + return *x.Product + } + return "" +} + +func (x *DiscountCondition) GetTerm() uint64 { + if x != nil && x.Term != nil { + return *x.Term + } + return 0 +} + +func (x *DiscountCondition) GetUsage() uint64 { + if x != nil && x.Usage != nil { + return *x.Usage + } + return 0 +} + +func (x *DiscountCondition) GetPriceFrom() float64 { + if x != nil && x.PriceFrom != nil { + return *x.PriceFrom + } + return 0 +} + +func (x *DiscountCondition) GetGroup() string { + if x != nil && x.Group != nil { + return *x.Group + } + return "" +} + +type ProductTarget struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + Factor float64 `protobuf:"fixed64,2,opt,name=Factor,proto3" json:"Factor,omitempty"` + Overhelm *bool `protobuf:"varint,3,opt,name=Overhelm,proto3,oneof" json:"Overhelm,omitempty"` +} + +func (x *ProductTarget) Reset() { + *x = ProductTarget{} + if protoimpl.UnsafeEnabled { + mi := &file_discount_discount_model_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProductTarget) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProductTarget) ProtoMessage() {} + +func (x *ProductTarget) ProtoReflect() protoreflect.Message { + mi := &file_discount_discount_model_proto_msgTypes[5] + 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 ProductTarget.ProtoReflect.Descriptor instead. +func (*ProductTarget) Descriptor() ([]byte, []int) { + return file_discount_discount_model_proto_rawDescGZIP(), []int{5} +} + +func (x *ProductTarget) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *ProductTarget) GetFactor() float64 { + if x != nil { + return x.Factor + } + return 0 +} + +func (x *ProductTarget) GetOverhelm() bool { + if x != nil && x.Overhelm != nil { + return *x.Overhelm + } + return false +} + +type PeriodCondition struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + From *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=From,proto3" json:"From,omitempty"` + To *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=To,proto3" json:"To,omitempty"` +} + +func (x *PeriodCondition) Reset() { + *x = PeriodCondition{} + if protoimpl.UnsafeEnabled { + mi := &file_discount_discount_model_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PeriodCondition) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PeriodCondition) ProtoMessage() {} + +func (x *PeriodCondition) ProtoReflect() protoreflect.Message { + mi := &file_discount_discount_model_proto_msgTypes[6] + 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 PeriodCondition.ProtoReflect.Descriptor instead. +func (*PeriodCondition) Descriptor() ([]byte, []int) { + return file_discount_discount_model_proto_rawDescGZIP(), []int{6} +} + +func (x *PeriodCondition) GetFrom() *timestamppb.Timestamp { + if x != nil { + return x.From + } + return nil +} + +func (x *PeriodCondition) GetTo() *timestamppb.Timestamp { + if x != nil { + return x.To + } + return nil +} + +type UserInformation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + Type string `protobuf:"bytes,2,opt,name=Type,proto3" json:"Type,omitempty"` + PurchasesAmount float64 `protobuf:"fixed64,3,opt,name=PurchasesAmount,proto3" json:"PurchasesAmount,omitempty"` + CartPurchasesAmount float64 `protobuf:"fixed64,4,opt,name=CartPurchasesAmount,proto3" json:"CartPurchasesAmount,omitempty"` +} + +func (x *UserInformation) Reset() { + *x = UserInformation{} + if protoimpl.UnsafeEnabled { + mi := &file_discount_discount_model_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UserInformation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserInformation) ProtoMessage() {} + +func (x *UserInformation) ProtoReflect() protoreflect.Message { + mi := &file_discount_discount_model_proto_msgTypes[7] + 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 UserInformation.ProtoReflect.Descriptor instead. +func (*UserInformation) Descriptor() ([]byte, []int) { + return file_discount_discount_model_proto_rawDescGZIP(), []int{7} +} + +func (x *UserInformation) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *UserInformation) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *UserInformation) GetPurchasesAmount() float64 { + if x != nil { + return x.PurchasesAmount + } + return 0 +} + +func (x *UserInformation) GetCartPurchasesAmount() float64 { + if x != nil { + return x.CartPurchasesAmount + } + return 0 +} + +type ProductInformation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + Price float64 `protobuf:"fixed64,2,opt,name=Price,proto3" json:"Price,omitempty"` + Term *uint64 `protobuf:"varint,3,opt,name=Term,proto3,oneof" json:"Term,omitempty"` + Usage *uint64 `protobuf:"varint,4,opt,name=Usage,proto3,oneof" json:"Usage,omitempty"` + Group *string `protobuf:"bytes,5,opt,name=Group,proto3,oneof" json:"Group,omitempty"` +} + +func (x *ProductInformation) Reset() { + *x = ProductInformation{} + if protoimpl.UnsafeEnabled { + mi := &file_discount_discount_model_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProductInformation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProductInformation) ProtoMessage() {} + +func (x *ProductInformation) ProtoReflect() protoreflect.Message { + mi := &file_discount_discount_model_proto_msgTypes[8] + 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 ProductInformation.ProtoReflect.Descriptor instead. +func (*ProductInformation) Descriptor() ([]byte, []int) { + return file_discount_discount_model_proto_rawDescGZIP(), []int{8} +} + +func (x *ProductInformation) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *ProductInformation) GetPrice() float64 { + if x != nil { + return x.Price + } + return 0 +} + +func (x *ProductInformation) GetTerm() uint64 { + if x != nil && x.Term != nil { + return *x.Term + } + return 0 +} + +func (x *ProductInformation) GetUsage() uint64 { + if x != nil && x.Usage != nil { + return *x.Usage + } + return 0 +} + +func (x *ProductInformation) GetGroup() string { + if x != nil && x.Group != nil { + return *x.Group + } + return "" +} + +var File_discount_discount_model_proto protoreflect.FileDescriptor + +var file_discount_discount_model_proto_rawDesc = []byte{ + 0x0a, 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, 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, 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, 0x22, 0xbb, 0x02, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x17, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, + 0x19, 0x0a, 0x05, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, + 0x52, 0x05, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x44, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x02, 0x52, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, + 0x01, 0x12, 0x3e, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, + 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, 0x48, 0x03, 0x52, 0x09, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, + 0x01, 0x12, 0x40, 0x0a, 0x06, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 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, 0x48, 0x04, 0x52, 0x06, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x08, 0x0a, 0x06, + 0x5f, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x44, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x43, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, + 0x3d, 0x0a, 0x09, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x30, 0x0a, 0x09, + 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 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, 0x09, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22, 0xa5, + 0x02, 0x0a, 0x08, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, + 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 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, 0x04, 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, 0x05, 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, 0x06, 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, 0x12, + 0x25, 0x0a, 0x05, 0x41, 0x75, 0x64, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, + 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x52, + 0x05, 0x41, 0x75, 0x64, 0x69, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, + 0x61, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x44, 0x65, 0x70, 0x72, + 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x22, 0x9b, 0x02, 0x0a, 0x19, 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, 0x12, 0x33, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, + 0x08, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x46, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x46, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x12, 0x3c, 0x0a, 0x0b, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x48, 0x00, 0x52, + 0x0b, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, + 0x25, 0x0a, 0x0b, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x4f, 0x76, 0x65, 0x72, 0x68, 0x65, + 0x6c, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x08, 0x4f, 0x76, 0x65, 0x72, + 0x68, 0x65, 0x6c, 0x6d, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x4f, 0x76, 0x65, 0x72, + 0x68, 0x65, 0x6c, 0x6d, 0x22, 0xa8, 0x04, 0x0a, 0x11, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x06, 0x50, 0x65, + 0x72, 0x69, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x43, 0x6f, 0x6e, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x88, + 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x01, 0x52, 0x04, 0x55, 0x73, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x55, + 0x73, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, + 0x08, 0x55, 0x73, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, + 0x43, 0x6f, 0x75, 0x70, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x06, + 0x43, 0x6f, 0x75, 0x70, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2d, 0x0a, 0x0f, 0x50, 0x75, 0x72, + 0x63, 0x68, 0x61, 0x73, 0x65, 0x73, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x01, 0x48, 0x04, 0x52, 0x0f, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x73, 0x41, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x13, 0x43, 0x61, 0x72, 0x74, + 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x73, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x01, 0x48, 0x05, 0x52, 0x13, 0x43, 0x61, 0x72, 0x74, 0x50, 0x75, 0x72, + 0x63, 0x68, 0x61, 0x73, 0x65, 0x73, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, + 0x1d, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x06, 0x52, 0x07, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x88, 0x01, 0x01, 0x12, 0x17, + 0x0a, 0x04, 0x54, 0x65, 0x72, 0x6d, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x48, 0x07, 0x52, 0x04, + 0x54, 0x65, 0x72, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x48, 0x08, 0x52, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x88, + 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x50, 0x72, 0x69, 0x63, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x01, 0x48, 0x09, 0x52, 0x09, 0x50, 0x72, 0x69, 0x63, 0x65, 0x46, 0x72, + 0x6f, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x0a, 0x52, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x88, 0x01, 0x01, + 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x5f, + 0x55, 0x73, 0x65, 0x72, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x55, 0x73, 0x65, 0x72, 0x54, 0x79, 0x70, + 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x43, 0x6f, 0x75, 0x70, 0x6f, 0x6e, 0x42, 0x12, 0x0a, 0x10, + 0x5f, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x73, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x43, 0x61, 0x72, 0x74, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, + 0x65, 0x73, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x50, 0x72, 0x6f, + 0x64, 0x75, 0x63, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x54, 0x65, 0x72, 0x6d, 0x42, 0x08, 0x0a, + 0x06, 0x5f, 0x55, 0x73, 0x61, 0x67, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x50, 0x72, 0x69, 0x63, + 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, + 0x65, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, + 0x12, 0x16, 0x0a, 0x06, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, + 0x52, 0x06, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x08, 0x4f, 0x76, 0x65, 0x72, + 0x68, 0x65, 0x6c, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x08, 0x4f, 0x76, + 0x65, 0x72, 0x68, 0x65, 0x6c, 0x6d, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x4f, 0x76, + 0x65, 0x72, 0x68, 0x65, 0x6c, 0x6d, 0x22, 0x6d, 0x0a, 0x0f, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, + 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x04, 0x46, 0x72, 0x6f, + 0x6d, 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, 0x04, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x2a, 0x0a, 0x02, 0x54, 0x6f, 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, 0x02, 0x54, 0x6f, 0x22, 0x91, 0x01, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, + 0x0f, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x73, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, + 0x73, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x13, 0x43, 0x61, 0x72, 0x74, 0x50, + 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x73, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x13, 0x43, 0x61, 0x72, 0x74, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, + 0x73, 0x65, 0x73, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xa6, 0x01, 0x0a, 0x12, 0x50, 0x72, + 0x6f, 0x64, 0x75, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, + 0x12, 0x14, 0x0a, 0x05, 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x05, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x17, 0x0a, 0x04, 0x54, 0x65, 0x72, 0x6d, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x04, 0x54, 0x65, 0x72, 0x6d, 0x88, 0x01, 0x01, 0x12, + 0x19, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, + 0x52, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x05, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x54, 0x65, 0x72, 0x6d, 0x42, 0x08, + 0x0a, 0x06, 0x5f, 0x55, 0x73, 0x61, 0x67, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x2a, 0x2b, 0x0a, 0x0b, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x63, 0x6f, 0x70, + 0x65, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x75, 0x6d, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x45, 0x61, 0x63, 0x68, 0x10, 0x02, 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_discount_model_proto_rawDescOnce sync.Once + file_discount_discount_model_proto_rawDescData = file_discount_discount_model_proto_rawDesc +) + +func file_discount_discount_model_proto_rawDescGZIP() []byte { + file_discount_discount_model_proto_rawDescOnce.Do(func() { + file_discount_discount_model_proto_rawDescData = protoimpl.X.CompressGZIP(file_discount_discount_model_proto_rawDescData) + }) + return file_discount_discount_model_proto_rawDescData +} + +var file_discount_discount_model_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_discount_discount_model_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_discount_discount_model_proto_goTypes = []interface{}{ + (TargetScope)(0), // 0: discount.TargetScope + (*DiscountOptional)(nil), // 1: discount.DiscountOptional + (*Discounts)(nil), // 2: discount.Discounts + (*Discount)(nil), // 3: discount.Discount + (*DiscountCalculationTarget)(nil), // 4: discount.DiscountCalculationTarget + (*DiscountCondition)(nil), // 5: discount.DiscountCondition + (*ProductTarget)(nil), // 6: discount.ProductTarget + (*PeriodCondition)(nil), // 7: discount.PeriodCondition + (*UserInformation)(nil), // 8: discount.UserInformation + (*ProductInformation)(nil), // 9: discount.ProductInformation + (*Audit)(nil), // 10: discount.Audit + (*timestamppb.Timestamp)(nil), // 11: google.protobuf.Timestamp +} +var file_discount_discount_model_proto_depIdxs = []int32{ + 5, // 0: discount.DiscountOptional.Condition:type_name -> discount.DiscountCondition + 4, // 1: discount.DiscountOptional.Target:type_name -> discount.DiscountCalculationTarget + 3, // 2: discount.Discounts.Discounts:type_name -> discount.Discount + 5, // 3: discount.Discount.Condition:type_name -> discount.DiscountCondition + 4, // 4: discount.Discount.Target:type_name -> discount.DiscountCalculationTarget + 10, // 5: discount.Discount.Audit:type_name -> discount.Audit + 6, // 6: discount.DiscountCalculationTarget.Products:type_name -> discount.ProductTarget + 0, // 7: discount.DiscountCalculationTarget.TargetScope:type_name -> discount.TargetScope + 7, // 8: discount.DiscountCondition.Period:type_name -> discount.PeriodCondition + 11, // 9: discount.PeriodCondition.From:type_name -> google.protobuf.Timestamp + 11, // 10: discount.PeriodCondition.To:type_name -> google.protobuf.Timestamp + 11, // [11:11] is the sub-list for method output_type + 11, // [11:11] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name +} + +func init() { file_discount_discount_model_proto_init() } +func file_discount_discount_model_proto_init() { + if File_discount_discount_model_proto != nil { + return + } + file_discount_audit_model_proto_init() + if !protoimpl.UnsafeEnabled { + file_discount_discount_model_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DiscountOptional); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_discount_discount_model_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Discounts); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_discount_discount_model_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Discount); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_discount_discount_model_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DiscountCalculationTarget); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_discount_discount_model_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DiscountCondition); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_discount_discount_model_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProductTarget); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_discount_discount_model_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PeriodCondition); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_discount_discount_model_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserInformation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_discount_discount_model_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProductInformation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_discount_discount_model_proto_msgTypes[0].OneofWrappers = []interface{}{} + file_discount_discount_model_proto_msgTypes[3].OneofWrappers = []interface{}{} + file_discount_discount_model_proto_msgTypes[4].OneofWrappers = []interface{}{} + file_discount_discount_model_proto_msgTypes[5].OneofWrappers = []interface{}{} + file_discount_discount_model_proto_msgTypes[8].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_discount_discount_model_proto_rawDesc, + NumEnums: 1, + NumMessages: 9, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_discount_discount_model_proto_goTypes, + DependencyIndexes: file_discount_discount_model_proto_depIdxs, + EnumInfos: file_discount_discount_model_proto_enumTypes, + MessageInfos: file_discount_discount_model_proto_msgTypes, + }.Build() + File_discount_discount_model_proto = out.File + file_discount_discount_model_proto_rawDesc = nil + file_discount_discount_model_proto_goTypes = nil + file_discount_discount_model_proto_depIdxs = nil +} diff --git a/internal/proto/discount/service.pb.go b/internal/proto/discount/service.pb.go new file mode 100644 index 0000000..5152942 --- /dev/null +++ b/internal/proto/discount/service.pb.go @@ -0,0 +1,524 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.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 float64 `protobuf:"fixed64,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() float64 { + 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, 0x01, 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, 0x55, 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, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, + 0x12, 0x0d, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x61, 0x6c, 0x6c, 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, 0x58, 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, 0x11, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x0b, 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 +} diff --git a/internal/proto/discount/service_grpc.pb.go b/internal/proto/discount/service_grpc.pb.go new file mode 100644 index 0000000..0341573 --- /dev/null +++ b/internal/proto/discount/service_grpc.pb.go @@ -0,0 +1,388 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +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 + +// 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, "/discount.DiscountService/GetAllDiscounts", 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, "/discount.DiscountService/GetUserDiscounts", 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, "/discount.DiscountService/DetermineDiscounts", 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, "/discount.DiscountService/ApplyDiscounts", 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, "/discount.DiscountService/GetDiscountByID", 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, "/discount.DiscountService/CreateDiscount", 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, "/discount.DiscountService/ReplaceDiscount", 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, "/discount.DiscountService/UpdateDiscount", 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, "/discount.DiscountService/DeleteDiscount", 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: "/discount.DiscountService/GetAllDiscounts", + } + 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: "/discount.DiscountService/GetUserDiscounts", + } + 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: "/discount.DiscountService/DetermineDiscounts", + } + 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: "/discount.DiscountService/ApplyDiscounts", + } + 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: "/discount.DiscountService/GetDiscountByID", + } + 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: "/discount.DiscountService/CreateDiscount", + } + 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: "/discount.DiscountService/ReplaceDiscount", + } + 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: "/discount.DiscountService/UpdateDiscount", + } + 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: "/discount.DiscountService/DeleteDiscount", + } + 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", +} diff --git a/internal/proto/google.golang.org/genproto/googleapis/api/annotations/annotations.pb.go b/internal/proto/google.golang.org/genproto/googleapis/api/annotations/annotations.pb.go new file mode 100644 index 0000000..1946360 --- /dev/null +++ b/internal/proto/google.golang.org/genproto/googleapis/api/annotations/annotations.pb.go @@ -0,0 +1,118 @@ +// Copyright (c) 2015, Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc (unknown) +// source: google/api/annotations.proto + +package annotations + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + descriptorpb "google.golang.org/protobuf/types/descriptorpb" + reflect "reflect" +) + +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) +) + +var file_google_api_annotations_proto_extTypes = []protoimpl.ExtensionInfo{ + { + ExtendedType: (*descriptorpb.MethodOptions)(nil), + ExtensionType: (*HttpRule)(nil), + Field: 72295728, + Name: "google.api.http", + Tag: "bytes,72295728,opt,name=http", + Filename: "google/api/annotations.proto", + }, +} + +// Extension fields to descriptorpb.MethodOptions. +var ( + // See `HttpRule`. + // + // optional google.api.HttpRule http = 72295728; + E_Http = &file_google_api_annotations_proto_extTypes[0] +) + +var File_google_api_annotations_proto protoreflect.FileDescriptor + +var file_google_api_annotations_proto_rawDesc = []byte{ + 0x0a, 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, 0x12, 0x0a, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x1a, 0x15, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x3a, 0x4b, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x12, 0x1e, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xb0, 0xca, 0xbc, 0x22, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, + 0x42, 0x6e, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, + 0x70, 0x69, 0x42, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, + 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3b, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xa2, 0x02, 0x04, 0x47, 0x41, 0x50, 0x49, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var file_google_api_annotations_proto_goTypes = []interface{}{ + (*descriptorpb.MethodOptions)(nil), // 0: google.protobuf.MethodOptions + (*HttpRule)(nil), // 1: google.api.HttpRule +} +var file_google_api_annotations_proto_depIdxs = []int32{ + 0, // 0: google.api.http:extendee -> google.protobuf.MethodOptions + 1, // 1: google.api.http:type_name -> google.api.HttpRule + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 1, // [1:2] is the sub-list for extension type_name + 0, // [0:1] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_google_api_annotations_proto_init() } +func file_google_api_annotations_proto_init() { + if File_google_api_annotations_proto != nil { + return + } + file_google_api_http_proto_init() + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_api_annotations_proto_rawDesc, + NumEnums: 0, + NumMessages: 0, + NumExtensions: 1, + NumServices: 0, + }, + GoTypes: file_google_api_annotations_proto_goTypes, + DependencyIndexes: file_google_api_annotations_proto_depIdxs, + ExtensionInfos: file_google_api_annotations_proto_extTypes, + }.Build() + File_google_api_annotations_proto = out.File + file_google_api_annotations_proto_rawDesc = nil + file_google_api_annotations_proto_goTypes = nil + file_google_api_annotations_proto_depIdxs = nil +} diff --git a/internal/proto/google.golang.org/genproto/googleapis/api/annotations/http.pb.go b/internal/proto/google.golang.org/genproto/googleapis/api/annotations/http.pb.go new file mode 100644 index 0000000..e1c9a0c --- /dev/null +++ b/internal/proto/google.golang.org/genproto/googleapis/api/annotations/http.pb.go @@ -0,0 +1,777 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc (unknown) +// source: google/api/http.proto + +package annotations + +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) +) + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +type Http struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + Rules []*HttpRule `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"` + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + FullyDecodeReservedExpansion bool `protobuf:"varint,2,opt,name=fully_decode_reserved_expansion,json=fullyDecodeReservedExpansion,proto3" json:"fully_decode_reserved_expansion,omitempty"` +} + +func (x *Http) Reset() { + *x = Http{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_http_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Http) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Http) ProtoMessage() {} + +func (x *Http) ProtoReflect() protoreflect.Message { + mi := &file_google_api_http_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 Http.ProtoReflect.Descriptor instead. +func (*Http) Descriptor() ([]byte, []int) { + return file_google_api_http_proto_rawDescGZIP(), []int{0} +} + +func (x *Http) GetRules() []*HttpRule { + if x != nil { + return x.Rules + } + return nil +} + +func (x *Http) GetFullyDecodeReservedExpansion() bool { + if x != nil { + return x.FullyDecodeReservedExpansion + } + return false +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +type HttpRule struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. + Selector string `protobuf:"bytes,1,opt,name=selector,proto3" json:"selector,omitempty"` + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + // + // Types that are assignable to Pattern: + // + // *HttpRule_Get + // *HttpRule_Put + // *HttpRule_Post + // *HttpRule_Delete + // *HttpRule_Patch + // *HttpRule_Custom + Pattern isHttpRule_Pattern `protobuf_oneof:"pattern"` + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + Body string `protobuf:"bytes,7,opt,name=body,proto3" json:"body,omitempty"` + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + ResponseBody string `protobuf:"bytes,12,opt,name=response_body,json=responseBody,proto3" json:"response_body,omitempty"` + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + AdditionalBindings []*HttpRule `protobuf:"bytes,11,rep,name=additional_bindings,json=additionalBindings,proto3" json:"additional_bindings,omitempty"` +} + +func (x *HttpRule) Reset() { + *x = HttpRule{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_http_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HttpRule) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HttpRule) ProtoMessage() {} + +func (x *HttpRule) ProtoReflect() protoreflect.Message { + mi := &file_google_api_http_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 HttpRule.ProtoReflect.Descriptor instead. +func (*HttpRule) Descriptor() ([]byte, []int) { + return file_google_api_http_proto_rawDescGZIP(), []int{1} +} + +func (x *HttpRule) GetSelector() string { + if x != nil { + return x.Selector + } + return "" +} + +func (m *HttpRule) GetPattern() isHttpRule_Pattern { + if m != nil { + return m.Pattern + } + return nil +} + +func (x *HttpRule) GetGet() string { + if x, ok := x.GetPattern().(*HttpRule_Get); ok { + return x.Get + } + return "" +} + +func (x *HttpRule) GetPut() string { + if x, ok := x.GetPattern().(*HttpRule_Put); ok { + return x.Put + } + return "" +} + +func (x *HttpRule) GetPost() string { + if x, ok := x.GetPattern().(*HttpRule_Post); ok { + return x.Post + } + return "" +} + +func (x *HttpRule) GetDelete() string { + if x, ok := x.GetPattern().(*HttpRule_Delete); ok { + return x.Delete + } + return "" +} + +func (x *HttpRule) GetPatch() string { + if x, ok := x.GetPattern().(*HttpRule_Patch); ok { + return x.Patch + } + return "" +} + +func (x *HttpRule) GetCustom() *CustomHttpPattern { + if x, ok := x.GetPattern().(*HttpRule_Custom); ok { + return x.Custom + } + return nil +} + +func (x *HttpRule) GetBody() string { + if x != nil { + return x.Body + } + return "" +} + +func (x *HttpRule) GetResponseBody() string { + if x != nil { + return x.ResponseBody + } + return "" +} + +func (x *HttpRule) GetAdditionalBindings() []*HttpRule { + if x != nil { + return x.AdditionalBindings + } + return nil +} + +type isHttpRule_Pattern interface { + isHttpRule_Pattern() +} + +type HttpRule_Get struct { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + Get string `protobuf:"bytes,2,opt,name=get,proto3,oneof"` +} + +type HttpRule_Put struct { + // Maps to HTTP PUT. Used for replacing a resource. + Put string `protobuf:"bytes,3,opt,name=put,proto3,oneof"` +} + +type HttpRule_Post struct { + // Maps to HTTP POST. Used for creating a resource or performing an action. + Post string `protobuf:"bytes,4,opt,name=post,proto3,oneof"` +} + +type HttpRule_Delete struct { + // Maps to HTTP DELETE. Used for deleting a resource. + Delete string `protobuf:"bytes,5,opt,name=delete,proto3,oneof"` +} + +type HttpRule_Patch struct { + // Maps to HTTP PATCH. Used for updating a resource. + Patch string `protobuf:"bytes,6,opt,name=patch,proto3,oneof"` +} + +type HttpRule_Custom struct { + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + Custom *CustomHttpPattern `protobuf:"bytes,8,opt,name=custom,proto3,oneof"` +} + +func (*HttpRule_Get) isHttpRule_Pattern() {} + +func (*HttpRule_Put) isHttpRule_Pattern() {} + +func (*HttpRule_Post) isHttpRule_Pattern() {} + +func (*HttpRule_Delete) isHttpRule_Pattern() {} + +func (*HttpRule_Patch) isHttpRule_Pattern() {} + +func (*HttpRule_Custom) isHttpRule_Pattern() {} + +// A custom pattern is used for defining custom HTTP verb. +type CustomHttpPattern struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The name of this custom HTTP verb. + Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` + // The path matched by this custom verb. + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` +} + +func (x *CustomHttpPattern) Reset() { + *x = CustomHttpPattern{} + if protoimpl.UnsafeEnabled { + mi := &file_google_api_http_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CustomHttpPattern) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CustomHttpPattern) ProtoMessage() {} + +func (x *CustomHttpPattern) ProtoReflect() protoreflect.Message { + mi := &file_google_api_http_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 CustomHttpPattern.ProtoReflect.Descriptor instead. +func (*CustomHttpPattern) Descriptor() ([]byte, []int) { + return file_google_api_http_proto_rawDescGZIP(), []int{2} +} + +func (x *CustomHttpPattern) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *CustomHttpPattern) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +var File_google_api_http_proto protoreflect.FileDescriptor + +var file_google_api_http_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x68, 0x74, 0x74, + 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x61, 0x70, 0x69, 0x22, 0x79, 0x0a, 0x04, 0x48, 0x74, 0x74, 0x70, 0x12, 0x2a, 0x0a, 0x05, 0x72, + 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x52, 0x75, 0x6c, 0x65, + 0x52, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x1f, 0x66, 0x75, 0x6c, 0x6c, 0x79, + 0x5f, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, + 0x5f, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x1c, 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x64, 0x45, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xda, + 0x02, 0x0a, 0x08, 0x48, 0x74, 0x74, 0x70, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x03, 0x67, 0x65, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x67, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x03, 0x70, + 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x70, 0x75, 0x74, 0x12, + 0x14, 0x0a, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x04, 0x70, 0x6f, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, + 0x16, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, + 0x52, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x12, 0x37, 0x0a, 0x06, 0x63, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x74, 0x74, 0x70, 0x50, + 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x48, 0x00, 0x52, 0x06, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x62, 0x6f, 0x64, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x45, 0x0a, 0x13, 0x61, 0x64, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, + 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x12, 0x61, 0x64, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, + 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x22, 0x3b, 0x0a, 0x11, 0x43, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x74, 0x74, 0x70, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, + 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x42, 0x6a, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x42, 0x09, 0x48, 0x74, 0x74, 0x70, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3b, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x04, + 0x47, 0x41, 0x50, 0x49, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_google_api_http_proto_rawDescOnce sync.Once + file_google_api_http_proto_rawDescData = file_google_api_http_proto_rawDesc +) + +func file_google_api_http_proto_rawDescGZIP() []byte { + file_google_api_http_proto_rawDescOnce.Do(func() { + file_google_api_http_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_api_http_proto_rawDescData) + }) + return file_google_api_http_proto_rawDescData +} + +var file_google_api_http_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_google_api_http_proto_goTypes = []interface{}{ + (*Http)(nil), // 0: google.api.Http + (*HttpRule)(nil), // 1: google.api.HttpRule + (*CustomHttpPattern)(nil), // 2: google.api.CustomHttpPattern +} +var file_google_api_http_proto_depIdxs = []int32{ + 1, // 0: google.api.Http.rules:type_name -> google.api.HttpRule + 2, // 1: google.api.HttpRule.custom:type_name -> google.api.CustomHttpPattern + 1, // 2: google.api.HttpRule.additional_bindings:type_name -> google.api.HttpRule + 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_google_api_http_proto_init() } +func file_google_api_http_proto_init() { + if File_google_api_http_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_google_api_http_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Http); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_http_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HttpRule); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_api_http_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CustomHttpPattern); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_google_api_http_proto_msgTypes[1].OneofWrappers = []interface{}{ + (*HttpRule_Get)(nil), + (*HttpRule_Put)(nil), + (*HttpRule_Post)(nil), + (*HttpRule_Delete)(nil), + (*HttpRule_Patch)(nil), + (*HttpRule_Custom)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_google_api_http_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_google_api_http_proto_goTypes, + DependencyIndexes: file_google_api_http_proto_depIdxs, + MessageInfos: file_google_api_http_proto_msgTypes, + }.Build() + File_google_api_http_proto = out.File + file_google_api_http_proto_rawDesc = nil + file_google_api_http_proto_goTypes = nil + file_google_api_http_proto_depIdxs = nil +} diff --git a/internal/proto/payment_callback/service.pb.go b/internal/proto/payment_callback/service.pb.go new file mode 100644 index 0000000..f477716 --- /dev/null +++ b/internal/proto/payment_callback/service.pb.go @@ -0,0 +1,325 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc (unknown) +// source: callback/service.proto + +package payment_callback + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + _ "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 Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=Key,proto3" json:"Key,omitempty"` + Message string `protobuf:"bytes,2,opt,name=Message,proto3" json:"Message,omitempty"` + Payment *Payment `protobuf:"bytes,3,opt,name=Payment,proto3" json:"Payment,omitempty"` +} + +func (x *Event) Reset() { + *x = Event{} + if protoimpl.UnsafeEnabled { + mi := &file_callback_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_callback_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 Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_callback_service_proto_rawDescGZIP(), []int{0} +} + +func (x *Event) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *Event) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Event) GetPayment() *Payment { + if x != nil { + return x.Payment + } + return nil +} + +type Payment struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + UserID string `protobuf:"bytes,2,opt,name=UserID,proto3" json:"UserID,omitempty"` + PaymentID string `protobuf:"bytes,3,opt,name=PaymentID,proto3" json:"PaymentID,omitempty"` + IdempotencePaymentID string `protobuf:"bytes,4,opt,name=IdempotencePaymentID,proto3" json:"IdempotencePaymentID,omitempty"` + Amount int64 `protobuf:"varint,5,opt,name=Amount,proto3" json:"Amount,omitempty"` + Currency string `protobuf:"bytes,6,opt,name=Currency,proto3" json:"Currency,omitempty"` + Type string `protobuf:"bytes,7,opt,name=Type,proto3" json:"Type,omitempty"` + Status string `protobuf:"bytes,8,opt,name=Status,proto3" json:"Status,omitempty"` + Completed bool `protobuf:"varint,9,opt,name=Completed,proto3" json:"Completed,omitempty"` +} + +func (x *Payment) Reset() { + *x = Payment{} + if protoimpl.UnsafeEnabled { + mi := &file_callback_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Payment) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payment) ProtoMessage() {} + +func (x *Payment) ProtoReflect() protoreflect.Message { + mi := &file_callback_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 Payment.ProtoReflect.Descriptor instead. +func (*Payment) Descriptor() ([]byte, []int) { + return file_callback_service_proto_rawDescGZIP(), []int{1} +} + +func (x *Payment) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *Payment) GetUserID() string { + if x != nil { + return x.UserID + } + return "" +} + +func (x *Payment) GetPaymentID() string { + if x != nil { + return x.PaymentID + } + return "" +} + +func (x *Payment) GetIdempotencePaymentID() string { + if x != nil { + return x.IdempotencePaymentID + } + return "" +} + +func (x *Payment) GetAmount() int64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *Payment) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *Payment) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Payment) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *Payment) GetCompleted() bool { + if x != nil { + return x.Completed + } + return false +} + +var File_callback_service_proto protoreflect.FileDescriptor + +var file_callback_service_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 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, 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, 0x22, 0x68, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x33, 0x0a, + 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x22, 0x81, 0x02, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x16, + 0x0a, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x49, 0x44, 0x12, 0x32, 0x0a, 0x14, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, + 0x6e, 0x63, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x14, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, + 0x54, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x43, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x32, 0x98, 0x01, 0x0a, 0x16, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x4f, 0x6e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x17, + 0x2e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, + 0x00, 0x12, 0x3e, 0x0a, 0x09, 0x4f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x17, + 0x2e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, + 0x00, 0x42, 0x14, 0x5a, 0x12, 0x2e, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_callback_service_proto_rawDescOnce sync.Once + file_callback_service_proto_rawDescData = file_callback_service_proto_rawDesc +) + +func file_callback_service_proto_rawDescGZIP() []byte { + file_callback_service_proto_rawDescOnce.Do(func() { + file_callback_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_callback_service_proto_rawDescData) + }) + return file_callback_service_proto_rawDescData +} + +var file_callback_service_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_callback_service_proto_goTypes = []interface{}{ + (*Event)(nil), // 0: payment_callback.Event + (*Payment)(nil), // 1: payment_callback.Payment + (*emptypb.Empty)(nil), // 2: google.protobuf.Empty +} +var file_callback_service_proto_depIdxs = []int32{ + 1, // 0: payment_callback.Event.Payment:type_name -> payment_callback.Payment + 0, // 1: payment_callback.PaymentCallbackService.OnSuccess:input_type -> payment_callback.Event + 0, // 2: payment_callback.PaymentCallbackService.OnFailure:input_type -> payment_callback.Event + 2, // 3: payment_callback.PaymentCallbackService.OnSuccess:output_type -> google.protobuf.Empty + 2, // 4: payment_callback.PaymentCallbackService.OnFailure:output_type -> google.protobuf.Empty + 3, // [3:5] is the sub-list for method output_type + 1, // [1:3] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_callback_service_proto_init() } +func file_callback_service_proto_init() { + if File_callback_service_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_callback_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_callback_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Payment); 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_callback_service_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_callback_service_proto_goTypes, + DependencyIndexes: file_callback_service_proto_depIdxs, + MessageInfos: file_callback_service_proto_msgTypes, + }.Build() + File_callback_service_proto = out.File + file_callback_service_proto_rawDesc = nil + file_callback_service_proto_goTypes = nil + file_callback_service_proto_depIdxs = nil +} diff --git a/internal/proto/payment_callback/service_grpc.pb.go b/internal/proto/payment_callback/service_grpc.pb.go new file mode 100644 index 0000000..e47ffb6 --- /dev/null +++ b/internal/proto/payment_callback/service_grpc.pb.go @@ -0,0 +1,136 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package payment_callback + +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 + +// PaymentCallbackServiceClient is the client API for PaymentCallbackService 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 PaymentCallbackServiceClient interface { + OnSuccess(ctx context.Context, in *Event, opts ...grpc.CallOption) (*emptypb.Empty, error) + OnFailure(ctx context.Context, in *Event, opts ...grpc.CallOption) (*emptypb.Empty, error) +} + +type paymentCallbackServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewPaymentCallbackServiceClient(cc grpc.ClientConnInterface) PaymentCallbackServiceClient { + return &paymentCallbackServiceClient{cc} +} + +func (c *paymentCallbackServiceClient) OnSuccess(ctx context.Context, in *Event, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, "/payment_callback.PaymentCallbackService/OnSuccess", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *paymentCallbackServiceClient) OnFailure(ctx context.Context, in *Event, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, "/payment_callback.PaymentCallbackService/OnFailure", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// PaymentCallbackServiceServer is the server API for PaymentCallbackService service. +// All implementations should embed UnimplementedPaymentCallbackServiceServer +// for forward compatibility +type PaymentCallbackServiceServer interface { + OnSuccess(context.Context, *Event) (*emptypb.Empty, error) + OnFailure(context.Context, *Event) (*emptypb.Empty, error) +} + +// UnimplementedPaymentCallbackServiceServer should be embedded to have forward compatible implementations. +type UnimplementedPaymentCallbackServiceServer struct { +} + +func (UnimplementedPaymentCallbackServiceServer) OnSuccess(context.Context, *Event) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method OnSuccess not implemented") +} +func (UnimplementedPaymentCallbackServiceServer) OnFailure(context.Context, *Event) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method OnFailure not implemented") +} + +// UnsafePaymentCallbackServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to PaymentCallbackServiceServer will +// result in compilation errors. +type UnsafePaymentCallbackServiceServer interface { + mustEmbedUnimplementedPaymentCallbackServiceServer() +} + +func RegisterPaymentCallbackServiceServer(s grpc.ServiceRegistrar, srv PaymentCallbackServiceServer) { + s.RegisterService(&PaymentCallbackService_ServiceDesc, srv) +} + +func _PaymentCallbackService_OnSuccess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Event) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PaymentCallbackServiceServer).OnSuccess(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/payment_callback.PaymentCallbackService/OnSuccess", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PaymentCallbackServiceServer).OnSuccess(ctx, req.(*Event)) + } + return interceptor(ctx, in, info, handler) +} + +func _PaymentCallbackService_OnFailure_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Event) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PaymentCallbackServiceServer).OnFailure(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/payment_callback.PaymentCallbackService/OnFailure", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PaymentCallbackServiceServer).OnFailure(ctx, req.(*Event)) + } + return interceptor(ctx, in, info, handler) +} + +// PaymentCallbackService_ServiceDesc is the grpc.ServiceDesc for PaymentCallbackService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var PaymentCallbackService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "payment_callback.PaymentCallbackService", + HandlerType: (*PaymentCallbackServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "OnSuccess", + Handler: _PaymentCallbackService_OnSuccess_Handler, + }, + { + MethodName: "OnFailure", + Handler: _PaymentCallbackService_OnFailure_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "callback/service.proto", +} diff --git a/internal/proto/treasurer/payment.model.pb.go b/internal/proto/treasurer/payment.model.pb.go new file mode 100644 index 0000000..04fce31 --- /dev/null +++ b/internal/proto/treasurer/payment.model.pb.go @@ -0,0 +1,299 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc (unknown) +// source: treasurer/payment.model.proto + +package treasurer + +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 MainPaymentSettings struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Currency string `protobuf:"bytes,1,opt,name=Currency,proto3" json:"Currency,omitempty"` + Amount int64 `protobuf:"varint,2,opt,name=Amount,proto3" json:"Amount,omitempty"` + CallbackHostGRPC []string `protobuf:"bytes,3,rep,name=CallbackHostGRPC,proto3" json:"CallbackHostGRPC,omitempty"` + ReturnURL string `protobuf:"bytes,4,opt,name=ReturnURL,proto3" json:"ReturnURL,omitempty"` + UserID string `protobuf:"bytes,5,opt,name=UserID,proto3" json:"UserID,omitempty"` + ClientIP string `protobuf:"bytes,6,opt,name=ClientIP,proto3" json:"ClientIP,omitempty"` +} + +func (x *MainPaymentSettings) Reset() { + *x = MainPaymentSettings{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_payment_model_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MainPaymentSettings) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MainPaymentSettings) ProtoMessage() {} + +func (x *MainPaymentSettings) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_payment_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 MainPaymentSettings.ProtoReflect.Descriptor instead. +func (*MainPaymentSettings) Descriptor() ([]byte, []int) { + return file_treasurer_payment_model_proto_rawDescGZIP(), []int{0} +} + +func (x *MainPaymentSettings) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *MainPaymentSettings) GetAmount() int64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *MainPaymentSettings) GetCallbackHostGRPC() []string { + if x != nil { + return x.CallbackHostGRPC + } + return nil +} + +func (x *MainPaymentSettings) GetReturnURL() string { + if x != nil { + return x.ReturnURL + } + return "" +} + +func (x *MainPaymentSettings) GetUserID() string { + if x != nil { + return x.UserID + } + return "" +} + +func (x *MainPaymentSettings) GetClientIP() string { + if x != nil { + return x.ClientIP + } + return "" +} + +type BankCardInformation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Number string `protobuf:"bytes,1,opt,name=Number,proto3" json:"Number,omitempty"` + ExpiryYear string `protobuf:"bytes,2,opt,name=ExpiryYear,proto3" json:"ExpiryYear,omitempty"` + ExpiryMonth string `protobuf:"bytes,3,opt,name=ExpiryMonth,proto3" json:"ExpiryMonth,omitempty"` + CSC *string `protobuf:"bytes,4,opt,name=CSC,proto3,oneof" json:"CSC,omitempty"` + CardHolderName *string `protobuf:"bytes,5,opt,name=CardHolderName,proto3,oneof" json:"CardHolderName,omitempty"` +} + +func (x *BankCardInformation) Reset() { + *x = BankCardInformation{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_payment_model_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BankCardInformation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BankCardInformation) ProtoMessage() {} + +func (x *BankCardInformation) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_payment_model_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 BankCardInformation.ProtoReflect.Descriptor instead. +func (*BankCardInformation) Descriptor() ([]byte, []int) { + return file_treasurer_payment_model_proto_rawDescGZIP(), []int{1} +} + +func (x *BankCardInformation) GetNumber() string { + if x != nil { + return x.Number + } + return "" +} + +func (x *BankCardInformation) GetExpiryYear() string { + if x != nil { + return x.ExpiryYear + } + return "" +} + +func (x *BankCardInformation) GetExpiryMonth() string { + if x != nil { + return x.ExpiryMonth + } + return "" +} + +func (x *BankCardInformation) GetCSC() string { + if x != nil && x.CSC != nil { + return *x.CSC + } + return "" +} + +func (x *BankCardInformation) GetCardHolderName() string { + if x != nil && x.CardHolderName != nil { + return *x.CardHolderName + } + return "" +} + +var File_treasurer_payment_model_proto protoreflect.FileDescriptor + +var file_treasurer_payment_model_proto_rawDesc = []byte{ + 0x0a, 0x1d, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2f, 0x70, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x09, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x22, 0xc7, 0x01, 0x0a, 0x13, 0x4d, + 0x61, 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x16, + 0x0a, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, + 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, + 0x63, 0x6b, 0x48, 0x6f, 0x73, 0x74, 0x47, 0x52, 0x50, 0x43, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x10, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x48, 0x6f, 0x73, 0x74, 0x47, 0x52, + 0x50, 0x43, 0x12, 0x1c, 0x0a, 0x09, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x55, 0x52, 0x4c, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x55, 0x52, 0x4c, + 0x12, 0x16, 0x0a, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x49, 0x50, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x49, 0x50, 0x22, 0xce, 0x01, 0x0a, 0x13, 0x42, 0x61, 0x6e, 0x6b, 0x43, 0x61, 0x72, + 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, + 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x4e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x59, 0x65, + 0x61, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, + 0x59, 0x65, 0x61, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x4d, 0x6f, + 0x6e, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x45, 0x78, 0x70, 0x69, 0x72, + 0x79, 0x4d, 0x6f, 0x6e, 0x74, 0x68, 0x12, 0x15, 0x0a, 0x03, 0x43, 0x53, 0x43, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x43, 0x53, 0x43, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, + 0x0e, 0x43, 0x61, 0x72, 0x64, 0x48, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0e, 0x43, 0x61, 0x72, 0x64, 0x48, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x43, + 0x53, 0x43, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x43, 0x61, 0x72, 0x64, 0x48, 0x6f, 0x6c, 0x64, 0x65, + 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_treasurer_payment_model_proto_rawDescOnce sync.Once + file_treasurer_payment_model_proto_rawDescData = file_treasurer_payment_model_proto_rawDesc +) + +func file_treasurer_payment_model_proto_rawDescGZIP() []byte { + file_treasurer_payment_model_proto_rawDescOnce.Do(func() { + file_treasurer_payment_model_proto_rawDescData = protoimpl.X.CompressGZIP(file_treasurer_payment_model_proto_rawDescData) + }) + return file_treasurer_payment_model_proto_rawDescData +} + +var file_treasurer_payment_model_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_treasurer_payment_model_proto_goTypes = []interface{}{ + (*MainPaymentSettings)(nil), // 0: treasurer.MainPaymentSettings + (*BankCardInformation)(nil), // 1: treasurer.BankCardInformation +} +var file_treasurer_payment_model_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_treasurer_payment_model_proto_init() } +func file_treasurer_payment_model_proto_init() { + if File_treasurer_payment_model_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_treasurer_payment_model_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MainPaymentSettings); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_treasurer_payment_model_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BankCardInformation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_treasurer_payment_model_proto_msgTypes[1].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_treasurer_payment_model_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_treasurer_payment_model_proto_goTypes, + DependencyIndexes: file_treasurer_payment_model_proto_depIdxs, + MessageInfos: file_treasurer_payment_model_proto_msgTypes, + }.Build() + File_treasurer_payment_model_proto = out.File + file_treasurer_payment_model_proto_rawDesc = nil + file_treasurer_payment_model_proto_goTypes = nil + file_treasurer_payment_model_proto_depIdxs = nil +} diff --git a/internal/proto/treasurer/service.pb.go b/internal/proto/treasurer/service.pb.go new file mode 100644 index 0000000..395df13 --- /dev/null +++ b/internal/proto/treasurer/service.pb.go @@ -0,0 +1,633 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc (unknown) +// source: treasurer/service.proto + +package treasurer + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + 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 GetBankCardPaymentLinkRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MainSettings *MainPaymentSettings `protobuf:"bytes,1,opt,name=MainSettings,proto3" json:"MainSettings,omitempty"` + BankCard *BankCardInformation `protobuf:"bytes,2,opt,name=BankCard,proto3" json:"BankCard,omitempty"` +} + +func (x *GetBankCardPaymentLinkRequest) Reset() { + *x = GetBankCardPaymentLinkRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBankCardPaymentLinkRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBankCardPaymentLinkRequest) ProtoMessage() {} + +func (x *GetBankCardPaymentLinkRequest) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_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 GetBankCardPaymentLinkRequest.ProtoReflect.Descriptor instead. +func (*GetBankCardPaymentLinkRequest) Descriptor() ([]byte, []int) { + return file_treasurer_service_proto_rawDescGZIP(), []int{0} +} + +func (x *GetBankCardPaymentLinkRequest) GetMainSettings() *MainPaymentSettings { + if x != nil { + return x.MainSettings + } + return nil +} + +func (x *GetBankCardPaymentLinkRequest) GetBankCard() *BankCardInformation { + if x != nil { + return x.BankCard + } + return nil +} + +type GetPaymentLinkBody struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MainSettings *MainPaymentSettings `protobuf:"bytes,1,opt,name=MainSettings,proto3" json:"MainSettings,omitempty"` +} + +func (x *GetPaymentLinkBody) Reset() { + *x = GetPaymentLinkBody{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetPaymentLinkBody) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPaymentLinkBody) ProtoMessage() {} + +func (x *GetPaymentLinkBody) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_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 GetPaymentLinkBody.ProtoReflect.Descriptor instead. +func (*GetPaymentLinkBody) Descriptor() ([]byte, []int) { + return file_treasurer_service_proto_rawDescGZIP(), []int{1} +} + +func (x *GetPaymentLinkBody) GetMainSettings() *MainPaymentSettings { + if x != nil { + return x.MainSettings + } + return nil +} + +type GetPhonePaymentLinkRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MainSettings *MainPaymentSettings `protobuf:"bytes,1,opt,name=MainSettings,proto3" json:"MainSettings,omitempty"` + Phone string `protobuf:"bytes,2,opt,name=Phone,proto3" json:"Phone,omitempty"` +} + +func (x *GetPhonePaymentLinkRequest) Reset() { + *x = GetPhonePaymentLinkRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_service_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetPhonePaymentLinkRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPhonePaymentLinkRequest) ProtoMessage() {} + +func (x *GetPhonePaymentLinkRequest) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_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 GetPhonePaymentLinkRequest.ProtoReflect.Descriptor instead. +func (*GetPhonePaymentLinkRequest) Descriptor() ([]byte, []int) { + return file_treasurer_service_proto_rawDescGZIP(), []int{2} +} + +func (x *GetPhonePaymentLinkRequest) GetMainSettings() *MainPaymentSettings { + if x != nil { + return x.MainSettings + } + return nil +} + +func (x *GetPhonePaymentLinkRequest) GetPhone() string { + if x != nil { + return x.Phone + } + return "" +} + +type GetLoginPaymentLinkRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MainSettings *MainPaymentSettings `protobuf:"bytes,1,opt,name=MainSettings,proto3" json:"MainSettings,omitempty"` + Login string `protobuf:"bytes,2,opt,name=Login,proto3" json:"Login,omitempty"` +} + +func (x *GetLoginPaymentLinkRequest) Reset() { + *x = GetLoginPaymentLinkRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_service_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLoginPaymentLinkRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLoginPaymentLinkRequest) ProtoMessage() {} + +func (x *GetLoginPaymentLinkRequest) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_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 GetLoginPaymentLinkRequest.ProtoReflect.Descriptor instead. +func (*GetLoginPaymentLinkRequest) Descriptor() ([]byte, []int) { + return file_treasurer_service_proto_rawDescGZIP(), []int{3} +} + +func (x *GetLoginPaymentLinkRequest) GetMainSettings() *MainPaymentSettings { + if x != nil { + return x.MainSettings + } + return nil +} + +func (x *GetLoginPaymentLinkRequest) GetLogin() string { + if x != nil { + return x.Login + } + return "" +} + +type GetB2BPaymentLinkRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MainSettings *MainPaymentSettings `protobuf:"bytes,1,opt,name=MainSettings,proto3" json:"MainSettings,omitempty"` + PaymentPurpose string `protobuf:"bytes,2,opt,name=PaymentPurpose,proto3" json:"PaymentPurpose,omitempty"` + VatData *emptypb.Empty `protobuf:"bytes,3,opt,name=VatData,proto3" json:"VatData,omitempty"` +} + +func (x *GetB2BPaymentLinkRequest) Reset() { + *x = GetB2BPaymentLinkRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_service_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetB2BPaymentLinkRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetB2BPaymentLinkRequest) ProtoMessage() {} + +func (x *GetB2BPaymentLinkRequest) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_service_proto_msgTypes[4] + 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 GetB2BPaymentLinkRequest.ProtoReflect.Descriptor instead. +func (*GetB2BPaymentLinkRequest) Descriptor() ([]byte, []int) { + return file_treasurer_service_proto_rawDescGZIP(), []int{4} +} + +func (x *GetB2BPaymentLinkRequest) GetMainSettings() *MainPaymentSettings { + if x != nil { + return x.MainSettings + } + return nil +} + +func (x *GetB2BPaymentLinkRequest) GetPaymentPurpose() string { + if x != nil { + return x.PaymentPurpose + } + return "" +} + +func (x *GetB2BPaymentLinkRequest) GetVatData() *emptypb.Empty { + if x != nil { + return x.VatData + } + return nil +} + +type GetPaymentLinkResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RedirectURL string `protobuf:"bytes,1,opt,name=RedirectURL,proto3" json:"RedirectURL,omitempty"` +} + +func (x *GetPaymentLinkResponse) Reset() { + *x = GetPaymentLinkResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_service_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetPaymentLinkResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPaymentLinkResponse) ProtoMessage() {} + +func (x *GetPaymentLinkResponse) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_service_proto_msgTypes[5] + 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 GetPaymentLinkResponse.ProtoReflect.Descriptor instead. +func (*GetPaymentLinkResponse) Descriptor() ([]byte, []int) { + return file_treasurer_service_proto_rawDescGZIP(), []int{5} +} + +func (x *GetPaymentLinkResponse) GetRedirectURL() string { + if x != nil { + return x.RedirectURL + } + return "" +} + +var File_treasurer_service_proto protoreflect.FileDescriptor + +var file_treasurer_service_proto_rawDesc = []byte{ + 0x0a, 0x17, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x72, 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, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2f, 0x70, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x9f, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6e, 0x6b, 0x43, 0x61, 0x72, 0x64, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0c, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x72, 0x2e, 0x4d, 0x61, 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0c, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x0a, 0x08, 0x42, 0x61, 0x6e, 0x6b, 0x43, 0x61, + 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x72, 0x2e, 0x42, 0x61, 0x6e, 0x6b, 0x43, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x42, 0x61, 0x6e, 0x6b, 0x43, 0x61, + 0x72, 0x64, 0x22, 0x58, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x4c, 0x69, 0x6e, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x42, 0x0a, 0x0c, 0x4d, 0x61, 0x69, 0x6e, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, + 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x4d, 0x61, 0x69, 0x6e, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0c, + 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x76, 0x0a, 0x1a, + 0x47, 0x65, 0x74, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, + 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0c, 0x4d, 0x61, + 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x4d, 0x61, 0x69, + 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x52, 0x0c, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x50, + 0x68, 0x6f, 0x6e, 0x65, 0x22, 0x76, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x69, 0x6e, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0c, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x72, 0x2e, 0x4d, 0x61, 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0c, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x22, 0xb8, 0x01, 0x0a, + 0x18, 0x47, 0x65, 0x74, 0x42, 0x32, 0x42, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, + 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0c, 0x4d, 0x61, 0x69, + 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x4d, 0x61, 0x69, 0x6e, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, + 0x0c, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x26, 0x0a, + 0x0e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x75, + 0x72, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x07, 0x56, 0x61, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x07, + 0x56, 0x61, 0x74, 0x44, 0x61, 0x74, 0x61, 0x22, 0x3a, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x55, 0x52, 0x4c, 0x32, 0xcc, 0x08, 0x0a, 0x10, 0x54, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x67, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x42, 0x61, 0x6e, 0x6b, 0x43, 0x61, + 0x72, 0x64, 0x12, 0x28, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, + 0x65, 0x74, 0x42, 0x61, 0x6e, 0x6b, 0x43, 0x61, 0x72, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, + 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x5c, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, + 0x69, 0x6e, 0x6b, 0x59, 0x6f, 0x6f, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x12, 0x1d, 0x2e, 0x74, 0x72, + 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x1a, 0x21, 0x2e, 0x74, 0x72, 0x65, + 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x60, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, + 0x6b, 0x51, 0x49, 0x57, 0x49, 0x12, 0x25, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, + 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x63, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, + 0x69, 0x6e, 0x6b, 0x53, 0x62, 0x65, 0x72, 0x50, 0x61, 0x79, 0x12, 0x25, 0x2e, 0x74, 0x72, 0x65, + 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, + 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x41, 0x6c, 0x66, 0x61, 0x43, 0x6c, 0x69, 0x63, + 0x6b, 0x12, 0x25, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, + 0x74, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, + 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, + 0x15, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x54, + 0x69, 0x6e, 0x6b, 0x6f, 0x66, 0x66, 0x12, 0x1d, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, + 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, + 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x1a, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x19, 0x47, 0x65, + 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x62, 0x65, 0x72, + 0x62, 0x61, 0x6e, 0x6b, 0x42, 0x32, 0x42, 0x12, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, + 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x32, 0x42, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, + 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x57, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, + 0x69, 0x6e, 0x6b, 0x53, 0x42, 0x50, 0x12, 0x1d, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, + 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, + 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x1a, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x14, 0x47, 0x65, + 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x4d, 0x6f, 0x62, 0x69, + 0x6c, 0x65, 0x12, 0x25, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, + 0x65, 0x74, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, + 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, + 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x60, + 0x0a, 0x12, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, + 0x43, 0x61, 0x73, 0x68, 0x12, 0x25, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, + 0x2e, 0x47, 0x65, 0x74, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x72, + 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x60, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, + 0x6e, 0x6b, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1d, + 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x1a, 0x21, 0x2e, + 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_treasurer_service_proto_rawDescOnce sync.Once + file_treasurer_service_proto_rawDescData = file_treasurer_service_proto_rawDesc +) + +func file_treasurer_service_proto_rawDescGZIP() []byte { + file_treasurer_service_proto_rawDescOnce.Do(func() { + file_treasurer_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_treasurer_service_proto_rawDescData) + }) + return file_treasurer_service_proto_rawDescData +} + +var file_treasurer_service_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_treasurer_service_proto_goTypes = []interface{}{ + (*GetBankCardPaymentLinkRequest)(nil), // 0: treasurer.GetBankCardPaymentLinkRequest + (*GetPaymentLinkBody)(nil), // 1: treasurer.GetPaymentLinkBody + (*GetPhonePaymentLinkRequest)(nil), // 2: treasurer.GetPhonePaymentLinkRequest + (*GetLoginPaymentLinkRequest)(nil), // 3: treasurer.GetLoginPaymentLinkRequest + (*GetB2BPaymentLinkRequest)(nil), // 4: treasurer.GetB2BPaymentLinkRequest + (*GetPaymentLinkResponse)(nil), // 5: treasurer.GetPaymentLinkResponse + (*MainPaymentSettings)(nil), // 6: treasurer.MainPaymentSettings + (*BankCardInformation)(nil), // 7: treasurer.BankCardInformation + (*emptypb.Empty)(nil), // 8: google.protobuf.Empty +} +var file_treasurer_service_proto_depIdxs = []int32{ + 6, // 0: treasurer.GetBankCardPaymentLinkRequest.MainSettings:type_name -> treasurer.MainPaymentSettings + 7, // 1: treasurer.GetBankCardPaymentLinkRequest.BankCard:type_name -> treasurer.BankCardInformation + 6, // 2: treasurer.GetPaymentLinkBody.MainSettings:type_name -> treasurer.MainPaymentSettings + 6, // 3: treasurer.GetPhonePaymentLinkRequest.MainSettings:type_name -> treasurer.MainPaymentSettings + 6, // 4: treasurer.GetLoginPaymentLinkRequest.MainSettings:type_name -> treasurer.MainPaymentSettings + 6, // 5: treasurer.GetB2BPaymentLinkRequest.MainSettings:type_name -> treasurer.MainPaymentSettings + 8, // 6: treasurer.GetB2BPaymentLinkRequest.VatData:type_name -> google.protobuf.Empty + 0, // 7: treasurer.TreasurerService.GetPaymentLinkBankCard:input_type -> treasurer.GetBankCardPaymentLinkRequest + 1, // 8: treasurer.TreasurerService.GetPaymentLinkYooMoney:input_type -> treasurer.GetPaymentLinkBody + 2, // 9: treasurer.TreasurerService.GetPaymentLinkQIWI:input_type -> treasurer.GetPhonePaymentLinkRequest + 2, // 10: treasurer.TreasurerService.GetPaymentLinkSberPay:input_type -> treasurer.GetPhonePaymentLinkRequest + 3, // 11: treasurer.TreasurerService.GetPaymentLinkAlfaClick:input_type -> treasurer.GetLoginPaymentLinkRequest + 1, // 12: treasurer.TreasurerService.GetPaymentLinkTinkoff:input_type -> treasurer.GetPaymentLinkBody + 4, // 13: treasurer.TreasurerService.GetPaymentLinkSberbankB2B:input_type -> treasurer.GetB2BPaymentLinkRequest + 1, // 14: treasurer.TreasurerService.GetPaymentLinkSBP:input_type -> treasurer.GetPaymentLinkBody + 2, // 15: treasurer.TreasurerService.GetPaymentLinkMobile:input_type -> treasurer.GetPhonePaymentLinkRequest + 2, // 16: treasurer.TreasurerService.GetPaymentLinkCash:input_type -> treasurer.GetPhonePaymentLinkRequest + 1, // 17: treasurer.TreasurerService.GetPaymentLinkInstallments:input_type -> treasurer.GetPaymentLinkBody + 5, // 18: treasurer.TreasurerService.GetPaymentLinkBankCard:output_type -> treasurer.GetPaymentLinkResponse + 5, // 19: treasurer.TreasurerService.GetPaymentLinkYooMoney:output_type -> treasurer.GetPaymentLinkResponse + 5, // 20: treasurer.TreasurerService.GetPaymentLinkQIWI:output_type -> treasurer.GetPaymentLinkResponse + 5, // 21: treasurer.TreasurerService.GetPaymentLinkSberPay:output_type -> treasurer.GetPaymentLinkResponse + 5, // 22: treasurer.TreasurerService.GetPaymentLinkAlfaClick:output_type -> treasurer.GetPaymentLinkResponse + 5, // 23: treasurer.TreasurerService.GetPaymentLinkTinkoff:output_type -> treasurer.GetPaymentLinkResponse + 5, // 24: treasurer.TreasurerService.GetPaymentLinkSberbankB2B:output_type -> treasurer.GetPaymentLinkResponse + 5, // 25: treasurer.TreasurerService.GetPaymentLinkSBP:output_type -> treasurer.GetPaymentLinkResponse + 5, // 26: treasurer.TreasurerService.GetPaymentLinkMobile:output_type -> treasurer.GetPaymentLinkResponse + 5, // 27: treasurer.TreasurerService.GetPaymentLinkCash:output_type -> treasurer.GetPaymentLinkResponse + 5, // 28: treasurer.TreasurerService.GetPaymentLinkInstallments:output_type -> treasurer.GetPaymentLinkResponse + 18, // [18:29] is the sub-list for method output_type + 7, // [7:18] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_treasurer_service_proto_init() } +func file_treasurer_service_proto_init() { + if File_treasurer_service_proto != nil { + return + } + file_treasurer_payment_model_proto_init() + if !protoimpl.UnsafeEnabled { + file_treasurer_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBankCardPaymentLinkRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_treasurer_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetPaymentLinkBody); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_treasurer_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetPhonePaymentLinkRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_treasurer_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLoginPaymentLinkRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_treasurer_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetB2BPaymentLinkRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_treasurer_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetPaymentLinkResponse); 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_treasurer_service_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_treasurer_service_proto_goTypes, + DependencyIndexes: file_treasurer_service_proto_depIdxs, + MessageInfos: file_treasurer_service_proto_msgTypes, + }.Build() + File_treasurer_service_proto = out.File + file_treasurer_service_proto_rawDesc = nil + file_treasurer_service_proto_goTypes = nil + file_treasurer_service_proto_depIdxs = nil +} diff --git a/internal/proto/treasurer/service_grpc.pb.go b/internal/proto/treasurer/service_grpc.pb.go new file mode 100644 index 0000000..d10d4c8 --- /dev/null +++ b/internal/proto/treasurer/service_grpc.pb.go @@ -0,0 +1,459 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package treasurer + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// TreasurerServiceClient is the client API for TreasurerService 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 TreasurerServiceClient interface { + GetPaymentLinkBankCard(ctx context.Context, in *GetBankCardPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkYooMoney(ctx context.Context, in *GetPaymentLinkBody, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkQIWI(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkSberPay(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkAlfaClick(ctx context.Context, in *GetLoginPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkTinkoff(ctx context.Context, in *GetPaymentLinkBody, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkSberbankB2B(ctx context.Context, in *GetB2BPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkSBP(ctx context.Context, in *GetPaymentLinkBody, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkMobile(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkCash(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkInstallments(ctx context.Context, in *GetPaymentLinkBody, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) +} + +type treasurerServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewTreasurerServiceClient(cc grpc.ClientConnInterface) TreasurerServiceClient { + return &treasurerServiceClient{cc} +} + +func (c *treasurerServiceClient) GetPaymentLinkBankCard(ctx context.Context, in *GetBankCardPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkBankCard", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkYooMoney(ctx context.Context, in *GetPaymentLinkBody, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkYooMoney", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkQIWI(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkQIWI", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkSberPay(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkSberPay", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkAlfaClick(ctx context.Context, in *GetLoginPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkAlfaClick", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkTinkoff(ctx context.Context, in *GetPaymentLinkBody, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkTinkoff", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkSberbankB2B(ctx context.Context, in *GetB2BPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkSberbankB2B", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkSBP(ctx context.Context, in *GetPaymentLinkBody, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkSBP", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkMobile(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkMobile", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkCash(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkCash", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkInstallments(ctx context.Context, in *GetPaymentLinkBody, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkInstallments", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// TreasurerServiceServer is the server API for TreasurerService service. +// All implementations should embed UnimplementedTreasurerServiceServer +// for forward compatibility +type TreasurerServiceServer interface { + GetPaymentLinkBankCard(context.Context, *GetBankCardPaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkYooMoney(context.Context, *GetPaymentLinkBody) (*GetPaymentLinkResponse, error) + GetPaymentLinkQIWI(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkSberPay(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkAlfaClick(context.Context, *GetLoginPaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkTinkoff(context.Context, *GetPaymentLinkBody) (*GetPaymentLinkResponse, error) + GetPaymentLinkSberbankB2B(context.Context, *GetB2BPaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkSBP(context.Context, *GetPaymentLinkBody) (*GetPaymentLinkResponse, error) + GetPaymentLinkMobile(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkCash(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkInstallments(context.Context, *GetPaymentLinkBody) (*GetPaymentLinkResponse, error) +} + +// UnimplementedTreasurerServiceServer should be embedded to have forward compatible implementations. +type UnimplementedTreasurerServiceServer struct { +} + +func (UnimplementedTreasurerServiceServer) GetPaymentLinkBankCard(context.Context, *GetBankCardPaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkBankCard not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkYooMoney(context.Context, *GetPaymentLinkBody) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkYooMoney not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkQIWI(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkQIWI not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkSberPay(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkSberPay not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkAlfaClick(context.Context, *GetLoginPaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkAlfaClick not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkTinkoff(context.Context, *GetPaymentLinkBody) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkTinkoff not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkSberbankB2B(context.Context, *GetB2BPaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkSberbankB2B not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkSBP(context.Context, *GetPaymentLinkBody) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkSBP not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkMobile(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkMobile not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkCash(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkCash not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkInstallments(context.Context, *GetPaymentLinkBody) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkInstallments not implemented") +} + +// UnsafeTreasurerServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to TreasurerServiceServer will +// result in compilation errors. +type UnsafeTreasurerServiceServer interface { + mustEmbedUnimplementedTreasurerServiceServer() +} + +func RegisterTreasurerServiceServer(s grpc.ServiceRegistrar, srv TreasurerServiceServer) { + s.RegisterService(&TreasurerService_ServiceDesc, srv) +} + +func _TreasurerService_GetPaymentLinkBankCard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetBankCardPaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkBankCard(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkBankCard", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkBankCard(ctx, req.(*GetBankCardPaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkYooMoney_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPaymentLinkBody) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkYooMoney(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkYooMoney", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkYooMoney(ctx, req.(*GetPaymentLinkBody)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkQIWI_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPhonePaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkQIWI(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkQIWI", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkQIWI(ctx, req.(*GetPhonePaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkSberPay_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPhonePaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkSberPay(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkSberPay", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkSberPay(ctx, req.(*GetPhonePaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkAlfaClick_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetLoginPaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkAlfaClick(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkAlfaClick", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkAlfaClick(ctx, req.(*GetLoginPaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkTinkoff_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPaymentLinkBody) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkTinkoff(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkTinkoff", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkTinkoff(ctx, req.(*GetPaymentLinkBody)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkSberbankB2B_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetB2BPaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkSberbankB2B(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkSberbankB2B", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkSberbankB2B(ctx, req.(*GetB2BPaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkSBP_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPaymentLinkBody) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkSBP(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkSBP", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkSBP(ctx, req.(*GetPaymentLinkBody)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkMobile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPhonePaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkMobile(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkMobile", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkMobile(ctx, req.(*GetPhonePaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkCash_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPhonePaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkCash(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkCash", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkCash(ctx, req.(*GetPhonePaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkInstallments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPaymentLinkBody) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkInstallments(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkInstallments", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkInstallments(ctx, req.(*GetPaymentLinkBody)) + } + return interceptor(ctx, in, info, handler) +} + +// TreasurerService_ServiceDesc is the grpc.ServiceDesc for TreasurerService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var TreasurerService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "treasurer.TreasurerService", + HandlerType: (*TreasurerServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetPaymentLinkBankCard", + Handler: _TreasurerService_GetPaymentLinkBankCard_Handler, + }, + { + MethodName: "GetPaymentLinkYooMoney", + Handler: _TreasurerService_GetPaymentLinkYooMoney_Handler, + }, + { + MethodName: "GetPaymentLinkQIWI", + Handler: _TreasurerService_GetPaymentLinkQIWI_Handler, + }, + { + MethodName: "GetPaymentLinkSberPay", + Handler: _TreasurerService_GetPaymentLinkSberPay_Handler, + }, + { + MethodName: "GetPaymentLinkAlfaClick", + Handler: _TreasurerService_GetPaymentLinkAlfaClick_Handler, + }, + { + MethodName: "GetPaymentLinkTinkoff", + Handler: _TreasurerService_GetPaymentLinkTinkoff_Handler, + }, + { + MethodName: "GetPaymentLinkSberbankB2B", + Handler: _TreasurerService_GetPaymentLinkSberbankB2B_Handler, + }, + { + MethodName: "GetPaymentLinkSBP", + Handler: _TreasurerService_GetPaymentLinkSBP_Handler, + }, + { + MethodName: "GetPaymentLinkMobile", + Handler: _TreasurerService_GetPaymentLinkMobile_Handler, + }, + { + MethodName: "GetPaymentLinkCash", + Handler: _TreasurerService_GetPaymentLinkCash_Handler, + }, + { + MethodName: "GetPaymentLinkInstallments", + Handler: _TreasurerService_GetPaymentLinkInstallments_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "treasurer/service.proto", +} diff --git a/internal/server/grpc.go b/internal/server/grpc.go new file mode 100644 index 0000000..acf6f95 --- /dev/null +++ b/internal/server/grpc.go @@ -0,0 +1,82 @@ +package server + +import ( + "context" + "fmt" + "net" + "time" + + grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" + grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" + grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery" + "go.uber.org/zap" + "google.golang.org/grpc" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/customer" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/payment_callback" +) + +type DepsGRPC struct { + Logger *zap.Logger +} + +type GRPC struct { + logger *zap.Logger + grpc *grpc.Server +} + +func NewGRPC(deps DepsGRPC) (*GRPC, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("Logger is nil on ", errors.ErrInvalidArgs) + } + + grpcStreamInterceptor := grpc.StreamInterceptor(grpc_middleware.ChainStreamServer( + grpc_zap.StreamServerInterceptor(deps.Logger), + grpc_recovery.StreamServerInterceptor(), + )) + + grpcUnaryInterceptor := grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( + grpc_zap.UnaryServerInterceptor(deps.Logger), + grpc_recovery.UnaryServerInterceptor(), + )) + + return &GRPC{ + grpc: grpc.NewServer(grpcStreamInterceptor, grpcUnaryInterceptor, grpc.ConnectionTimeout(5*time.Second)), + logger: deps.Logger, + }, nil +} + +func (receiver *GRPC) Run(config *models.ConfigurationGRPC) { + connectionString := fmt.Sprintf("%s:%s", config.Host, config.Port) + + receiver.logger.Info("Starting GRPC Server", zap.String("host", connectionString)) + + if err := receiver.listen(connectionString); err != nil && err != grpc.ErrServerStopped { + receiver.logger.Error("GRPC Listen error", zap.Error(err)) + } +} + +func (receiver *GRPC) Stop(_ context.Context) error { + receiver.grpc.GracefulStop() + receiver.logger.Info("Shutting down GRPC server...") + + return nil +} + +func (receiver *GRPC) Register(controllers *initialize.Controllers) *GRPC { + payment_callback.RegisterPaymentCallbackServiceServer(receiver.grpc, controllers.PaymentController) + customer.RegisterCustomerServiceServer(receiver.grpc, controllers.CustomerController) + + return receiver +} + +func (receiver *GRPC) listen(address string) error { + listener, err := net.Listen("tcp", address) + if err != nil { + return err + } + + return receiver.grpc.Serve(listener) +} diff --git a/internal/server/http.go b/internal/server/http.go new file mode 100644 index 0000000..1ad7b5d --- /dev/null +++ b/internal/server/http.go @@ -0,0 +1,93 @@ +package server + +import ( + "context" + "fmt" + "net/http" + "time" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/getkin/kin-openapi/openapi3filter" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +type DepsHTTP struct { + Logger *zap.Logger + Swagger *openapi3.T + AuthenticationFunc openapi3filter.AuthenticationFunc +} + +type HTTP struct { + logger *zap.Logger + server *http.Server + echo *echo.Echo +} + +func NewHTTP(deps DepsHTTP) (*HTTP, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("failed to init http server: logger is nil ", errors.ErrInvalidArgs) + } + + if deps.Swagger == nil { + return nil, errors.NewWithMessage("failed to init http server: swagger is nil ", errors.ErrInvalidArgs) + } + + if deps.AuthenticationFunc == nil { + return nil, errors.NewWithMessage("failed to init http server: AuthenticationFunc is nil ", errors.ErrInvalidArgs) + } + + echo := echo.New() + + echo.Use(middleware.Recover()) + echo.Use(swagger.CreateMiddleware(deps.Swagger, deps.AuthenticationFunc)) + + return &HTTP{ + echo: echo, + logger: deps.Logger, + server: &http.Server{ + Handler: echo, + MaxHeaderBytes: 1 << 20, + ReadTimeout: 20 * time.Second, + WriteTimeout: 20 * time.Second, + IdleTimeout: 20 * time.Second, + }, + }, nil +} + +func (receiver *HTTP) Listen(address string) error { + receiver.server.Addr = address + + return receiver.server.ListenAndServe() +} + +func (receiver *HTTP) Run(config *models.ConfigurationHTTP) { + connectionString := fmt.Sprintf("%s:%s", config.Host, config.Port) + startServerMessage := fmt.Sprintf("starting http server on %s", connectionString) + + receiver.logger.Info(startServerMessage) + + if err := receiver.Listen(connectionString); err != nil && err != http.ErrServerClosed { + receiver.logger.Error("http listen error: ", zap.Error(err)) + } +} + +func (receiver *HTTP) Stop(ctx context.Context) error { + receiver.logger.Info("shutting down server...") + + if err := receiver.server.Shutdown(ctx); err != nil { + return fmt.Errorf("failed to shutdown server: %w", err) + } + + return nil +} + +func (receiver *HTTP) Register(api *swagger.API) *HTTP { + swagger.RegisterHandlers(receiver.echo, api) + + return receiver +} diff --git a/internal/service/account/account.go b/internal/service/account/account.go new file mode 100644 index 0000000..fce6250 --- /dev/null +++ b/internal/service/account/account.go @@ -0,0 +1,237 @@ +package account + +import ( + "context" + "fmt" + "log" + "math" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +type accountRepository interface { + FindByUserID(ctx context.Context, id string) (*models.Account, errors.Error) + FindMany(ctx context.Context, page, limit int64) ([]models.Account, errors.Error) + Insert(ctx context.Context, account *models.Account) (*models.Account, errors.Error) + Remove(ctx context.Context, id string) (*models.Account, errors.Error) + Delete(ctx context.Context, id string) (*models.Account, errors.Error) + CountAll(ctx context.Context) (int64, errors.Error) + SetStatus(ctx context.Context, userID string, status models.AccountStatus) (*models.Account, errors.Error) + UpdateName(ctx context.Context, userID string, name *models.Name) (*models.Account, errors.Error) +} + +type authClient interface { + GetUser(ctx context.Context, userID string) (*models.User, errors.Error) +} + +type Deps struct { + Logger *zap.Logger + Repository accountRepository + AuthClient authClient +} + +type Service struct { + logger *zap.Logger + repository accountRepository + authClient authClient +} + +func New(deps Deps) *Service { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.Repository == nil { + log.Panicln("repository is nil on ") + } + + if deps.AuthClient == nil { + log.Panicln("auth client is nil on ") + } + + return &Service{ + logger: deps.Logger, + repository: deps.Repository, + authClient: deps.AuthClient, + } +} + +func (receiver *Service) GetAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error) { + account, err := receiver.repository.FindByUserID(ctx, userID) + if err != nil { + receiver.logger.Error("failed to get account by id on of ", + zap.Error(err), + zap.String("userID", userID), + ) + + return nil, err + } + + return account, nil +} + +func (receiver *Service) GetAccountsList(ctx context.Context, pagination *models.Pagination) (*models.PaginationResponse[models.Account], errors.Error) { + if pagination == nil { + return nil, errors.New( + fmt.Errorf("pagination is nil on of : %w", errors.ErrInternalError), + errors.ErrInternalError, + ) + } + + count, err := receiver.repository.CountAll(ctx) + if err != nil { + receiver.logger.Error("failed to count accounts on of ", + zap.Error(err), + ) + + return nil, err + } + + if count == 0 { + return &models.PaginationResponse[models.Account]{TotalPages: 0, Records: []models.Account{}}, nil + } + + totalPages := int64(math.Ceil(float64(count) / float64(pagination.Limit))) + + accounts, err := receiver.repository.FindMany(ctx, pagination.Page, pagination.Limit) + if err != nil { + receiver.logger.Error("failed to get accounts list on of ", + zap.Error(err), + zap.Int64("page", pagination.Page), + zap.Int64("limit", pagination.Limit), + ) + + return nil, err + } + + return &models.PaginationResponse[models.Account]{ + TotalPages: totalPages, + Records: accounts, + }, nil +} + +func (receiver *Service) CreateAccount(ctx context.Context, account *models.Account) (*models.Account, errors.Error) { + findedAccount, err := receiver.GetAccountByUserID(ctx, account.UserID) + if err != nil && err.Type() != errors.ErrNotFound { + receiver.logger.Error("failed to find account on of ", + zap.Error(err), + ) + + return nil, err + } + + if findedAccount != nil { + return nil, errors.New( + fmt.Errorf("failed to create account with <%s> on of : %w", + account.UserID, + errors.ErrConflict, + ), + errors.ErrConflict, + ) + } + + createdAccount, err := receiver.repository.Insert(ctx, account) + if err != nil { + receiver.logger.Error("failed to create account on of ", + zap.Error(err), + zap.Any("account", account), + ) + + return nil, err + } + + return createdAccount, nil +} + +func (receiver *Service) CreateAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error) { + account, err := receiver.GetAccountByUserID(ctx, userID) + if err != nil && err.Type() != errors.ErrNotFound { + receiver.logger.Error("failed to find account on of ", + zap.Error(err), + ) + + return nil, err + } + + if account != nil { + return nil, errors.New( + fmt.Errorf("failed to create account with <%s> on of : %w", + userID, + errors.ErrConflict, + ), + errors.ErrConflict, + ) + } + + user, err := receiver.authClient.GetUser(ctx, userID) + if err != nil { + receiver.logger.Error("failed to get user on of ", + zap.Error(err), + zap.String("userID", userID), + ) + + return nil, err + } + + createdAccount, err := receiver.repository.Insert(ctx, &models.Account{UserID: user.ID}) + if err != nil { + receiver.logger.Error("failed to create account on of ", + zap.Error(err), + zap.String("userID", userID), + ) + + return nil, err + } + + return createdAccount, nil +} + +func (receiver *Service) RemoveAccount(ctx context.Context, userID string) (*models.Account, errors.Error) { + account, err := receiver.repository.Remove(ctx, userID) + if err != nil { + receiver.logger.Error("failed to remove account on of ", + zap.Error(err), + zap.String("userID", userID), + ) + + return nil, err + } + + return account, nil +} + +func (receiver *Service) DeleteAccount(ctx context.Context, userID string) (*models.Account, errors.Error) { + account, err := receiver.repository.Delete(ctx, userID) + if err != nil { + receiver.logger.Error("failed to delete account on of ", + zap.Error(err), + zap.String("userID", userID), + ) + + return nil, err + } + + return account, nil +} + +func (receiver *Service) SetVerificationStatus(ctx context.Context, userID string, status models.AccountStatus) (*models.Account, errors.Error) { + account, err := receiver.repository.SetStatus(ctx, userID, status) + if err != nil { + receiver.logger.Error("failed to set status on of ", zap.Error(err)) + return nil, err + } + + return account, nil +} + +func (receiver *Service) UpdateAccountName(ctx context.Context, userID string, name *models.Name) (*models.Account, errors.Error) { + account, err := receiver.repository.UpdateName(ctx, userID, name) + if err != nil { + receiver.logger.Error("failed to update account name on of ", zap.Error(err)) + return nil, err + } + + return account, nil +} diff --git a/internal/service/callback/payment.go b/internal/service/callback/payment.go new file mode 100644 index 0000000..25bf24a --- /dev/null +++ b/internal/service/callback/payment.go @@ -0,0 +1,107 @@ +package callback + +import ( + "context" + "log" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +type accountRepository interface { + FindByUserID(context.Context, string) (*models.Account, errors.Error) +} + +type historyService interface { + CreateHistory(ctx context.Context, history *models.History) (*models.History, errors.Error) +} + +type walletService interface { + ReplenishAccountWallet(context.Context, *models.ReplenishAccountWallet) (*models.Account, errors.Error) +} + +type PaymentCallbackServiceDeps struct { + Logger *zap.Logger + AccountRepository accountRepository + WalletService walletService + HistoryService historyService +} + +type PaymentCallbackService struct { + logger *zap.Logger + accountRepository accountRepository + walletService walletService + historyService historyService +} + +func NewPaymentCallbackService(deps PaymentCallbackServiceDeps) *PaymentCallbackService { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.AccountRepository == nil { + log.Panicln("AccountRepository is nil on ") + } + + if deps.WalletService == nil { + log.Panicln("WalletService is nil on ") + } + + if deps.HistoryService == nil { + log.Panicln("HistoryService is nil on ") + } + + return &PaymentCallbackService{ + logger: deps.Logger, + accountRepository: deps.AccountRepository, + walletService: deps.WalletService, + historyService: deps.HistoryService, + } +} + +func (receiver *PaymentCallbackService) SuccessEvent(ctx context.Context, event *models.PaymentEvent) errors.Error { + account, err := receiver.accountRepository.FindByUserID(ctx, event.UserID) + if err != nil { + receiver.logger.Error("failed to get account on of ", zap.Error(err)) + return err + } + + if account.Wallet.LastPaymentID == event.PaymentID { + receiver.logger.Info("payment already was executed on of ", zap.String("paymentID", event.PaymentID)) + return nil + } + + if _, err := receiver.walletService.ReplenishAccountWallet(ctx, &models.ReplenishAccountWallet{ + Cash: event.Amount, + Currency: event.Currency, + PaymentID: event.PaymentID, + Account: account, + }); err != nil { + receiver.logger.Error("failed to replenish wallet on of ", zap.Error(err)) + return err + } + + if _, err := receiver.historyService.CreateHistory(ctx, &models.History{ + UserID: account.UserID, + Comment: event.Message, + Key: event.Key, + }); err != nil { + receiver.logger.Error("failed to create history on of ", zap.Error(err)) + } + + return nil +} + +func (receiver *PaymentCallbackService) FailureEvent(ctx context.Context, event *models.PaymentEvent) errors.Error { + if _, err := receiver.historyService.CreateHistory(ctx, &models.History{ + UserID: event.UserID, + Comment: event.Message, + Key: event.Key, + }); err != nil { + receiver.logger.Error("failed to create history on of ", zap.Error(err)) + return err + } + + return nil +} diff --git a/internal/service/cart/cart.go b/internal/service/cart/cart.go new file mode 100644 index 0000000..4540844 --- /dev/null +++ b/internal/service/cart/cart.go @@ -0,0 +1,188 @@ +package cart + +import ( + "context" + "fmt" + "log" + "time" + + "go.uber.org/zap" + "google.golang.org/protobuf/types/known/timestamppb" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/discount" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils/transfer" +) + +type accountRepository interface { + FindByUserID(ctx context.Context, id string) (*models.Account, errors.Error) + AddItemToCart(ctx context.Context, userID, itemID string) (*models.Account, errors.Error) + RemoveItemFromCart(ctx context.Context, userID, itemID string) (*models.Account, errors.Error) + ClearCart(ctx context.Context, userID string) (*models.Account, errors.Error) +} + +type hubadminClient interface { + GetTariff(ctx context.Context, tariffID string) (*models.Tariff, errors.Error) + GetTariffs(ctx context.Context, tarriffIDs []string) ([]models.Tariff, errors.Error) +} + +type discountClient interface { + Apply(context.Context, *discount.ApplyDiscountRequest) (*discount.ApplyDiscountResponse, errors.Error) +} + +type walletService interface { + ReplenishAccountWallet(context.Context, *models.ReplenishAccountWallet) (*models.Account, errors.Error) + WithdrawAccountWalletMoney(context.Context, *models.WithdrawAccountWallet) (*models.Account, errors.Error) +} + +type historyService interface { + CreateHistory(ctx context.Context, history *models.History) (*models.History, errors.Error) +} + +type Deps struct { + Logger *zap.Logger + Repository accountRepository + HubadminClient hubadminClient + DiscountClient discountClient + WalletService walletService + HistoryService historyService +} + +type Service struct { + logger *zap.Logger + repository accountRepository + hubadminClient hubadminClient + discountClient discountClient + walletService walletService + historyService historyService +} + +func New(deps Deps) *Service { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.Repository == nil { + log.Panicln("repository is nil on ") + } + + if deps.HubadminClient == nil { + log.Panicln("HubadminClient is nil on ") + } + + if deps.DiscountClient == nil { + log.Panicln("DiscountClient is nil on ") + } + + if deps.WalletService == nil { + log.Panicln("WalletService is nil on ") + } + + if deps.HistoryService == nil { + log.Panicln("HistoryService is nil on ") + } + + return &Service{ + logger: deps.Logger, + repository: deps.Repository, + hubadminClient: deps.HubadminClient, + discountClient: deps.DiscountClient, + walletService: deps.WalletService, + historyService: deps.HistoryService, + } +} + +func (receiver *Service) Remove(ctx context.Context, userID, itemID string) ([]string, errors.Error) { + account, err := receiver.repository.RemoveItemFromCart(ctx, userID, itemID) + if err != nil { + receiver.logger.Error("failed to remove item from cart on of ", zap.Error(err)) + return []string{}, err + } + + return account.Cart, nil +} + +func (receiver *Service) Add(ctx context.Context, userID, itemID string) ([]string, errors.Error) { + tariff, err := receiver.hubadminClient.GetTariff(ctx, itemID) + if err != nil { + receiver.logger.Error("failed to get tariff on of ", zap.Error(err), zap.String("tariffID", itemID)) + return []string{}, err + } + + if tariff == nil { + return []string{}, errors.New( + fmt.Errorf("failed to get tariff <%s> on of : tariff not found", itemID), + errors.ErrNotFound, + ) + } + + account, err := receiver.repository.AddItemToCart(ctx, userID, itemID) + if err != nil { + receiver.logger.Error("failed to add item to cart on of ", zap.Error(err)) + return []string{}, err + } + + return account.Cart, nil +} + +func (receiver *Service) Pay(ctx context.Context, userID string) (string, errors.Error) { + account, err := receiver.repository.FindByUserID(ctx, userID) + if err != nil { + receiver.logger.Error("failed to find account on of ", zap.String("userID", userID), zap.Error(err)) + return "", err + } + + tariffs, err := receiver.hubadminClient.GetTariffs(ctx, account.Cart) + if err != nil { + receiver.logger.Error("failed to get tarrifs on of ", zap.Strings("cart", account.Cart), zap.Error(err)) + return "", err + } + + tariffsAmount := utils.CalculateCartPurchasesAmount(tariffs) + + // TODO: убрать все конвертеры чисел: float64, int64 + + response, err := receiver.discountClient.Apply(ctx, &discount.ApplyDiscountRequest{ + UserInformation: &discount.UserInformation{ + ID: account.UserID, + Type: string(account.Status), + PurchasesAmount: float64(account.Wallet.PurchasesAmount), + CartPurchasesAmount: float64(tariffsAmount), + }, + Products: transfer.TariffsToProductInformations(tariffs), + Date: timestamppb.New(time.Now()), + }) + if err != nil { + receiver.logger.Error("failed to discount on of ", zap.Error(err)) + return "", err + } + + if account.Wallet.Money < int64(response.Price) { + receiver.logger.Error("insufficient funds on of ") + return "", errors.New(fmt.Errorf("insufficient funds: %d", int64(response.Price)-account.Wallet.Money), errors.ErrInsufficientFunds) + } + + if _, err := receiver.walletService.WithdrawAccountWalletMoney(ctx, &models.WithdrawAccountWallet{ + Money: int64(response.Price), + Account: account, + }); err != nil { + receiver.logger.Error("failed to withdraw money on of ", zap.Error(err)) + return "", err + } + + if _, historyErr := receiver.historyService.CreateHistory(ctx, &models.History{ + Key: models.CustomerHistoryKeyPayCart, + UserID: account.UserID, + Comment: "Успешная оплата корзины", + }); historyErr != nil { + receiver.logger.Error("failed to insert history on of ", zap.Error(historyErr)) + } + + if _, err := receiver.repository.ClearCart(ctx, account.UserID); err != nil { + receiver.logger.Error("failed to clear cart on of ", zap.Error(err)) + return "", err + } + + return "", nil +} diff --git a/internal/service/currency/currency.go b/internal/service/currency/currency.go new file mode 100644 index 0000000..e645d19 --- /dev/null +++ b/internal/service/currency/currency.go @@ -0,0 +1,93 @@ +package currency + +import ( + "context" + "log" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +type currencyRepository interface { + FindCurrenciesList(ctx context.Context, name string) (*models.CurrencyList, errors.Error) + ReplaceCurrencies(ctx context.Context, list *models.CurrencyList) (*models.CurrencyList, errors.Error) + Insert(ctx context.Context, list *models.CurrencyList) (*models.CurrencyList, errors.Error) +} + +type Deps struct { + Logger *zap.Logger + Repository currencyRepository +} + +type Service struct { + logger *zap.Logger + repository currencyRepository +} + +func New(deps Deps) *Service { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.Repository == nil { + log.Panicln("repository is nil on ") + } + + return &Service{ + logger: deps.Logger, + repository: deps.Repository, + } +} + +func (receiver *Service) GetCurrencies(ctx context.Context) ([]string, errors.Error) { + currencyList, err := receiver.repository.FindCurrenciesList(ctx, models.DefaultCurrencyListName) + if err != nil && err.Type() != errors.ErrNotFound { + receiver.logger.Error( + "failed to get currencies on of ", + zap.Error(err), + ) + + return []string{}, err + } + + if err != nil && err.Type() == errors.ErrNotFound { + return []string{}, nil + } + + return currencyList.Currencies, nil +} + +func (receiver *Service) PutCurrencies(ctx context.Context, currencies []string) ([]string, errors.Error) { + currencyList, err := receiver.repository.ReplaceCurrencies(ctx, &models.CurrencyList{ + Name: models.DefaultCurrencyListName, + Currencies: currencies, + }) + if err != nil && err.Type() != errors.ErrNotFound { + receiver.logger.Error( + "failed to put currencies on of ", + zap.Error(err), + ) + + return []string{}, err + } + + if err != nil && err.Type() == errors.ErrNotFound { + newCurrencyList, err := receiver.repository.Insert(ctx, &models.CurrencyList{ + Name: models.DefaultCurrencyListName, + Currencies: currencies, + }) + if err != nil && err.Type() != errors.ErrNotFound { + receiver.logger.Error( + "failed to insert new currency list on of ", + zap.Error(err), + ) + + return []string{}, err + } + + return newCurrencyList.Currencies, nil + } + + return currencyList.Currencies, nil +} diff --git a/internal/service/history/history.go b/internal/service/history/history.go new file mode 100644 index 0000000..809cad2 --- /dev/null +++ b/internal/service/history/history.go @@ -0,0 +1,93 @@ +package history + +import ( + "context" + "fmt" + "log" + "math" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +type historyRepository interface { + CountAll(context.Context) (int64, errors.Error) + FindMany(ctx context.Context, page, limit int64) ([]models.History, errors.Error) + Insert(context.Context, *models.History) (*models.History, errors.Error) +} + +type Deps struct { + Logger *zap.Logger + Repository historyRepository +} + +type Service struct { + logger *zap.Logger + repository historyRepository +} + +func New(deps Deps) *Service { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.Repository == nil { + log.Panicln("repository is nil on ") + } + + return &Service{ + logger: deps.Logger, + repository: deps.Repository, + } +} + +func (receiver *Service) GetHistoryList(ctx context.Context, pagination *models.Pagination) (*models.PaginationResponse[models.History], errors.Error) { + if pagination == nil { + return nil, errors.New( + fmt.Errorf("pagination is nil on of : %w", errors.ErrInternalError), + errors.ErrInternalError, + ) + } + + count, err := receiver.repository.CountAll(ctx) + if err != nil { + receiver.logger.Error("failed to count histories on of ", + zap.Error(err), + ) + + return nil, err + } + + if count == 0 { + return &models.PaginationResponse[models.History]{TotalPages: 0, Records: []models.History{}}, nil + } + + totalPages := int64(math.Ceil(float64(count) / float64(pagination.Limit))) + + histories, err := receiver.repository.FindMany(ctx, pagination.Page, pagination.Limit) + if err != nil { + receiver.logger.Error("failed to get historiy list on of ", + zap.Error(err), + zap.Int64("page", pagination.Page), + zap.Int64("limit", pagination.Limit), + ) + + return nil, err + } + + return &models.PaginationResponse[models.History]{ + TotalPages: totalPages, + Records: histories, + }, nil +} + +func (receiver *Service) CreateHistory(ctx context.Context, history *models.History) (*models.History, errors.Error) { + createdHistory, err := receiver.repository.Insert(ctx, history) + if err != nil { + receiver.logger.Error("failed to create history on of ", zap.Error(err)) + return nil, err + } + + return createdHistory, nil +} diff --git a/internal/service/payment/payment.go b/internal/service/payment/payment.go new file mode 100644 index 0000000..27c6c30 --- /dev/null +++ b/internal/service/payment/payment.go @@ -0,0 +1,261 @@ +package payment + +import ( + "context" + "log" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/treasurer" +) + +type paymentClient interface { + GetPaymentLinkBankCard(ctx context.Context, request *treasurer.GetBankCardPaymentLinkRequest) (string, errors.Error) + GetPaymentLinkYooMoney(ctx context.Context, request *treasurer.GetPaymentLinkBody) (string, errors.Error) + GetPaymentLinkQIWI(ctx context.Context, request *treasurer.GetPhonePaymentLinkRequest) (string, errors.Error) + GetPaymentLinkSberPay(ctx context.Context, request *treasurer.GetPhonePaymentLinkRequest) (string, errors.Error) + GetPaymentLinkAlfaClick(ctx context.Context, request *treasurer.GetLoginPaymentLinkRequest) (string, errors.Error) + GetPaymentLinkTinkoff(ctx context.Context, request *treasurer.GetPaymentLinkBody) (string, errors.Error) + GetPaymentLinkSberbankB2B(ctx context.Context, request *treasurer.GetB2BPaymentLinkRequest) (string, errors.Error) + GetPaymentLinkMobile(ctx context.Context, request *treasurer.GetPhonePaymentLinkRequest) (string, errors.Error) + GetPaymentLinkCash(ctx context.Context, request *treasurer.GetPhonePaymentLinkRequest) (string, errors.Error) + GetPaymentLinkInstallments(ctx context.Context, request *treasurer.GetPaymentLinkBody) (string, errors.Error) +} + +type authClient interface { + GetUser(ctx context.Context, userID string) (*models.User, errors.Error) +} + +type Deps struct { + Logger *zap.Logger + ConfigurationGRPC *models.ConfigurationGRPC + PaymentClient paymentClient + AuthClient authClient +} + +type Service struct { + logger *zap.Logger + configurationGRPC *models.ConfigurationGRPC + paymentClient paymentClient + authClient authClient +} + +func New(deps Deps) *Service { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.ConfigurationGRPC == nil { + log.Panicln("ConfigurationGRPC is nil on ") + } + + if deps.PaymentClient == nil { + log.Panicln("PaymentClient is nil on ") + } + + if deps.AuthClient == nil { + log.Panicln("AuthClient is nil on ") + } + + return &Service{ + logger: deps.Logger, + paymentClient: deps.PaymentClient, + authClient: deps.AuthClient, + configurationGRPC: deps.ConfigurationGRPC, + } +} + +func (receiver *Service) GetPaymentLink(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { + if _, userErr := receiver.authClient.GetUser(ctx, request.UserID); userErr != nil { + receiver.logger.Error("failed to get user on on ", + zap.Error(userErr), + zap.String("userID", request.UserID), + ) + + return "", userErr + } + + switch request.Body.Type { + case models.PaymentTypeBankCard: + return receiver.GetPaymentLinkBankCard(ctx, request) + case models.PaymentTypeYoomoney: + return receiver.GetPaymentLinkYooMoney(ctx, request) + case models.PaymentTypeQiwi: + return receiver.GetPaymentLinkQIWI(ctx, request) + case models.PaymentTypeSberPay: + return receiver.GetPaymentLinkSberPay(ctx, request) + case models.PaymentTypeAlfabank: + return receiver.GetPaymentLinkAlfaClick(ctx, request) + case models.PaymentTypeTinkoff: + return receiver.GetPaymentLinkTinkoff(ctx, request) + case models.PaymentTypeMobile: + return receiver.GetPaymentLinkMobile(ctx, request) + case models.PaymentTypeCash: + return receiver.GetPaymentLinkCash(ctx, request) + } + + return "", errors.NewWithMessage("invalid payment method type", errors.ErrInvalidArgs) +} + +func (receiver *Service) GetPaymentLinkBankCard(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { + link, err := receiver.paymentClient.GetPaymentLinkBankCard(ctx, &treasurer.GetBankCardPaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: request.Body.Currency, + Amount: request.Body.Amount, + UserID: request.UserID, + ClientIP: request.ClientIP, + CallbackHostGRPC: []string{receiver.configurationGRPC.Domen}, + ReturnURL: request.Body.ReturnURL, + }, + BankCard: &treasurer.BankCardInformation{ + Number: request.Body.BankCard.Number, + ExpiryYear: request.Body.BankCard.ExpiryYear, + ExpiryMonth: request.Body.BankCard.ExpiryMonth, + }, + }) + if err != nil { + receiver.logger.Error("failed to get bankcard payment link on of ", zap.Error(err)) + return "", err + } + + return link, nil +} + +func (receiver *Service) GetPaymentLinkYooMoney(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { + link, err := receiver.paymentClient.GetPaymentLinkYooMoney(ctx, &treasurer.GetPaymentLinkBody{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: request.Body.Currency, + Amount: request.Body.Amount, + UserID: request.UserID, + ClientIP: request.ClientIP, + CallbackHostGRPC: []string{receiver.configurationGRPC.Domen}, + ReturnURL: request.Body.ReturnURL, + }, + }) + if err != nil { + receiver.logger.Error("failed to get yoomoney payment link on of ", zap.Error(err)) + return "", err + } + + return link, nil +} + +func (receiver *Service) GetPaymentLinkQIWI(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { + link, err := receiver.paymentClient.GetPaymentLinkQIWI(ctx, &treasurer.GetPhonePaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: request.Body.Currency, + Amount: request.Body.Amount, + UserID: request.UserID, + ClientIP: request.ClientIP, + CallbackHostGRPC: []string{receiver.configurationGRPC.Domen}, + ReturnURL: request.Body.ReturnURL, + }, + Phone: request.Body.PhoneNumber, + }) + if err != nil { + receiver.logger.Error("failed to get qiwi payment link on of ", zap.Error(err)) + return "", err + } + + return link, nil +} + +func (receiver *Service) GetPaymentLinkSberPay(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { + link, err := receiver.paymentClient.GetPaymentLinkSberPay(ctx, &treasurer.GetPhonePaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: request.Body.Currency, + Amount: request.Body.Amount, + UserID: request.UserID, + ClientIP: request.ClientIP, + CallbackHostGRPC: []string{receiver.configurationGRPC.Domen}, + ReturnURL: request.Body.ReturnURL, + }, + Phone: request.Body.PhoneNumber, + }) + if err != nil { + receiver.logger.Error("failed to get sberpay payment link on of ", zap.Error(err)) + return "", err + } + + return link, nil +} + +func (receiver *Service) GetPaymentLinkAlfaClick(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { + link, err := receiver.paymentClient.GetPaymentLinkAlfaClick(ctx, &treasurer.GetLoginPaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: request.Body.Currency, + Amount: request.Body.Amount, + UserID: request.UserID, + ClientIP: request.ClientIP, + CallbackHostGRPC: []string{receiver.configurationGRPC.Domen}, + ReturnURL: request.Body.ReturnURL, + }, + Login: request.Body.Login, + }) + if err != nil { + receiver.logger.Error("failed to get alfaclick payment link on of ", zap.Error(err)) + return "", err + } + + return link, nil +} + +func (receiver *Service) GetPaymentLinkTinkoff(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { + link, err := receiver.paymentClient.GetPaymentLinkTinkoff(ctx, &treasurer.GetPaymentLinkBody{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: request.Body.Currency, + Amount: request.Body.Amount, + UserID: request.UserID, + ClientIP: request.ClientIP, + CallbackHostGRPC: []string{receiver.configurationGRPC.Domen}, + ReturnURL: request.Body.ReturnURL, + }, + }) + if err != nil { + receiver.logger.Error("failed to get tinkoff payment link on of ", zap.Error(err)) + return "", err + } + + return link, nil +} + +func (receiver *Service) GetPaymentLinkMobile(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { + link, err := receiver.paymentClient.GetPaymentLinkMobile(ctx, &treasurer.GetPhonePaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: request.Body.Currency, + Amount: request.Body.Amount, + UserID: request.UserID, + ClientIP: request.ClientIP, + CallbackHostGRPC: []string{receiver.configurationGRPC.Domen}, + ReturnURL: request.Body.ReturnURL, + }, + Phone: request.Body.PhoneNumber, + }) + if err != nil { + receiver.logger.Error("failed to get mobile payment link on of ", zap.Error(err)) + return "", err + } + + return link, nil +} + +func (receiver *Service) GetPaymentLinkCash(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { + link, err := receiver.paymentClient.GetPaymentLinkCash(ctx, &treasurer.GetPhonePaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: request.Body.Currency, + Amount: request.Body.Amount, + UserID: request.UserID, + ClientIP: request.ClientIP, + CallbackHostGRPC: []string{receiver.configurationGRPC.Domen}, + ReturnURL: request.Body.ReturnURL, + }, + Phone: request.Body.PhoneNumber, + }) + if err != nil { + receiver.logger.Error("failed to get cash payment link on of ", zap.Error(err)) + return "", err + } + + return link, nil +} diff --git a/internal/service/wallet/wallet.go b/internal/service/wallet/wallet.go new file mode 100644 index 0000000..e0c2f83 --- /dev/null +++ b/internal/service/wallet/wallet.go @@ -0,0 +1,249 @@ +package wallet + +import ( + "context" + "fmt" + "log" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate" +) + +type accountRepository interface { + ChangeWallet(ctx context.Context, userID string, wallet *models.Wallet) (*models.Account, errors.Error) + FindByUserID(ctx context.Context, id string) (*models.Account, errors.Error) +} + +type currencyClient interface { + Translate(context.Context, *models.TranslateCurrency) (int64, errors.Error) +} + +type historyService interface { + CreateHistory(ctx context.Context, history *models.History) (*models.History, errors.Error) +} + +type Deps struct { + Logger *zap.Logger + Repository accountRepository + CurrencyClient currencyClient + HistoryService historyService +} + +type Service struct { + logger *zap.Logger + repository accountRepository + currencyClient currencyClient + historyService historyService +} + +func New(deps Deps) *Service { + if deps.Logger == nil { + log.Panicln("logger is nil on ") + } + + if deps.Repository == nil { + log.Panicln("repository is nil on ") + } + + if deps.CurrencyClient == nil { + log.Panicln("CurrencyClient is nil on ") + } + + if deps.HistoryService == nil { + log.Panicln("HistoryService is nil on ") + } + + return &Service{ + logger: deps.Logger, + repository: deps.Repository, + currencyClient: deps.CurrencyClient, + historyService: deps.HistoryService, + } +} + +func (receiver *Service) ReplenishAccountWallet(ctx context.Context, request *models.ReplenishAccountWallet) (*models.Account, errors.Error) { + if validate.IsStringEmpty(request.Account.Wallet.Currency) { + return nil, errors.New( + fmt.Errorf("currency of account <%s> is empty of ", request.Account.UserID), + errors.ErrInternalError, + ) + } + + cash := request.Cash + + if request.Currency != request.Account.Wallet.Currency { + translatedCash, translateErr := receiver.currencyClient.Translate(ctx, &models.TranslateCurrency{ + Money: request.Cash, + From: request.Currency, + To: request.Account.Wallet.Currency, + }) + if translateErr != nil { + receiver.logger.Error("failed to translate cash on of ", + zap.Error(translateErr), + ) + + return nil, translateErr + } + + cash = translatedCash + } + + if request.Currency == models.InternalCurrencyKey { + updatedAccount, changeErr := receiver.repository.ChangeWallet(ctx, request.Account.UserID, &models.Wallet{ + Cash: request.Account.Wallet.Cash + cash, + Money: request.Account.Wallet.Money + request.Cash, + PurchasesAmount: request.Account.Wallet.PurchasesAmount + request.Cash, + Currency: request.Account.Wallet.Currency, + LastPaymentID: request.PaymentID, + }) + if changeErr != nil { + receiver.logger.Error("failed to replenish wallet on of ", + zap.Error(changeErr), + zap.String("Currency", request.Account.Wallet.Currency), + zap.Int64("Money", request.Account.Wallet.Money+request.Cash), + zap.Int64("Cash", request.Account.Wallet.Cash+cash), + zap.Bool("Is currensy equal internal", request.Currency == models.InternalCurrencyKey), + ) + + return nil, changeErr + } + + if _, historyErr := receiver.historyService.CreateHistory(ctx, &models.History{ + Key: models.CustomerHistoryKeyReplenish, + UserID: request.Account.UserID, + Comment: "Успешное пополнение средств (Без конвертации валюты)", + }); historyErr != nil { + receiver.logger.Error("failed to insert history on of ", zap.Error(historyErr)) + } + + return updatedAccount, nil + } + + money, err := receiver.currencyClient.Translate(ctx, &models.TranslateCurrency{ + Money: request.Cash, + From: request.Currency, + To: models.InternalCurrencyKey, + }) + if err != nil { + receiver.logger.Error("failed to translate money on of ", + zap.Error(err), + ) + + return nil, err + } + + updatedAccount, err := receiver.repository.ChangeWallet(ctx, request.Account.UserID, &models.Wallet{ + Cash: request.Account.Wallet.Cash + cash, + Money: request.Account.Wallet.Money + money, + PurchasesAmount: request.Account.Wallet.PurchasesAmount + money, + Spent: request.Account.Wallet.Spent, + Currency: request.Account.Wallet.Currency, + LastPaymentID: request.PaymentID, + }) + if err != nil { + receiver.logger.Error("failed to replenish wallet on of ", + zap.Error(err), + zap.String("Currency", request.Account.Wallet.Currency), + zap.Int64("Money", request.Account.Wallet.Money+request.Cash), + zap.Int64("Cash", request.Account.Wallet.Cash+cash), + zap.Bool("Is currensy equal internal", request.Currency == models.InternalCurrencyKey), + ) + + return nil, err + } + + if _, historyErr := receiver.historyService.CreateHistory(ctx, &models.History{ + Key: models.CustomerHistoryKeyReplenish, + UserID: request.Account.UserID, + Comment: "Успешное пополнение средств (C конвертацией валюты)", + }); historyErr != nil { + receiver.logger.Error("failed to insert history on of ", zap.Error(historyErr)) + } + + return updatedAccount, nil +} + +func (receiver *Service) WithdrawAccountWalletMoney(ctx context.Context, request *models.WithdrawAccountWallet) (*models.Account, errors.Error) { + if validate.IsStringEmpty(request.Account.Wallet.Currency) { + return nil, errors.New( + fmt.Errorf("currency of account <%s> is empty of ", request.Account.UserID), + errors.ErrInternalError, + ) + } + + cash, err := receiver.currencyClient.Translate(ctx, &models.TranslateCurrency{ + Money: request.Money, + From: models.InternalCurrencyKey, + To: request.Account.Wallet.Currency, + }) + if err != nil { + receiver.logger.Error("failed to translate money on of ", + zap.Error(err), + ) + + return nil, err + } + + updatedAccount, err := receiver.repository.ChangeWallet(ctx, request.Account.UserID, &models.Wallet{ + Cash: request.Account.Wallet.Cash - cash, + Money: request.Account.Wallet.Money - request.Money, + Spent: request.Account.Wallet.Spent + request.Money, + PurchasesAmount: request.Account.Wallet.PurchasesAmount, + Currency: request.Account.Wallet.Currency, + }) + if err != nil { + receiver.logger.Error("failed to replenish wallet on of ", + zap.Error(err), + zap.String("Currency", request.Account.Wallet.Currency), + zap.Int64("Money", request.Account.Wallet.Money-request.Money), + zap.Int64("Cash", request.Account.Wallet.Cash+cash), + ) + + return nil, err + } + + return updatedAccount, nil +} + +func (receiver *Service) ChangeCurrency(ctx context.Context, userID string, currency models.CurrencyKey) (*models.Account, errors.Error) { + account, err := receiver.repository.FindByUserID(ctx, userID) + if err != nil { + receiver.logger.Error("failed to find account on of ", + zap.Error(err), + zap.String("userID", userID), + zap.Any("currency", currency), + ) + + return nil, err + } + + cash, err := receiver.currencyClient.Translate(ctx, &models.TranslateCurrency{ + Money: account.Wallet.Cash, + From: account.Wallet.Currency, + To: currency, + }) + if err != nil { + receiver.logger.Error("failed to translate currency on of ", + zap.Error(err), + ) + + return nil, err + } + + updatedAccount, err := receiver.repository.ChangeWallet(ctx, account.UserID, &models.Wallet{ + Cash: cash, + Currency: currency, + Money: account.Wallet.Money, + }) + if err != nil { + receiver.logger.Error("failed to update wallet on of ", + zap.Error(err), + ) + + return nil, err + } + + return updatedAccount, nil +} diff --git a/internal/utils/authenticator.go b/internal/utils/authenticator.go new file mode 100644 index 0000000..0680d61 --- /dev/null +++ b/internal/utils/authenticator.go @@ -0,0 +1,71 @@ +package utils + +import ( + "context" + "fmt" + "net/http" + "strings" + + "github.com/deepmap/oapi-codegen/pkg/middleware" + "github.com/getkin/kin-openapi/openapi3filter" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +const ( + prefix = "Bearer " +) + +func NewAuthenticator(jwtUtil *JWT) openapi3filter.AuthenticationFunc { + return func(ctx context.Context, input *openapi3filter.AuthenticationInput) error { + if jwtUtil == nil { + return errors.New( + fmt.Errorf("jwt util is nil: %w", errors.ErrInvalidArgs), + errors.ErrInvalidArgs, + ) + } + + return authenticate(ctx, jwtUtil, input) + } +} + +func authenticate(ctx context.Context, jwtUtil *JWT, input *openapi3filter.AuthenticationInput) error { + if input.SecuritySchemeName != "Bearer" { + return fmt.Errorf("security scheme %s != 'Bearer'", input.SecuritySchemeName) + } + + // Now, we need to get the JWS from the request, to match the request expectations + // against request contents. + jws, err := parseJWSFromRequest(input.RequestValidationInput.Request) + if err != nil { + return err + } + + // if the JWS is valid, we have a JWT, which will contain a bunch of claims. + token, validateErr := jwtUtil.Validate(jws) + if validateErr != nil { + return validateErr + } + + // Set the property on the echo context so the handler is able to + // access the claims data we generate in here. + echoCtx := middleware.GetEchoContext(ctx) + + echoCtx.Set(models.AuthJWTDecodedUserIDKey, token) + + return nil +} + +// extracts a JWS string from an Authorization: Bearer header. +func parseJWSFromRequest(request *http.Request) (string, errors.Error) { + header := request.Header.Get("Authorization") + + if header == "" || !strings.HasPrefix(header, prefix) { + return "", errors.New( + fmt.Errorf("failed to parse jws from request header: %s", header), + errors.ErrNoAccess, + ) + } + + return strings.TrimPrefix(header, prefix), nil +} diff --git a/internal/utils/client_response.go b/internal/utils/client_response.go new file mode 100644 index 0000000..fb318a5 --- /dev/null +++ b/internal/utils/client_response.go @@ -0,0 +1,24 @@ +package utils + +import ( + "net/http" + + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" +) + +var clientErrors = map[int]error{ + http.StatusInternalServerError: errors.ErrInternalError, + http.StatusBadRequest: errors.ErrInvalidArgs, + http.StatusNotImplemented: errors.ErrMethodNotImplemented, + http.StatusNotFound: errors.ErrNotFound, + http.StatusForbidden: errors.ErrNoAccess, +} + +func DetermineClientErrorResponse(statusCode int) error { + err, ok := clientErrors[statusCode] + if !ok { + return errors.ErrInternalError + } + + return err +} diff --git a/internal/utils/jwt.go b/internal/utils/jwt.go new file mode 100644 index 0000000..93829ee --- /dev/null +++ b/internal/utils/jwt.go @@ -0,0 +1,90 @@ +package utils + +import ( + "errors" + "fmt" + "time" + + "github.com/golang-jwt/jwt/v5" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" +) + +type JWT struct { + privateKey []byte + publicKey []byte + algorithm *jwt.SigningMethodRSA + expiresIn time.Duration + issuer string + audience string +} + +func NewJWT(configuration *models.JWTConfiguration) *JWT { + return &JWT{ + privateKey: []byte(configuration.PrivateKey), + publicKey: []byte(configuration.PublicKey), + algorithm: &configuration.Algorithm, + expiresIn: configuration.ExpiresIn, + issuer: configuration.Issuer, + audience: configuration.Audience, + } +} + +func (receiver *JWT) Create(id string) (string, error) { + privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(receiver.privateKey) + if err != nil { + return "", fmt.Errorf("failed to parse private key on of : %w", err) + } + + now := time.Now().UTC() + + claims := jwt.MapClaims{ + "id": id, // Our userID + "exp": now.Add(receiver.expiresIn).Unix(), // The expiration time after which the token must be disregarded. + "aud": receiver.audience, // Audience + "iss": receiver.issuer, // Issuer + } + + token, err := jwt.NewWithClaims(receiver.algorithm, claims).SignedString(privateKey) + if err != nil { + return "", fmt.Errorf("failed to sing on of : %w", err) + } + + return token, nil +} + +func (receiver *JWT) Validate(tokenString string) (string, error) { + key, err := jwt.ParseRSAPublicKeyFromPEM(receiver.publicKey) + if err != nil { + return "", fmt.Errorf("failed to parse rsa public key on of : %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(receiver.audience), + jwt.WithIssuer(receiver.issuer), + ) + if err != nil { + return "", fmt.Errorf("failed to parse jwt token on of : %w", err) + } + + claims, ok := token.Claims.(jwt.MapClaims) + if !ok || !token.Valid { + return "", errors.New("token is invalid on of ") + } + + data, ok := claims["id"].(string) + if !ok { + return "", errors.New("data is empty or not a string on of ") + } + + return data, nil +} diff --git a/internal/utils/jwt_test.go b/internal/utils/jwt_test.go new file mode 100644 index 0000000..e931d02 --- /dev/null +++ b/internal/utils/jwt_test.go @@ -0,0 +1,129 @@ +package utils_test + +import ( + "strings" + "testing" + "time" + + "github.com/golang-jwt/jwt/v5" + "github.com/stretchr/testify/assert" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" +) + +func TestJWT(t *testing.T) { + type user struct { + Name string `json:"name"` + Age int `json:"age"` + } + + testUser := user{ + Name: "test", + Age: 80, + } + + publicKey := `-----BEGIN PUBLIC KEY----- + MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyt4XuLovUY7i12K2PIMb + QZOKn+wFFKUvxvKQDel049/+VMpHMx1FLolUKuyGp9zi6gOwjHsBPgc9oqr/eaXG + QSh7Ult7i9f+Ht563Y0er5UU9Zc5ZPSxf9O75KYD48ruGkqiFoncDqPENK4dtUa7 + w0OqlN4bwVBbmIsP8B3EDC5Dof+vtiNTSHSXPx+zifKeZGyknp+nyOHVrRDhPjOh + zQzCom0MSZA/sJYmps8QZgiPA0k4Z6jTupDymPOIwYeD2C57zSxnAv0AfC3/pZYJ + bZYH/0TszRzmy052DME3zMnhMK0ikdN4nzYqU0dkkA5kb5GtKDymspHIJ9eWbUuw + gtg8Rq/LrVBj1I3UFgs0ibio40k6gqinLKslc5Y1I5mro7J3OSEP5eO/XeDLOLlO + JjEqkrx4fviI1cL3m5L6QV905xmcoNZG1+RmOg7D7cZQUf27TXqM381jkbNdktm1 + JLTcMScxuo3vaRftnIVw70V8P8sIkaKY8S8HU1sQgE2LB9t04oog5u59htx2FHv4 + B13NEm8tt8Tv1PexpB4UVh7PIualF6SxdFBrKbraYej72wgjXVPQ0eGXtGGD57j8 + DUEzk7DK2OvIWhehlVqtiRnFdAvdBj2ynHT2/5FJ/Zpd4n5dKGJcQvy1U1qWMs+8 + M7AHfWyt2+nZ04s48+bK3yMCAwEAAQ== + -----END PUBLIC KEY-----` + + privateKey := `-----BEGIN RSA PRIVATE KEY----- + MIIJKQIBAAKCAgEAyt4XuLovUY7i12K2PIMbQZOKn+wFFKUvxvKQDel049/+VMpH + Mx1FLolUKuyGp9zi6gOwjHsBPgc9oqr/eaXGQSh7Ult7i9f+Ht563Y0er5UU9Zc5 + ZPSxf9O75KYD48ruGkqiFoncDqPENK4dtUa7w0OqlN4bwVBbmIsP8B3EDC5Dof+v + tiNTSHSXPx+zifKeZGyknp+nyOHVrRDhPjOhzQzCom0MSZA/sJYmps8QZgiPA0k4 + Z6jTupDymPOIwYeD2C57zSxnAv0AfC3/pZYJbZYH/0TszRzmy052DME3zMnhMK0i + kdN4nzYqU0dkkA5kb5GtKDymspHIJ9eWbUuwgtg8Rq/LrVBj1I3UFgs0ibio40k6 + gqinLKslc5Y1I5mro7J3OSEP5eO/XeDLOLlOJjEqkrx4fviI1cL3m5L6QV905xmc + oNZG1+RmOg7D7cZQUf27TXqM381jkbNdktm1JLTcMScxuo3vaRftnIVw70V8P8sI + kaKY8S8HU1sQgE2LB9t04oog5u59htx2FHv4B13NEm8tt8Tv1PexpB4UVh7PIual + F6SxdFBrKbraYej72wgjXVPQ0eGXtGGD57j8DUEzk7DK2OvIWhehlVqtiRnFdAvd + Bj2ynHT2/5FJ/Zpd4n5dKGJcQvy1U1qWMs+8M7AHfWyt2+nZ04s48+bK3yMCAwEA + AQKCAgEAhodpK7MsFeWvQC3Rs6ctt/rjftHBPMOeP0wzg0ZBoau0uP264YaTjhy7 + mAtp8H9matEvjrkzRbL/iJPk/wKTyjnSLfdEoqQFfOsEh09B/iXa1FIIWY569s2u + WB5PjgvQgdbkThX1vC+VuWmNgdz6Pq7su/Pea/+h/jKZyx2yGHHFn/QyzZH3dKD8 + e3vGT8B4kRgKwrYVSf2Y+T+sXtdWgOfpWlT+RPpHgg7QauX9dexPClrP8M3gOmRM + vGkjU1NOd1m7939ugGjOnYrTcTdh4S4Q95L5hbuYwVGyrxqiqkdl8iWeOx4Fa287 + +iXp5i3lJKdyMLCnytsp5GHu+2OqFKyYQli23eMEEiTq/7PrzJas0BD3LfuT55Ht + UCwI/pRdgHvc/xEHqr7eF0C3f+PPG9/C85StDbm9WqhCVQ9nGt2ezkLeUSM/DBAh + DgI/LDFqRwLlIDrhkTT7BJGz6+2cmHwV80+eGPG2WzjpI619qhqgqB0fGBjLlVcZ + qoHy0K6NXuBqaoPOQq0TGkhl3SjurSe9EXeZHrrCT3LcSAIT7ZYoZDYuIvKBj7Sh + 7r/wdYS9nzsBhU0xeGzfAs+5yxDCp1/GzLK0H8LlJcjJOxqArtEzf55v7ZBB8erR + sqmbpGoQAwzwyw1zosmhzQwZRlAMPNi0yfnjfi8yQu4kZchyJyECggEBAOStATj0 + JNYWrPoHSgdW+NkzMRNIjjkHkUM/zs9F1bIlYwYDzmduXUoLChCHcjbOyE2tsPi8 + eFbyJ0vpMa0ZgoQmAnqUhYOwceu/tmI2CE7jLB2luq9oFhQIblKR6Fi8TyvPzn4N + Q4iD1I2VjffSSQher+hNVdLmpRkP8s2UiY7OQOZMBWKNqfORddQWcXp3Wrg2Lkbd + 7KcAtaMLYWg2W3mRdz6dnsqjMomRMi5arhroG3CtIpb62uiEdq2ZwyGF/Awon/kr + /XnfRLQeH0xVFPuVS/EbP6Ipq0TiieElTh4erhUIbmLZg7B5Fe9z1c528GUzTxhP + geQwN3bS5q71/f8CggEBAOMbosN7S+merANPzCOnRruLDPXukW+u20t/8CrOibJM + MO0embITOJfEdG4jBVRwnm5qacojuzFwfD7C18fJ1Hty010yQkjnB/zch3i8Fjx1 + vtsWnYOfbViuIzuEi+9bPWRlMZh504zDjgqo8P24JU5qziw/ySLfMZAX7iNsohRB + R+bBdP933kPoCo5ehSj4QyVgRIWN751x5sZ0eyCUTZIw9OswuOmsmnlw4nMsqWIx + OXlARVkbA97+1pp21pAromekE/bzN8Qo4pn4inZTTy9yAeAvSp+vScCiaVJ4n+ag + WAgLeQBLxqRCU6BMvKiRjQ8dBMAn1DjKCrlV+5zFZt0CggEAd8TZEBBnPq4vuOCa + eE+oFHKIcJYez2XUQkmoMs1byGtmet8BexDF0aMIiXG3c1dId87SEuT7jmZUCKFB + gG0M+9PAlp01dKy0bgpCJxwvq8m18G094uL8NU/ZIGwFKnyuZr73YvPlfBm3+NPs + wHCmCbk2HtBqdASTUhYVUHFMvrvuJ/CHHYAfFFAKS6PZmY/rtvHBuSJA8ZMgjx3F + zcQykvCKaQQ7B90D+iNPChI6gCMzRAeaR0Np5kCCvBf9qJA5W9DnQKU2pF8457Gj + KOKjE8W1ObnQ0UlLx89y8bYNPR9Kg/+feSx9ma9BuuGLiRCohgiik5QI7xAF7Lk3 + U0nJ1wKCAQAmkbjwre3UfSgFX/XxUCVJEHJhCeUVLIL9rXqiKnVkHGBqxLmhbnY8 + ABct5TCwiHe/lL7mn27ZFJtlJT30Jii51mRi/XgYXXQT03gGXxr/pZeGKa8SfW7a + kqhVIUuKmNoyRKVJmdb9nvBuiwZycGWVjbn59dM44uLN7+J3jalw+y002UH/aOIM + cknop9DBhngQzuqUK+i3unJQ3dNTUxxhaYMOtjWRKckKOsuad8lEbcuu9eVRHq9n + navgi7IgxehM5aamV+PuomrpbzZEph1al2gOJLntqJ1D49EzOl0dk7mflCM2k6fm + mYUOQjn//sgP+wOlhp4aDuYHV7zlgPjZAoIBAQDXPUl6NeA2ZMWbSO+WRc8zzjQ9 + qyxRA7g3ZSu+E5OqkxfwayXr/kAVKQNHJvn5wr9rLFhEF6CkBJ7XgOrHN0RjgXq2 + z0DpwG5JEFMeqkQWI+rVJ+ZJ4g0SAa9k39+WDxQhpZM8/IlkuIYqRI0mlcHwxhkG + 7JhkLtELhlxaGobAIinWiskKqX85tzZtCLe1wkErWOCueWviiuoCY2HWfELoA5+4 + wAvKspBO6oa+R2JtjA0nE72jKWuIz4m0QaCE7yInyCG9ikrBHSh/85eMu37nqegU + ziOydfDNcQp17fBjy8NVeQBjdjxVYejl8pKAVcQP9iM4vIyRIx0Ersv1fySA + -----END RSA PRIVATE KEY-----` + + publicKey = strings.Replace(publicKey, "\t", "", -1) + privateKey = strings.Replace(privateKey, "\t", "", -1) + + jwt := utils.NewJWT(&models.JWTConfiguration{ + PrivateKey: privateKey, + PublicKey: publicKey, + Algorithm: *jwt.SigningMethodRS256, + ExpiresIn: 15 * time.Minute, + Issuer: "issuer1", + Audience: "audience1", + }) + + t.Run("Успешная генерация токена", func(t *testing.T) { + assert.NotPanics(t, func() { + token, err := jwt.Create(testUser.Name) + + assert.NoError(t, err) + assert.NotZero(t, token) + assert.NotEmpty(t, token) + }) + }) + + t.Run("Успешная валидация токена", func(t *testing.T) { + assert.NotPanics(t, func() { + token, err := jwt.Create(testUser.Name) + + isNoError := assert.NoError(t, err) + + if isNoError { + parsedUser, err := jwt.Validate(token) + + assert.NoError(t, err) + assert.NotNil(t, parsedUser) + assert.Equal(t, testUser.Name, parsedUser) + } + }) + }) +} diff --git a/internal/utils/pagination.go b/internal/utils/pagination.go new file mode 100644 index 0000000..ccb63c8 --- /dev/null +++ b/internal/utils/pagination.go @@ -0,0 +1,38 @@ +package utils + +import "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + +func DeterminePagination(page, limit interface{}) *models.Pagination { + determinePage := func() int64 { + if page == nil { + return models.DefaultPageNumber + } + + pageNumber, isPageNumberOK := page.(int64) + + if !isPageNumberOK || pageNumber < 1 { + return models.DefaultPageNumber + } + + return pageNumber + } + + determineLimit := func() int64 { + if limit == nil { + return models.DefaultLimit + } + + limitNumber, isLimitNumberOK := limit.(int64) + + if !isLimitNumberOK || limitNumber > models.DefaultLimit || limitNumber < 1 { + return models.DefaultLimit + } + + return limitNumber + } + + return &models.Pagination{ + Page: determinePage(), + Limit: determineLimit(), + } +} diff --git a/internal/utils/payment.go b/internal/utils/payment.go new file mode 100644 index 0000000..0213555 --- /dev/null +++ b/internal/utils/payment.go @@ -0,0 +1,62 @@ +package utils + +import ( + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate" +) + +func ValidateGetPaymentLinkBody(request *models.GetPaymentLinkBody) errors.Error { + if request == nil { + return errors.NewWithMessage("request body is empty", errors.ErrInvalidArgs) + } + + switch request.Type { + case models.PaymentTypeInstallments, models.PaymentTypeSBP, models.PaymentTypeTinkoff, models.PaymentTypeYoomoney: + return nil + case models.PaymentTypeQiwi, models.PaymentTypeSberPay, models.PaymentTypeMobile, models.PaymentTypeCash: + return validateMobilePayment(request) + case models.PaymentTypeAlfabank: + return validateLoginPayment(request) + case models.PaymentTypeBankCard: + return validateBankcardPayment(request) + default: + return errors.NewWithMessage("unknown payment type", errors.ErrInvalidArgs) + } +} + +func validateMobilePayment(request *models.GetPaymentLinkBody) errors.Error { + if validate.IsStringEmpty(request.PhoneNumber) { + return errors.NewWithMessage("phone number is empty", errors.ErrInvalidArgs) + } + + return nil +} + +func validateLoginPayment(request *models.GetPaymentLinkBody) errors.Error { + if validate.IsStringEmpty(request.Login) { + return errors.NewWithMessage("login is empty", errors.ErrInvalidArgs) + } + + return nil +} + +func validateBankcardPayment(request *models.GetPaymentLinkBody) errors.Error { + if request.BankCard == nil { + return errors.NewWithMessage("bank card is empty", errors.ErrInvalidArgs) + } + + if validate.IsStringEmpty(request.BankCard.Number) { + return errors.NewWithMessage("bankcard number is empty", errors.ErrInvalidArgs) + } + + if validate.IsStringEmpty(request.BankCard.ExpiryYear) { + return errors.NewWithMessage("bankcard expiry year is empty", errors.ErrInvalidArgs) + } + + if validate.IsStringEmpty(request.BankCard.ExpiryMonth) { + return errors.NewWithMessage("bankcard expiry month is empty", errors.ErrInvalidArgs) + } + + return nil +} diff --git a/internal/utils/payment_test.go b/internal/utils/payment_test.go new file mode 100644 index 0000000..e70017a --- /dev/null +++ b/internal/utils/payment_test.go @@ -0,0 +1,192 @@ +package utils_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" +) + +func TestValidateGetPaymentLinkBody(t *testing.T) { + t.Run("Валидация объекта запроса для получения платёжной ссылки по банковской карточке (Успешно)", func(t *testing.T) { + assert.NoError(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeBankCard, + Currency: "RUB", + Amount: 10050, + BankCard: &models.BankCard{ + Number: "5315 5310 5310 5011", + ExpiryYear: "2021", + ExpiryMonth: "05", + }, + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки по банковской карточке (Не успешно)", func(t *testing.T) { + assert.Error(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeBankCard, + Currency: "RUB", + Amount: 10050, + })) + assert.Error(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeBankCard, + Currency: "RUB", + Amount: 10050, + BankCard: &models.BankCard{ + Number: "5315 5310 5310 5011", + ExpiryYear: "2021", + }, + })) + assert.Error(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeBankCard, + Currency: "RUB", + Amount: 10050, + BankCard: &models.BankCard{ + Number: "5315 5310 5310 5011", + ExpiryMonth: "05", + }, + })) + assert.Error(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeBankCard, + Currency: "RUB", + Amount: 10050, + BankCard: &models.BankCard{ + ExpiryYear: "2021", + ExpiryMonth: "05", + }, + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через yoomoney (Успешно)", func(t *testing.T) { + assert.NoError(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeYoomoney, + Currency: "USD", + Amount: 10050, + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через yoomoney (Успешно)", func(t *testing.T) { + assert.NoError(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeYoomoney, + Currency: "USD", + Amount: 10050, + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через qiwi (Успешно)", func(t *testing.T) { + assert.NoError(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeQiwi, + Currency: "USD", + Amount: 10050, + PhoneNumber: "79000000000", + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через qiwi (Не успешно)", func(t *testing.T) { + assert.Error(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeQiwi, + Currency: "USD", + Amount: 10050, + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через sberpay (Успешно)", func(t *testing.T) { + assert.NoError(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeSberPay, + Currency: "USD", + Amount: 10050, + PhoneNumber: "79000000000", + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через sberpay (Не успешно)", func(t *testing.T) { + assert.Error(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeSberPay, + Currency: "USD", + Amount: 10050, + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через alfaclick (Успешно)", func(t *testing.T) { + assert.NoError(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeAlfabank, + Currency: "USD", + Amount: 10050, + Login: "login_test", + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через alfaclick (Не успешно)", func(t *testing.T) { + assert.Error(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeAlfabank, + Currency: "BYN", + Amount: 10050, + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через sbp (Успешно)", func(t *testing.T) { + assert.NoError(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeSBP, + Currency: "USD", + Amount: 10050, + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через tinkoff (Успешно)", func(t *testing.T) { + assert.NoError(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeTinkoff, + Currency: "USD", + Amount: 10050, + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через мобильный (Успешно)", func(t *testing.T) { + assert.NoError(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeMobile, + Currency: "USD", + Amount: 10050, + PhoneNumber: "79000000000", + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через мобильный (Не успешно)", func(t *testing.T) { + assert.Error(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeMobile, + Currency: "USD", + Amount: 10050, + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через наличные (Успешно)", func(t *testing.T) { + assert.NoError(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeCash, + Currency: "USD", + Amount: 10050, + PhoneNumber: "79000000000", + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через наличные (Не успешно)", func(t *testing.T) { + assert.Error(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeCash, + Currency: "USD", + Amount: 10050, + })) + }) + + t.Run("Валидация объекта запроса для получения платёжной ссылки через оплату по частям (Успешно)", func(t *testing.T) { + assert.NoError(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: models.PaymentTypeInstallments, + Currency: "USD", + Amount: 10050, + })) + }) + + t.Run("Не успешная валидация из-за неопределённого типа оплаты", func(t *testing.T) { + assert.Error(t, utils.ValidateGetPaymentLinkBody(&models.GetPaymentLinkBody{ + Type: "some_radmon_payment_type", + Currency: "USD", + Amount: 10050, + })) + }) +} diff --git a/internal/utils/tariff.go b/internal/utils/tariff.go new file mode 100644 index 0000000..7c8173e --- /dev/null +++ b/internal/utils/tariff.go @@ -0,0 +1,13 @@ +package utils + +import "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + +func CalculateCartPurchasesAmount(tariffs []models.Tariff) int64 { + sum := int64(0) + + for _, tariff := range tariffs { + sum += tariff.Price + } + + return sum +} diff --git a/internal/utils/tariff_test.go b/internal/utils/tariff_test.go new file mode 100644 index 0000000..e92e3f0 --- /dev/null +++ b/internal/utils/tariff_test.go @@ -0,0 +1,18 @@ +package utils_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" +) + +func TestCalculateCartPurchasesAmount(t *testing.T) { + t.Run("Успешное вычиление суммы корзины", func(t *testing.T) { + assert.Equal(t, int64(200000), utils.CalculateCartPurchasesAmount([]models.Tariff{ + {Price: 90000}, + {Price: 110000}, + })) + }) +} diff --git a/internal/utils/transfer/tariff.go b/internal/utils/transfer/tariff.go new file mode 100644 index 0000000..0ad821e --- /dev/null +++ b/internal/utils/transfer/tariff.go @@ -0,0 +1,23 @@ +package transfer + +import ( + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/discount" +) + +func TariffsToProductInformations(tarrifs []models.Tariff) []*discount.ProductInformation { + productInformations := make([]*discount.ProductInformation, len(tarrifs)) + + for index, tariff := range tarrifs { + productInformations[index] = TariffToProductInformation(tariff) + } + + return productInformations +} + +func TariffToProductInformation(tarrif models.Tariff) *discount.ProductInformation { + return &discount.ProductInformation{ + ID: tarrif.ID, + Price: float64(tarrif.Price), + } +} diff --git a/internal/utils/transfer/tariff_test.go b/internal/utils/transfer/tariff_test.go new file mode 100644 index 0000000..0b1e45a --- /dev/null +++ b/internal/utils/transfer/tariff_test.go @@ -0,0 +1,25 @@ +package transfer_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/discount" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils/transfer" +) + +func TestTariffsToProductInformations(t *testing.T) { + t.Run("Успешный перевод массива моделей тарифов в массив информации о продуктах", func(t *testing.T) { + assert.Equal(t, + []*discount.ProductInformation{ + {ID: "1", Price: 20}, + {ID: "2", Price: 10}, + }, + transfer.TariffsToProductInformations([]models.Tariff{ + {ID: "1", Price: 20}, + {ID: "2", Price: 10}, + }), + ) + }) +} diff --git a/internal/utils/validate_configuration_urls.go b/internal/utils/validate_configuration_urls.go new file mode 100644 index 0000000..e94f0d4 --- /dev/null +++ b/internal/utils/validate_configuration_urls.go @@ -0,0 +1,20 @@ +package utils + +import ( + "fmt" + + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate" +) + +func ValidateConfigurationURLs(config *models.ServiceConfiguration) error { + return validateAuthMicroserviceURLs(&config.AuthMicroservice.URL) +} + +func validateAuthMicroserviceURLs(urls *models.AuthMicroserviceURL) error { + if !validate.URL(urls.User) { + return fmt.Errorf("invalid auth user url: %s", urls.User) + } + + return nil +} diff --git a/migrations/test/001_accounts_insert.down.json b/migrations/test/001_accounts_insert.down.json new file mode 100644 index 0000000..83c8a4b --- /dev/null +++ b/migrations/test/001_accounts_insert.down.json @@ -0,0 +1 @@ +[{ "delete": "accounts", "deletes": [{ "q": {} }] }] diff --git a/migrations/test/001_accounts_insert.up.json b/migrations/test/001_accounts_insert.up.json new file mode 100644 index 0000000..9724208 --- /dev/null +++ b/migrations/test/001_accounts_insert.up.json @@ -0,0 +1,28 @@ +[ + { + "insert": "accounts", + "ordered": true, + "documents": [ + { + "_id": { + "$oid": "641b2d73e0e07a7e90b59616" + }, + "userId": "807f1f77bcf81cd799439077", + "name": {}, + "cart": ["807f1f77bcf81cd791439022", "807f1f77bcf81cd799439012"], + "wallet": { + "currency": "RUB", + "cash": 0, + "purchasesAmount": 0, + "spent": 0, + "money": 0 + }, + "status": "no", + "isDeleted": false, + "createdAt": "2023-06-16T08:15:30.336Z", + "updatedAt": "2023-06-16T08:15:30.336Z", + "deletedAt": "2023-06-16T08:15:30.336Z" + } + ] + } +] diff --git a/openapi.yaml b/openapi.yaml new file mode 100644 index 0000000..7032e09 --- /dev/null +++ b/openapi.yaml @@ -0,0 +1,743 @@ +openapi: 3.0.1 +info: + title: Customer - сервис для управления клиентами + description: |- + Область ответственности сервиса - предоставление пользователю функционала корзины и кошелька. + version: 1.0.0 + +tags: + - name: account + description: аккаунт + - name: currency + description: доступные валюты + - name: cart + description: корзина + - name: wallet + description: кошелёк + - name: history + description: история + +paths: + /account: + get: + tags: + - account + summary: Получение текущего аккаунта юзера + description: Используется при заходе юзера в систему. Айдишник юзера получает из токена, который передаётся в заголовке authorization + operationId: getAccount + security: + - Bearer: [] + responses: + '200': + description: успешное получение + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '401': + description: Неавторизован + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Такого пользователя нет + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + post: + tags: + - account + summary: Создать новый аккаунт + description: Создаёт новый аккаунт для юзера, если такого ещё не было + operationId: addAccount + security: + - Bearer: [] + responses: + '200': + description: успешное создание аккаунта + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '401': + description: Неавторизован + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + patch: + tags: + - account + summary: Отредактировать аккаунт + description: Изменяет имя и название организации пользователя + operationId: changeAccount + security: + - Bearer: [] + requestBody: + description: Модель имени + content: + application/json: + schema: + $ref: '#/components/schemas/Name' + responses: + '200': + description: успешное создание аккаунта + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '401': + description: Неавторизован + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - account + summary: удалить собственный аккаунт + description: помечает аккаунт удалённым + operationId: deleteAccount + security: + - Bearer: [] + responses: + '200': + description: успешное удаление аккаунта + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '401': + description: Неавторизован + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /accounts: + get: + tags: + - account + summary: списко аккаунтов с пагинацией + description: получает список аккаунтов на указанной странице + общее количество аккаунтов + operationId: paginationAccounts + parameters: + - name: page + in: query + description: Номер страницы, начиная с 1 + required: false + explode: false + schema: + type: integer + default: 1 + - name: limit + in: query + description: размер страницы + required: false + explode: false + schema: + type: integer + default: 100 + responses: + '200': + description: успешное получение страницы аккаунтов + content: + application/json: + schema: + type: object + properties: + totalPages: + type: integer + example: 11 + accounts: + type: array + items: + $ref: "#/components/schemas/Account" + + /account/{userId}: + get: + tags: + - account + summary: Получить аккаунт по ID пользователя системы единой авторизации + description: Метод необходимый в основном только менеджерам, для получения данных о клиенте + security: + - Bearer: [] + operationId: getDirectAccount + parameters: + - name: userId + in: path + description: ID аккаунта + required: true + schema: + type: string + responses: + '200': + description: Успешное получение аккаунта + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '401': + description: Неавторизован + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Нет такого пользователя + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + patch: + tags: + - account + summary: Выставление статуса верификации + description: Используется сервисом верификации для выставления статуса верификации, является ли юзер НКО, Юр Лицом или Физ Лицом + operationId: setAccountVerificationStatus + security: + - Bearer: [] + parameters: + - name: userId + in: path + description: ID аккаунта + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + type: object + properties: + status: + $ref: '#/components/schemas/AccountStatus' + responses: + '200': + description: Успешное выставление статуса + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '404': + description: Нет такого пользователя + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - account + summary: Удалить аккаунт по айди + description: Метод необходимый в основном только менеджерам, для удаления клиента + operationId: deleteDirectAccount + security: + - Bearer: [] + parameters: + - name: userId + in: path + description: ID аккаунта + required: true + schema: + type: string + responses: + '200': + description: Успешное удаление аккаунта + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '401': + description: Неавторизован + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Нет такого пользователя + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /currencies: + get: + tags: + - currency + summary: получить список одобренных валют + operationId: getCurrencies + responses: + '200': + description: успешное получение списка валют + content: + application/json: + schema: + type: array + example: + - "RUB" + - "USD" + - "CAD" + items: + type: string + put: + tags: + - currency + summary: обновляет список одобренных валют + operationId: updateCurrencies + security: + - Bearer: [] + requestBody: + content: + application/json: + schema: + type: array + example: + - "RUB" + - "USD" + - "CAD" + items: + type: string + responses: + '200': + description: успешное обновление списка валют + content: + application/json: + schema: + type: array + example: + - "RUB" + - "USD" + - "CAD" + items: + type: string + '401': + description: Uanuthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /cart: + patch: + tags: + - cart + summary: Добавляем в корзину тариф + description: Необходимо проверить, что такой тариф существует + operationId: add2cart + security: + - Bearer: [] + parameters: + - name: id + in: query + required: true + schema: + type: string + example: "807f1f77bcf81cd799439011" + responses: + '200': + description: Добавлено в корзину + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '401': + description: Неавторизован + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Не найден такой тариф + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + tags: + - cart + summary: Удаляем из корзины тариф + operationId: removeFromCart + security: + - Bearer: [] + parameters: + - name: id + in: query + required: true + schema: + type: string + example: "807f1f77bcf81cd799439011" + responses: + '200': + description: Удалено из корзины + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '401': + description: Неавторизован + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Не найден такой тариф + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /cart/pay: + post: + tags: + - cart + summary: оплатить козину + operationId: payCart + security: + - Bearer: [] + description: Запрос на проведение оплаты корзины + responses: + '200': + description: успешная оплата корзины + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '401': + description: Неавторизован + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /wallet: + patch: + tags: + - wallet + summary: Изменить валюту кошелька + operationId: changeCurrency + description: >- + При запросе необходимо: + + - Отвалидировать, что такая валюта одобрена + + - Получить данные из сервиса cbrf (выдам задачу на него чуть позднее) + + - Перевести валюту кошелька в новую валюту. Кошелёк нарочно целочисленный, чтобы не было претензий к точности с плавающей точкой, поэтому, например долларовый счёт считается в центах + + security: + - Bearer: [] + requestBody: + content: + application/json: + schema: + type: object + required: [currency] + properties: + currency: + type: string + example: "USD" + responses: + '200': + description: Успешная смена валюты кошелька + content: + application/json: + schema: + $ref: "#/components/schemas/Account" + '401': + description: Неавторизован + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + '400': + description: Такая валюта не одобрена + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + '404': + description: Пользователь не найден + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + tags: + - wallet + summary: Запрос на получение ссылки на оплату + operationId: requestMoney + security: + - Bearer: [] + description: >- + - Формируем запрос к сервису оплаты, с необходимыми постбэками + + - Получаем ответ от сервиса оплаты с ссылкой на оплату, которую надо передать пользователю + requestBody: + content: + application/json: + schema: + type: object + required: [type, currency, amount] + properties: + type: + $ref: '#/components/schemas/PaymentType' + currency: + type: string + description: "ISO-4217 формат" + example: "RUB" + amount: + type: integer + example: 15020 + bankCard: + $ref: '#/components/schemas/BankCard' + phoneNumber: + type: string + example: "79000000000" + login: + type: string + example: "login_test" + returnUrl: + type: string + example: "https://site.ru/cart" + responses: + '200': + description: Успешный запрос денег + content: + application/json: + schema: + type: object + properties: + link: + type: string + example: "https://google.ru" + '500': + description: Сервис оплаты вернул ошибку + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /history: + get: + tags: + - history + summary: Получение лога событий связанных с аккаунтом + operationId: getHistory + security: + - Bearer: [] + parameters: + - name: page + in: query + description: Номер страницы, начиная с 1 + required: false + explode: false + schema: + type: integer + default: 1 + - name: limit + in: query + description: Размер страницы + required: false + explode: false + schema: + type: integer + default: 100 + responses: + '200': + description: Успешное получение событий + content: + application/json: + schema: + type: object + properties: + totalPages: + type: integer + example: 11 + records: + type: array + items: + $ref: '#/components/schemas/History' + '401': + description: Неавторизован + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + +components: + schemas: + + Account: + type: object + required: [_id, userId, cart, wallet, name, status, deleted, createdAt, updatedAt] + properties: + _id: + type: string + example: "807f1f77bcf81cd799439011" + userId: + type: string + example: "807f1f77bcf81cd799439011" + name: + $ref: '#/components/schemas/Name' + cart: + type: array + items: + type: string + example: + - "807f1f77bcf81cd799439011" + - "807f1f77bcf81cd799439012" + wallet: + $ref: "#/components/schemas/Wallet" + status: + $ref: '#/components/schemas/AccountStatus' + isDeleted: + type: boolean + example: false + createdAt: + type: string + format: "date-time" + updatedAt: + type: string + format: "date-time" + deletedAt: + type: string + format: "date-time" + + Name: + type: object + properties: + firstname: + type: string + example: Иван + secondname: + type: string + example: Иванов + middlename: + type: string + example: Иванович + orgname: + type: string + example: ООО \"Моя Оборона\" + + BankCard: + type: object + required: [number, expiryYear, expiryMonth] + properties: + number: + type: string + example: "RUB" + description: Номер карты + expiryYear: + type: string + example: "2021" + description: Год истечения срока карты (YYYY) + expiryMonth: + type: string + example: "05" + description: Месяц истечения срока карты (MM) + csc: + type: string + example: "05" + description: Код CVC2 или CVV2, 3 или 4 символа, печатается на обратной стороне карты + cardholder: + type: string + description: Имя владельца карты + example: "IVAN IVANOV" + + Wallet: + type: object + required: [currency, cash, money, purchasesAmount, spent] + properties: + currency: + description: Текущий курс валюты + type: string + example: "RUB" + cash: + description: Сумма money переведённая на текущий курс + type: integer + format: int64 + example: 10701 + purchasesAmount: + type: integer + format: int64 + description: Общая сумма денег, которые внёс пользователь + spent: + type: integer + format: int64 + description: Общая сумма потраченных денег за всё время существования аккаунта + money: + type: integer + format: int64 + description: >- + Деньги на счету в копейках. Чтобы при перессчётах не + возникало денег изниоткуда. фиксируемся к одной валюте, + она будет внутренней, никому её не покажем + example: 10701 + + History: + type: object + required: [id, userId, type, deleted, createdAt, updatedAt, comment, subject] + properties: + id: + type: string + example: "807f1f77bcf81cd799439011" + userId: + type: string + example: "807f1f77bcf81cd799439011" + type: + type: string + example: "customer.tariffEnded" + isDeleted: + type: boolean + example: false + createdAt: + type: string + format: "date-time" + updatedAt: + type: string + format: "date-time" + deletedAt: + type: string + format: "date-time" + comment: + type: string + example: "я это сделал потому что 42" + rawDetails: + type: string + description: >- + Я пока не могу предположить, какие будут фильтры по + истории, поэтому предлагаю в это + поле просто класть строку с json. + Ибо для каждого типа записи она своя. + example: '{"tariffs":["807f1f77bcf81cd799439011","807f1f77bcf81cd799439011"]}' + + PaymentType: + type: string + enum: ["bankCard", "tinkoffBank", "qiwi", "sberbank", "yoomoney", "mobile", "installments", "cash", "sbp", "b2bSberbank", "alfabank"] + + AccountStatus: + type: string + enum: ["no", "nko", "org"] + + Error: + type: object + required: [code, message] + properties: + statusCode: + type: integer + format: int64 + example: 404 + message: + type: string + example: user not found + + securitySchemes: + Bearer: # arbitrary name for the security scheme + type: http + scheme: bearer + bearerFormat: JWT + description: >- + Enter the token with the `Bearer: ` prefix, e.g. "Bearer abcde12345". + diff --git a/pkg/client/client.go b/pkg/client/client.go new file mode 100644 index 0000000..0753c64 --- /dev/null +++ b/pkg/client/client.go @@ -0,0 +1,37 @@ +package client + +import ( + "context" +) + +type RequestSettings struct { + URL string + Headers map[string]string + QueryParams map[string]string + Body any + Formdata any +} + +type Response[T any, R any] struct { + StatusCode int + Error *R + Body *T +} + +func Post[T any, R any](ctx context.Context, settings *RequestSettings) (*Response[T, R], error) { + request := buildRequest(ctx, settings) + + return makeRequest[T, R](settings.URL, request.Post) +} + +func Get[T any, R any](ctx context.Context, settings *RequestSettings) (*Response[T, R], error) { + request := buildRequest(ctx, settings) + + return makeRequest[T, R](settings.URL, request.Get) +} + +func Patch[T any, R any](ctx context.Context, settings *RequestSettings) (*Response[T, R], error) { + request := buildRequest(ctx, settings) + + return makeRequest[T, R](settings.URL, request.Patch) +} diff --git a/pkg/client/request.go b/pkg/client/request.go new file mode 100644 index 0000000..b1deabe --- /dev/null +++ b/pkg/client/request.go @@ -0,0 +1,56 @@ +package client + +import ( + "context" + + "github.com/go-resty/resty/v2" +) + +func buildRequest(ctx context.Context, settings *RequestSettings) *resty.Request { + request := resty.New().R().SetContext(ctx) + + if settings == nil { + return request + } + + if settings.Body != nil { + request.SetBody(settings.Body) + } + + if settings.QueryParams != nil { + request.SetQueryParams(settings.QueryParams) + } + + request.SetHeaders(settings.Headers) + + return request +} + +func makeRequest[T any, R any](url string, requestMethod func(url string) (*resty.Response, error)) (*Response[T, R], error) { + responseInstance, err := requestMethod(url) + if err != nil { + return nil, err + } + + if responseInstance.IsError() { + responseBody, parseErr := parseResponse[R](responseInstance.Body(), responseInstance.RawResponse) + if parseErr != nil { + return nil, parseErr + } + + return &Response[T, R]{ + StatusCode: responseInstance.StatusCode(), + Error: responseBody, + }, nil + } + + responseBody, parseErr := parseResponse[T](responseInstance.Body(), responseInstance.RawResponse) + if parseErr != nil { + return nil, parseErr + } + + return &Response[T, R]{ + StatusCode: responseInstance.StatusCode(), + Body: responseBody, + }, nil +} diff --git a/pkg/client/response.go b/pkg/client/response.go new file mode 100644 index 0000000..e905a7f --- /dev/null +++ b/pkg/client/response.go @@ -0,0 +1,28 @@ +package client + +import ( + "bytes" + "net/http" + + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/json" +) + +func parseResponse[T any](body []byte, response *http.Response) (*T, error) { + isJSONResponse := response.Header.Get("Content-Type") == "application/json" + + if !isJSONResponse { + responseBody, unmarshalErr := json.Unmarshal[T](body) + if unmarshalErr != nil { + return nil, unmarshalErr + } + + return responseBody, nil + } + + responseBody, parseErr := json.Parse[T](bytes.NewReader(body)) + if parseErr != nil { + return nil, parseErr + } + + return responseBody, nil +} diff --git a/pkg/closer/closer.go b/pkg/closer/closer.go new file mode 100644 index 0000000..1e993e9 --- /dev/null +++ b/pkg/closer/closer.go @@ -0,0 +1,50 @@ +package closer + +import ( + "context" + "fmt" + "log" + "sync" +) + +type Callback func(ctx context.Context) error + +type Closer struct { + mutex sync.Mutex + callbacks []Callback +} + +func New() *Closer { + return &Closer{} +} + +func (receiver *Closer) Add(callback Callback) { + receiver.mutex.Lock() + defer receiver.mutex.Unlock() + + receiver.callbacks = append(receiver.callbacks, callback) +} + +func (receiver *Closer) Close(ctx context.Context) error { + receiver.mutex.Lock() + defer receiver.mutex.Unlock() + + complete := make(chan struct{}, 1) + + go func() { + for index, callback := range receiver.callbacks { + if err := callback(ctx); err != nil { + log.Printf("[! (%d)] %v", index, err) + } + } + + complete <- struct{}{} + }() + + select { + case <-complete: + return nil + case <-ctx.Done(): + return fmt.Errorf("shutdown cancelled: %v", ctx.Err()) + } +} diff --git a/pkg/echotools/bind.go b/pkg/echotools/bind.go new file mode 100644 index 0000000..50bfef9 --- /dev/null +++ b/pkg/echotools/bind.go @@ -0,0 +1,13 @@ +package echotools + +import "github.com/labstack/echo/v4" + +func Bind[T any](ctx echo.Context) (*T, error) { + item := new(T) + + if err := ctx.Bind(item); err != nil { + return nil, err + } + + return item, nil +} diff --git a/pkg/env/parse.go b/pkg/env/parse.go new file mode 100644 index 0000000..b241f03 --- /dev/null +++ b/pkg/env/parse.go @@ -0,0 +1,23 @@ +package env + +import ( + "context" + "fmt" + "log" + + envLoader "github.com/joho/godotenv" + envParser "github.com/sethvargo/go-envconfig" +) + +func Parse[T interface{}](envFilePath string) (*T, error) { + var configuration T + + if err := envLoader.Load(envFilePath); err != nil { + log.Printf("load local env file error: %s", err.Error()) + } + if err := envParser.Process(context.Background(), &configuration); err != nil { + return nil, fmt.Errorf("parsing env error: %s", err.Error()) + } + + return &configuration, nil +} diff --git a/pkg/json/encode.go b/pkg/json/encode.go new file mode 100644 index 0000000..306cb90 --- /dev/null +++ b/pkg/json/encode.go @@ -0,0 +1,16 @@ +package json + +import ( + "bytes" + "encoding/json" +) + +func EncodeBuffer(body interface{}) (*bytes.Buffer, error) { + buffer := new(bytes.Buffer) + + if err := json.NewEncoder(buffer).Encode(body); err != nil { + return nil, err + } + + return buffer, nil +} diff --git a/pkg/json/parse.go b/pkg/json/parse.go new file mode 100644 index 0000000..33a15c4 --- /dev/null +++ b/pkg/json/parse.go @@ -0,0 +1,26 @@ +package json + +import ( + "encoding/json" + "io" +) + +func Parse[T any](reader io.Reader) (*T, error) { + jsonData := new(T) + + if err := json.NewDecoder(reader).Decode(jsonData); err != nil { + return nil, err + } + + return jsonData, nil +} + +func Unmarshal[T any](data []byte) (*T, error) { + unmarshaled := new(T) + + if err := json.Unmarshal(data, unmarshaled); err != nil { + return nil, err + } + + return unmarshaled, nil +} diff --git a/pkg/mongo/config.go b/pkg/mongo/config.go new file mode 100644 index 0000000..56880f0 --- /dev/null +++ b/pkg/mongo/config.go @@ -0,0 +1,22 @@ +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 { + Host string `env:"MONGO_HOST,default=localhost"` + Port string `env:"MONGO_PORT,default=27017"` + User string `env:"MONGO_USER,required"` + Password string `env:"MONGO_PASSWORD,required"` + Auth string `env:"MONGO_AUTH,required"` + DatabaseName string `env:"MONGO_DB_NAME,required"` +} + +type RequestSettings struct { + Driver *mongo.Collection + Options *options.FindOptions + Filter primitive.M +} diff --git a/pkg/mongo/connection.go b/pkg/mongo/connection.go new file mode 100644 index 0000000..241b446 --- /dev/null +++ b/pkg/mongo/connection.go @@ -0,0 +1,75 @@ +package mongo + +import ( + "context" + "fmt" + "log" + "net" + "net/url" + "time" + + "go.mongodb.org/mongo-driver/event" + "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.Host, deps.Configuration.Port), + } + + cmdMonitor := &event.CommandMonitor{ + Started: func(_ context.Context, evt *event.CommandStartedEvent) { + log.Println(evt.Command) + }, + Succeeded: func(_ context.Context, evt *event.CommandSucceededEvent) { + log.Println(evt.Reply) + }, + Failed: func(_ context.Context, evt *event.CommandFailedEvent) { + log.Println(evt.Failure) + }, + } + + connectionOptions := options.Client(). + ApplyURI(mongoURI.String()). + SetAuth(options.Credential{ + AuthMechanism: "SCRAM-SHA-1", + AuthSource: deps.Configuration.Auth, + Username: deps.Configuration.User, + Password: deps.Configuration.Password, + }). + SetMonitor(cmdMonitor) + + fmt.Println(connectionOptions.GetURI()) + + 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.DatabaseName), 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) + } + } +} diff --git a/pkg/mongo/errors.go b/pkg/mongo/errors.go new file mode 100644 index 0000000..2e592f2 --- /dev/null +++ b/pkg/mongo/errors.go @@ -0,0 +1,7 @@ +package mongo + +import "errors" + +var ( + ErrEmptyArgs = errors.New("arguments are empty") +) diff --git a/pkg/mongo/find.go b/pkg/mongo/find.go new file mode 100644 index 0000000..7a46859 --- /dev/null +++ b/pkg/mongo/find.go @@ -0,0 +1,55 @@ +package mongo + +import ( + "context" + "log" +) + +func Find[T any](ctx context.Context, settings *RequestSettings) ([]T, error) { + if settings == nil { + return []T{}, ErrEmptyArgs + } + + results := make([]T, 0) + + cursor, err := settings.Driver.Find(ctx, settings.Filter, settings.Options) + if err != nil { + return []T{}, err + } + + defer func() { + if err := cursor.Close(ctx); err != nil { + log.Printf("failed to close cursor: %v", err) + } + }() + + for cursor.Next(ctx) { + result := new(T) + + if err := cursor.Decode(result); err != nil { + return []T{}, err + } + + results = append(results, *result) + } + + if err := cursor.Err(); err != nil { + return []T{}, err + } + + return results, nil +} + +func FindOne[T any](ctx context.Context, settings *RequestSettings) (*T, error) { + if settings == nil { + return nil, ErrEmptyArgs + } + + result := new(T) + + if err := settings.Driver.FindOne(ctx, settings.Filter).Decode(result); err != nil { + return nil, err + } + + return result, nil +} diff --git a/pkg/validate/string.go b/pkg/validate/string.go new file mode 100644 index 0000000..ad98ee4 --- /dev/null +++ b/pkg/validate/string.go @@ -0,0 +1,7 @@ +package validate + +import "strings" + +func IsStringEmpty(text string) bool { + return strings.TrimSpace(text) == "" +} diff --git a/pkg/validate/string_test.go b/pkg/validate/string_test.go new file mode 100644 index 0000000..a3c8e4a --- /dev/null +++ b/pkg/validate/string_test.go @@ -0,0 +1,16 @@ +package validate_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate" +) + +func TestIsStringEmpty(t *testing.T) { + assert.True(t, validate.IsStringEmpty("")) + assert.True(t, validate.IsStringEmpty(" ")) + assert.True(t, validate.IsStringEmpty(" ")) + assert.False(t, validate.IsStringEmpty("tett")) + assert.False(t, validate.IsStringEmpty(" t ")) +} diff --git a/pkg/validate/url.go b/pkg/validate/url.go new file mode 100644 index 0000000..04fb19a --- /dev/null +++ b/pkg/validate/url.go @@ -0,0 +1,28 @@ +package validate + +import ( + "net" + "net/url" + "regexp" + "strings" +) + +func URL(text string) bool { + url, err := url.Parse(text) + if err != nil { + return false + } + + address := net.ParseIP(url.Host) + + if address == nil { + regex := regexp.MustCompile(`\b\w+:\d+\b`) + return strings.Contains(url.Host, ".") || regex.MatchString(url.Host) + } + + if IsStringEmpty(url.Scheme) || IsStringEmpty(url.Host) { + return false + } + + return true +} diff --git a/pkg/validate/url_test.go b/pkg/validate/url_test.go new file mode 100644 index 0000000..7d910c7 --- /dev/null +++ b/pkg/validate/url_test.go @@ -0,0 +1,25 @@ +package validate_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate" +) + +func TestValidateURL(t *testing.T) { + assert.True(t, validate.URL("http://google.com")) + assert.True(t, validate.URL("http://w.com/cn")) + assert.True(t, validate.URL("http://192.158.0.1:90")) + assert.False(t, validate.URL("192.158.0.1:9090")) + assert.False(t, validate.URL("http://w")) + assert.False(t, validate.URL("fsw")) + assert.True(t, validate.URL("http://192.158.1/1")) + assert.True(t, validate.URL("https://www.googleapis.com/oauth2/v3")) + assert.True(t, validate.URL("http://localhost:8080/google/callback")) + assert.True(t, validate.URL("https://oauth.pena.digital/amocrm/callback")) + assert.True(t, validate.URL("https://www.amocrm.ru/oauth")) + assert.True(t, validate.URL("https://www.amocrm.ru/oauth/access_token")) + assert.True(t, validate.URL("http://localhost:8080/vk/callback")) + assert.True(t, validate.URL("http://mock:8000/api/v4/account")) +} diff --git a/proto/callback/service.proto b/proto/callback/service.proto new file mode 100644 index 0000000..d9eea36 --- /dev/null +++ b/proto/callback/service.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package payment_callback; + +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "./payment_callback"; + +service PaymentCallbackService { + rpc OnSuccess(Event) returns (google.protobuf.Empty) {} + rpc OnFailure(Event) returns (google.protobuf.Empty) {} +} + +message Event { + string Key = 1; + string Message = 2; + Payment Payment = 3; +} + +message Payment { + string ID = 1; + string UserID = 2; + string PaymentID = 3; + string IdempotencePaymentID = 4; + int64 Amount = 5; + string Currency = 6; + string Type = 7; + string Status = 8; + bool Completed = 9; +} diff --git a/proto/customer/service.proto b/proto/customer/service.proto new file mode 100644 index 0000000..5c501f1 --- /dev/null +++ b/proto/customer/service.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package customer; + +import "google/protobuf/empty.proto"; + +option go_package = "./customer"; + +service CustomerService { + rpc InsertHistory(History) returns (google.protobuf.Empty) {} +} + +message History { + string UserID = 1; + string Comment = 2; + string Key = 3; + string RawDetails = 4; +} diff --git a/proto/discount/audit.model.proto b/proto/discount/audit.model.proto new file mode 100644 index 0000000..5c783a6 --- /dev/null +++ b/proto/discount/audit.model.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package discount; + +import "google/api/annotations.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/empty.proto"; + +option go_package = "./discount"; + +message Audit { + google.protobuf.Timestamp UpdatedAt = 1; + google.protobuf.Timestamp CreatedAt = 2; + optional google.protobuf.Timestamp DeletedAt = 3; + bool Deleted = 4; +} + diff --git a/proto/discount/discount.model.proto b/proto/discount/discount.model.proto new file mode 100644 index 0000000..54db7c9 --- /dev/null +++ b/proto/discount/discount.model.proto @@ -0,0 +1,88 @@ +syntax = "proto3"; + +package discount; + +import "google/api/annotations.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/empty.proto"; +import "discount/audit.model.proto"; + +option go_package = "./discount"; + +message DiscountOptional { + string ID = 1; + optional string Name = 2; + optional uint32 Layer = 3; + optional string Description = 4; + optional DiscountCondition Condition = 5; + optional DiscountCalculationTarget Target = 6; +} + +message Discounts { + repeated Discount Discounts = 1; +} + +message Discount { + string ID = 1; + string Name = 2; + uint32 Layer = 3; + string Description = 4; + DiscountCondition Condition = 5; + DiscountCalculationTarget Target = 6; + Audit Audit = 7; + bool Deprecated = 8; +} + +message DiscountCalculationTarget { + repeated ProductTarget Products = 1; + double Factor = 2; + optional TargetScope TargetScope = 3; + optional string TargetGroup = 4; + optional bool Overhelm = 5; +} + +message DiscountCondition { + optional PeriodCondition Period = 1; + optional string User = 2; + optional string UserType = 3; + optional string Coupon = 4; + optional double PurchasesAmount = 5; + optional double CartPurchasesAmount = 6; + optional string Product = 7; + optional uint64 Term = 8; + optional uint64 Usage = 9; + optional double PriceFrom = 10; + optional string Group = 11; +} + +message ProductTarget { + string ID = 1; + double Factor = 2; + optional bool Overhelm = 3; +} + +message PeriodCondition { + google.protobuf.Timestamp From = 1; + google.protobuf.Timestamp To = 2; +} + +message UserInformation { + string ID = 1; + string Type = 2; + double PurchasesAmount = 3; + double CartPurchasesAmount = 4; +} + +message ProductInformation { + string ID = 1; + double Price = 2; + optional uint64 Term = 3; + optional uint64 Usage = 4; + optional string Group = 5; +} + +enum TargetScope { + Sum = 0; + Group = 1; + Each = 2; +} \ No newline at end of file diff --git a/proto/discount/service.proto b/proto/discount/service.proto new file mode 100644 index 0000000..b8ff240 --- /dev/null +++ b/proto/discount/service.proto @@ -0,0 +1,94 @@ +syntax = "proto3"; + +package discount; + +import "google/api/annotations.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/empty.proto"; +import "discount/discount.model.proto"; + +option go_package = "./discount"; + +service DiscountService { + rpc GetAllDiscounts(google.protobuf.Empty) returns (Discounts) { + option (google.api.http) = { + get: "/discount/all" + }; + } + + rpc GetUserDiscounts(GetDiscountByIDRequest) returns (Discounts) { + option (google.api.http) = { + get: "/discount/user/{ID}" + }; + } + + rpc DetermineDiscounts(ApplyDiscountRequest) returns (Discounts) { + option (google.api.http) = { + post: "/discount/determine" + body: "*" + }; + } + + rpc ApplyDiscounts(ApplyDiscountRequest) returns (ApplyDiscountResponse) { + option (google.api.http) = { + post: "/discount/apply" + body: "*" + }; + } + + rpc GetDiscountByID(GetDiscountByIDRequest) returns (Discount) { + option (google.api.http) = { + get: "/discount/{ID}" + }; + } + + rpc CreateDiscount(CreateDiscountRequest) returns (Discount) { + option (google.api.http) = { + post: "/discount" + }; + } + + rpc ReplaceDiscount(DiscountOptional) returns (Discount) { + option (google.api.http) = { + put: "/discount/{ID}" + body: "*" + }; + } + + rpc UpdateDiscount(DiscountOptional) returns (Discount) { + option (google.api.http) = { + patch: "/discount/{ID}" + body: "*" + }; + } + + rpc DeleteDiscount(GetDiscountByIDRequest) returns (Discount) { + option (google.api.http) = { + delete: "/discount/{ID}" + }; + } +} + +message GetDiscountByIDRequest { + string ID = 1; +} + +message ApplyDiscountRequest { + UserInformation UserInformation = 1; + repeated ProductInformation Products = 2; + google.protobuf.Timestamp Date = 3; + optional string Coupon = 4; +} + +message ApplyDiscountResponse { + double Price = 1; + repeated Discount AppliedDiscounts = 2; +} + +message CreateDiscountRequest { + string Name = 1; + uint32 Layer = 2; + string Description = 3; + DiscountCondition Condition = 4; + DiscountCalculationTarget Target = 5; +} diff --git a/proto/google/api/annotations.proto b/proto/google/api/annotations.proto new file mode 100644 index 0000000..18dcf20 --- /dev/null +++ b/proto/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright (c) 2015, Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} \ No newline at end of file diff --git a/proto/google/api/http.proto b/proto/google/api/http.proto new file mode 100644 index 0000000..9d0a1e5 --- /dev/null +++ b/proto/google/api/http.proto @@ -0,0 +1,375 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} \ No newline at end of file diff --git a/proto/treasurer/payment.model.proto b/proto/treasurer/payment.model.proto new file mode 100644 index 0000000..f64e166 --- /dev/null +++ b/proto/treasurer/payment.model.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package treasurer; + +option go_package = "./treasurer"; + +message MainPaymentSettings { + string Currency = 1; + int64 Amount = 2; + repeated string CallbackHostGRPC = 3; + string ReturnURL = 4; + string UserID = 5; + string ClientIP = 6; +} + +message BankCardInformation { + string Number = 1; + string ExpiryYear = 2; + string ExpiryMonth = 3; + optional string CSC = 4; + optional string CardHolderName = 5; +} \ No newline at end of file diff --git a/proto/treasurer/service.proto b/proto/treasurer/service.proto new file mode 100644 index 0000000..31abcf9 --- /dev/null +++ b/proto/treasurer/service.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; + +package treasurer; + +import "google/protobuf/empty.proto"; +import "treasurer/payment.model.proto"; + +option go_package = "./treasurer"; + +service TreasurerService { + rpc GetPaymentLinkBankCard(GetBankCardPaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkYooMoney(GetPaymentLinkBody) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkQIWI(GetPhonePaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkSberPay(GetPhonePaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkAlfaClick(GetLoginPaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkTinkoff(GetPaymentLinkBody) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkSberbankB2B(GetB2BPaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkSBP(GetPaymentLinkBody) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkMobile(GetPhonePaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkCash(GetPhonePaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkInstallments(GetPaymentLinkBody) returns (GetPaymentLinkResponse) {} +} + +message GetBankCardPaymentLinkRequest { + MainPaymentSettings MainSettings = 1; + BankCardInformation BankCard = 2; +} + +message GetPaymentLinkBody { + MainPaymentSettings MainSettings = 1; +} + +message GetPhonePaymentLinkRequest { + MainPaymentSettings MainSettings = 1; + string Phone = 2; +} + +message GetLoginPaymentLinkRequest { + MainPaymentSettings MainSettings = 1; + string Login = 2; +} + +message GetB2BPaymentLinkRequest { + MainPaymentSettings MainSettings = 1; + string PaymentPurpose = 2; + google.protobuf.Empty VatData = 3; +} + +message GetPaymentLinkResponse { + string RedirectURL = 1; +} diff --git a/tests/helpers/jwt.go b/tests/helpers/jwt.go new file mode 100644 index 0000000..5632bd7 --- /dev/null +++ b/tests/helpers/jwt.go @@ -0,0 +1,88 @@ +package helpers + +import ( + "strings" + "time" + + "github.com/golang-jwt/jwt/v5" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" +) + +func InitializeJWT() *utils.JWT { + publicKey := strings.Replace(`-----BEGIN PUBLIC KEY----- + MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyt4XuLovUY7i12K2PIMb + QZOKn+wFFKUvxvKQDel049/+VMpHMx1FLolUKuyGp9zi6gOwjHsBPgc9oqr/eaXG + QSh7Ult7i9f+Ht563Y0er5UU9Zc5ZPSxf9O75KYD48ruGkqiFoncDqPENK4dtUa7 + w0OqlN4bwVBbmIsP8B3EDC5Dof+vtiNTSHSXPx+zifKeZGyknp+nyOHVrRDhPjOh + zQzCom0MSZA/sJYmps8QZgiPA0k4Z6jTupDymPOIwYeD2C57zSxnAv0AfC3/pZYJ + bZYH/0TszRzmy052DME3zMnhMK0ikdN4nzYqU0dkkA5kb5GtKDymspHIJ9eWbUuw + gtg8Rq/LrVBj1I3UFgs0ibio40k6gqinLKslc5Y1I5mro7J3OSEP5eO/XeDLOLlO + JjEqkrx4fviI1cL3m5L6QV905xmcoNZG1+RmOg7D7cZQUf27TXqM381jkbNdktm1 + JLTcMScxuo3vaRftnIVw70V8P8sIkaKY8S8HU1sQgE2LB9t04oog5u59htx2FHv4 + B13NEm8tt8Tv1PexpB4UVh7PIualF6SxdFBrKbraYej72wgjXVPQ0eGXtGGD57j8 + DUEzk7DK2OvIWhehlVqtiRnFdAvdBj2ynHT2/5FJ/Zpd4n5dKGJcQvy1U1qWMs+8 + M7AHfWyt2+nZ04s48+bK3yMCAwEAAQ== + -----END PUBLIC KEY-----`, "\t", "", -1) + + privateKey := strings.Replace(`-----BEGIN RSA PRIVATE KEY----- + MIIJKQIBAAKCAgEAyt4XuLovUY7i12K2PIMbQZOKn+wFFKUvxvKQDel049/+VMpH + Mx1FLolUKuyGp9zi6gOwjHsBPgc9oqr/eaXGQSh7Ult7i9f+Ht563Y0er5UU9Zc5 + ZPSxf9O75KYD48ruGkqiFoncDqPENK4dtUa7w0OqlN4bwVBbmIsP8B3EDC5Dof+v + tiNTSHSXPx+zifKeZGyknp+nyOHVrRDhPjOhzQzCom0MSZA/sJYmps8QZgiPA0k4 + Z6jTupDymPOIwYeD2C57zSxnAv0AfC3/pZYJbZYH/0TszRzmy052DME3zMnhMK0i + kdN4nzYqU0dkkA5kb5GtKDymspHIJ9eWbUuwgtg8Rq/LrVBj1I3UFgs0ibio40k6 + gqinLKslc5Y1I5mro7J3OSEP5eO/XeDLOLlOJjEqkrx4fviI1cL3m5L6QV905xmc + oNZG1+RmOg7D7cZQUf27TXqM381jkbNdktm1JLTcMScxuo3vaRftnIVw70V8P8sI + kaKY8S8HU1sQgE2LB9t04oog5u59htx2FHv4B13NEm8tt8Tv1PexpB4UVh7PIual + F6SxdFBrKbraYej72wgjXVPQ0eGXtGGD57j8DUEzk7DK2OvIWhehlVqtiRnFdAvd + Bj2ynHT2/5FJ/Zpd4n5dKGJcQvy1U1qWMs+8M7AHfWyt2+nZ04s48+bK3yMCAwEA + AQKCAgEAhodpK7MsFeWvQC3Rs6ctt/rjftHBPMOeP0wzg0ZBoau0uP264YaTjhy7 + mAtp8H9matEvjrkzRbL/iJPk/wKTyjnSLfdEoqQFfOsEh09B/iXa1FIIWY569s2u + WB5PjgvQgdbkThX1vC+VuWmNgdz6Pq7su/Pea/+h/jKZyx2yGHHFn/QyzZH3dKD8 + e3vGT8B4kRgKwrYVSf2Y+T+sXtdWgOfpWlT+RPpHgg7QauX9dexPClrP8M3gOmRM + vGkjU1NOd1m7939ugGjOnYrTcTdh4S4Q95L5hbuYwVGyrxqiqkdl8iWeOx4Fa287 + +iXp5i3lJKdyMLCnytsp5GHu+2OqFKyYQli23eMEEiTq/7PrzJas0BD3LfuT55Ht + UCwI/pRdgHvc/xEHqr7eF0C3f+PPG9/C85StDbm9WqhCVQ9nGt2ezkLeUSM/DBAh + DgI/LDFqRwLlIDrhkTT7BJGz6+2cmHwV80+eGPG2WzjpI619qhqgqB0fGBjLlVcZ + qoHy0K6NXuBqaoPOQq0TGkhl3SjurSe9EXeZHrrCT3LcSAIT7ZYoZDYuIvKBj7Sh + 7r/wdYS9nzsBhU0xeGzfAs+5yxDCp1/GzLK0H8LlJcjJOxqArtEzf55v7ZBB8erR + sqmbpGoQAwzwyw1zosmhzQwZRlAMPNi0yfnjfi8yQu4kZchyJyECggEBAOStATj0 + JNYWrPoHSgdW+NkzMRNIjjkHkUM/zs9F1bIlYwYDzmduXUoLChCHcjbOyE2tsPi8 + eFbyJ0vpMa0ZgoQmAnqUhYOwceu/tmI2CE7jLB2luq9oFhQIblKR6Fi8TyvPzn4N + Q4iD1I2VjffSSQher+hNVdLmpRkP8s2UiY7OQOZMBWKNqfORddQWcXp3Wrg2Lkbd + 7KcAtaMLYWg2W3mRdz6dnsqjMomRMi5arhroG3CtIpb62uiEdq2ZwyGF/Awon/kr + /XnfRLQeH0xVFPuVS/EbP6Ipq0TiieElTh4erhUIbmLZg7B5Fe9z1c528GUzTxhP + geQwN3bS5q71/f8CggEBAOMbosN7S+merANPzCOnRruLDPXukW+u20t/8CrOibJM + MO0embITOJfEdG4jBVRwnm5qacojuzFwfD7C18fJ1Hty010yQkjnB/zch3i8Fjx1 + vtsWnYOfbViuIzuEi+9bPWRlMZh504zDjgqo8P24JU5qziw/ySLfMZAX7iNsohRB + R+bBdP933kPoCo5ehSj4QyVgRIWN751x5sZ0eyCUTZIw9OswuOmsmnlw4nMsqWIx + OXlARVkbA97+1pp21pAromekE/bzN8Qo4pn4inZTTy9yAeAvSp+vScCiaVJ4n+ag + WAgLeQBLxqRCU6BMvKiRjQ8dBMAn1DjKCrlV+5zFZt0CggEAd8TZEBBnPq4vuOCa + eE+oFHKIcJYez2XUQkmoMs1byGtmet8BexDF0aMIiXG3c1dId87SEuT7jmZUCKFB + gG0M+9PAlp01dKy0bgpCJxwvq8m18G094uL8NU/ZIGwFKnyuZr73YvPlfBm3+NPs + wHCmCbk2HtBqdASTUhYVUHFMvrvuJ/CHHYAfFFAKS6PZmY/rtvHBuSJA8ZMgjx3F + zcQykvCKaQQ7B90D+iNPChI6gCMzRAeaR0Np5kCCvBf9qJA5W9DnQKU2pF8457Gj + KOKjE8W1ObnQ0UlLx89y8bYNPR9Kg/+feSx9ma9BuuGLiRCohgiik5QI7xAF7Lk3 + U0nJ1wKCAQAmkbjwre3UfSgFX/XxUCVJEHJhCeUVLIL9rXqiKnVkHGBqxLmhbnY8 + ABct5TCwiHe/lL7mn27ZFJtlJT30Jii51mRi/XgYXXQT03gGXxr/pZeGKa8SfW7a + kqhVIUuKmNoyRKVJmdb9nvBuiwZycGWVjbn59dM44uLN7+J3jalw+y002UH/aOIM + cknop9DBhngQzuqUK+i3unJQ3dNTUxxhaYMOtjWRKckKOsuad8lEbcuu9eVRHq9n + navgi7IgxehM5aamV+PuomrpbzZEph1al2gOJLntqJ1D49EzOl0dk7mflCM2k6fm + mYUOQjn//sgP+wOlhp4aDuYHV7zlgPjZAoIBAQDXPUl6NeA2ZMWbSO+WRc8zzjQ9 + qyxRA7g3ZSu+E5OqkxfwayXr/kAVKQNHJvn5wr9rLFhEF6CkBJ7XgOrHN0RjgXq2 + z0DpwG5JEFMeqkQWI+rVJ+ZJ4g0SAa9k39+WDxQhpZM8/IlkuIYqRI0mlcHwxhkG + 7JhkLtELhlxaGobAIinWiskKqX85tzZtCLe1wkErWOCueWviiuoCY2HWfELoA5+4 + wAvKspBO6oa+R2JtjA0nE72jKWuIz4m0QaCE7yInyCG9ikrBHSh/85eMu37nqegU + ziOydfDNcQp17fBjy8NVeQBjdjxVYejl8pKAVcQP9iM4vIyRIx0Ersv1fySA + -----END RSA PRIVATE KEY-----`, "\t", "", -1) + + return utils.NewJWT(&models.JWTConfiguration{ + PrivateKey: privateKey, + PublicKey: publicKey, + Audience: "audience", + Issuer: "issuer", + Algorithm: *jwt.SigningMethodRS256, + ExpiresIn: 15 * time.Minute, + }) +} diff --git a/tests/integration/set_account_verification_status_test copy.go b/tests/integration/set_account_verification_status_test copy.go new file mode 100644 index 0000000..550917d --- /dev/null +++ b/tests/integration/set_account_verification_status_test copy.go @@ -0,0 +1,133 @@ +package integration_test + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/client" + "penahub.gitlab.yandexcloud.net/pena-services/customer/tests/helpers" +) + +func TestSetAccountVerificationStatusNO(t *testing.T) { + jwtUtil := helpers.InitializeJWT() + + t.Run("Успешная установка статуса верификации аккаунту", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + assert.NotPanics(t, func() { + token, tokenErr := jwtUtil.Create("807f1f77bcf81cd799439077") + if isNoError := assert.NoError(t, tokenErr); !isNoError { + return + } + + response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{ + URL: "http://localhost:8082/account/807f1f77bcf81cd799439077", + Body: models.SetAccountStatus{Status: models.AccountStatusNo}, + Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)}, + }) + if isNoError := assert.NoError(t, getAccountErr); !isNoError { + return + } + if isNoRequestError := assert.Nil(t, response.Error); !isNoRequestError { + return + } + + assert.Equal(t, "807f1f77bcf81cd799439077", response.Body.UserID) + assert.Equal(t, models.AccountStatusNo, response.Body.Status) + }) + }) +} + +func TestSetAccountVerificationStatusORG(t *testing.T) { + jwtUtil := helpers.InitializeJWT() + + t.Run("Успешная установка статуса верификации аккаунту", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + assert.NotPanics(t, func() { + token, tokenErr := jwtUtil.Create("807f1f77bcf81cd799439077") + if isNoError := assert.NoError(t, tokenErr); !isNoError { + return + } + + response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{ + URL: "http://localhost:8082/account/807f1f77bcf81cd799439077", + Body: models.SetAccountStatus{Status: models.AccountStatusOrg}, + Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)}, + }) + if isNoError := assert.NoError(t, getAccountErr); !isNoError { + return + } + if isNoRequestError := assert.Nil(t, response.Error); !isNoRequestError { + return + } + + assert.Equal(t, "807f1f77bcf81cd799439077", response.Body.UserID) + assert.Equal(t, models.AccountStatusOrg, response.Body.Status) + }) + }) +} + +func TestSetAccountVerificationStatusNKO(t *testing.T) { + jwtUtil := helpers.InitializeJWT() + + t.Run("Успешная установка статуса верификации аккаунту", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + assert.NotPanics(t, func() { + token, tokenErr := jwtUtil.Create("807f1f77bcf81cd799439077") + if isNoError := assert.NoError(t, tokenErr); !isNoError { + return + } + + response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{ + URL: "http://localhost:8082/account/807f1f77bcf81cd799439077", + Body: models.SetAccountStatus{Status: models.AccountStatusNko}, + Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)}, + }) + if isNoError := assert.NoError(t, getAccountErr); !isNoError { + return + } + if isNoRequestError := assert.Nil(t, response.Error); !isNoRequestError { + return + } + + assert.Equal(t, "807f1f77bcf81cd799439077", response.Body.UserID) + assert.Equal(t, models.AccountStatusNko, response.Body.Status) + }) + }) +} + +func TestSetAccountVerificationStatusFailure(t *testing.T) { + jwtUtil := helpers.InitializeJWT() + + t.Run("Проваленная установка статуса верификации из-за невалидного значения статуса", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + assert.NotPanics(t, func() { + token, tokenErr := jwtUtil.Create("807f1f77bcf81cd799439077") + if isNoError := assert.NoError(t, tokenErr); !isNoError { + return + } + + response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{ + URL: "http://localhost:8082/account/807f1f77bcf81cd799439077", + Body: models.SetAccountStatus{Status: "radnom-status"}, + Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)}, + }) + if isNoError := assert.NoError(t, getAccountErr); !isNoError { + return + } + if isNotNil := assert.NotNil(t, response.Error); isNotNil { + assert.Equal(t, 400, response.StatusCode) + } + }) + }) +} diff --git a/tests/integration/update_account_verification_status_test.go b/tests/integration/update_account_verification_status_test.go new file mode 100644 index 0000000..26567ab --- /dev/null +++ b/tests/integration/update_account_verification_status_test.go @@ -0,0 +1,51 @@ +package integration_test + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" + "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/client" + "penahub.gitlab.yandexcloud.net/pena-services/customer/tests/helpers" +) + +func TestUpdateAccountName(t *testing.T) { + jwtUtil := helpers.InitializeJWT() + + t.Run("Успешное обновление имени аккаунта", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + assert.NotPanics(t, func() { + token, tokenErr := jwtUtil.Create("807f1f77bcf81cd799439077") + if isNoError := assert.NoError(t, tokenErr); !isNoError { + return + } + + response, getAccountErr := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{ + URL: "http://localhost:8082/account", + Body: models.Name{ + FirstName: "Ivan", + Secondname: "Ivanov", + Middlename: "Ivanovich", + Orgname: "OOO Моя Оборона", + }, + Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)}, + }) + if isNoError := assert.NoError(t, getAccountErr); !isNoError { + return + } + if isNoRequestError := assert.Nil(t, response.Error); !isNoRequestError { + return + } + + assert.Equal(t, "807f1f77bcf81cd799439077", response.Body.UserID) + assert.Equal(t, "Ivan", response.Body.Name.FirstName) + assert.Equal(t, "Ivanov", response.Body.Name.Secondname) + assert.Equal(t, "Ivanovich", response.Body.Name.Middlename) + assert.Equal(t, "OOO Моя Оборона", response.Body.Name.Orgname) + }) + }) +}