From 95d6b247e24e0d131d6aecd452b0adc54e09cf46 Mon Sep 17 00:00:00 2001 From: MikeMwita Date: Mon, 22 May 2023 10:46:54 +0300 Subject: [PATCH 1/3] Reverted "OpenAPI file update" This reverts commit 97bc5113a30e6b51f2aa99ea2b3fb2f48ce36c0c. --- docs/everyshilling_openapi.yaml | 240 ++++++++---------- services/app-exchange/config/config.go | 0 .../internal/application/buy/buy_service.go | 1 - .../app-exchange/internal/dto/http.gen.go | 1 - .../internal/routes/handlers/handler_gen.go | 1 - 5 files changed, 108 insertions(+), 135 deletions(-) delete mode 100644 services/app-exchange/config/config.go delete mode 100644 services/app-exchange/internal/application/buy/buy_service.go delete mode 100644 services/app-exchange/internal/dto/http.gen.go delete mode 100644 services/app-exchange/internal/routes/handlers/handler_gen.go diff --git a/docs/everyshilling_openapi.yaml b/docs/everyshilling_openapi.yaml index 7293f43..ce56d92 100644 --- a/docs/everyshilling_openapi.yaml +++ b/docs/everyshilling_openapi.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - title: Every shillings OpenAPI File Description + title: Every shillings Auth OpenAPI endpoint description: Every Shilling version: 2022-04-01 termsOfService: https://everyshilling.ke/terms @@ -31,8 +31,6 @@ paths: in: path required: true description: user id - schema: - type: string responses: '200': description: user object response @@ -309,8 +307,7 @@ paths: description: "Password reset request UUID" required: true in: path - schema: - type: string + responses: "200": description: OTP generated and sent successfully @@ -457,11 +454,11 @@ paths: $ref: "#/components/schemas/LogoutRequest" responses: "200": - description: OTP generated and sent successfully + description: User logged out successfully content: application/json: schema: - $ref: "#/components/schemas/PhoneVerificationResponse" + $ref: "#/components/schemas/LogoutResponseData" "401": description: "Unauthorized: Invalid or expired token" "500": @@ -494,8 +491,6 @@ paths: post: summary: Buy Currencies description: Endpoint to buy currency - operationId : buycurrency - tags: [BuyCurrency] requestBody: required: true content: @@ -515,36 +510,31 @@ paths: /top-up-account: - post: - summary: Top up Account - description: Endpoint to top up the account - operationId: TopUp - tags : [TopUP] - - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/TopUpRequest" - responses: - '200': - description: Successful operation + post: + summary: Top up Account + description: Endpoint to top up the account + requestBody: + required: true content: application/json: schema: - $ref: "#/components/schemas/TopUpRequestData" + $ref: "#/components/schemas/TopUpRequest" + responses: + '200': + description: Successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/TopUpRequestData" - '400': - description: Invalid request payload + '400': + description: Invalid request payload /marketplace-offers: get: summary: Marketplace Offers - operationId: Offers description: Endpoint to get marketplace offers - tags: [Offers] responses: '200': description: Successful operation @@ -559,8 +549,6 @@ paths: get: summary: Account Overview description: Endpoint to get account overview - operationId : Accountoverview - tags : [AccountOverview] responses: '200': description: Successful operation @@ -569,14 +557,13 @@ paths: schema: $ref: "#/components/schemas/AccountOverviewResponse" '400': - description: Invalid request payload + description: Invalid request payload tags: - name: Register description: Registration description - name: Users description: Users path - - name: Login description: Login description @@ -584,16 +571,6 @@ tags: description: Help the user gain access and reset password - name: Phone Verification description: Make sure the user's phone is valid inorder to activate the account - - name: Offers - description: Show the available offers - - - name: BuyCurrency - description: Buy currency description - - - name: TopUp - description: Top up Account - - name : AccountOverview - description: The account overview components: securitySchemes: @@ -933,42 +910,41 @@ components: - refresh_token - bearer_token ResendNewOTPResponse: - type: object - properties: - message: - type: string - description: "Message" - error: - type: string - description: "Error" - status_code: - type: number - description: "Status code" - data: - $ref: "#/components/schemas/OTPResponseObj" + type: object + properties: + message: + type: string + description: "Message" + error: + type: string + description: "Error" + status_code: + type: number + description: "Status code" + data: + $ref: "#/components/schemas/OTPResponseObj" PhoneVerificationResponse: - type: object - properties: - message: - type: string - description: "Message" - error: - type: string - description: "Error" - status_code: - type: number - description: "Status code" - data: - type: object - properties: - bearer_token: - type: string - refresh_token: - type: string - required: - - bearer_token - - refresh_token + type: object + properties: + message: + type: string + description: "Message" + error: + type: string + description: "Error" + status_code: + type: number + description: "Status code" + data: + type: object + properties: + bearer_token: + type: string + required: true + refresh_token: + type: string + required: true BuyRequest: type: object @@ -1000,12 +976,12 @@ components: data: $ref: "#/components/schemas/BuyRequestObj" BuyRequestObj: - type: object - properties: - buyId: - type: integer - description: "The unique identifier of the buy request" - example: 1234 + type: object + properties: + buyId: + type: integer + description: "The unique identifier of the buy request" + example: 1234 TopUpRequest: type: object properties: @@ -1016,25 +992,25 @@ components: type: number TopUpRequestData: - type: object - properties: - message: - type: string - example: "Your account has been topped up" - error: - type: string - code: - type: number - example: 200 - data: - $ref: "#/components/schemas/TopUpResponseObj" + type: object + properties: + message: + type: string + example: "Your account has been topped up" + error: + type: string + code: + type: number + example: 200 + data: + $ref: "#/components/schemas/TopUpResponseObj" TopUpResponseObj: - type: object - properties: - topUpId: - type: integer - description: "The unique identifier of the top-up request" - example: 1234 + type: object + properties: + topUpId: + type: integer + description: "The unique identifier of the top-up request" + example: 1234 MarketplaceOffersResponse: type: object @@ -1049,41 +1025,41 @@ components: type: number description: Conversion rate from US Dollar to Kenyan Shilling data: - $ref: "#/components/schemas/MarketplaceOffersResponseObj" + $ref: "#/components/schemas/MarketplaceOffersResponseObj" MarketplaceOffersResponseObj: - type: array - items: + type: array + items: + type: object + properties: + currency: + type: string + description: Currency code + example: "USD" + rate: + type: number + description: Exchange rate for the currency + example: 110.0 + + AccountOverviewResponse: type: object properties: - currency: - type: string - description: Currency code - example: "USD" - rate: + balance: type: number - description: Exchange rate for the currency - example: 110.0 - - AccountOverviewResponse: - type: object - properties: - balance: - type: number - description: Current account balance - paymentDetails: - type: object - properties: - amountPaid: - type: number - description: Amount paid in the transaction - remainingBalance: - type: number - description: Remaining balance after the transaction - transactionSummary: - type: object - description: Summary or details of the payment made - data: - $ref: "#/components/schemas/AccountOverviewResponseObj" + description: Current account balance + paymentDetails: + type: object + properties: + amountPaid: + type: number + description: Amount paid in the transaction + remainingBalance: + type: number + description: Remaining balance after the transaction + transactionSummary: + type: object + description: Summary or details of the payment made + data: + $ref: "#/components/schemas/AccountOverviewResponseObj" AccountOverviewResponseObj: type: object properties: diff --git a/services/app-exchange/config/config.go b/services/app-exchange/config/config.go deleted file mode 100644 index e69de29..0000000 diff --git a/services/app-exchange/internal/application/buy/buy_service.go b/services/app-exchange/internal/application/buy/buy_service.go deleted file mode 100644 index fc657ef..0000000 --- a/services/app-exchange/internal/application/buy/buy_service.go +++ /dev/null @@ -1 +0,0 @@ -package buy diff --git a/services/app-exchange/internal/dto/http.gen.go b/services/app-exchange/internal/dto/http.gen.go deleted file mode 100644 index 76d3a17..0000000 --- a/services/app-exchange/internal/dto/http.gen.go +++ /dev/null @@ -1 +0,0 @@ -package dto diff --git a/services/app-exchange/internal/routes/handlers/handler_gen.go b/services/app-exchange/internal/routes/handlers/handler_gen.go deleted file mode 100644 index 5ac8282..0000000 --- a/services/app-exchange/internal/routes/handlers/handler_gen.go +++ /dev/null @@ -1 +0,0 @@ -package handlers From 765b08c23fdfbc96a76ef669e16a3d27188210b8 Mon Sep 17 00:00:00 2001 From: MikeMwita Date: Sun, 2 Jul 2023 15:02:43 +0300 Subject: [PATCH 2/3] implemented exchange adapter --- .github/workflows/deploy_images.yml | 14 +- .github/workflows/github-actions.yml | 2 +- Makefile | 6 +- Monitoring/alertmanager/config.yml | 10 + Monitoring/docker-compose.yml | 78 ++++++- Monitoring/grafana.ini | 2 - Monitoring/grafana/config.monitoring | 3 + .../provisioning/dashboards/dashboard.yml | 11 + .../provisioning/datasources/datasource.yml | 50 +++++ Monitoring/prometheus.yml | 33 ++- Monitoring/prometheus/alerts.rule | 22 ++ Monitoring/prometheus/prometheus.yml | 106 +++++++++ docker-compose-dev.yml | 12 ++ docs/protos/db/account.proto | 45 ++++ docs/protos/db/dbserver.proto | 30 +++ docs/protos/db/rate.proto | 43 ++++ docs/protos/db/trade.proto | 45 ++++ docs/protos/db/transactions.proto | 44 ++++ docs/protos/exchange/account.proto | 45 ++++ docs/protos/exchange/exchangeserver.proto | 37 ++++ docs/protos/exchange/rate.proto | 43 ++++ docs/protos/exchange/trade.proto | 45 ++++ docs/protos/exchange/transactions.proto | 44 ++++ docs/protos/exchange/user.proto | 85 ++++++++ docs/protos/generate-go-grpc.sh | 11 + go.mod | 17 ++ go.sum | 20 ++ protocompiled.proto | 195 +++++++++++++++++ sdk/go-proto-gen | 2 +- services/app-auth/Prod.Dockerfile | 4 +- services/app-auth/go.mod | 5 +- services/app-auth/go.sum | 6 +- .../internal/core/adapters/exchange.go | 85 ++++++++ .../internal/core/adapters/usecase.go | 28 +++ .../internal/core/repository/exchange.go | 164 ++++++++++++++ .../internal/core/storage/exchange.go | 163 ++++++++++++++ .../internal/core/usecase/exchange.go | 167 ++++++++++++++ services/app-auth/internal/dto/http.gen.go | 14 +- .../app-auth/internal/routes/server/server.go | 7 +- services/app-auth/main.go | 1 + services/app-db/go.mod | 3 +- services/app-db/go.sum | 4 +- .../app-db/internal/core/models/account.go | 16 ++ services/app-db/internal/core/models/rate.go | 9 + services/app-db/internal/core/models/trade.go | 13 ++ .../internal/core/models/transaction.go | 12 ++ .../core/ports/accounts-collection.go | 22 ++ .../internal/core/ports/rates-collection.go | 21 ++ .../internal/core/ports/trade-collection.go | 33 +++ .../core/ports/transactions-collection.go | 28 +++ .../{user-database.go => user-collection.go} | 0 .../core/storage/rates-postgres-storage.go | 84 ++++++++ services/app-db/internal/dto/rates.go | 6 + .../internal/platform/postgres/postgresql.go | 2 +- .../app-db/internal/repository/accounts.go | 65 ++++++ services/app-db/internal/repository/rates.go | 32 +++ services/app-db/internal/repository/trades.go | 64 ++++++ .../internal/repository/transactions.go | 58 +++++ .../handlers/{handler.go => prepare.go} | 8 +- .../internal/routes/handlers/rates-handler.go | 126 +++++++++++ .../internal/routes/server/grpc-server.go | 7 +- services/app-exchange/.env.example | 0 services/app-exchange/Dockerfile | 29 +++ services/app-exchange/config/config.go | 48 +++++ services/app-exchange/go.mod | 18 ++ services/app-exchange/go.sum | 36 ++++ .../internal/core/model/account.go | 32 +++ .../internal/core/ports/accounts.go | 22 ++ .../internal/core/ports/database.go | 30 +++ .../internal/core/ports/exchange.go | 14 ++ .../app-exchange/internal/core/ports/trade.go | 23 ++ .../internal/core/ports/transactions.go | 23 ++ .../internal/core/repositories/account.go | 49 +++++ .../internal/core/repositories/exchange.go | 25 +++ .../internal/core/repositories/trade.go | 58 +++++ .../core/repositories/transactions.go | 58 +++++ .../internal/core/services/accounts.go | 66 ++++++ .../internal/core/services/exchange.go | 19 ++ .../internal/core/services/trade.go | 203 ++++++++++++++++++ .../internal/core/services/transactions.go | 130 +++++++++++ .../internal/core/storage/app-db.go | 146 +++++++++++++ .../app-exchange/internal/platform/app-db.go | 22 ++ .../internal/routes/handlers/account.go | 102 +++++++++ .../internal/routes/handlers/handler.go | 34 +++ .../internal/routes/handlers/rates.go | 63 ++++++ .../internal/routes/handlers/trade.go | 138 ++++++++++++ .../internal/routes/handlers/transactions.go | 30 +++ .../internal/routes/server/grpc-server.go | 82 +++++++ services/app-exchange/main.go | 13 ++ services/app-ratescron/.env.example | 4 + services/app-ratescron/Dockerfile | 39 ++++ services/app-ratescron/README.md | 3 + services/app-ratescron/config/config.go | 56 +++++ services/app-ratescron/go.mod | 19 ++ services/app-ratescron/go.sum | 38 ++++ .../internal/core/model/conversion-rate.go | 8 + .../internal/core/ports/database.go | 27 +++ .../internal/core/ports/rates.go | 29 +++ .../internal/core/repository/database.go | 31 +++ .../internal/core/repository/rates-api.go | 33 +++ .../internal/core/storage/database.go | 70 ++++++ .../core/storage/exchange-rate-api.go | 23 ++ .../internal/core/storage/fixer-api.go | 19 ++ .../internal/dto/exchange-rate-api.go | 31 +++ .../app-ratescron/internal/dto/fixer-api.go | 24 +++ .../app-ratescron/internal/platform/app-db.go | 25 +++ .../internal/platform/exchange-rate-api.go | 45 ++++ .../internal/platform/fixer-api.go | 45 ++++ .../internal/usecase/fetch-rates.go | 45 ++++ services/app-ratescron/main.go | 36 ++++ 110 files changed, 4472 insertions(+), 48 deletions(-) create mode 100644 Monitoring/alertmanager/config.yml delete mode 100644 Monitoring/grafana.ini create mode 100644 Monitoring/grafana/config.monitoring create mode 100644 Monitoring/grafana/provisioning/dashboards/dashboard.yml create mode 100644 Monitoring/grafana/provisioning/datasources/datasource.yml create mode 100644 Monitoring/prometheus/alerts.rule create mode 100644 Monitoring/prometheus/prometheus.yml create mode 100644 docs/protos/db/account.proto create mode 100644 docs/protos/db/rate.proto create mode 100644 docs/protos/db/trade.proto create mode 100644 docs/protos/db/transactions.proto create mode 100644 docs/protos/exchange/account.proto create mode 100644 docs/protos/exchange/exchangeserver.proto create mode 100644 docs/protos/exchange/rate.proto create mode 100644 docs/protos/exchange/trade.proto create mode 100644 docs/protos/exchange/transactions.proto create mode 100644 docs/protos/exchange/user.proto create mode 100644 go.mod create mode 100644 go.sum create mode 100644 protocompiled.proto create mode 100644 services/app-auth/internal/core/adapters/exchange.go create mode 100644 services/app-auth/internal/core/repository/exchange.go create mode 100644 services/app-auth/internal/core/storage/exchange.go create mode 100644 services/app-auth/internal/core/usecase/exchange.go create mode 100644 services/app-db/internal/core/models/account.go create mode 100644 services/app-db/internal/core/models/rate.go create mode 100644 services/app-db/internal/core/models/trade.go create mode 100644 services/app-db/internal/core/models/transaction.go create mode 100644 services/app-db/internal/core/ports/accounts-collection.go create mode 100644 services/app-db/internal/core/ports/rates-collection.go create mode 100644 services/app-db/internal/core/ports/trade-collection.go create mode 100644 services/app-db/internal/core/ports/transactions-collection.go rename services/app-db/internal/core/ports/{user-database.go => user-collection.go} (100%) create mode 100644 services/app-db/internal/core/storage/rates-postgres-storage.go create mode 100644 services/app-db/internal/dto/rates.go create mode 100644 services/app-db/internal/repository/accounts.go create mode 100644 services/app-db/internal/repository/rates.go create mode 100644 services/app-db/internal/repository/trades.go create mode 100644 services/app-db/internal/repository/transactions.go rename services/app-db/internal/routes/handlers/{handler.go => prepare.go} (72%) create mode 100644 services/app-db/internal/routes/handlers/rates-handler.go create mode 100644 services/app-exchange/.env.example create mode 100644 services/app-exchange/Dockerfile create mode 100644 services/app-exchange/config/config.go create mode 100644 services/app-exchange/go.mod create mode 100644 services/app-exchange/go.sum create mode 100644 services/app-exchange/internal/core/model/account.go create mode 100644 services/app-exchange/internal/core/ports/accounts.go create mode 100644 services/app-exchange/internal/core/ports/database.go create mode 100644 services/app-exchange/internal/core/ports/exchange.go create mode 100644 services/app-exchange/internal/core/ports/trade.go create mode 100644 services/app-exchange/internal/core/ports/transactions.go create mode 100644 services/app-exchange/internal/core/repositories/account.go create mode 100644 services/app-exchange/internal/core/repositories/exchange.go create mode 100644 services/app-exchange/internal/core/repositories/trade.go create mode 100644 services/app-exchange/internal/core/repositories/transactions.go create mode 100644 services/app-exchange/internal/core/services/accounts.go create mode 100644 services/app-exchange/internal/core/services/exchange.go create mode 100644 services/app-exchange/internal/core/services/trade.go create mode 100644 services/app-exchange/internal/core/services/transactions.go create mode 100644 services/app-exchange/internal/core/storage/app-db.go create mode 100644 services/app-exchange/internal/platform/app-db.go create mode 100644 services/app-exchange/internal/routes/handlers/account.go create mode 100644 services/app-exchange/internal/routes/handlers/handler.go create mode 100644 services/app-exchange/internal/routes/handlers/rates.go create mode 100644 services/app-exchange/internal/routes/handlers/trade.go create mode 100644 services/app-exchange/internal/routes/handlers/transactions.go create mode 100644 services/app-exchange/internal/routes/server/grpc-server.go create mode 100644 services/app-ratescron/.env.example create mode 100644 services/app-ratescron/Dockerfile create mode 100644 services/app-ratescron/README.md create mode 100644 services/app-ratescron/config/config.go create mode 100644 services/app-ratescron/go.mod create mode 100644 services/app-ratescron/go.sum create mode 100644 services/app-ratescron/internal/core/model/conversion-rate.go create mode 100644 services/app-ratescron/internal/core/ports/database.go create mode 100644 services/app-ratescron/internal/core/ports/rates.go create mode 100644 services/app-ratescron/internal/core/repository/database.go create mode 100644 services/app-ratescron/internal/core/repository/rates-api.go create mode 100644 services/app-ratescron/internal/core/storage/database.go create mode 100644 services/app-ratescron/internal/core/storage/exchange-rate-api.go create mode 100644 services/app-ratescron/internal/core/storage/fixer-api.go create mode 100644 services/app-ratescron/internal/dto/exchange-rate-api.go create mode 100644 services/app-ratescron/internal/dto/fixer-api.go create mode 100644 services/app-ratescron/internal/platform/app-db.go create mode 100644 services/app-ratescron/internal/platform/exchange-rate-api.go create mode 100644 services/app-ratescron/internal/platform/fixer-api.go create mode 100644 services/app-ratescron/internal/usecase/fetch-rates.go create mode 100644 services/app-ratescron/main.go diff --git a/.github/workflows/deploy_images.yml b/.github/workflows/deploy_images.yml index 727d897..d3f1f52 100644 --- a/.github/workflows/deploy_images.yml +++ b/.github/workflows/deploy_images.yml @@ -42,10 +42,10 @@ jobs: docker push ${{ secrets.ECR_ACCOUNT }}/$ECR_REPOSITORY:app-otp -# # everyshilling is the stack name -# - name: Deploy Docker stack -# run: | -# echo ${{ secrets.AWS_ACCESS_KEY_ID }} > ~/.aws/credentials -# echo ${{ secrets.AWS_SECRET_ACCESS_KEY }} >> ~/.aws/credentials -# aws ecr get-login-password --region us-east-1 | docker secret create ecr-login - -# docker stack deploy -c dockerswarm-compose.yml everyshilling + # everyshilling is the stack name + - name: Deploy Docker stack + run: | + echo ${{ secrets.AWS_ACCESS_KEY_ID }} > ~/.aws/credentials + echo ${{ secrets.AWS_SECRET_ACCESS_KEY }} >> ~/.aws/credentials + aws ecr get-login-password --region us-east-1 | docker secret create ecr-login - + docker stack deploy -c dockerswarm-compose.yml everyshilling diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 1ed3476..b7768a7 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -40,7 +40,7 @@ jobs: #- name: install dependencies # run: go mod download - #setting up python application + #setting up python core - name: Set up Python uses: actions/setup-python@v2 with: diff --git a/Makefile b/Makefile index 2217d99..712454e 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ build-dev: - docker-compose -f docker-compose-dev.yml -p backend-everyshilling build + docker-compose --verbose -f docker-compose-dev.yml -p backend-everyshilling build run-dev: - docker-compose -f docker-compose-dev.yml -p backend-everyshilling up --build + docker-compose --verbose -f docker-compose-dev.yml -p backend-everyshilling up --build --remove-orphans prune-dev: - docker-compose -f docker-compose-dev.yml -p backend-everyshilling down --remove-orphans + docker-compose -f docker-compose-dev.yml -p backend-everyshilling down --remove-orphans --volumes --rmi all diff --git a/Monitoring/alertmanager/config.yml b/Monitoring/alertmanager/config.yml new file mode 100644 index 0000000..c931962 --- /dev/null +++ b/Monitoring/alertmanager/config.yml @@ -0,0 +1,10 @@ +route: + receiver: 'slack' + +receivers: + - name: 'slack' +# slack_configs: +# - send_resolved: true +# username: '' +# channel: '#' +# api_url: '' \ No newline at end of file diff --git a/Monitoring/docker-compose.yml b/Monitoring/docker-compose.yml index 2c721c8..2337238 100644 --- a/Monitoring/docker-compose.yml +++ b/Monitoring/docker-compose.yml @@ -1,17 +1,87 @@ -version: '3' +version: '3.8' + +volumes: + prometheus_data: {} + grafana_data: {} + services: prometheus: image: prom/prometheus + restart: always + volumes: + - ./prometheus:/etc/prometheus/ + - prometheus_data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/usr/share/prometheus/console_libraries' + - '--web.console.templates=/usr/share/prometheus/consoles' ports: - 9090:9090 + links: + - cadvisor:cadvisor + - alertmanager:alertmanager + depends_on: + - cadvisor + + node-exporter: + image: prom/node-exporter volumes: - - ./prometheus.yml:/etc/prometheus/prometheus.yml + - /proc:/host/proc:ro + - /sys:/host/sys:ro + - /:/rootfs:ro + command: + - '--path.procfs=/host/proc' + - '--path.sysfs=/host/sys' + - --collector.filesystem.ignored-mount-points + - '^/(sys|proc|dev|host|etc|rootfs/var/lib/docker/containers|rootfs/var/lib/docker/overlay2|rootfs/run/docker/netns|rootfs/var/lib/docker/aufs)($$|/)' + ports: + - 9100:9100 + restart: always + deploy: + mode: global + + alertmanager: + image: prom/alertmanager + restart: always + ports: + - 9093:9093 + volumes: + - ./alertmanager/:/etc/alertmanager/ + command: + - '--config.file=/etc/alertmanager/config.yml' + - '--storage.path=/alertmanager' + + cadvisor: + image: gcr.io/cadvisor/cadvisor + volumes: + - /:/rootfs:ro + - /var/run:/var/run:rw + - /sys:/sys:ro + - /var/lib/docker/:/var/lib/docker:ro + ports: + - 8080:8080 + restart: always + deploy: + mode: global grafana: image: grafana/grafana + user: '472' + restart: always + environment: + GF_INSTALL_PLUGINS: 'grafana-clock-panel,grafana-simple-json-datasource' + volumes: + - grafana_data:/var/lib/grafana + - ./grafana/provisioning/:/etc/grafana/provisioning/ + env_file: + - ./grafana/config.monitoring ports: - 3000:3000 - volumes: - - ./grafana.ini:/etc/grafana/grafana.ini depends_on: - prometheus + +# networks: +# default: +# external: +# name: backend-everyshilling-1_default \ No newline at end of file diff --git a/Monitoring/grafana.ini b/Monitoring/grafana.ini deleted file mode 100644 index cbf6a67..0000000 --- a/Monitoring/grafana.ini +++ /dev/null @@ -1,2 +0,0 @@ -[server] -http_port = 3000 diff --git a/Monitoring/grafana/config.monitoring b/Monitoring/grafana/config.monitoring new file mode 100644 index 0000000..b31d46a --- /dev/null +++ b/Monitoring/grafana/config.monitoring @@ -0,0 +1,3 @@ +GF_SECURITY_ADMIN_USER=admin +GF_SECURITY_ADMIN_PASSWORD=foobar +GF_USERS_ALLOW_SIGN_UP=false \ No newline at end of file diff --git a/Monitoring/grafana/provisioning/dashboards/dashboard.yml b/Monitoring/grafana/provisioning/dashboards/dashboard.yml new file mode 100644 index 0000000..2e2dee6 --- /dev/null +++ b/Monitoring/grafana/provisioning/dashboards/dashboard.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +providers: +- name: 'Prometheus' + orgId: 1 + folder: '' + type: file + disableDeletion: false + editable: true + options: + path: /etc/grafana/provisioning/dashboards \ No newline at end of file diff --git a/Monitoring/grafana/provisioning/datasources/datasource.yml b/Monitoring/grafana/provisioning/datasources/datasource.yml new file mode 100644 index 0000000..ebe5c89 --- /dev/null +++ b/Monitoring/grafana/provisioning/datasources/datasource.yml @@ -0,0 +1,50 @@ +# config file version +apiVersion: 1 + +# list of datasources that should be deleted from the database +deleteDatasources: + - name: Prometheus + orgId: 1 + +# list of datasources to insert/update depending +# whats available in the database +datasources: + # name of the datasource. Required +- name: Prometheus + # datasource type. Required + type: prometheus + # access mode. direct or proxy. Required + access: proxy + # org id. will default to orgId 1 if not specified + orgId: 1 + # url + url: http://prometheus:9090 + # database password, if used + password: + # database user, if used + user: + # database name, if used + database: + # enable/disable basic auth + basicAuth: false + # basic auth username, if used + basicAuthUser: + # basic auth password, if used + basicAuthPassword: + # enable/disable with credentials headers + withCredentials: + # mark as default datasource. Max one per org + isDefault: true + # fields that will be converted to json and stored in json_data + jsonData: + graphiteVersion: "1.1" + tlsAuth: false + tlsAuthWithCACert: false + # json object of data that will be encrypted. + secureJsonData: + tlsCACert: "..." + tlsClientCert: "..." + tlsClientKey: "..." + version: 1 + # allow users to edit datasources from the UI. + editable: true \ No newline at end of file diff --git a/Monitoring/prometheus.yml b/Monitoring/prometheus.yml index 6cca209..96eff2f 100644 --- a/Monitoring/prometheus.yml +++ b/Monitoring/prometheus.yml @@ -1,4 +1,29 @@ -scrape_configs: - - job_name: 'myapp' - static_configs: - - targets: ['app-auth:3002'] # Replace with your application's hostname and port +# config.file: ./Monitoring/prometheus.yml + +# global: +# scrape_interval: 15s +# evaluation_interval: 15s + +# rule_files: +# # - "first.rules" +# # - "second.rules" + +# scrape_configs: +# - job_name: prometheus +# static_configs: +# - targets: ['localhost:9090'] + +# - job_name: app-auth +# scrape_interval: 5s +# static_configs: +# - targets: ['app-auth:3002'] + +# - job_name: app-db +# scrape_interval: 5s +# static_configs: +# - targets: ['app-db:3001'] + +# - job_name: app-otp +# scrape_interval: 5s +# static_configs: +# - targets: ['app-otp:3008'] diff --git a/Monitoring/prometheus/alerts.rule b/Monitoring/prometheus/alerts.rule new file mode 100644 index 0000000..2cdbc81 --- /dev/null +++ b/Monitoring/prometheus/alerts.rule @@ -0,0 +1,22 @@ +groups: +- name: example + rules: + + # Alert for any instance that is unreachable for >2 minutes. + - alert: service_down + expr: up == 0 + for: 2m + labels: + severity: page + annotations: + summary: "Instance {{ $labels.instance }} down" + description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 2 minutes." + + - alert: high_load + expr: node_load1 > 0.5 + for: 2m + labels: + severity: page + annotations: + summary: "Instance {{ $labels.instance }} under high load" + description: "{{ $labels.instance }} of job {{ $labels.job }} is under high load." \ No newline at end of file diff --git a/Monitoring/prometheus/prometheus.yml b/Monitoring/prometheus/prometheus.yml new file mode 100644 index 0000000..09200fb --- /dev/null +++ b/Monitoring/prometheus/prometheus.yml @@ -0,0 +1,106 @@ +# my global config +global: + scrape_interval: 15s + evaluation_interval: 15s + # scrape_timeout is set to the global default (10s). + + + external_labels: + monitor: 'everyshilling' + +# Load and evaluate rules in this file every 'evaluation_interval' seconds. +rule_files: + - 'alert.rules' + # - "first.rules" + # - "second.rules" + +alerting: + alertmanagers: + - scheme: http + static_configs: + - targets: + - "alertmanager:9093" + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + # The job name is added as a label `job=` to any timeseries scraped from this config. + + - job_name: app + scrape_interval: 5s + static_configs: + - targets: ['host.docker.internal:8000'] + + - job_name: 'prometheus' + + # Override the global default and scrape targets from this job every 5 seconds. + scrape_interval: 5s + + static_configs: + - targets: ['localhost:9090'] + + + - job_name: 'cadvisor' + # Override the global default and scrape targets from this job every 5 seconds. + scrape_interval: 5s + + dns_sd_configs: + - names: + - 'tasks.cadvisor' + type: 'A' + port: 8080 + +# static_configs: +# - targets: ['cadvisor:8080'] + + - job_name: 'node-exporter' + + # Override the global default and scrape targets from this job every 5 seconds. + scrape_interval: 5s + + dns_sd_configs: + - names: + - 'tasks.node-exporter' + type: 'A' + port: 9100 + +# - job_name: 'pushgateway' +# scrape_interval: 10s +# dns_sd_configs: +# - names: +# - 'tasks.pushgateway' +# type: 'A' +# port: 9091 + +# static_configs: +# - targets: ['node-exporter:9100'] + +# config.file: ./Monitoring/prometheus.yml + +# global: +# scrape_interval: 15s +# evaluation_interval: 15s + +# rule_files: +# # - "first.rules" +# # - "second.rules" + +# scrape_configs: +# - job_name: prometheus +# static_configs: +# - targets: ['localhost:9090'] + + - job_name: app-auth + scrape_interval: 5s + static_configs: + - targets: ['app-auth:3002'] + + - job_name: app-db + scrape_interval: 5s + static_configs: + - targets: ['app-db:3001'] + + - job_name: app-otp + scrape_interval: 5s + static_configs: + - targets: ['app-otp:3008'] diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index a84244f..b777d0a 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -127,6 +127,18 @@ services: - postgres-db - liquibase + app-ratescron: + container_name: app-ratescron + build: + context: services/app-ratescron + dockerfile: Dockerfile + env_file: + - services/app-ratescron/.env.example + networks: + - internal_network + depends_on: + - app-db + #to be changed once handlers is set networks: internal_network: diff --git a/docs/protos/db/account.proto b/docs/protos/db/account.proto new file mode 100644 index 0000000..f1dcb7b --- /dev/null +++ b/docs/protos/db/account.proto @@ -0,0 +1,45 @@ +syntax="proto3"; +package db; +option go_package = "github.com/AppsLab-KE/backend-everyshillings/db"; + + +message Account { + string account_id = 1; + string user_id = 2; + int64 balance = 3; + string base_currency = 4; + int64 created_at = 5; + string parent_account_id = 6; +} + +message CreateAccountRequest { + Account account = 1; +} + +message CreateAccountResponse { + Account account = 1; +} + +message DeleteAccountRequest { + string account_id = 1; +} + +message DeleteAccountResponse { +} + +message UpdateAccountRequest { + Account account = 1; +} + +message UpdateAccountResponse { + Account account = 1; +} + +message SearchAccountRequest { + string user_id = 1; + string account_id = 2; +} + +message SearchAccountResponse { + repeated Account accounts = 1; +} \ No newline at end of file diff --git a/docs/protos/db/dbserver.proto b/docs/protos/db/dbserver.proto index 41ccdc0..a20cac1 100644 --- a/docs/protos/db/dbserver.proto +++ b/docs/protos/db/dbserver.proto @@ -1,6 +1,11 @@ syntax = "proto3"; import "user.proto"; +import "rate.proto"; +import "account.proto"; +import "trade.proto"; +import "transactions.proto"; +import "google/protobuf/empty.proto"; package db; option go_package = "github.com/AppsLab-KE/backend-everyshillings/db"; @@ -11,6 +16,31 @@ service DbService { rpc UpdateUser(UpdateUserReq) returns (UpdateUserRes) {} rpc GetPagedUsers(GetPagedUsersReq) returns (GetPagedUsersRes) {} rpc GetUserByField(GetByfieldReq) returns (GetByfieldRes) {} + // RATES + rpc CreateConversionRate(CreateConversionRateRequest) returns (CreateConversionRateResponse) {} + rpc ReadConversionRate(ReadConversionRateRequest) returns (ReadConversionRateResponse) {} + rpc UpdateConversionRate(UpdateConversionRateRequest) returns (UpdateConversionRateResponse) {} + rpc DeleteConversionRate(DeleteConversionRateRequest) returns (DeleteConversionRateResponse) {} + + // ACCOUNT + rpc CreateAccount(CreateAccountRequest) returns (CreateAccountResponse); + rpc DeleteAccount(DeleteAccountRequest) returns (DeleteAccountResponse); + rpc UpdateAccount(UpdateAccountRequest) returns (UpdateAccountResponse); + rpc SearchAccount(SearchAccountRequest) returns (SearchAccountResponse); + + // TRANSACTION + rpc CreateTransaction(CreateTransactionRequest) returns (CreateTransactionResponse); + rpc DeleteTransaction(DeleteTransactionRequest) returns (google.protobuf.Empty); + rpc UpdateTransaction(UpdateTransactionRequest) returns (google.protobuf.Empty); + rpc GetTransaction(GetTransactionRequest) returns (Transaction); + rpc GetTransactionByAccount(GetTransactionByAccountRequest) returns (GetTransactionByAccountResponse); + + // TRADING + rpc CreateTrade(CreateTradeRequest) returns (CreateTradeResponse); + rpc DeleteTrade(DeleteTradeRequest) returns (google.protobuf.Empty); + rpc UpdateTrade(UpdateTradeRequest) returns (google.protobuf.Empty); + rpc GetTrade(GetTradeRequest) returns (Trade); + rpc GetTradeByAccount(GetTradeByAccountRequest) returns (GetTradeByAccountResponse); } message DefaultRequest {} diff --git a/docs/protos/db/rate.proto b/docs/protos/db/rate.proto new file mode 100644 index 0000000..8387bda --- /dev/null +++ b/docs/protos/db/rate.proto @@ -0,0 +1,43 @@ +syntax="proto3"; +package db; +option go_package = "github.com/AppsLab-KE/backend-everyshillings/db"; + +message ConversionRate { + string from_currency = 1; + string to_currency = 2; + double rate = 3; + int64 date_updated_unix_utc = 4; + string uuid = 5; +} + + +message CreateConversionRateRequest { + repeated ConversionRate conversion_rate = 1; +} + +message CreateConversionRateResponse { + repeated ConversionRate conversion_rate = 1; +} + +message ReadConversionRateRequest { + int64 from_unix_utc = 1; + int64 to_unix_utc = 2; +} + +message ReadConversionRateResponse { + repeated ConversionRate conversion_rate = 1; +} + +message UpdateConversionRateRequest { + ConversionRate conversion_rate = 1; +} + +message UpdateConversionRateResponse {} + +message DeleteConversionRateRequest { + string uuid = 1; +} + +message DeleteConversionRateResponse {} + + diff --git a/docs/protos/db/trade.proto b/docs/protos/db/trade.proto new file mode 100644 index 0000000..46b9aab --- /dev/null +++ b/docs/protos/db/trade.proto @@ -0,0 +1,45 @@ +syntax="proto3"; +package db; +option go_package = "github.com/AppsLab-KE/backend-everyshillings/db"; + + +message Trade { + string trade_id = 1; + string account_id = 2; + string trade_type = 3; + string trade_status = 4; + string from_currency = 5; + string to_currency = 6; + int64 from_amount = 7; + int64 final_amount = 8; +} + +message CreateTradeRequest { + Trade trade = 1; +} + +message CreateTradeResponse { + string trade_id = 1; +} + +message DeleteTradeRequest { + string trade_id = 1; +} + +message UpdateTradeRequest { + Trade trade = 1; +} + +message GetTradeRequest { + string trade_id = 1; +} + +message GetTradeByAccountRequest { + string account_id = 1; +} + +message GetTradeByAccountResponse { + repeated Trade trades = 1; +} + + diff --git a/docs/protos/db/transactions.proto b/docs/protos/db/transactions.proto new file mode 100644 index 0000000..1e7ef2a --- /dev/null +++ b/docs/protos/db/transactions.proto @@ -0,0 +1,44 @@ +syntax="proto3"; +package db; +option go_package = "github.com/AppsLab-KE/backend-everyshillings/db"; + + +message Transaction { + string transaction_id = 1; + string account_id = 2; + int64 amount = 3; + string transaction_type = 4; + string transaction_status = 5; + string transaction_code = 6; + string transaction_description = 7; + int64 created_at = 8; +} + + +message CreateTransactionRequest { + Transaction transaction = 1; +} + +message CreateTransactionResponse { + string transaction_id = 1; +} + +message DeleteTransactionRequest { + string transaction_id = 1; +} + +message UpdateTransactionRequest { + Transaction transaction = 1; +} + +message GetTransactionRequest { + string transaction_id = 1; +} + +message GetTransactionByAccountRequest { + string account_id = 1; +} + +message GetTransactionByAccountResponse { + repeated Transaction transactions = 1; +} diff --git a/docs/protos/exchange/account.proto b/docs/protos/exchange/account.proto new file mode 100644 index 0000000..2248d44 --- /dev/null +++ b/docs/protos/exchange/account.proto @@ -0,0 +1,45 @@ +syntax="proto3"; +package db; +option go_package = "github.com/AppsLab-KE/backend-everyshillings/exchange"; + + +message Account { + string account_id = 1; + string user_id = 2; + int64 balance = 3; + string base_currency = 4; + int64 created_at = 5; + string parent_account_id = 6; +} + +message CreateAccountRequest { + Account account = 1; +} + +message CreateAccountResponse { + Account account = 1; +} + +message DeleteAccountRequest { + string account_id = 1; +} + +message DeleteAccountResponse { +} + +message UpdateAccountRequest { + Account account = 1; +} + +message UpdateAccountResponse { + Account account = 1; +} + +message SearchAccountRequest { + string user_id = 1; + string account_id = 2; +} + +message SearchAccountResponse { + repeated Account accounts = 1; +} \ No newline at end of file diff --git a/docs/protos/exchange/exchangeserver.proto b/docs/protos/exchange/exchangeserver.proto new file mode 100644 index 0000000..c264eed --- /dev/null +++ b/docs/protos/exchange/exchangeserver.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; +import "rate.proto"; +import "account.proto"; +import "trade.proto"; +import "transactions.proto"; + +import "google/protobuf/empty.proto"; +package db; +option go_package = "github.com/AppsLab-KE/backend-everyshillings/exchange"; + +service ExchangeService { + // RATES + rpc CreateConversionRate(CreateConversionRateRequest) returns (CreateConversionRateResponse) {} + rpc ReadConversionRate(ReadConversionRateRequest) returns (ReadConversionRateResponse) {} + rpc UpdateConversionRate(UpdateConversionRateRequest) returns (UpdateConversionRateResponse) {} + rpc DeleteConversionRate(DeleteConversionRateRequest) returns (DeleteConversionRateResponse) {} + + // ACCOUNT + rpc CreateAccount(CreateAccountRequest) returns (CreateAccountResponse); + rpc DeleteAccount(DeleteAccountRequest) returns (DeleteAccountResponse); + rpc UpdateAccount(UpdateAccountRequest) returns (UpdateAccountResponse); + rpc SearchAccount(SearchAccountRequest) returns (SearchAccountResponse); + + // TRANSACTION + rpc CreateTransaction(CreateTransactionRequest) returns (CreateTransactionResponse); + rpc DeleteTransaction(DeleteTransactionRequest) returns (google.protobuf.Empty); + rpc UpdateTransaction(UpdateTransactionRequest) returns (google.protobuf.Empty); + rpc GetTransaction(GetTransactionRequest) returns (Transaction); + rpc GetTransactionByAccount(GetTransactionByAccountRequest) returns (GetTransactionByAccountResponse); + + // TRADING + rpc CreateTrade(CreateTradeRequest) returns (CreateTradeResponse); + rpc DeleteTrade(DeleteTradeRequest) returns (google.protobuf.Empty); + rpc UpdateTrade(UpdateTradeRequest) returns (google.protobuf.Empty); + rpc GetTrade(GetTradeRequest) returns (Trade); + rpc GetTradeByAccount(GetTradeByAccountRequest) returns (GetTradeByAccountResponse); +} \ No newline at end of file diff --git a/docs/protos/exchange/rate.proto b/docs/protos/exchange/rate.proto new file mode 100644 index 0000000..350971a --- /dev/null +++ b/docs/protos/exchange/rate.proto @@ -0,0 +1,43 @@ +syntax="proto3"; +package db; +option go_package = "github.com/AppsLab-KE/backend-everyshillings/exchange"; + +message ConversionRate { + string from_currency = 1; + string to_currency = 2; + double rate = 3; + int64 date_updated_unix_utc = 4; + string uuid = 5; +} + + +message CreateConversionRateRequest { + repeated ConversionRate conversion_rate = 1; +} + +message CreateConversionRateResponse { + repeated ConversionRate conversion_rate = 1; +} + +message ReadConversionRateRequest { + int64 from_unix_utc = 1; + int64 to_unix_utc = 2; +} + +message ReadConversionRateResponse { + repeated ConversionRate conversion_rate = 1; +} + +message UpdateConversionRateRequest { + ConversionRate conversion_rate = 1; +} + +message UpdateConversionRateResponse {} + +message DeleteConversionRateRequest { + string uuid = 1; +} + +message DeleteConversionRateResponse {} + + diff --git a/docs/protos/exchange/trade.proto b/docs/protos/exchange/trade.proto new file mode 100644 index 0000000..2e22307 --- /dev/null +++ b/docs/protos/exchange/trade.proto @@ -0,0 +1,45 @@ +syntax="proto3"; +package db; +option go_package = "github.com/AppsLab-KE/backend-everyshillings/exchange"; + + +message Trade { + string trade_id = 1; + string account_id = 2; + string trade_type = 3; + string trade_status = 4; + string from_currency = 5; + string to_currency = 6; + int64 from_amount = 7; + int64 final_amount = 8; +} + +message CreateTradeRequest { + Trade trade = 1; +} + +message CreateTradeResponse { + string trade_id = 1; +} + +message DeleteTradeRequest { + string trade_id = 1; +} + +message UpdateTradeRequest { + Trade trade = 1; +} + +message GetTradeRequest { + string trade_id = 1; +} + +message GetTradeByAccountRequest { + string account_id = 1; +} + +message GetTradeByAccountResponse { + repeated Trade trades = 1; +} + + diff --git a/docs/protos/exchange/transactions.proto b/docs/protos/exchange/transactions.proto new file mode 100644 index 0000000..4adf8ee --- /dev/null +++ b/docs/protos/exchange/transactions.proto @@ -0,0 +1,44 @@ +syntax="proto3"; +package db; +option go_package = "github.com/AppsLab-KE/backend-everyshillings/exchange"; + + +message Transaction { + string transaction_id = 1; + string account_id = 2; + int64 amount = 3; + string transaction_type = 4; + string transaction_status = 5; + string transaction_code = 6; + string transaction_description = 7; + int64 created_at = 8; +} + + +message CreateTransactionRequest { + Transaction transaction = 1; +} + +message CreateTransactionResponse { + string transaction_id = 1; +} + +message DeleteTransactionRequest { + string transaction_id = 1; +} + +message UpdateTransactionRequest { + Transaction transaction = 1; +} + +message GetTransactionRequest { + string transaction_id = 1; +} + +message GetTransactionByAccountRequest { + string account_id = 1; +} + +message GetTransactionByAccountResponse { + repeated Transaction transactions = 1; +} diff --git a/docs/protos/exchange/user.proto b/docs/protos/exchange/user.proto new file mode 100644 index 0000000..d8eba40 --- /dev/null +++ b/docs/protos/exchange/user.proto @@ -0,0 +1,85 @@ +syntax = "proto3"; + +import "google/protobuf/timestamp.proto"; +import "google/protobuf/any.proto"; +package db; +option go_package = "github.com/AppsLab-KE/backend-everyshillings/exchange"; + +// Base model +message User { + string name = 1; + string email = 2; + string phone_number = 3; + string userID = 4; + google.protobuf.Timestamp created_at = 6; + google.protobuf.Timestamp updated_at = 7; + google.protobuf.Timestamp deleted_at = 8; + string hash = 9; + bool verified = 10; +} + + +// CREATE +message CreateUserReq { + string name = 1; + string email = 2; + string phone_number = 3; + string password_hash = 4; +} + + +message CreateUserRes { + string name = 1; + string email = 2; + string phone_number = 3; + string userID = 7; + bool verified = 8; + google.protobuf.Timestamp created_at = 4; + google.protobuf.Timestamp updated_at = 5; + google.protobuf.Timestamp deleted_at = 6; + string hash = 9; +} + + +// UPDATE +message UpdateUserReq { + string name = 1; + string email = 2; + string phone_number = 3; + string password_hash = 4; + bool verified = 8; + string userID = 5; +} + +message UpdateUserRes { + string name = 1; + string email = 2; + string phone_number = 3; + string userID = 7; + bool verified = 8; + google.protobuf.Timestamp created_at = 4; + google.protobuf.Timestamp updated_at = 5; + google.protobuf.Timestamp deleted_at = 6; +} + +// READ +message GetPagedUsersReq { + int32 offset = 1; + int32 limit = 2; +} + +message GetPagedUsersRes{ + int32 offset = 1; + int32 limit = 2; + repeated User users = 3; +} + +message GetByfieldReq { + map filter = 1; + int64 offset = 2; + int64 limit = 3; +} + +message GetByfieldRes { + repeated User users = 1; +} \ No newline at end of file diff --git a/docs/protos/generate-go-grpc.sh b/docs/protos/generate-go-grpc.sh index edf0ee9..f575427 100755 --- a/docs/protos/generate-go-grpc.sh +++ b/docs/protos/generate-go-grpc.sh @@ -3,6 +3,17 @@ #Generate db proto to sdk/go-proto-gen protoc --proto_path=./db --go_out=./../../sdk/go-proto-gen/db --go_opt=paths=source_relative --go-grpc_out=./../../sdk/go-proto-gen/db --go-grpc_opt=paths=source_relative db/dbserver.proto protoc --proto_path=./db --go_out=./../../sdk/go-proto-gen/db --go_opt=paths=source_relative --go-grpc_out=./../../sdk/go-proto-gen/db --go-grpc_opt=paths=source_relative db/user.proto +protoc --proto_path=./db --go_out=./../../sdk/go-proto-gen/db --go_opt=paths=source_relative --go-grpc_out=./../../sdk/go-proto-gen/db --go-grpc_opt=paths=source_relative db/rate.proto +protoc --proto_path=./db --go_out=./../../sdk/go-proto-gen/db --go_opt=paths=source_relative --go-grpc_out=./../../sdk/go-proto-gen/db --go-grpc_opt=paths=source_relative db/account.proto +protoc --proto_path=./db --go_out=./../../sdk/go-proto-gen/db --go_opt=paths=source_relative --go-grpc_out=./../../sdk/go-proto-gen/db --go-grpc_opt=paths=source_relative db/trade.proto +protoc --proto_path=./db --go_out=./../../sdk/go-proto-gen/db --go_opt=paths=source_relative --go-grpc_out=./../../sdk/go-proto-gen/db --go-grpc_opt=paths=source_relative db/transactions.proto + +# Generate exchange proto to sdk/go-proto-gen +protoc --proto_path=./exchange --go_out=./../../sdk/go-proto-gen/exchange --go_opt=paths=source_relative --go-grpc_out=./../../sdk/go-proto-gen/exchange --go-grpc_opt=paths=source_relative exchange/exchangeserver.proto +protoc --proto_path=./exchange --go_out=./../../sdk/go-proto-gen/exchange --go_opt=paths=source_relative --go-grpc_out=./../../sdk/go-proto-gen/exchange --go-grpc_opt=paths=source_relative exchange/rate.proto +protoc --proto_path=./exchange --go_out=./../../sdk/go-proto-gen/exchange --go_opt=paths=source_relative --go-grpc_out=./../../sdk/go-proto-gen/exchange --go-grpc_opt=paths=source_relative exchange/account.proto +protoc --proto_path=./exchange --go_out=./../../sdk/go-proto-gen/exchange --go_opt=paths=source_relative --go-grpc_out=./../../sdk/go-proto-gen/exchange --go-grpc_opt=paths=source_relative exchange/trade.proto +protoc --proto_path=./exchange --go_out=./../../sdk/go-proto-gen/exchange --go_opt=paths=source_relative --go-grpc_out=./../../sdk/go-proto-gen/exchange --go-grpc_opt=paths=source_relative exchange/transactions.proto #Generate otp proto to sdk/go-proto-gen protoc --proto_path=./otp --go_out=./../../sdk/go-proto-gen/otp --go_opt=paths=source_relative --go-grpc_out=./../../sdk/go-proto-gen/otp --go-grpc_opt=paths=source_relative otp/otpserver.proto diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a898b14 --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module github.com/AppsLab-KE/be-go-gen-grpc + +go 1.19 + +require ( + github.com/golang/protobuf v1.5.3 + google.golang.org/grpc v1.55.0 + google.golang.org/protobuf v1.30.0 +) + +require ( + golang.org/x/net v0.8.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect + +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..dc51440 --- /dev/null +++ b/go.sum @@ -0,0 +1,20 @@ +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/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +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= diff --git a/protocompiled.proto b/protocompiled.proto new file mode 100644 index 0000000..08ac08a --- /dev/null +++ b/protocompiled.proto @@ -0,0 +1,195 @@ +syntax = "proto3"; + +import "google/protobuf/empty.proto"; + + +message Trade { + string trade_id = 1; + string account_id = 2; + string trade_type = 3; + string trade_status = 4; + string from_currency = 5; + string to_currency = 6; + int64 from_amount = 7; + int64 final_amount = 8; +} + +message CreateTradeRequest { + Trade trade = 1; +} + +message CreateTradeResponse { + string trade_id = 1; +} + +message DeleteTradeRequest { + string trade_id = 1; +} + +message UpdateTradeRequest { + Trade trade = 1; +} + +message GetTradeRequest { + string trade_id = 1; +} + +message GetTradeByAccountRequest { + string account_id = 1; +} + +message GetTradeByAccountResponse { + repeated Trade trades = 1; +} + + + +message Account { + string account_id = 1; + string user_id = 2; + int64 balance = 3; + string base_currency = 4; + int64 created_at = 5; + string parent_account_id = 6; +} + +message CreateAccountRequest { + Account account = 1; +} + +message CreateAccountResponse { + Account account = 1; +} + +message DeleteAccountRequest { + string account_id = 1; +} + +message DeleteAccountResponse { +} + +message UpdateAccountRequest { + Account account = 1; +} + +message UpdateAccountResponse { + Account account = 1; +} + +message SearchAccountRequest { + string user_id = 1; + string account_id = 2; +} + +message SearchAccountResponse { + repeated Account accounts = 1; +} + message ConversionRate { +string from_currency = 1; + string to_currency = 2; +double rate = 3; + int64 date_updated_unix_utc = 4; +string uuid = 5; + } + + +message CreateConversionRateRequest { + repeated ConversionRate conversion_rate = 1; +} + +message CreateConversionRateResponse { + repeated ConversionRate conversion_rate = 1; +} + +message ReadConversionRateRequest { + int64 from_unix_utc = 1; + int64 to_unix_utc = 2; +} + +message ReadConversionRateResponse { + repeated ConversionRate conversion_rate = 1; +} + +message UpdateConversionRateRequest { + ConversionRate conversion_rate = 1; +} + +message UpdateConversionRateResponse {} + +message DeleteConversionRateRequest { + string uuid = 1; +} + +message DeleteConversionRateResponse {} + + +message Transaction { + string transaction_id = 1; + string account_id = 2; + int64 amount = 3; + string transaction_type = 4; + string transaction_status = 5; + string transaction_code = 6; + string transaction_description = 7; + int64 created_at = 8; +} + + +message CreateTransactionRequest { + Transaction transaction = 1; +} + +message CreateTransactionResponse { + string transaction_id = 1; +} + +message DeleteTransactionRequest { + string transaction_id = 1; +} + +message UpdateTransactionRequest { + Transaction transaction = 1; +} + +message GetTransactionRequest { + string transaction_id = 1; +} + +message GetTransactionByAccountRequest { + string account_id = 1; +} + +message GetTransactionByAccountResponse { + repeated Transaction transactions = 1; +} + + + + +service ExchangeService { + // RATES + rpc CreateConversionRate(CreateConversionRateRequest) returns (CreateConversionRateResponse) {} + rpc ReadConversionRate(ReadConversionRateRequest) returns (ReadConversionRateResponse) {} + rpc UpdateConversionRate(UpdateConversionRateRequest) returns (UpdateConversionRateResponse) {} + rpc DeleteConversionRate(DeleteConversionRateRequest) returns (DeleteConversionRateResponse) {} + + // ACCOUNT + rpc CreateAccount(CreateAccountRequest) returns (CreateAccountResponse); + rpc DeleteAccount(DeleteAccountRequest) returns (DeleteAccountResponse); + rpc UpdateAccount(UpdateAccountRequest) returns (UpdateAccountResponse); + rpc SearchAccount(SearchAccountRequest) returns (SearchAccountResponse); + + // TRANSACTION + rpc CreateTransaction(CreateTransactionRequest) returns (CreateTransactionResponse); + rpc DeleteTransaction(DeleteTransactionRequest) returns (google.protobuf.Empty); + rpc UpdateTransaction(UpdateTransactionRequest) returns (google.protobuf.Empty); + rpc GetTransaction(GetTransactionRequest) returns (Transaction); + rpc GetTransactionByAccount(GetTransactionByAccountRequest) returns (GetTransactionByAccountResponse); + + // TRADING + rpc CreateTrade(CreateTradeRequest) returns (CreateTradeResponse); + rpc DeleteTrade(DeleteTradeRequest) returns (google.protobuf.Empty); + rpc UpdateTrade(UpdateTradeRequest) returns (google.protobuf.Empty); + rpc GetTrade(GetTradeRequest) returns (Trade); + rpc GetTradeByAccount(GetTradeByAccountRequest) returns (GetTradeByAccountResponse); +} \ No newline at end of file diff --git a/sdk/go-proto-gen b/sdk/go-proto-gen index fdcf0aa..b5df800 160000 --- a/sdk/go-proto-gen +++ b/sdk/go-proto-gen @@ -1 +1 @@ -Subproject commit fdcf0aa71c34a27f364a2cb77737eb745c15d2a4 +Subproject commit b5df8001ee94280b9f2b9311f69935c1f3441586 diff --git a/services/app-auth/Prod.Dockerfile b/services/app-auth/Prod.Dockerfile index 7d7df6d..cad82c4 100644 --- a/services/app-auth/Prod.Dockerfile +++ b/services/app-auth/Prod.Dockerfile @@ -1,6 +1,6 @@ ## Multistage build to reduce the image size -# Stage 1: Build the application +# Stage 1: Build the core FROM golang:1.19-alpine AS builder WORKDIR /usr/src/app COPY . . @@ -8,7 +8,7 @@ RUN go mod download RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app-auth . RUN go build -o dev/auth-app -# Stage 2: Run the application +# Stage 2: Run the core FROM gcr.io/distroless/base-debian10 #path to the file to be copied diff --git a/services/app-auth/go.mod b/services/app-auth/go.mod index 49f1643..de51f5f 100644 --- a/services/app-auth/go.mod +++ b/services/app-auth/go.mod @@ -3,10 +3,11 @@ module github.com/AppsLab-KE/backend-everyshilling/services/app-authentication go 1.19 require ( - github.com/AppsLab-KE/be-go-gen-grpc v0.0.10 + github.com/AppsLab-KE/be-go-gen-grpc v0.0.16 github.com/deepmap/oapi-codegen v1.12.4 github.com/gin-gonic/gin v1.9.0 github.com/golang-jwt/jwt/v5 v5.0.0-rc.2 + github.com/golang/protobuf v1.5.2 github.com/google/uuid v1.3.0 github.com/hashicorp/consul/api v1.20.0 github.com/redis/go-redis/v9 v9.0.3 @@ -29,14 +30,12 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.11.2 // indirect github.com/goccy/go-json v0.10.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.1 // indirect github.com/hashicorp/go-hclog v0.12.0 // indirect github.com/hashicorp/go-immutable-radix v1.0.0 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/serf v0.10.1 // indirect - github.com/inconshreveable/ngrok v0.0.0-20160531001041-a8e7fa486348 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/leodido/go-urn v1.2.1 // indirect diff --git a/services/app-auth/go.sum b/services/app-auth/go.sum index 8a9cbd0..0c33c38 100644 --- a/services/app-auth/go.sum +++ b/services/app-auth/go.sum @@ -1,5 +1,5 @@ -github.com/AppsLab-KE/be-go-gen-grpc v0.0.10 h1:l2uOLKXOmGxvGiYHAJ3BggiE08eZwJtJUU7nFOwINrU= -github.com/AppsLab-KE/be-go-gen-grpc v0.0.10/go.mod h1:F0zAKKv7O9FfplN/28CsWBRJu2ZArWNgIZbix6isDKY= +github.com/AppsLab-KE/be-go-gen-grpc v0.0.16 h1:qu0xjUhyBurj1uQvrGJxrPUEZy4XgMm878wh44rKre8= +github.com/AppsLab-KE/be-go-gen-grpc v0.0.16/go.mod h1:F0zAKKv7O9FfplN/28CsWBRJu2ZArWNgIZbix6isDKY= 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= @@ -89,8 +89,6 @@ github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= -github.com/inconshreveable/ngrok v0.0.0-20160531001041-a8e7fa486348 h1:DfYCTz4I7AIqh9Oxv/iN6d1nRkhQsK0S+jX2yC2culc= -github.com/inconshreveable/ngrok v0.0.0-20160531001041-a8e7fa486348/go.mod h1:zBiwBxTW9puHCrrzHZTjSGKLnVhGEYGflc82Oy09kOQ= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= diff --git a/services/app-auth/internal/core/adapters/exchange.go b/services/app-auth/internal/core/adapters/exchange.go new file mode 100644 index 0000000..a7a585e --- /dev/null +++ b/services/app-auth/internal/core/adapters/exchange.go @@ -0,0 +1,85 @@ +package adapters + +import ( + "context" + "github.com/AppsLab-KE/be-go-gen-grpc/exchange" + "github.com/golang/protobuf/ptypes/empty" + "google.golang.org/grpc" +) + +type ExchangeStorage interface { + // RATES + CreateConversionRate(ctx context.Context, in *exchange.CreateConversionRateRequest, opts ...grpc.CallOption) (*exchange.CreateConversionRateResponse, error) + ReadConversionRate(ctx context.Context, in *exchange.ReadConversionRateRequest, opts ...grpc.CallOption) (*exchange.ReadConversionRateResponse, error) + UpdateConversionRate(ctx context.Context, in *exchange.UpdateConversionRateRequest, opts ...grpc.CallOption) (*exchange.UpdateConversionRateResponse, error) + DeleteConversionRate(ctx context.Context, in *exchange.DeleteConversionRateRequest, opts ...grpc.CallOption) (*exchange.DeleteConversionRateResponse, error) + // ACCOUNT + CreateAccount(ctx context.Context, in *exchange.CreateAccountRequest, opts ...grpc.CallOption) (*exchange.CreateAccountResponse, error) + DeleteAccount(ctx context.Context, in *exchange.DeleteAccountRequest, opts ...grpc.CallOption) (*exchange.DeleteAccountResponse, error) + UpdateAccount(ctx context.Context, in *exchange.UpdateAccountRequest, opts ...grpc.CallOption) (*exchange.UpdateAccountResponse, error) + SearchAccount(ctx context.Context, in *exchange.SearchAccountRequest, opts ...grpc.CallOption) (*exchange.SearchAccountResponse, error) + // TRANSACTION + CreateTransaction(ctx context.Context, in *exchange.CreateTransactionRequest, opts ...grpc.CallOption) (*exchange.CreateTransactionResponse, error) + DeleteTransaction(ctx context.Context, in *exchange.DeleteTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) + UpdateTransaction(ctx context.Context, in *exchange.UpdateTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) + GetTransaction(ctx context.Context, in *exchange.GetTransactionRequest, opts ...grpc.CallOption) (*exchange.Transaction, error) + GetTransactionByAccount(ctx context.Context, in *exchange.GetTransactionByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTransactionByAccountResponse, error) + // TRADING + CreateTrade(ctx context.Context, in *exchange.CreateTradeRequest, opts ...grpc.CallOption) (*exchange.CreateTradeResponse, error) + DeleteTrade(ctx context.Context, in *exchange.DeleteTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) + UpdateTrade(ctx context.Context, in *exchange.UpdateTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) + GetTrade(ctx context.Context, in *exchange.GetTradeRequest, opts ...grpc.CallOption) (*exchange.Trade, error) + GetTradeByAccount(ctx context.Context, in *exchange.GetTradeByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTradeByAccountResponse, error) +} + +type ExchangeRepository interface { + + // RATES + CreateConversionRate(ctx context.Context, in *exchange.CreateConversionRateRequest, opts ...grpc.CallOption) (*exchange.CreateConversionRateResponse, error) + ReadConversionRate(ctx context.Context, in *exchange.ReadConversionRateRequest, opts ...grpc.CallOption) (*exchange.ReadConversionRateResponse, error) + UpdateConversionRate(ctx context.Context, in *exchange.UpdateConversionRateRequest, opts ...grpc.CallOption) (*exchange.UpdateConversionRateResponse, error) + DeleteConversionRate(ctx context.Context, in *exchange.DeleteConversionRateRequest, opts ...grpc.CallOption) (*exchange.DeleteConversionRateResponse, error) + // ACCOUNT + CreateAccount(ctx context.Context, in *exchange.CreateAccountRequest, opts ...grpc.CallOption) (*exchange.CreateAccountResponse, error) + DeleteAccount(ctx context.Context, in *exchange.DeleteAccountRequest, opts ...grpc.CallOption) (*exchange.DeleteAccountResponse, error) + UpdateAccount(ctx context.Context, in *exchange.UpdateAccountRequest, opts ...grpc.CallOption) (*exchange.UpdateAccountResponse, error) + SearchAccount(ctx context.Context, in *exchange.SearchAccountRequest, opts ...grpc.CallOption) (*exchange.SearchAccountResponse, error) + // TRANSACTION + CreateTransaction(ctx context.Context, in *exchange.CreateTransactionRequest, opts ...grpc.CallOption) (*exchange.CreateTransactionResponse, error) + DeleteTransaction(ctx context.Context, in *exchange.DeleteTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) + UpdateTransaction(ctx context.Context, in *exchange.UpdateTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) + GetTransaction(ctx context.Context, in *exchange.GetTransactionRequest, opts ...grpc.CallOption) (*exchange.Transaction, error) + GetTransactionByAccount(ctx context.Context, in *exchange.GetTransactionByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTransactionByAccountResponse, error) + // TRADING + CreateTrade(ctx context.Context, in *exchange.CreateTradeRequest, opts ...grpc.CallOption) (*exchange.CreateTradeResponse, error) + DeleteTrade(ctx context.Context, in *exchange.DeleteTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) + UpdateTrade(ctx context.Context, in *exchange.UpdateTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) + GetTrade(ctx context.Context, in *exchange.GetTradeRequest, opts ...grpc.CallOption) (*exchange.Trade, error) + GetTradeByAccount(ctx context.Context, in *exchange.GetTradeByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTradeByAccountResponse, error) +} + +type ExchangeService interface { + + // RATES + CreateConversionRate(ctx context.Context, in *exchange.CreateConversionRateRequest, opts ...grpc.CallOption) (*exchange.CreateConversionRateResponse, error) + ReadConversionRate(ctx context.Context, in *exchange.ReadConversionRateRequest, opts ...grpc.CallOption) (*exchange.ReadConversionRateResponse, error) + UpdateConversionRate(ctx context.Context, in *exchange.UpdateConversionRateRequest, opts ...grpc.CallOption) (*exchange.UpdateConversionRateResponse, error) + DeleteConversionRate(ctx context.Context, in *exchange.DeleteConversionRateRequest, opts ...grpc.CallOption) (*exchange.DeleteConversionRateResponse, error) + // ACCOUNT + CreateAccount(ctx context.Context, in *exchange.CreateAccountRequest, opts ...grpc.CallOption) (*exchange.CreateAccountResponse, error) + DeleteAccount(ctx context.Context, in *exchange.DeleteAccountRequest, opts ...grpc.CallOption) (*exchange.DeleteAccountResponse, error) + UpdateAccount(ctx context.Context, in *exchange.UpdateAccountRequest, opts ...grpc.CallOption) (*exchange.UpdateAccountResponse, error) + SearchAccount(ctx context.Context, in *exchange.SearchAccountRequest, opts ...grpc.CallOption) (*exchange.SearchAccountResponse, error) + // TRANSACTION + CreateTransaction(ctx context.Context, in *exchange.CreateTransactionRequest, opts ...grpc.CallOption) (*exchange.CreateTransactionResponse, error) + DeleteTransaction(ctx context.Context, in *exchange.DeleteTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) + UpdateTransaction(ctx context.Context, in *exchange.UpdateTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) + GetTransaction(ctx context.Context, in *exchange.GetTransactionRequest, opts ...grpc.CallOption) (*exchange.Transaction, error) + GetTransactionByAccount(ctx context.Context, in *exchange.GetTransactionByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTransactionByAccountResponse, error) + // TRADING + CreateTrade(ctx context.Context, in *exchange.CreateTradeRequest, opts ...grpc.CallOption) (*exchange.CreateTradeResponse, error) + DeleteTrade(ctx context.Context, in *exchange.DeleteTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) + UpdateTrade(ctx context.Context, in *exchange.UpdateTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) + GetTrade(ctx context.Context, in *exchange.GetTradeRequest, opts ...grpc.CallOption) (*exchange.Trade, error) + GetTradeByAccount(ctx context.Context, in *exchange.GetTradeByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTradeByAccountResponse, error) +} diff --git a/services/app-auth/internal/core/adapters/usecase.go b/services/app-auth/internal/core/adapters/usecase.go index 3d59323..191ce32 100644 --- a/services/app-auth/internal/core/adapters/usecase.go +++ b/services/app-auth/internal/core/adapters/usecase.go @@ -3,6 +3,9 @@ package adapters import ( "context" "github.com/AppsLab-KE/backend-everyshilling/services/app-authentication/internal/dto" + "github.com/AppsLab-KE/be-go-gen-grpc/exchange" + "github.com/golang/protobuf/ptypes/empty" + "google.golang.org/grpc" ) type AuthUseCase interface { @@ -26,3 +29,28 @@ type AuthUseCase interface { VerifyAccessToken(token string) (string, error) } + +type ExchangeStorageUsecase interface { + // RATES + CreateConversionRate(ctx context.Context, in *exchange.CreateConversionRateRequest, opts ...grpc.CallOption) (*exchange.CreateConversionRateResponse, error) + ReadConversionRate(ctx context.Context, in *exchange.ReadConversionRateRequest, opts ...grpc.CallOption) (*exchange.ReadConversionRateResponse, error) + UpdateConversionRate(ctx context.Context, in *exchange.UpdateConversionRateRequest, opts ...grpc.CallOption) (*exchange.UpdateConversionRateResponse, error) + DeleteConversionRate(ctx context.Context, in *exchange.DeleteConversionRateRequest, opts ...grpc.CallOption) (*exchange.DeleteConversionRateResponse, error) + // ACCOUNT + CreateAccount(ctx context.Context, in *exchange.CreateAccountRequest, opts ...grpc.CallOption) (*exchange.CreateAccountResponse, error) + DeleteAccount(ctx context.Context, in *exchange.DeleteAccountRequest, opts ...grpc.CallOption) (*exchange.DeleteAccountResponse, error) + UpdateAccount(ctx context.Context, in *exchange.UpdateAccountRequest, opts ...grpc.CallOption) (*exchange.UpdateAccountResponse, error) + SearchAccount(ctx context.Context, in *exchange.SearchAccountRequest, opts ...grpc.CallOption) (*exchange.SearchAccountResponse, error) + // TRANSACTION + CreateTransaction(ctx context.Context, in *exchange.CreateTransactionRequest, opts ...grpc.CallOption) (*exchange.CreateTransactionResponse, error) + DeleteTransaction(ctx context.Context, in *exchange.DeleteTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) + UpdateTransaction(ctx context.Context, in *exchange.UpdateTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) + GetTransaction(ctx context.Context, in *exchange.GetTransactionRequest, opts ...grpc.CallOption) (*exchange.Transaction, error) + GetTransactionByAccount(ctx context.Context, in *exchange.GetTransactionByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTransactionByAccountResponse, error) + // TRADING + CreateTrade(ctx context.Context, in *exchange.CreateTradeRequest, opts ...grpc.CallOption) (*exchange.CreateTradeResponse, error) + DeleteTrade(ctx context.Context, in *exchange.DeleteTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) + UpdateTrade(ctx context.Context, in *exchange.UpdateTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) + GetTrade(ctx context.Context, in *exchange.GetTradeRequest, opts ...grpc.CallOption) (*exchange.Trade, error) + GetTradeByAccount(ctx context.Context, in *exchange.GetTradeByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTradeByAccountResponse, error) +} diff --git a/services/app-auth/internal/core/repository/exchange.go b/services/app-auth/internal/core/repository/exchange.go new file mode 100644 index 0000000..bd32ab2 --- /dev/null +++ b/services/app-auth/internal/core/repository/exchange.go @@ -0,0 +1,164 @@ +package repository + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-authentication/internal/core/adapters" + "github.com/AppsLab-KE/be-go-gen-grpc/exchange" + "github.com/golang/protobuf/ptypes/empty" + "google.golang.org/grpc" +) + +type ExchangeRepositoryImplementation struct { + exchangeStorage adapters.ExchangeStorage +} + +func (e ExchangeRepositoryImplementation) CreateConversionRate(ctx context.Context, in *exchange.CreateConversionRateRequest, opts ...grpc.CallOption) (*exchange.CreateConversionRateResponse, error) { + // Call the corresponding method in exchangeStorage to create a conversion rate + res, err := e.exchangeStorage.CreateConversionRate(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) ReadConversionRate(ctx context.Context, in *exchange.ReadConversionRateRequest, opts ...grpc.CallOption) (*exchange.ReadConversionRateResponse, error) { + res, err := e.exchangeStorage.ReadConversionRate(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) UpdateConversionRate(ctx context.Context, in *exchange.UpdateConversionRateRequest, opts ...grpc.CallOption) (*exchange.UpdateConversionRateResponse, error) { + res, err := e.exchangeStorage.UpdateConversionRate(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) DeleteConversionRate(ctx context.Context, in *exchange.DeleteConversionRateRequest, opts ...grpc.CallOption) (*exchange.DeleteConversionRateResponse, error) { + res, err := e.exchangeStorage.DeleteConversionRate(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) CreateAccount(ctx context.Context, in *exchange.CreateAccountRequest, opts ...grpc.CallOption) (*exchange.CreateAccountResponse, error) { + res, err := e.exchangeStorage.CreateAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) DeleteAccount(ctx context.Context, in *exchange.DeleteAccountRequest, opts ...grpc.CallOption) (*exchange.DeleteAccountResponse, error) { + res, err := e.exchangeStorage.DeleteAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) UpdateAccount(ctx context.Context, in *exchange.UpdateAccountRequest, opts ...grpc.CallOption) (*exchange.UpdateAccountResponse, error) { + res, err := e.exchangeStorage.UpdateAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) SearchAccount(ctx context.Context, in *exchange.SearchAccountRequest, opts ...grpc.CallOption) (*exchange.SearchAccountResponse, error) { + res, err := e.exchangeStorage.SearchAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) CreateTransaction(ctx context.Context, in *exchange.CreateTransactionRequest, opts ...grpc.CallOption) (*exchange.CreateTransactionResponse, error) { + res, err := e.exchangeStorage.CreateTransaction(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) DeleteTransaction(ctx context.Context, in *exchange.DeleteTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + res, err := e.exchangeStorage.DeleteTransaction(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) UpdateTransaction(ctx context.Context, in *exchange.UpdateTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + res, err := e.exchangeStorage.UpdateTransaction(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) GetTransaction(ctx context.Context, in *exchange.GetTransactionRequest, opts ...grpc.CallOption) (*exchange.Transaction, error) { + res, err := e.exchangeStorage.GetTransaction(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) GetTransactionByAccount(ctx context.Context, in *exchange.GetTransactionByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTransactionByAccountResponse, error) { + res, err := e.exchangeStorage.GetTransactionByAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) CreateTrade(ctx context.Context, in *exchange.CreateTradeRequest, opts ...grpc.CallOption) (*exchange.CreateTradeResponse, error) { + res, err := e.exchangeStorage.CreateTrade(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) DeleteTrade(ctx context.Context, in *exchange.DeleteTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + res, err := e.exchangeStorage.DeleteTrade(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) UpdateTrade(ctx context.Context, in *exchange.UpdateTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + res, err := e.exchangeStorage.UpdateTrade(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) GetTrade(ctx context.Context, in *exchange.GetTradeRequest, opts ...grpc.CallOption) (*exchange.Trade, error) { + res, err := e.exchangeStorage.GetTrade(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (e ExchangeRepositoryImplementation) GetTradeByAccount(ctx context.Context, in *exchange.GetTradeByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTradeByAccountResponse, error) { + res, err := e.exchangeStorage.GetTradeByAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func NewExchangeRepositoryImplementation(exchangeStorage adapters.ExchangeStorage) adapters.ExchangeStorage { + return &ExchangeRepositoryImplementation{ + exchangeStorage: exchangeStorage, + } +} diff --git a/services/app-auth/internal/core/storage/exchange.go b/services/app-auth/internal/core/storage/exchange.go new file mode 100644 index 0000000..2931e82 --- /dev/null +++ b/services/app-auth/internal/core/storage/exchange.go @@ -0,0 +1,163 @@ +package storage + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-authentication/internal/core/adapters" + "github.com/AppsLab-KE/be-go-gen-grpc/exchange" + "github.com/golang/protobuf/ptypes/empty" + "google.golang.org/grpc" +) + +type ExchangeStorageImpl struct { + exchangeClient exchange.ExchangeServiceClient +} + +func (e ExchangeStorageImpl) CreateConversionRate(ctx context.Context, in *exchange.CreateConversionRateRequest, opts ...grpc.CallOption) (*exchange.CreateConversionRateResponse, error) { + response, err := e.exchangeClient.CreateConversionRate(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) ReadConversionRate(ctx context.Context, in *exchange.ReadConversionRateRequest, opts ...grpc.CallOption) (*exchange.ReadConversionRateResponse, error) { + response, err := e.exchangeClient.ReadConversionRate(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) UpdateConversionRate(ctx context.Context, in *exchange.UpdateConversionRateRequest, opts ...grpc.CallOption) (*exchange.UpdateConversionRateResponse, error) { + response, err := e.exchangeClient.UpdateConversionRate(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) DeleteConversionRate(ctx context.Context, in *exchange.DeleteConversionRateRequest, opts ...grpc.CallOption) (*exchange.DeleteConversionRateResponse, error) { + response, err := e.exchangeClient.DeleteConversionRate(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) CreateAccount(ctx context.Context, in *exchange.CreateAccountRequest, opts ...grpc.CallOption) (*exchange.CreateAccountResponse, error) { + response, err := e.exchangeClient.CreateAccount(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) DeleteAccount(ctx context.Context, in *exchange.DeleteAccountRequest, opts ...grpc.CallOption) (*exchange.DeleteAccountResponse, error) { + response, err := e.exchangeClient.DeleteAccount(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) UpdateAccount(ctx context.Context, in *exchange.UpdateAccountRequest, opts ...grpc.CallOption) (*exchange.UpdateAccountResponse, error) { + response, err := e.exchangeClient.UpdateAccount(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) SearchAccount(ctx context.Context, in *exchange.SearchAccountRequest, opts ...grpc.CallOption) (*exchange.SearchAccountResponse, error) { + response, err := e.exchangeClient.SearchAccount(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) CreateTransaction(ctx context.Context, in *exchange.CreateTransactionRequest, opts ...grpc.CallOption) (*exchange.CreateTransactionResponse, error) { + response, err := e.exchangeClient.CreateTransaction(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) DeleteTransaction(ctx context.Context, in *exchange.DeleteTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + response, err := e.exchangeClient.DeleteTransaction(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) UpdateTransaction(ctx context.Context, in *exchange.UpdateTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + response, err := e.exchangeClient.UpdateTransaction(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) GetTransaction(ctx context.Context, in *exchange.GetTransactionRequest, opts ...grpc.CallOption) (*exchange.Transaction, error) { + response, err := e.exchangeClient.GetTransaction(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) GetTransactionByAccount(ctx context.Context, in *exchange.GetTransactionByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTransactionByAccountResponse, error) { + response, err := e.exchangeClient.GetTransactionByAccount(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) CreateTrade(ctx context.Context, in *exchange.CreateTradeRequest, opts ...grpc.CallOption) (*exchange.CreateTradeResponse, error) { + response, err := e.exchangeClient.CreateTrade(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) DeleteTrade(ctx context.Context, in *exchange.DeleteTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + response, err := e.exchangeClient.DeleteTrade(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) UpdateTrade(ctx context.Context, in *exchange.UpdateTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + response, err := e.exchangeClient.UpdateTrade(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) GetTrade(ctx context.Context, in *exchange.GetTradeRequest, opts ...grpc.CallOption) (*exchange.Trade, error) { + response, err := e.exchangeClient.GetTrade(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeStorageImpl) GetTradeByAccount(ctx context.Context, in *exchange.GetTradeByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTradeByAccountResponse, error) { + response, err := e.exchangeClient.GetTradeByAccount(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func NewExchangeStorageImpl(exchangeClient exchange.ExchangeServiceClient) adapters.ExchangeStorage { + return ExchangeStorageImpl{ + exchangeClient: exchangeClient, + } +} diff --git a/services/app-auth/internal/core/usecase/exchange.go b/services/app-auth/internal/core/usecase/exchange.go new file mode 100644 index 0000000..d20a35a --- /dev/null +++ b/services/app-auth/internal/core/usecase/exchange.go @@ -0,0 +1,167 @@ +package usecase + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-authentication/internal/core/adapters" + "github.com/AppsLab-KE/be-go-gen-grpc/exchange" + "github.com/golang/protobuf/ptypes/empty" + "google.golang.org/grpc" +) + +type ExchangeUseCase struct { + exchangeService adapters.ExchangeService +} + +func (e ExchangeUseCase) CreateConversionRate(ctx context.Context, in *exchange.CreateConversionRateRequest, opts ...grpc.CallOption) (*exchange.CreateConversionRateResponse, error) { + // Call the exchange service method to create the conversion rate + response, err := e.exchangeService.CreateConversionRate(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) ReadConversionRate(ctx context.Context, in *exchange.ReadConversionRateRequest, opts ...grpc.CallOption) (*exchange.ReadConversionRateResponse, error) { + // Call the exchange service method to read the conversion rate + response, err := e.exchangeService.ReadConversionRate(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil + +} + +func (e ExchangeUseCase) UpdateConversionRate(ctx context.Context, in *exchange.UpdateConversionRateRequest, opts ...grpc.CallOption) (*exchange.UpdateConversionRateResponse, error) { + response, err := e.exchangeService.UpdateConversionRate(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) DeleteConversionRate(ctx context.Context, in *exchange.DeleteConversionRateRequest, opts ...grpc.CallOption) (*exchange.DeleteConversionRateResponse, error) { + response, err := e.exchangeService.DeleteConversionRate(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil + +} + +func (e ExchangeUseCase) CreateAccount(ctx context.Context, in *exchange.CreateAccountRequest, opts ...grpc.CallOption) (*exchange.CreateAccountResponse, error) { + response, err := e.exchangeService.CreateAccount(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) DeleteAccount(ctx context.Context, in *exchange.DeleteAccountRequest, opts ...grpc.CallOption) (*exchange.DeleteAccountResponse, error) { + response, err := e.exchangeService.DeleteAccount(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) UpdateAccount(ctx context.Context, in *exchange.UpdateAccountRequest, opts ...grpc.CallOption) (*exchange.UpdateAccountResponse, error) { + response, err := e.exchangeService.UpdateAccount(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) SearchAccount(ctx context.Context, in *exchange.SearchAccountRequest, opts ...grpc.CallOption) (*exchange.SearchAccountResponse, error) { + response, err := e.exchangeService.SearchAccount(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) CreateTransaction(ctx context.Context, in *exchange.CreateTransactionRequest, opts ...grpc.CallOption) (*exchange.CreateTransactionResponse, error) { + response, err := e.exchangeService.CreateTransaction(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) DeleteTransaction(ctx context.Context, in *exchange.DeleteTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + response, err := e.exchangeService.DeleteTransaction(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) UpdateTransaction(ctx context.Context, in *exchange.UpdateTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + response, err := e.exchangeService.UpdateTransaction(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) GetTransaction(ctx context.Context, in *exchange.GetTransactionRequest, opts ...grpc.CallOption) (*exchange.Transaction, error) { + response, err := e.exchangeService.GetTransaction(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) GetTransactionByAccount(ctx context.Context, in *exchange.GetTransactionByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTransactionByAccountResponse, error) { + response, err := e.exchangeService.GetTransactionByAccount(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) CreateTrade(ctx context.Context, in *exchange.CreateTradeRequest, opts ...grpc.CallOption) (*exchange.CreateTradeResponse, error) { + response, err := e.exchangeService.CreateTrade(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) DeleteTrade(ctx context.Context, in *exchange.DeleteTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + response, err := e.exchangeService.DeleteTrade(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) UpdateTrade(ctx context.Context, in *exchange.UpdateTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + response, err := e.exchangeService.UpdateTrade(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) GetTrade(ctx context.Context, in *exchange.GetTradeRequest, opts ...grpc.CallOption) (*exchange.Trade, error) { + response, err := e.exchangeService.GetTrade(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func (e ExchangeUseCase) GetTradeByAccount(ctx context.Context, in *exchange.GetTradeByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTradeByAccountResponse, error) { + response, err := e.exchangeService.GetTradeByAccount(ctx, in, opts...) + if err != nil { + return nil, err + } + return response, nil +} + +func NewExchangeUseCase(exchangeService adapters.ExchangeService) adapters.ExchangeService { + return &ExchangeUseCase{ + exchangeService: exchangeService, + } +} diff --git a/services/app-auth/internal/dto/http.gen.go b/services/app-auth/internal/dto/http.gen.go index a342258..d668837 100644 --- a/services/app-auth/internal/dto/http.gen.go +++ b/services/app-auth/internal/dto/http.gen.go @@ -86,23 +86,23 @@ type ResponseSuccess struct { Message *string `json:"message,omitempty"` } -// RefreshTokenJSONRequestBody defines body for RefreshToken for application/json ContentType. +// RefreshTokenJSONRequestBody defines body for RefreshToken for core/json ContentType. type RefreshTokenJSONRequestBody = RefreshTokenReq -// RegisterJSONRequestBody defines body for Register for application/json ContentType. +// RegisterJSONRequestBody defines body for Register for core/json ContentType. type RegisterJSONRequestBody = RegisterRequest -// ResetJSONRequestBody defines body for Reset for application/json ContentType. +// ResetJSONRequestBody defines body for Reset for core/json ContentType. type ResetJSONRequestBody = Phone -// VerifyResetOTPJSONRequestBody defines body for VerifyResetOTP for application/json ContentType. +// VerifyResetOTPJSONRequestBody defines body for VerifyResetOTP for core/json ContentType. type VerifyResetOTPJSONRequestBody = RequestOTP -// ChangePasswordJSONRequestBody defines body for ChangePassword for application/json ContentType. +// ChangePasswordJSONRequestBody defines body for ChangePassword for core/json ContentType. type ChangePasswordJSONRequestBody = RequestResetCredentials -// VerifyPhoneJSONRequestBody defines body for VerifyPhone for application/json ContentType. +// VerifyPhoneJSONRequestBody defines body for VerifyPhone for core/json ContentType. type VerifyPhoneJSONRequestBody = Phone -// VerifyVerificationOTPJSONRequestBody defines body for VerifyVerificationOTP for application/json ContentType. +// VerifyVerificationOTPJSONRequestBody defines body for VerifyVerificationOTP for core/json ContentType. type VerifyVerificationOTPJSONRequestBody = RequestOTP diff --git a/services/app-auth/internal/routes/server/server.go b/services/app-auth/internal/routes/server/server.go index 8353f84..3cd4b1c 100644 --- a/services/app-auth/internal/routes/server/server.go +++ b/services/app-auth/internal/routes/server/server.go @@ -11,10 +11,11 @@ import ( const BaseUrl = "/api" type Config struct { - AuthUsecase adapters.AuthUseCase + AuthUsecase adapters.AuthUseCase + ExchangeUsecase adapters.ExchangeStorageUsecase } -func NewServer(authUseCase adapters.AuthUseCase, cfg config.Config) *gin.Engine { +func NewServer(authUseCase adapters.AuthUseCase, exchangeUseCase adapters.ExchangeStorageUsecase, cfg config.Config) *gin.Engine { r := gin.Default() middlewareManager := middleware.NewManager(cfg, authUseCase) @@ -33,7 +34,9 @@ func NewServer(authUseCase adapters.AuthUseCase, cfg config.Config) *gin.Engine // Serve swagger spec // Map handlers + //h := handlers.NewHandler(authUseCase,exchangeUseCase) h := handlers.NewHandler(authUseCase) + handlers.RegisterHandlersWithOptions(r, h, options) return r diff --git a/services/app-auth/main.go b/services/app-auth/main.go index 7ce1ea8..267747b 100644 --- a/services/app-auth/main.go +++ b/services/app-auth/main.go @@ -58,6 +58,7 @@ func main() { // Initialise usecases authUC := usecase.NewAuthUsecase(authService, nil) + //exchangeUseCase := usecase.NewExchangeUseCase(exchangeService,nil) // server config handler := server.NewServer(authUC, *cfg) diff --git a/services/app-db/go.mod b/services/app-db/go.mod index 6b0e5c4..007d21d 100644 --- a/services/app-db/go.mod +++ b/services/app-db/go.mod @@ -3,7 +3,7 @@ module github.com/AppsLab-KE/backend-everyshilling/services/app-db go 1.19 require ( - github.com/AppsLab-KE/be-go-gen-grpc v0.0.10 + github.com/AppsLab-KE/be-go-gen-grpc v0.0.13 github.com/google/uuid v1.3.0 github.com/sirupsen/logrus v1.9.0 google.golang.org/grpc v1.54.0 @@ -25,3 +25,4 @@ require ( golang.org/x/text v0.8.0 // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect ) + diff --git a/services/app-db/go.sum b/services/app-db/go.sum index d918994..59e213d 100644 --- a/services/app-db/go.sum +++ b/services/app-db/go.sum @@ -1,5 +1,5 @@ -github.com/AppsLab-KE/be-go-gen-grpc v0.0.10 h1:l2uOLKXOmGxvGiYHAJ3BggiE08eZwJtJUU7nFOwINrU= -github.com/AppsLab-KE/be-go-gen-grpc v0.0.10/go.mod h1:F0zAKKv7O9FfplN/28CsWBRJu2ZArWNgIZbix6isDKY= +github.com/AppsLab-KE/be-go-gen-grpc v0.0.13 h1:x3nsaE7AAiLI1xSKBtHkgzpjWY9sxSeVCD+0Vk9jW9g= +github.com/AppsLab-KE/be-go-gen-grpc v0.0.13/go.mod h1:F0zAKKv7O9FfplN/28CsWBRJu2ZArWNgIZbix6isDKY= 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= diff --git a/services/app-db/internal/core/models/account.go b/services/app-db/internal/core/models/account.go new file mode 100644 index 0000000..cdd04d3 --- /dev/null +++ b/services/app-db/internal/core/models/account.go @@ -0,0 +1,16 @@ +package models + +type Account struct { + AccountId string `json:"account_id"` + UserId string `json:""` + Balance int64 `json:"balance"` + BaseCurrency string `json:"base_currency"` + CreatedAt int64 `json:"created_at"` + ParentAccountId string `json:"parent_account_id"` + AccountID string `json:"account_id"` +} + +func (a Account) Error() string { + //TODO implement me + panic("implement me") +} diff --git a/services/app-db/internal/core/models/rate.go b/services/app-db/internal/core/models/rate.go new file mode 100644 index 0000000..9b05d64 --- /dev/null +++ b/services/app-db/internal/core/models/rate.go @@ -0,0 +1,9 @@ +package models + +type ConversionRate struct { + BaseModel + FromCurrency string `json:"from_currency" gorm:"column:from_currency"` + ToCurrency string `json:"to_currency" gorm:"column:to_currency"` + Rate float64 `json:"rate" gorm:"column:rate"` + TimeStampUTC int64 `json:"time_stamp_utc" gorm:"column:time_stamp_utc"` +} diff --git a/services/app-db/internal/core/models/trade.go b/services/app-db/internal/core/models/trade.go new file mode 100644 index 0000000..efd6827 --- /dev/null +++ b/services/app-db/internal/core/models/trade.go @@ -0,0 +1,13 @@ +package models + +type Trade struct { + TradeId string `json:"trade_id"` + AccountId string `json:"account_id"` + TradeType string `json:"trade_type"` + TradeStatus string `json:"trade_status"` + FromCurrency string `json:"from_currency"` + ToCurrency string `json:"to_currency"` + FromAmount int64 `json:"from_amount"` + FinalAmount int64 `json:"final_amount"` + TradeID string `json:"trade_id"` +} diff --git a/services/app-db/internal/core/models/transaction.go b/services/app-db/internal/core/models/transaction.go new file mode 100644 index 0000000..904c8e6 --- /dev/null +++ b/services/app-db/internal/core/models/transaction.go @@ -0,0 +1,12 @@ +package models + +type Transaction struct { + TransactionId string `json:"transaction_id"` + AccountId string `json:"account_id"` + Amount int64 `json:"amount"` + TransactionType string `json:"transaction_type"` + TransactionStatus string `json:"transaction_status"` + TransactionCode string `json:"transaction_code"` + TransactionDescription string `json:"transaction_description"` + CreatedAt int64 `json:"created_at"` +} diff --git a/services/app-db/internal/core/ports/accounts-collection.go b/services/app-db/internal/core/ports/accounts-collection.go new file mode 100644 index 0000000..6d71a6f --- /dev/null +++ b/services/app-db/internal/core/ports/accounts-collection.go @@ -0,0 +1,22 @@ +package ports + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/models" +) + +type AccountRepo interface { + CreateAccount(ctx context.Context, account *models.Account) (*models.Account, error) + DeleteAccount(ctx context.Context, accountID string) error + UpdateAccount(ctx context.Context, account *models.Account) (*models.Account, error) + SearchAccount(ctx context.Context, query string) ([]*models.Account, error) + GetAccount(ctx context.Context, id string) error + GetAccounts(ctx context.Context, id string) error +} + +type AccountStorage interface { + CreateAccount(ctx context.Context, account *models.Account) (*models.Account, error) + DeleteAccount(ctx context.Context, accountID string) error + UpdateAccount(ctx context.Context, account *models.Account) (*models.Account, error) + SearchAccount(ctx context.Context, query string) ([]*models.Account, error) +} diff --git a/services/app-db/internal/core/ports/rates-collection.go b/services/app-db/internal/core/ports/rates-collection.go new file mode 100644 index 0000000..3abd481 --- /dev/null +++ b/services/app-db/internal/core/ports/rates-collection.go @@ -0,0 +1,21 @@ +package ports + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/models" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/dto" +) + +type RatesRepo interface { + CreateRate(context.Context, []*models.ConversionRate) ([]*models.ConversionRate, error) + UpdateRate(context.Context, *models.ConversionRate) (*models.ConversionRate, error) + FetchRates(context.Context, dto.FetchRatesRequest) ([]models.ConversionRate, error) + DeleteRate(context.Context, string) error +} + +type RatesStorage interface { + CreateRate(context.Context, []*models.ConversionRate) ([]*models.ConversionRate, error) + UpdateRate(context.Context, *models.ConversionRate) (*models.ConversionRate, error) + FetchRates(context.Context, dto.FetchRatesRequest) ([]models.ConversionRate, error) + DeleteRate(context.Context, string) error +} diff --git a/services/app-db/internal/core/ports/trade-collection.go b/services/app-db/internal/core/ports/trade-collection.go new file mode 100644 index 0000000..4401146 --- /dev/null +++ b/services/app-db/internal/core/ports/trade-collection.go @@ -0,0 +1,33 @@ +package ports + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/models" +) + +// TradeStorage defines the methods for interacting with the trade collection in the data store. +type TradeStorage interface { + CreateTrade(ctx context.Context, trade *models.Trade) error + DeleteTrade(ctx context.Context, tradeID string) error + UpdateTrade(ctx context.Context, trade *models.Trade) error + GetTrade(ctx context.Context, tradeID string) (*models.Trade, error) + GetTradeByAccount(ctx context.Context, accountID string) ([]*models.Trade, error) +} + +// TradeRepo defines the methods for the trade repository. +type TradeRepo interface { + CreateTrade(ctx context.Context, trade *models.Trade) error + DeleteTrade(ctx context.Context, tradeID string) error + UpdateTrade(ctx context.Context, trade *models.Trade) error + GetTrade(ctx context.Context, tradeID string) (*models.Trade, error) + GetTradeByAccount(ctx context.Context, accountID string) ([]*models.Trade, error) +} + +// TradeUsecase defines the methods for the trade use case. +type TradeUsecase interface { + CreateTrade(ctx context.Context, trade *models.Trade) error + DeleteTrade(ctx context.Context, tradeID string) error + UpdateTrade(ctx context.Context, trade *models.Trade) error + GetTrade(ctx context.Context, tradeID string) (*models.Trade, error) + GetTradeByAccount(ctx context.Context, accountID string) ([]*models.Trade, error) +} diff --git a/services/app-db/internal/core/ports/transactions-collection.go b/services/app-db/internal/core/ports/transactions-collection.go new file mode 100644 index 0000000..b253938 --- /dev/null +++ b/services/app-db/internal/core/ports/transactions-collection.go @@ -0,0 +1,28 @@ +package ports + +import ( + "context" + "errors" + + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/models" +) + +var ( + ErrNotFound = errors.New("not found") +) + +type TransactionRepo interface { + CreateTransaction(ctx context.Context, transaction *models.Transaction) error + DeleteTransaction(ctx context.Context, transactionID string) error + UpdateTransaction(ctx context.Context, transaction *models.Transaction) error + GetTransaction(ctx context.Context, transactionID string) (*models.Transaction, error) + GetTransactionByAccount(ctx context.Context, accountID string) ([]*models.Transaction, error) +} + +type TransactionStorage interface { + CreateTransaction(ctx context.Context, transaction *models.Transaction) error + DeleteTransaction(ctx context.Context, transactionID string) error + UpdateTransaction(ctx context.Context, transaction *models.Transaction) error + GetTransaction(ctx context.Context, transactionID string) (*models.Transaction, error) + GetTransactionsByAccount(ctx context.Context, accountID string) ([]*models.Transaction, error) +} diff --git a/services/app-db/internal/core/ports/user-database.go b/services/app-db/internal/core/ports/user-collection.go similarity index 100% rename from services/app-db/internal/core/ports/user-database.go rename to services/app-db/internal/core/ports/user-collection.go diff --git a/services/app-db/internal/core/storage/rates-postgres-storage.go b/services/app-db/internal/core/storage/rates-postgres-storage.go new file mode 100644 index 0000000..e68c911 --- /dev/null +++ b/services/app-db/internal/core/storage/rates-postgres-storage.go @@ -0,0 +1,84 @@ +package storage + +import ( + "context" + "errors" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/models" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/ports" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/dto" + "gorm.io/gorm" + "sort" +) + +type ratesPostgresStorage struct { + db *gorm.DB +} + +// SortConversionRatesByTimestamp sorts the ConversionRate slice in descending order based on TimeStampUTC +func sortConversionRatesByTimestamp(rates []*models.ConversionRate) { + sort.Slice(rates, func(i, j int) bool { + return rates[i].TimeStampUTC > rates[j].TimeStampUTC + }) +} + +func (r ratesPostgresStorage) CreateRate(ctx context.Context, rates []*models.ConversionRate) ([]*models.ConversionRate, error) { + // sort the rates by timestamp starting with the latest + sortConversionRatesByTimestamp(rates) + + // check if the range of rates already exists and return the counts of the rates + var count int64 + r.db.Model(&models.ConversionRate{}).Where("time_stamp_utc BETWEEN ? AND ?", rates[len(rates)-1].TimeStampUTC, rates[0].TimeStampUTC).Count(&count) + + if count > 0 { + return nil, errors.New("rates already exist") + } + + // create the rates + if err := r.db.Create(&rates).Error; err != nil { + return nil, err + } + + return rates, nil +} + +func (r ratesPostgresStorage) UpdateRate(ctx context.Context, rate *models.ConversionRate) (*models.ConversionRate, error) { + err := r.db.Model(&models.ConversionRate{}).Where("time_stamp_utc = ?", rate.TimeStampUTC).Updates(rate).Error + if err != nil { + return nil, err + } + + return rate, nil +} + +func (r ratesPostgresStorage) FetchRates(ctx context.Context, request dto.FetchRatesRequest) ([]models.ConversionRate, error) { + var rates []models.ConversionRate + + // fetch in the specified range + if request.ToUnixUtc != 0 && request.FromUnixUtc != 0 { + err := r.db.Where("time_stamp_utc BETWEEN ? AND ?", request.FromUnixUtc, request.ToUnixUtc).Find(&rates).Error + if err != nil { + return nil, err + } + return rates, nil + } + + // fetch the latest + err := r.db.Order("time_stamp_utc desc").Limit(50).Find(&rates).Error + if err != nil { + return nil, err + } + + return rates, nil +} + +func (r ratesPostgresStorage) DeleteRate(ctx context.Context, s string) error { + err := r.db.Where("time_stamp_utc = ?", s).Delete(&models.ConversionRate{}).Error + if err != nil { + return err + } + return nil +} + +func NewRatesPostgresStorage(db *gorm.DB) ports.RatesStorage { + return &ratesPostgresStorage{db: db} +} diff --git a/services/app-db/internal/dto/rates.go b/services/app-db/internal/dto/rates.go new file mode 100644 index 0000000..6e0a8e7 --- /dev/null +++ b/services/app-db/internal/dto/rates.go @@ -0,0 +1,6 @@ +package dto + +type FetchRatesRequest struct { + FromUnixUtc int64 + ToUnixUtc int64 +} diff --git a/services/app-db/internal/platform/postgres/postgresql.go b/services/app-db/internal/platform/postgres/postgresql.go index 3890ee4..23e7be9 100644 --- a/services/app-db/internal/platform/postgres/postgresql.go +++ b/services/app-db/internal/platform/postgres/postgresql.go @@ -30,7 +30,7 @@ func NewClient(postgresConfig config.Postgres) (*gorm.DB, error) { log.Info("database setup: migrations") // automigrate - err = db.AutoMigrate(&models.User{}) + err = db.AutoMigrate(&models.User{}, &models.ConversionRate{}) if err != nil { return nil, err diff --git a/services/app-db/internal/repository/accounts.go b/services/app-db/internal/repository/accounts.go new file mode 100644 index 0000000..36e90c8 --- /dev/null +++ b/services/app-db/internal/repository/accounts.go @@ -0,0 +1,65 @@ +package repository + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/models" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/ports" +) + +type accountRepo struct { + accountStorage ports.AccountStorage +} + +func (r *accountRepo) GetAccount(ctx context.Context, id string) error { + //TODO implement me + panic("implement me") +} + +func (r *accountRepo) GetAccounts(ctx context.Context, id string) error { + //TODO implement me + panic("implement me") +} + +func (r *accountRepo) DeleteAccount(ctx context.Context, accountID string) error { + err := r.accountStorage.DeleteAccount(ctx, accountID) + if err != nil { + + return err + } + + return nil +} + +func (r *accountRepo) UpdateAccount(ctx context.Context, account *models.Account) (*models.Account, error) { + updatedAccount, err := r.accountStorage.UpdateAccount(ctx, account) + if err != nil { + + return nil, err + } + + return updatedAccount, nil +} + +func (r *accountRepo) SearchAccount(ctx context.Context, query string) ([]*models.Account, error) { + accounts, err := r.accountStorage.SearchAccount(ctx, query) + if err != nil { + + return nil, err + } + + return accounts, nil +} + +func (r *accountRepo) CreateAccount(ctx context.Context, account *models.Account) (*models.Account, error) { + createdAccount, err := r.accountStorage.CreateAccount(ctx, account) + if err != nil { + + return nil, err + } + + return createdAccount, nil +} + +func NewAccountRepo(accountStorage ports.AccountStorage) ports.AccountRepo { + return &accountRepo{accountStorage: accountStorage} +} diff --git a/services/app-db/internal/repository/rates.go b/services/app-db/internal/repository/rates.go new file mode 100644 index 0000000..c698f97 --- /dev/null +++ b/services/app-db/internal/repository/rates.go @@ -0,0 +1,32 @@ +package repository + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/models" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/ports" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/dto" +) + +type ratesRepo struct { + ratesStorage ports.RatesStorage +} + +func (r ratesRepo) CreateRate(ctx context.Context, rates []*models.ConversionRate) ([]*models.ConversionRate, error) { + return r.ratesStorage.CreateRate(ctx, rates) +} + +func (r ratesRepo) UpdateRate(ctx context.Context, rate *models.ConversionRate) (*models.ConversionRate, error) { + return r.ratesStorage.UpdateRate(ctx, rate) +} + +func (r ratesRepo) FetchRates(ctx context.Context, request dto.FetchRatesRequest) ([]models.ConversionRate, error) { + return r.ratesStorage.FetchRates(ctx, request) +} + +func (r ratesRepo) DeleteRate(ctx context.Context, s string) error { + return r.ratesStorage.DeleteRate(ctx, s) +} + +func NewRatesRepo(ratesStorage ports.RatesStorage) ports.RatesRepo { + return &ratesRepo{ratesStorage: ratesStorage} +} diff --git a/services/app-db/internal/repository/trades.go b/services/app-db/internal/repository/trades.go new file mode 100644 index 0000000..e30fd4a --- /dev/null +++ b/services/app-db/internal/repository/trades.go @@ -0,0 +1,64 @@ +package repository + +import ( + "context" + "errors" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/models" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/ports" + "log" +) + +type TradeRepository struct { + tradeStorage ports.TradeStorage +} + +func NewTradeRepository(tradeStorage ports.TradeStorage) *TradeRepository { + return &TradeRepository{ + tradeStorage: tradeStorage, + } +} + +func (r *TradeRepository) CreateTrade(ctx context.Context, trade *models.Trade) error { + err := r.tradeStorage.CreateTrade(ctx, trade) + if err != nil { + log.Println("Error creating trade:", err) + return errors.New("failed to create trade") + } + return nil +} + +func (r *TradeRepository) DeleteTrade(ctx context.Context, tradeID string) error { + err := r.tradeStorage.DeleteTrade(ctx, tradeID) + if err != nil { + log.Println("Error deleting trade:", err) + return errors.New("failed to delete trade") + } + return nil +} + +func (r *TradeRepository) UpdateTrade(ctx context.Context, trade *models.Trade) error { + err := r.tradeStorage.UpdateTrade(ctx, trade) + if err != nil { + log.Println("Error updating trade:", err) + return errors.New("failed to update trade") + } + return nil +} + +func (r *TradeRepository) GetTrade(ctx context.Context, tradeID string) (*models.Trade, error) { + trade, err := r.tradeStorage.GetTrade(ctx, tradeID) + if err != nil { + log.Println("Error retrieving trade:", err) + return nil, errors.New("failed to retrieve trade") + } + return trade, nil +} + +func (r *TradeRepository) GetTradeByAccount(ctx context.Context, accountID string) ([]*models.Trade, error) { + trades, err := r.tradeStorage.GetTradeByAccount(ctx, accountID) + if err != nil { + log.Println("Error retrieving trades by account:", err) + return nil, errors.New("failed to retrieve trades by account") + } + return trades, nil +} diff --git a/services/app-db/internal/repository/transactions.go b/services/app-db/internal/repository/transactions.go new file mode 100644 index 0000000..9decc85 --- /dev/null +++ b/services/app-db/internal/repository/transactions.go @@ -0,0 +1,58 @@ +package repository + +import ( + "context" + "errors" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/models" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/ports" +) + +type transactionRepo struct { + transactionStorage ports.TransactionStorage +} + +func NewTransactionRepo(transactionStorage ports.TransactionStorage) ports.TransactionRepo { + return &transactionRepo{ + transactionStorage: transactionStorage, + } +} + +func (r *transactionRepo) CreateTransaction(ctx context.Context, transaction *models.Transaction) error { + err := r.transactionStorage.CreateTransaction(ctx, transaction) + if err != nil { + return errors.New("failed to create transaction") + } + return nil +} + +func (r *transactionRepo) DeleteTransaction(ctx context.Context, transactionID string) error { + err := r.transactionStorage.DeleteTransaction(ctx, transactionID) + if err != nil { + return errors.New("failed to delete transaction") + } + return nil +} + +func (r *transactionRepo) UpdateTransaction(ctx context.Context, transaction *models.Transaction) error { + err := r.transactionStorage.UpdateTransaction(ctx, transaction) + if err != nil { + return errors.New("failed to update transaction") + } + return nil +} + +func (r *transactionRepo) GetTransaction(ctx context.Context, transactionID string) (*models.Transaction, error) { + transaction, err := r.transactionStorage.GetTransaction(ctx, transactionID) + if err != nil { + return nil, errors.New("failed to retrieve transaction") + } + return transaction, nil +} + +func (r *transactionRepo) GetTransactionByAccount(ctx context.Context, accountID string) ([]*models.Transaction, error) { + transactions, err := r.transactionStorage.GetTransactionsByAccount(ctx, accountID) + if err != nil { + return nil, errors.New("failed to retrieve transactions by account") + } + return transactions, nil +} diff --git a/services/app-db/internal/routes/handlers/handler.go b/services/app-db/internal/routes/handlers/prepare.go similarity index 72% rename from services/app-db/internal/routes/handlers/handler.go rename to services/app-db/internal/routes/handlers/prepare.go index faf9456..268c712 100644 --- a/services/app-db/internal/routes/handlers/handler.go +++ b/services/app-db/internal/routes/handlers/prepare.go @@ -13,7 +13,8 @@ var ( type Handler struct { db.UnimplementedDbServiceServer - userRepo ports.UserRepo + userRepo ports.UserRepo + ratesRepo ports.RatesRepo } func (s *Handler) HealthCheck(context.Context, *db.DefaultRequest) (*db.HealthResponse, error) { @@ -22,8 +23,9 @@ func (s *Handler) HealthCheck(context.Context, *db.DefaultRequest) (*db.HealthRe }, nil } -func NewHandler(userRepo ports.UserRepo) *Handler { +func NewHandler(userRepo ports.UserRepo, ratesRepo ports.RatesRepo) *Handler { return &Handler{ - userRepo: userRepo, + userRepo: userRepo, + ratesRepo: ratesRepo, } } diff --git a/services/app-db/internal/routes/handlers/rates-handler.go b/services/app-db/internal/routes/handlers/rates-handler.go new file mode 100644 index 0000000..d56275e --- /dev/null +++ b/services/app-db/internal/routes/handlers/rates-handler.go @@ -0,0 +1,126 @@ +package handlers + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/core/models" + "github.com/AppsLab-KE/backend-everyshilling/services/app-db/internal/dto" + "github.com/AppsLab-KE/be-go-gen-grpc/db" +) + +// CreateConversionRate creates a conversion rate +func (h *Handler) CreateConversionRate(ctx context.Context, req *db.CreateConversionRateRequest) (*db.CreateConversionRateResponse, error) { + if req == nil { + return nil, ErrEmptyRequest + } + + var rates []*models.ConversionRate + for _, rate := range req.ConversionRate { + rates = append(rates, &models.ConversionRate{ + FromCurrency: rate.FromCurrency, + ToCurrency: rate.ToCurrency, + Rate: rate.Rate, + TimeStampUTC: rate.DateUpdatedUnixUtc, + }) + } + + createdRates, err := h.ratesRepo.CreateRate(ctx, rates) + if err != nil { + return nil, err + } + + // Example response + resp := &db.CreateConversionRateResponse{ + // Set the response fields accordingly + } + + for _, rate := range createdRates { + resp.ConversionRate = append(resp.ConversionRate, &db.ConversionRate{ + FromCurrency: rate.FromCurrency, + ToCurrency: rate.ToCurrency, + Rate: rate.Rate, + DateUpdatedUnixUtc: rate.TimeStampUTC, + }) + } + + return resp, nil +} + +// ReadConversionRate reads a conversion rate +func (h *Handler) ReadConversionRate(ctx context.Context, req *db.ReadConversionRateRequest) (*db.ReadConversionRateResponse, error) { + if req == nil { + return nil, ErrEmptyRequest + } + + var fetchRequest dto.FetchRatesRequest = dto.FetchRatesRequest{ + FromUnixUtc: req.FromUnixUtc, + ToUnixUtc: req.ToUnixUtc, + } + + rates, err := h.ratesRepo.FetchRates(ctx, fetchRequest) + if err != nil { + return nil, err + } + + // Example response + resp := &db.ReadConversionRateResponse{ + // Set the response fields accordingly + } + + for _, rate := range rates { + resp.ConversionRate = append(resp.ConversionRate, &db.ConversionRate{ + FromCurrency: rate.FromCurrency, + ToCurrency: rate.ToCurrency, + Rate: rate.Rate, + DateUpdatedUnixUtc: rate.TimeStampUTC, + }) + } + + return resp, nil +} + +// UpdateConversionRate updates a conversion rate +func (h *Handler) UpdateConversionRate(ctx context.Context, req *db.UpdateConversionRateRequest) (*db.UpdateConversionRateResponse, error) { + if req == nil { + return nil, ErrEmptyRequest + } + + var request *models.ConversionRate = &models.ConversionRate{ + FromCurrency: req.ConversionRate.FromCurrency, + ToCurrency: req.ConversionRate.ToCurrency, + Rate: req.ConversionRate.Rate, + TimeStampUTC: req.ConversionRate.DateUpdatedUnixUtc, + } + + _, err := h.ratesRepo.UpdateRate(ctx, request) + if err != nil { + return nil, err + } + + // Example response + resp := &db.UpdateConversionRateResponse{ + // Set the response fields accordingly + } + + return resp, nil +} + +// DeleteConversionRate deletes a conversion rate +func (h *Handler) DeleteConversionRate(ctx context.Context, req *db.DeleteConversionRateRequest) (*db.DeleteConversionRateResponse, error) { + if req == nil { + return nil, ErrEmptyRequest + } + + var request string = req.Uuid + + err := h.ratesRepo.DeleteRate(ctx, request) + if err != nil { + return nil, err + } + + // Example response + resp := &db.DeleteConversionRateResponse{ + // Set the response fields accordingly + } + + return resp, nil +} diff --git a/services/app-db/internal/routes/server/grpc-server.go b/services/app-db/internal/routes/server/grpc-server.go index 39df9c7..6eb0e9c 100644 --- a/services/app-db/internal/routes/server/grpc-server.go +++ b/services/app-db/internal/routes/server/grpc-server.go @@ -24,6 +24,7 @@ func (s *Server) Run() { // create database client postgresClient, err := postgres.NewClient(s.cfg.Postgres) + if err != nil { log.Panic("error:", err) } @@ -32,11 +33,15 @@ func (s *Server) Run() { userStorage := storage.NewUserStorage(postgresClient) userCache := storage.NewUserCacheStorage() + // rates storage + ratesStorage := storage.NewRatesPostgresStorage(postgresClient) + // create repository userRepo := repository.NewUserRepo(userStorage, userCache) + ratesRepo := repository.NewRatesRepo(ratesStorage) // create handler - grpcHandler := handlers.NewHandler(userRepo) + grpcHandler := handlers.NewHandler(userRepo, ratesRepo) // run server lis, err := net.Listen("tcp", ":"+s.cfg.Server.Port) diff --git a/services/app-exchange/.env.example b/services/app-exchange/.env.example new file mode 100644 index 0000000..e69de29 diff --git a/services/app-exchange/Dockerfile b/services/app-exchange/Dockerfile new file mode 100644 index 0000000..12b4f9b --- /dev/null +++ b/services/app-exchange/Dockerfile @@ -0,0 +1,29 @@ +# Initial stage: download modules +FROM golang:1.18-alpine as golang-builder + +RUN apk add build-base +RUN apk --update add git ca-certificates +RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64; + + +FROM golang-builder AS app-builder +WORKDIR /app/exchange + +# Copy go mod files +COPY go.mod go.sum \ + /app/exchange/ + +RUN go mod download + +COPY . /app/exchange + + +RUN go build -o /tmp/app-exchange + +FROM app-builder AS prepare-bin + +COPY --from=app-builder /tmp/app-exchange /usr/bin/exchange-service + +# Create the target folder and copy the contents + +ENTRYPOINT ["/usr/bin/exchange-service"] \ No newline at end of file diff --git a/services/app-exchange/config/config.go b/services/app-exchange/config/config.go new file mode 100644 index 0000000..6111752 --- /dev/null +++ b/services/app-exchange/config/config.go @@ -0,0 +1,48 @@ +package config + +import ( + "fmt" + "os" + "strconv" +) + +type DB struct { + Host string + Port int +} + +type Server struct { + Port string +} +type Config struct { + DB DB + Server Server +} + +func LoadFromEnv() (*Config, error) { + var config Config + + if dbHost, ok := os.LookupEnv("DB_HOST"); ok { + config.DB.Host = dbHost + } else { + return nil, fmt.Errorf("missing DB_HOST environment variable") + } + + if dbPortStr, ok := os.LookupEnv("DB_PORT"); ok { + dbPort, err := strconv.Atoi(dbPortStr) + if err != nil { + return nil, fmt.Errorf("failed to parse DB_PORT: %w", err) + } + config.DB.Port = dbPort + } else { + return nil, fmt.Errorf("missing DB_PORT environment variable") + } + + if serverPort, ok := os.LookupEnv("PORT"); ok { + config.Server.Port = serverPort + } else { + return nil, fmt.Errorf("missing PORT environmental variable") + } + + return &config, nil +} diff --git a/services/app-exchange/go.mod b/services/app-exchange/go.mod new file mode 100644 index 0000000..d7e3f70 --- /dev/null +++ b/services/app-exchange/go.mod @@ -0,0 +1,18 @@ +module github.com/AppsLab-KE/backend-everyshilling/services/app-exchange + +go 1.19 + +require ( + github.com/AppsLab-KE/be-go-gen-grpc v0.0.14 + github.com/sirupsen/logrus v1.9.2 + google.golang.org/grpc v1.54.0 + google.golang.org/protobuf v1.30.0 +) + +require ( + github.com/golang/protobuf v1.5.2 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect +) diff --git a/services/app-exchange/go.sum b/services/app-exchange/go.sum new file mode 100644 index 0000000..6d6e141 --- /dev/null +++ b/services/app-exchange/go.sum @@ -0,0 +1,36 @@ +github.com/AppsLab-KE/be-go-gen-grpc v0.0.14 h1:/9xlOF2duu5U4nOV/ukrnGG+Psf4j4Itz+dHM6HOO7w= +github.com/AppsLab-KE/be-go-gen-grpc v0.0.14/go.mod h1:F0zAKKv7O9FfplN/28CsWBRJu2ZArWNgIZbix6isDKY= +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/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= +github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/services/app-exchange/internal/core/model/account.go b/services/app-exchange/internal/core/model/account.go new file mode 100644 index 0000000..84a7a6d --- /dev/null +++ b/services/app-exchange/internal/core/model/account.go @@ -0,0 +1,32 @@ +package model + +type Account struct { + AccountId string `json:"account_id"` + UserId string `json:""` + Balance int64 `json:"balance"` + BaseCurrency string `json:"base_currency"` + CreatedAt int64 `json:"created_at"` + ParentAccountId string `json:"parent_account_id"` +} + +type Transaction struct { + TransactionId string `json:"transaction_id"` + AccountId string `json:"account_id"` + Amount int64 `json:"amount"` + TransactionType string `json:"transaction_type"` + TransactionStatus string `json:"transaction_status"` + TransactionCode string `json:"transaction_code"` + TransactionDescription string `json:"transaction_description"` + CreatedAt int64 `json:"created_at"` +} + +type Trade struct { + TradeId string `json:"trade_id"` + AccountId string `json:"account_id"` + TradeType string `json:"trade_type"` + TradeStatus string `json:"trade_status"` + FromCurrency string `json:"from_currency"` + ToCurrency string `json:"to_currency"` + FromAmount int64 `json:"from_amount"` + FinalAmount int64 `json:"final_amount"` +} diff --git a/services/app-exchange/internal/core/ports/accounts.go b/services/app-exchange/internal/core/ports/accounts.go new file mode 100644 index 0000000..ac445fa --- /dev/null +++ b/services/app-exchange/internal/core/ports/accounts.go @@ -0,0 +1,22 @@ +package ports + +import ( + "context" + "github.com/AppsLab-KE/be-go-gen-grpc/db" +) + +type AccountRepository interface { + // ACCOUNT + CreateAccount(ctx context.Context, in *db.CreateAccountRequest) (*db.CreateAccountResponse, error) + DeleteAccount(ctx context.Context, in *db.DeleteAccountRequest) (*db.DeleteAccountResponse, error) + UpdateAccount(ctx context.Context, in *db.UpdateAccountRequest) (*db.UpdateAccountResponse, error) + SearchAccount(ctx context.Context, in *db.SearchAccountRequest) (*db.SearchAccountResponse, error) +} + +type AccountsService interface { + // ACCOUNT + CreateAccount(ctx context.Context, in *db.CreateAccountRequest) (*db.CreateAccountResponse, error) + DeleteAccount(ctx context.Context, in *db.DeleteAccountRequest) (*db.DeleteAccountResponse, error) + UpdateAccount(ctx context.Context, in *db.UpdateAccountRequest) (*db.UpdateAccountResponse, error) + SearchAccount(ctx context.Context, in *db.SearchAccountRequest) (*db.SearchAccountResponse, error) +} diff --git a/services/app-exchange/internal/core/ports/database.go b/services/app-exchange/internal/core/ports/database.go new file mode 100644 index 0000000..779c1b4 --- /dev/null +++ b/services/app-exchange/internal/core/ports/database.go @@ -0,0 +1,30 @@ +package ports + +import ( + "context" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "google.golang.org/protobuf/types/known/emptypb" +) + +type DBStorage interface { + HealthCheck(ctx context.Context, in *db.DefaultRequest) (*db.HealthResponse, error) + // RATES + ReadConversionRate(ctx context.Context, in *db.ReadConversionRateRequest) (*db.ReadConversionRateResponse, error) + // ACCOUNT + CreateAccount(ctx context.Context, in *db.CreateAccountRequest) (*db.CreateAccountResponse, error) + DeleteAccount(ctx context.Context, in *db.DeleteAccountRequest) (*db.DeleteAccountResponse, error) + UpdateAccount(ctx context.Context, in *db.UpdateAccountRequest) (*db.UpdateAccountResponse, error) + SearchAccount(ctx context.Context, in *db.SearchAccountRequest) (*db.SearchAccountResponse, error) + // TRANSACTION + CreateTransaction(ctx context.Context, in *db.CreateTransactionRequest) (*db.CreateTransactionResponse, error) + DeleteTransaction(ctx context.Context, in *db.DeleteTransactionRequest) (emptypb.Empty, error) + UpdateTransaction(ctx context.Context, in *db.UpdateTransactionRequest) (emptypb.Empty, error) + GetTransaction(ctx context.Context, in *db.GetTransactionRequest) (*db.Transaction, error) + GetTransactionByAccount(ctx context.Context, in *db.GetTransactionByAccountRequest) (*db.GetTransactionByAccountResponse, error) + // TRADING + CreateTrade(ctx context.Context, in *db.CreateTradeRequest) (*db.CreateTradeResponse, error) + DeleteTrade(ctx context.Context, in *db.DeleteTradeRequest) (emptypb.Empty, error) + UpdateTrade(ctx context.Context, in *db.UpdateTradeRequest) (emptypb.Empty, error) + GetTrade(ctx context.Context, in *db.GetTradeRequest) (*db.Trade, error) + GetTradeByAccount(ctx context.Context, in *db.GetTradeByAccountRequest) (*db.GetTradeByAccountResponse, error) +} diff --git a/services/app-exchange/internal/core/ports/exchange.go b/services/app-exchange/internal/core/ports/exchange.go new file mode 100644 index 0000000..b07acd5 --- /dev/null +++ b/services/app-exchange/internal/core/ports/exchange.go @@ -0,0 +1,14 @@ +package ports + +import ( + "context" + "github.com/AppsLab-KE/be-go-gen-grpc/db" +) + +type ExchangeRepository interface { + ReadConversionRate(ctx context.Context, in *db.ReadConversionRateRequest) (*db.ReadConversionRateResponse, error) +} + +type ExchangeService interface { + ReadConversionRate(ctx context.Context, in *db.ReadConversionRateRequest) (*db.ReadConversionRateResponse, error) +} diff --git a/services/app-exchange/internal/core/ports/trade.go b/services/app-exchange/internal/core/ports/trade.go new file mode 100644 index 0000000..08049ac --- /dev/null +++ b/services/app-exchange/internal/core/ports/trade.go @@ -0,0 +1,23 @@ +package ports + +import ( + "context" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "google.golang.org/protobuf/types/known/emptypb" +) + +type TradeRepository interface { + CreateTrade(ctx context.Context, in *db.CreateTradeRequest) (*db.CreateTradeResponse, error) + DeleteTrade(ctx context.Context, in *db.DeleteTradeRequest) (emptypb.Empty, error) + UpdateTrade(ctx context.Context, in *db.UpdateTradeRequest) (emptypb.Empty, error) + GetTrade(ctx context.Context, in *db.GetTradeRequest) (*db.Trade, error) + GetTradeByAccount(ctx context.Context, in *db.GetTradeByAccountRequest) (*db.GetTradeByAccountResponse, error) +} + +type TradeService interface { + CreateTrade(ctx context.Context, in *db.CreateTradeRequest) (*db.CreateTradeResponse, error) + DeleteTrade(ctx context.Context, in *db.DeleteTradeRequest) (emptypb.Empty, error) + UpdateTrade(ctx context.Context, in *db.UpdateTradeRequest) (emptypb.Empty, error) + GetTrade(ctx context.Context, in *db.GetTradeRequest) (*db.Trade, error) + GetTradeByAccount(ctx context.Context, in *db.GetTradeByAccountRequest) (*db.GetTradeByAccountResponse, error) +} diff --git a/services/app-exchange/internal/core/ports/transactions.go b/services/app-exchange/internal/core/ports/transactions.go new file mode 100644 index 0000000..280bfe7 --- /dev/null +++ b/services/app-exchange/internal/core/ports/transactions.go @@ -0,0 +1,23 @@ +package ports + +import ( + "context" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "google.golang.org/protobuf/types/known/emptypb" +) + +type TransactionRepository interface { + CreateTransaction(ctx context.Context, in *db.CreateTransactionRequest) (*db.CreateTransactionResponse, error) + DeleteTransaction(ctx context.Context, in *db.DeleteTransactionRequest) (emptypb.Empty, error) + UpdateTransaction(ctx context.Context, in *db.UpdateTransactionRequest) (emptypb.Empty, error) + GetTransaction(ctx context.Context, in *db.GetTransactionRequest) (*db.Transaction, error) + GetTransactionByAccount(ctx context.Context, in *db.GetTransactionByAccountRequest) (*db.GetTransactionByAccountResponse, error) +} + +type TransactionService interface { + CreateTransaction(ctx context.Context, in *db.CreateTransactionRequest) (*db.CreateTransactionResponse, error) + DeleteTransaction(ctx context.Context, in *db.DeleteTransactionRequest) (emptypb.Empty, error) + UpdateTransaction(ctx context.Context, in *db.UpdateTransactionRequest) (emptypb.Empty, error) + GetTransaction(ctx context.Context, in *db.GetTransactionRequest) (*db.Transaction, error) + GetTransactionByAccount(ctx context.Context, in *db.GetTransactionByAccountRequest) (*db.GetTransactionByAccountResponse, error) +} diff --git a/services/app-exchange/internal/core/repositories/account.go b/services/app-exchange/internal/core/repositories/account.go new file mode 100644 index 0000000..7887cd7 --- /dev/null +++ b/services/app-exchange/internal/core/repositories/account.go @@ -0,0 +1,49 @@ +package repositories + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/core/ports" + "github.com/AppsLab-KE/be-go-gen-grpc/db" +) + +type accountRepository struct { + dbStorage ports.DBStorage +} + +func (a accountRepository) CreateAccount(ctx context.Context, in *db.CreateAccountRequest) (*db.CreateAccountResponse, error) { + account, err := a.dbStorage.CreateAccount(ctx, in) + if err != nil { + return nil, err + } + return account, nil +} + +func (a accountRepository) DeleteAccount(ctx context.Context, in *db.DeleteAccountRequest) (*db.DeleteAccountResponse, error) { + result, err := a.dbStorage.DeleteAccount(ctx, in) + if err != nil { + return nil, err + } + return result, nil +} + +func (a accountRepository) UpdateAccount(ctx context.Context, in *db.UpdateAccountRequest) (*db.UpdateAccountResponse, error) { + res, err := a.dbStorage.UpdateAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (a accountRepository) SearchAccount(ctx context.Context, in *db.SearchAccountRequest) (*db.SearchAccountResponse, error) { + res, err := a.dbStorage.SearchAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func NewAccountRepository(dbStorage ports.DBStorage) ports.AccountRepository { + return &accountRepository{ + dbStorage: dbStorage, + } +} diff --git a/services/app-exchange/internal/core/repositories/exchange.go b/services/app-exchange/internal/core/repositories/exchange.go new file mode 100644 index 0000000..065b508 --- /dev/null +++ b/services/app-exchange/internal/core/repositories/exchange.go @@ -0,0 +1,25 @@ +package repositories + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/core/ports" + "github.com/AppsLab-KE/be-go-gen-grpc/db" +) + +type exchangeRepository struct { + dbStorage ports.DBStorage +} + +func (e exchangeRepository) ReadConversionRate(ctx context.Context, in *db.ReadConversionRateRequest) (*db.ReadConversionRateResponse, error) { + res, err := e.dbStorage.ReadConversionRate(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func NewExchangeRepository(dbStorage ports.DBStorage) ports.ExchangeRepository { + return &exchangeRepository{ + dbStorage: dbStorage, + } +} diff --git a/services/app-exchange/internal/core/repositories/trade.go b/services/app-exchange/internal/core/repositories/trade.go new file mode 100644 index 0000000..f9f0ee9 --- /dev/null +++ b/services/app-exchange/internal/core/repositories/trade.go @@ -0,0 +1,58 @@ +package repositories + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/core/ports" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "google.golang.org/protobuf/types/known/emptypb" +) + +type tradeRepository struct { + dbStorage ports.DBStorage +} + +func (t tradeRepository) CreateTrade(ctx context.Context, in *db.CreateTradeRequest) (*db.CreateTradeResponse, error) { + res, err := t.dbStorage.CreateTrade(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (t tradeRepository) DeleteTrade(ctx context.Context, in *db.DeleteTradeRequest) (emptypb.Empty, error) { + res, err := t.dbStorage.DeleteTrade(ctx, in) + if err != nil { + return emptypb.Empty{}, err + } + return res, nil +} + +func (t tradeRepository) UpdateTrade(ctx context.Context, in *db.UpdateTradeRequest) (emptypb.Empty, error) { + res, err := t.dbStorage.UpdateTrade(ctx, in) + if err != nil { + return emptypb.Empty{}, err + } + return res, nil +} + +func (t tradeRepository) GetTrade(ctx context.Context, in *db.GetTradeRequest) (*db.Trade, error) { + res, err := t.dbStorage.GetTrade(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (t tradeRepository) GetTradeByAccount(ctx context.Context, in *db.GetTradeByAccountRequest) (*db.GetTradeByAccountResponse, error) { + res, err := t.dbStorage.GetTradeByAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func NewTradeRepository(dbStorage ports.DBStorage) ports.TradeRepository { + return &tradeRepository{ + dbStorage: dbStorage, + } +} diff --git a/services/app-exchange/internal/core/repositories/transactions.go b/services/app-exchange/internal/core/repositories/transactions.go new file mode 100644 index 0000000..6b9bfd7 --- /dev/null +++ b/services/app-exchange/internal/core/repositories/transactions.go @@ -0,0 +1,58 @@ +package repositories + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/core/ports" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "google.golang.org/protobuf/types/known/emptypb" +) + +type transactionRepository struct { + dbStorage ports.DBStorage +} + +func (t transactionRepository) CreateTransaction(ctx context.Context, in *db.CreateTransactionRequest) (*db.CreateTransactionResponse, error) { + res, err := t.dbStorage.CreateTransaction(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (t transactionRepository) DeleteTransaction(ctx context.Context, in *db.DeleteTransactionRequest) (emptypb.Empty, error) { + res, err := t.dbStorage.DeleteTransaction(ctx, in) + if err != nil { + return emptypb.Empty{}, err + } + return res, nil +} + +func (t transactionRepository) UpdateTransaction(ctx context.Context, in *db.UpdateTransactionRequest) (emptypb.Empty, error) { + res, err := t.dbStorage.UpdateTransaction(ctx, in) + if err != nil { + return emptypb.Empty{}, err + } + return res, nil +} + +func (t transactionRepository) GetTransaction(ctx context.Context, in *db.GetTransactionRequest) (*db.Transaction, error) { + res, err := t.dbStorage.GetTransaction(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (t transactionRepository) GetTransactionByAccount(ctx context.Context, in *db.GetTransactionByAccountRequest) (*db.GetTransactionByAccountResponse, error) { + res, err := t.dbStorage.GetTransactionByAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func NewTransactionRepository(dbStorage ports.DBStorage) ports.TransactionRepository { + return &transactionRepository{ + dbStorage: dbStorage, + } +} diff --git a/services/app-exchange/internal/core/services/accounts.go b/services/app-exchange/internal/core/services/accounts.go new file mode 100644 index 0000000..f68093e --- /dev/null +++ b/services/app-exchange/internal/core/services/accounts.go @@ -0,0 +1,66 @@ +package services + +import ( + "context" + "errors" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/core/ports" + "github.com/AppsLab-KE/be-go-gen-grpc/db" +) + +var ( + AccountExistsErr = errors.New("account already exists") + ErrParentAccountRequired = errors.New("parent account is required") +) + +type accountsService struct { + accountRepository ports.AccountRepository +} + +func (a accountsService) CreateAccount(ctx context.Context, in *db.CreateAccountRequest) (*db.CreateAccountResponse, error) { + // list all the account that the user has + accounts, err := a.accountRepository.SearchAccount(ctx, &db.SearchAccountRequest{UserId: in.Account.UserId}) + if err != nil { + return nil, err + } + + // check if the account already exists + for _, account := range accounts.Accounts { + if account.BaseCurrency == in.Account.BaseCurrency { + return nil, AccountExistsErr + } + } + + // if an account exists, make sure parent account is specified + if len(accounts.Accounts) > 0 && in.Account.ParentAccountId == "" { + return nil, ErrParentAccountRequired + } + + // check if the parent account exists + _, err = a.accountRepository.SearchAccount(ctx, &db.SearchAccountRequest{AccountId: in.Account.ParentAccountId}) + if err != nil { + return nil, NonExistingAccountErr + } + + // go ahead and create the account + return a.accountRepository.CreateAccount(ctx, in) + +} + +func (a accountsService) DeleteAccount(ctx context.Context, in *db.DeleteAccountRequest) (*db.DeleteAccountResponse, error) { + // just delete the account + return a.accountRepository.DeleteAccount(ctx, in) +} + +func (a accountsService) UpdateAccount(ctx context.Context, in *db.UpdateAccountRequest) (*db.UpdateAccountResponse, error) { + // just update the account + return a.accountRepository.UpdateAccount(ctx, in) +} + +func (a accountsService) SearchAccount(ctx context.Context, in *db.SearchAccountRequest) (*db.SearchAccountResponse, error) { + // just search the account + return a.accountRepository.SearchAccount(ctx, in) +} + +func NewAccountsService(accountRepository ports.AccountRepository) ports.AccountsService { + return &accountsService{accountRepository: accountRepository} +} diff --git a/services/app-exchange/internal/core/services/exchange.go b/services/app-exchange/internal/core/services/exchange.go new file mode 100644 index 0000000..65a2bf1 --- /dev/null +++ b/services/app-exchange/internal/core/services/exchange.go @@ -0,0 +1,19 @@ +package services + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/core/ports" + "github.com/AppsLab-KE/be-go-gen-grpc/db" +) + +type exchangeService struct { + exchangeRepo ports.ExchangeRepository +} + +func (e exchangeService) ReadConversionRate(ctx context.Context, in *db.ReadConversionRateRequest) (*db.ReadConversionRateResponse, error) { + return e.exchangeRepo.ReadConversionRate(ctx, in) +} + +func NewExchangeService(exchangeRepo ports.ExchangeRepository) ports.ExchangeService { + return &exchangeService{exchangeRepo: exchangeRepo} +} diff --git a/services/app-exchange/internal/core/services/trade.go b/services/app-exchange/internal/core/services/trade.go new file mode 100644 index 0000000..bce0aff --- /dev/null +++ b/services/app-exchange/internal/core/services/trade.go @@ -0,0 +1,203 @@ +package services + +import ( + "context" + "errors" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/core/ports" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "google.golang.org/protobuf/types/known/emptypb" + "time" +) + +var ( + ErrSameCurrency = errors.New("cannot trade same currency") + ErrNonMatchingCurrency = errors.New("currency does not match account currency") + ErrDestinationAccount = errors.New("destination account does not have the currency") +) + +type tradeService struct { + tradeRepository ports.TradeRepository + accountRepository ports.AccountRepository + rateRepository ports.ExchangeRepository + transactionRepository ports.TransactionRepository +} + +func (t tradeService) CreateTrade(ctx context.Context, in *db.CreateTradeRequest) (*db.CreateTradeResponse, error) { + // check that currencies are not the same + if in.Trade.FromCurrency == in.Trade.ToCurrency { + return nil, ErrSameCurrency + } + + // check that the account has enough balance + account, err := t.accountRepository.SearchAccount(ctx, &db.SearchAccountRequest{AccountId: in.Trade.AccountId}) + if err != nil { + return nil, NonExistingAccountErr + } + + // fetch the account that is being traded with + var tradeAccount *db.Account + var destinationAccount *db.Account + + for _, acc := range account.Accounts { + if acc.AccountId == in.Trade.AccountId { + tradeAccount = acc + } + } + + if tradeAccount == nil { + return nil, NonExistingAccountErr + } + + // check if currency matched the account currency + if tradeAccount.BaseCurrency != in.Trade.FromCurrency { + return nil, ErrNonMatchingCurrency + } + + if tradeAccount.Balance < in.Trade.FromAmount { + return nil, InsufficientFundErr + } + + // get a destination account that matches the to currency + for _, acc := range account.Accounts { + if acc.BaseCurrency == in.Trade.ToCurrency { + destinationAccount = acc + } + } + + if destinationAccount == nil { + return nil, ErrDestinationAccount + } + + // check the rate + rates, err := t.rateRepository.ReadConversionRate(ctx, &db.ReadConversionRateRequest{}) + if err != nil { + return nil, err + } + + // get the base currency + var baseCurrency = in.Trade.FromCurrency + + var exchangeRate float64 + for _, rate := range rates.ConversionRate { + if rate.FromCurrency == baseCurrency && rate.ToCurrency == in.Trade.FromCurrency { + exchangeRate = rate.Rate + } + } + + // if base currency is not USD then convert money using USD as the base currency + if baseCurrency != "USD" { + for _, rate := range rates.ConversionRate { + if rate.FromCurrency == "USD" && rate.ToCurrency == in.Trade.FromCurrency { + exchangeRate = rate.Rate + } + } + } + + // calculate the amount to be deducted from the account + amountToBeDeducted := float64(in.Trade.FromAmount) * exchangeRate + + // deduct the amount from the account + tradeAccount.Balance -= int64(in.Trade.FromAmount) + + // update the account + _, err = t.accountRepository.UpdateAccount(ctx, &db.UpdateAccountRequest{Account: tradeAccount}) + if err != nil { + return nil, err + } + + // add the amount to the destination account + destinationAccount.Balance += int64(amountToBeDeducted) + // update the account + _, err = t.accountRepository.UpdateAccount(ctx, &db.UpdateAccountRequest{Account: destinationAccount}) + if err != nil { + return nil, err + } + + // create debit and credit transactions + // credit transaction + creditTransaction := &db.Transaction{ + AccountId: in.Trade.AccountId, + Amount: in.Trade.FromAmount, + TransactionType: "credit", + TransactionStatus: "", + TransactionCode: "", + TransactionDescription: "", + CreatedAt: time.Now().UnixNano(), + } + + _, err = t.transactionRepository.CreateTransaction(ctx, &db.CreateTransactionRequest{Transaction: creditTransaction}) + if err != nil { + return nil, err + } + + // debitTransaction + debitTransaction := &db.Transaction{ + AccountId: destinationAccount.AccountId, + Amount: int64(amountToBeDeducted), + TransactionType: "debit", + TransactionStatus: "", + TransactionCode: "", + TransactionDescription: "", + CreatedAt: time.Now().UnixNano(), + } + + _, err = t.transactionRepository.CreateTransaction(ctx, &db.CreateTransactionRequest{Transaction: debitTransaction}) + if err != nil { + return nil, err + } + + // create the trade + trade, err := t.tradeRepository.CreateTrade(ctx, in) + if err != nil { + return nil, err + } + + // return the trade + return trade, nil + +} + +func (t tradeService) DeleteTrade(ctx context.Context, in *db.DeleteTradeRequest) (emptypb.Empty, error) { + // just delete the trade without any checks + _, err := t.tradeRepository.DeleteTrade(ctx, in) + if err != nil { + return emptypb.Empty{}, err + } + return emptypb.Empty{}, nil +} + +func (t tradeService) UpdateTrade(ctx context.Context, in *db.UpdateTradeRequest) (emptypb.Empty, error) { + // just update the trade without any checks + _, err := t.tradeRepository.UpdateTrade(ctx, in) + if err != nil { + return emptypb.Empty{}, err + } + return emptypb.Empty{}, nil +} + +func (t tradeService) GetTrade(ctx context.Context, in *db.GetTradeRequest) (*db.Trade, error) { + // just get the trade without any checks + trade, err := t.tradeRepository.GetTrade(ctx, in) + if err != nil { + return nil, err + } + return trade, nil +} + +func (t tradeService) GetTradeByAccount(ctx context.Context, in *db.GetTradeByAccountRequest) (*db.GetTradeByAccountResponse, error) { + // just get the trade without any checks + trade, err := t.tradeRepository.GetTradeByAccount(ctx, in) + if err != nil { + return nil, err + } + return trade, nil +} + +func NewTradeService(tradeRepository ports.TradeRepository, accountRepo ports.AccountRepository, rateRepo ports.ExchangeRepository, transactionRepo ports.TransactionRepository) ports.TradeService { + return &tradeService{ + tradeRepository: tradeRepository, + accountRepository: accountRepo, + rateRepository: rateRepo, + transactionRepository: transactionRepo, + } +} diff --git a/services/app-exchange/internal/core/services/transactions.go b/services/app-exchange/internal/core/services/transactions.go new file mode 100644 index 0000000..5d868c2 --- /dev/null +++ b/services/app-exchange/internal/core/services/transactions.go @@ -0,0 +1,130 @@ +package services + +import ( + "context" + "errors" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/core/ports" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "google.golang.org/protobuf/types/known/emptypb" +) + +var ( + InvalidTransactionTypeErr = errors.New("invalid transaction type") + NonExistingAccountErr = errors.New("account does not exist") + NonExistingTransactionErr = errors.New("transaction does not exist") + InsufficientFundErr = errors.New("insufficient funds") +) + +const ( + TransactionDebit = "debit" + TransactionCredit = "credit" +) + +type transactionService struct { + transactionRepo ports.TransactionRepository + accountsRepo ports.AccountRepository +} + +func (t transactionService) CreateTransaction(ctx context.Context, in *db.CreateTransactionRequest) (*db.CreateTransactionResponse, error) { + // validate transaction type + if in.Transaction.TransactionType != TransactionDebit && in.Transaction.TransactionType != TransactionCredit { + return nil, InvalidTransactionTypeErr + } + + // validate account exists + account, err := t.accountsRepo.SearchAccount(ctx, &db.SearchAccountRequest{AccountId: in.Transaction.AccountId}) + if err != nil { + return nil, NonExistingAccountErr + } + + // get the account matching the account id + var accountLookedUp *db.Account + for _, acc := range account.Accounts { + if acc.AccountId == in.Transaction.AccountId { + accountLookedUp = acc + } + } + + if accountLookedUp == nil { + return nil, NonExistingAccountErr + } + + // check balance + if in.Transaction.TransactionType == TransactionDebit && accountLookedUp.Balance < in.Transaction.Amount { + return nil, InsufficientFundErr + } + + res, err := t.transactionRepo.CreateTransaction(ctx, in) + if err != nil { + return nil, err + } + + // update account balance + if in.Transaction.TransactionType == TransactionDebit { + accountLookedUp.Balance -= in.Transaction.Amount + } else { + accountLookedUp.Balance += in.Transaction.Amount + } + + _, err = t.accountsRepo.UpdateAccount(ctx, &db.UpdateAccountRequest{Account: accountLookedUp}) + if err != nil { + return nil, err + } + + return res, nil +} + +func (t transactionService) DeleteTransaction(ctx context.Context, in *db.DeleteTransactionRequest) (emptypb.Empty, error) { + // check if transaction exists + _, err := t.transactionRepo.GetTransaction(ctx, &db.GetTransactionRequest{TransactionId: in.TransactionId}) + if err != nil { + return emptypb.Empty{}, NonExistingTransactionErr + } + + // delete transaction + _, err = t.transactionRepo.DeleteTransaction(ctx, in) + if err != nil { + return emptypb.Empty{}, err + } + + return emptypb.Empty{}, nil +} + +func (t transactionService) UpdateTransaction(ctx context.Context, in *db.UpdateTransactionRequest) (emptypb.Empty, error) { + _, err := t.transactionRepo.GetTransaction(ctx, &db.GetTransactionRequest{TransactionId: in.Transaction.TransactionId}) + if err != nil { + return emptypb.Empty{}, NonExistingTransactionErr + } + + _, err = t.transactionRepo.UpdateTransaction(ctx, in) + if err != nil { + return emptypb.Empty{}, err + } + + return emptypb.Empty{}, nil +} + +func (t transactionService) GetTransaction(ctx context.Context, in *db.GetTransactionRequest) (*db.Transaction, error) { + transactions, err := t.transactionRepo.GetTransaction(ctx, in) + if err != nil { + return nil, err + } + + return transactions, nil +} + +func (t transactionService) GetTransactionByAccount(ctx context.Context, in *db.GetTransactionByAccountRequest) (*db.GetTransactionByAccountResponse, error) { + transactions, err := t.transactionRepo.GetTransactionByAccount(ctx, in) + if err != nil { + return nil, err + } + + return transactions, nil +} + +func NewTransactionService(repository ports.TransactionRepository, accountsRepo ports.AccountRepository) ports.TransactionService { + return &transactionService{ + transactionRepo: repository, + accountsRepo: accountsRepo, + } +} diff --git a/services/app-exchange/internal/core/storage/app-db.go b/services/app-exchange/internal/core/storage/app-db.go new file mode 100644 index 0000000..58a04fd --- /dev/null +++ b/services/app-exchange/internal/core/storage/app-db.go @@ -0,0 +1,146 @@ +package storage + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/core/ports" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "google.golang.org/protobuf/types/known/emptypb" +) + +type dbStorage struct { + client db.DbServiceClient +} + +func (d dbStorage) HealthCheck(ctx context.Context, in *db.DefaultRequest) (*db.HealthResponse, error) { + res, err := d.client.HealthCheck(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (d dbStorage) ReadConversionRate(ctx context.Context, in *db.ReadConversionRateRequest) (*db.ReadConversionRateResponse, error) { + res, err := d.client.ReadConversionRate(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (d dbStorage) CreateAccount(ctx context.Context, in *db.CreateAccountRequest) (*db.CreateAccountResponse, error) { + res, err := d.client.CreateAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (d dbStorage) DeleteAccount(ctx context.Context, in *db.DeleteAccountRequest) (*db.DeleteAccountResponse, error) { + res, err := d.client.DeleteAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (d dbStorage) UpdateAccount(ctx context.Context, in *db.UpdateAccountRequest) (*db.UpdateAccountResponse, error) { + res, err := d.client.UpdateAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (d dbStorage) SearchAccount(ctx context.Context, in *db.SearchAccountRequest) (*db.SearchAccountResponse, error) { + res, err := d.client.SearchAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (d dbStorage) CreateTransaction(ctx context.Context, in *db.CreateTransactionRequest) (*db.CreateTransactionResponse, error) { + res, err := d.client.CreateTransaction(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (d dbStorage) DeleteTransaction(ctx context.Context, in *db.DeleteTransactionRequest) (emptypb.Empty, error) { + res, err := d.client.DeleteTransaction(ctx, in) + if err != nil { + return emptypb.Empty{}, err + } + return *res, nil +} + +func (d dbStorage) UpdateTransaction(ctx context.Context, in *db.UpdateTransactionRequest) (emptypb.Empty, error) { + res, err := d.client.UpdateTransaction(ctx, in) + if err != nil { + return emptypb.Empty{}, err + } + return *res, nil +} + +func (d dbStorage) GetTransaction(ctx context.Context, in *db.GetTransactionRequest) (*db.Transaction, error) { + res, err := d.client.GetTransaction(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (d dbStorage) GetTransactionByAccount(ctx context.Context, in *db.GetTransactionByAccountRequest) (*db.GetTransactionByAccountResponse, error) { + res, err := d.client.GetTransactionByAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (d dbStorage) CreateTrade(ctx context.Context, in *db.CreateTradeRequest) (*db.CreateTradeResponse, error) { + res, err := d.client.CreateTrade(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (d dbStorage) DeleteTrade(ctx context.Context, in *db.DeleteTradeRequest) (emptypb.Empty, error) { + _, err := d.client.DeleteTrade(ctx, in) + if err != nil { + return emptypb.Empty{}, err + } + return emptypb.Empty{}, nil +} + +func (d dbStorage) UpdateTrade(ctx context.Context, in *db.UpdateTradeRequest) (emptypb.Empty, error) { + _, err := d.client.UpdateTrade(ctx, in) + if err != nil { + return emptypb.Empty{}, err + } + return emptypb.Empty{}, nil +} + +func (d dbStorage) GetTrade(ctx context.Context, in *db.GetTradeRequest) (*db.Trade, error) { + res, err := d.client.GetTrade(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func (d dbStorage) GetTradeByAccount(ctx context.Context, in *db.GetTradeByAccountRequest) (*db.GetTradeByAccountResponse, error) { + res, err := d.client.GetTradeByAccount(ctx, in) + if err != nil { + return nil, err + } + return res, nil +} + +func NewDBStorage(client db.DbServiceClient) ports.DBStorage { + return &dbStorage{ + client: client, + } +} diff --git a/services/app-exchange/internal/platform/app-db.go b/services/app-exchange/internal/platform/app-db.go new file mode 100644 index 0000000..157ea1c --- /dev/null +++ b/services/app-exchange/internal/platform/app-db.go @@ -0,0 +1,22 @@ +package platform + +import ( + "fmt" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/config" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "log" +) + +func NewDBServiceClient(config config.DB) (db.DbServiceClient, error) { + uri := fmt.Sprintf("%s:%d", config.Host, config.Port) + conn, err := grpc.Dial(uri, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, err + } + + log.Println("connected to app-db") + client := db.NewDbServiceClient(conn) + return client, nil +} diff --git a/services/app-exchange/internal/routes/handlers/account.go b/services/app-exchange/internal/routes/handlers/account.go new file mode 100644 index 0000000..c4d008c --- /dev/null +++ b/services/app-exchange/internal/routes/handlers/account.go @@ -0,0 +1,102 @@ +package handlers + +import ( + "context" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "github.com/AppsLab-KE/be-go-gen-grpc/exchange" + "time" +) + +func (h Handler) CreateAccount(ctx context.Context, request *exchange.CreateAccountRequest) (*exchange.CreateAccountResponse, error) { + accountsRequest := &db.CreateAccountRequest{ + Account: &db.Account{ + UserId: request.Account.UserId, + BaseCurrency: request.Account.BaseCurrency, + CreatedAt: time.Now().UnixNano(), + ParentAccountId: request.Account.ParentAccountId, + }, + } + res, err := h.accountsService.CreateAccount(ctx, accountsRequest) + if err != nil { + return nil, err + } + + accountsResult := &exchange.CreateAccountResponse{ + Account: &exchange.Account{ + AccountId: res.Account.AccountId, + UserId: res.Account.UserId, + Balance: res.Account.Balance, + BaseCurrency: res.Account.BaseCurrency, + CreatedAt: res.Account.CreatedAt, + ParentAccountId: res.Account.ParentAccountId, + }, + } + + return accountsResult, nil +} + +func (h Handler) DeleteAccount(ctx context.Context, request *exchange.DeleteAccountRequest) (*exchange.DeleteAccountResponse, error) { + deleteRequest := &db.DeleteAccountRequest{ + AccountId: request.AccountId, + } + + _, err := h.accountsService.DeleteAccount(ctx, deleteRequest) + if err != nil { + return nil, err + } + + deleteResult := &exchange.DeleteAccountResponse{} + + return deleteResult, nil + +} + +func (h Handler) UpdateAccount(ctx context.Context, request *exchange.UpdateAccountRequest) (*exchange.UpdateAccountResponse, error) { + updateRequest := &db.UpdateAccountRequest{ + Account: &db.Account{ + AccountId: request.Account.AccountId, + UserId: request.Account.UserId, + Balance: request.Account.Balance, + BaseCurrency: request.Account.BaseCurrency, + CreatedAt: request.Account.CreatedAt, + ParentAccountId: request.Account.ParentAccountId, + }, + } + + _, err := h.accountsService.UpdateAccount(ctx, updateRequest) + if err != nil { + return nil, err + } + + updateResult := &exchange.UpdateAccountResponse{} + + return updateResult, nil +} + +func (h Handler) SearchAccount(ctx context.Context, request *exchange.SearchAccountRequest) (*exchange.SearchAccountResponse, error) { + searchRequest := &db.SearchAccountRequest{ + AccountId: request.AccountId, + } + + res, err := h.accountsService.SearchAccount(ctx, searchRequest) + if err != nil { + return nil, err + } + + searchResult := &exchange.SearchAccountResponse{ + Accounts: []*exchange.Account{}, + } + + for _, account := range res.Accounts { + searchResult.Accounts = append(searchResult.Accounts, &exchange.Account{ + AccountId: account.AccountId, + UserId: account.UserId, + Balance: account.Balance, + BaseCurrency: account.BaseCurrency, + CreatedAt: account.CreatedAt, + ParentAccountId: account.ParentAccountId, + }) + } + + return searchResult, nil +} diff --git a/services/app-exchange/internal/routes/handlers/handler.go b/services/app-exchange/internal/routes/handlers/handler.go new file mode 100644 index 0000000..9efbd3e --- /dev/null +++ b/services/app-exchange/internal/routes/handlers/handler.go @@ -0,0 +1,34 @@ +package handlers + +import ( + "errors" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/core/ports" + "github.com/AppsLab-KE/be-go-gen-grpc/exchange" + log "github.com/sirupsen/logrus" +) + +type Handler struct { + exchange.UnimplementedExchangeServiceServer + exchangeService ports.ExchangeService + accountsService ports.AccountsService + tradeService ports.TradeService + transactionService ports.TransactionService +} + +var ( + ErrEmptyRequest = errors.New("empty request") +) + +func (h Handler) mustEmbedUnimplementedExchangeServiceServer() { + //TODO implement me + log.Error("not implemented") +} + +func NewHandler(exchangeService ports.ExchangeService, accountsService ports.AccountsService, tradeService ports.TradeService, transactionService ports.TransactionService) *Handler { + return &Handler{ + exchangeService: exchangeService, + accountsService: accountsService, + tradeService: tradeService, + transactionService: transactionService, + } +} diff --git a/services/app-exchange/internal/routes/handlers/rates.go b/services/app-exchange/internal/routes/handlers/rates.go new file mode 100644 index 0000000..ee18e1e --- /dev/null +++ b/services/app-exchange/internal/routes/handlers/rates.go @@ -0,0 +1,63 @@ +package handlers + +import ( + "context" + "errors" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "github.com/AppsLab-KE/be-go-gen-grpc/exchange" +) + +func (h Handler) CreateConversionRate(ctx context.Context, request *exchange.CreateConversionRateRequest) (*exchange.CreateConversionRateResponse, error) { + if request == nil { + return nil, ErrEmptyRequest + } + + return nil, errors.New("feature moved") +} + +func (h Handler) ReadConversionRate(ctx context.Context, request *exchange.ReadConversionRateRequest) (*exchange.ReadConversionRateResponse, error) { + if request == nil { + return nil, ErrEmptyRequest + } + + readConversionRateRequest := db.ReadConversionRateRequest{ + FromUnixUtc: request.FromUnixUtc, + ToUnixUtc: request.ToUnixUtc, + } + + readConversionRateResponse, err := h.exchangeService.ReadConversionRate(ctx, &readConversionRateRequest) + if err != nil { + return nil, err + } + + readConversionRateResponseResult := &exchange.ReadConversionRateResponse{ + ConversionRate: []*exchange.ConversionRate{}, + } + + for _, conversionRate := range readConversionRateResponse.ConversionRate { + readConversionRateResponseResult.ConversionRate = append(readConversionRateResponseResult.ConversionRate, &exchange.ConversionRate{ + FromCurrency: conversionRate.FromCurrency, + ToCurrency: conversionRate.ToCurrency, + Rate: conversionRate.Rate, + DateUpdatedUnixUtc: conversionRate.DateUpdatedUnixUtc, + }) + } + + return readConversionRateResponseResult, nil +} + +func (h Handler) UpdateConversionRate(ctx context.Context, request *exchange.UpdateConversionRateRequest) (*exchange.UpdateConversionRateResponse, error) { + if request == nil { + return nil, ErrEmptyRequest + } + + return nil, errors.New("feature moved") +} + +func (h Handler) DeleteConversionRate(ctx context.Context, request *exchange.DeleteConversionRateRequest) (*exchange.DeleteConversionRateResponse, error) { + if request == nil { + return nil, ErrEmptyRequest + } + + return nil, errors.New("feature moved") +} diff --git a/services/app-exchange/internal/routes/handlers/trade.go b/services/app-exchange/internal/routes/handlers/trade.go new file mode 100644 index 0000000..95e96a9 --- /dev/null +++ b/services/app-exchange/internal/routes/handlers/trade.go @@ -0,0 +1,138 @@ +package handlers + +import ( + "context" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "github.com/AppsLab-KE/be-go-gen-grpc/exchange" + "google.golang.org/protobuf/types/known/emptypb" +) + +func (h Handler) CreateTrade(ctx context.Context, request *exchange.CreateTradeRequest) (*exchange.CreateTradeResponse, error) { + if request == nil { + return nil, ErrEmptyRequest + } + + tradeRequest := &db.CreateTradeRequest{ + Trade: &db.Trade{ + AccountId: request.Trade.AccountId, + TradeType: request.Trade.TradeType, + TradeStatus: request.Trade.TradeStatus, + FromCurrency: request.Trade.FromCurrency, + ToCurrency: request.Trade.ToCurrency, + FromAmount: request.Trade.FromAmount, + }, + } + + tradeResponse, err := h.tradeService.CreateTrade(ctx, tradeRequest) + if err != nil { + return nil, err + } + + tradeResponseResult := &exchange.CreateTradeResponse{ + TradeId: tradeResponse.TradeId, + } + + return tradeResponseResult, nil +} + +func (h Handler) DeleteTrade(ctx context.Context, request *exchange.DeleteTradeRequest) (*emptypb.Empty, error) { + if request == nil { + return nil, ErrEmptyRequest + } + + deleteRequest := &db.DeleteTradeRequest{ + TradeId: request.TradeId, + } + + _, err := h.tradeService.DeleteTrade(ctx, deleteRequest) + if err != nil { + return nil, err + } + + return &emptypb.Empty{}, nil +} + +func (h Handler) UpdateTrade(ctx context.Context, request *exchange.UpdateTradeRequest) (*emptypb.Empty, error) { + if request == nil { + return nil, ErrEmptyRequest + } + + updateRequest := &db.UpdateTradeRequest{ + Trade: &db.Trade{ + TradeId: request.Trade.TradeId, + AccountId: request.Trade.AccountId, + TradeType: request.Trade.TradeType, + TradeStatus: request.Trade.TradeStatus, + FromCurrency: request.Trade.FromCurrency, + ToCurrency: request.Trade.ToCurrency, + FromAmount: request.Trade.FromAmount, + }, + } + + _, err := h.tradeService.UpdateTrade(ctx, updateRequest) + if err != nil { + return nil, err + } + + return &emptypb.Empty{}, nil +} + +func (h Handler) GetTrade(ctx context.Context, request *exchange.GetTradeRequest) (*exchange.Trade, error) { + if request == nil { + return nil, ErrEmptyRequest + } + + getRequest := &db.GetTradeRequest{ + TradeId: request.TradeId, + } + + tradeResponse, err := h.tradeService.GetTrade(ctx, getRequest) + if err != nil { + return nil, err + } + + tradeResponseResult := &exchange.Trade{ + TradeId: tradeResponse.TradeId, + AccountId: tradeResponse.AccountId, + TradeType: tradeResponse.TradeType, + TradeStatus: tradeResponse.TradeStatus, + FromCurrency: tradeResponse.FromCurrency, + ToCurrency: tradeResponse.ToCurrency, + FromAmount: tradeResponse.FromAmount, + } + + return tradeResponseResult, nil +} + +func (h Handler) GetTradeByAccount(ctx context.Context, request *exchange.GetTradeByAccountRequest) (*exchange.GetTradeByAccountResponse, error) { + if request == nil { + return nil, ErrEmptyRequest + } + + getRequest := &db.GetTradeByAccountRequest{ + AccountId: request.AccountId, + } + + tradeResponse, err := h.tradeService.GetTradeByAccount(ctx, getRequest) + if err != nil { + return nil, err + } + + tradeResponseResult := &exchange.GetTradeByAccountResponse{ + Trades: []*exchange.Trade{}, + } + + for _, trade := range tradeResponse.Trades { + tradeResponseResult.Trades = append(tradeResponseResult.Trades, &exchange.Trade{ + TradeId: trade.TradeId, + AccountId: trade.AccountId, + TradeType: trade.TradeType, + TradeStatus: trade.TradeStatus, + FromCurrency: trade.FromCurrency, + ToCurrency: trade.ToCurrency, + FromAmount: trade.FromAmount, + }) + } + + return tradeResponseResult, nil +} diff --git a/services/app-exchange/internal/routes/handlers/transactions.go b/services/app-exchange/internal/routes/handlers/transactions.go new file mode 100644 index 0000000..1787bca --- /dev/null +++ b/services/app-exchange/internal/routes/handlers/transactions.go @@ -0,0 +1,30 @@ +package handlers + +import ( + "context" + "errors" + "github.com/AppsLab-KE/be-go-gen-grpc/exchange" + "google.golang.org/protobuf/types/known/emptypb" +) + +func (h Handler) CreateTransaction(ctx context.Context, request *exchange.CreateTransactionRequest) (*exchange.CreateTransactionResponse, error) { + return nil, errors.New("not supported") +} + +func (h Handler) DeleteTransaction(ctx context.Context, request *exchange.DeleteTransactionRequest) (*emptypb.Empty, error) { + return nil, errors.New("not supported") + +} + +func (h Handler) UpdateTransaction(ctx context.Context, request *exchange.UpdateTransactionRequest) (*emptypb.Empty, error) { + return nil, errors.New("not supported") + +} + +func (h Handler) GetTransaction(ctx context.Context, request *exchange.GetTransactionRequest) (*exchange.Transaction, error) { + return nil, errors.New("not supported") +} + +func (h Handler) GetTransactionByAccount(ctx context.Context, request *exchange.GetTransactionByAccountRequest) (*exchange.GetTransactionByAccountResponse, error) { + return nil, errors.New("not supported") +} diff --git a/services/app-exchange/internal/routes/server/grpc-server.go b/services/app-exchange/internal/routes/server/grpc-server.go new file mode 100644 index 0000000..64859f4 --- /dev/null +++ b/services/app-exchange/internal/routes/server/grpc-server.go @@ -0,0 +1,82 @@ +package server + +import ( + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/config" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/core/repositories" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/core/services" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/core/storage" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/platform" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/routes/handlers" + "github.com/AppsLab-KE/be-go-gen-grpc/exchange" + log "github.com/sirupsen/logrus" + "google.golang.org/grpc" + "net" + "os" + "os/signal" + "syscall" +) + +type Server struct { + cfg config.Config +} + +func (s *Server) Run() { + log.Infof("DATABASE GRPC server initialising") + + // create database client + postgresClient, err := platform.NewDBServiceClient(s.cfg.DB) + if err != nil { + log.Panic("error:", err) + } + + // Create User storage + dbStorage := storage.NewDBStorage(postgresClient) + + // create repository + accountsRepo := repositories.NewAccountRepository(dbStorage) + exchangeRepo := repositories.NewExchangeRepository(dbStorage) + tradeRepo := repositories.NewTradeRepository(dbStorage) + transactionsRepo := repositories.NewTransactionRepository(dbStorage) + + // create services + accountsService := services.NewAccountsService(accountsRepo) + exchangeService := services.NewExchangeService(exchangeRepo) + tradeService := services.NewTradeService(tradeRepo, accountsRepo, exchangeRepo, transactionsRepo) + transactionService := services.NewTransactionService(transactionsRepo, accountsRepo) + + // create handler + grpcHandler := handlers.NewHandler(exchangeService, accountsService, tradeService, transactionService) + + // run server + lis, err := net.Listen("tcp", ":"+s.cfg.Server.Port) + if err != nil { + log.Fatal(err) + return + } + + grpcServer := grpc.NewServer() + exchange.RegisterExchangeServiceServer(grpcServer, grpcHandler) + + // Run the server + + // wait for interrupt signal to gracefully shutdown the server with + quit := make(chan os.Signal, 1) + signal.Notify(quit, os.Interrupt, syscall.SIGTERM) + + go func() { + err = grpcServer.Serve(lis) + if err != nil { + log.Fatal("cannot start apps server:", err) + return + } + }() + + <-quit + +} + +func NewServer(cfg config.Config) *Server { + return &Server{ + cfg: cfg, + } +} diff --git a/services/app-exchange/main.go b/services/app-exchange/main.go index 7905807..dfb0191 100644 --- a/services/app-exchange/main.go +++ b/services/app-exchange/main.go @@ -1,5 +1,18 @@ package main +import ( + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/config" + "github.com/AppsLab-KE/backend-everyshilling/services/app-exchange/internal/routes/server" + log "github.com/sirupsen/logrus" +) + func main() { + cfg, err := config.LoadFromEnv() + + if err != nil { + log.Panic("config error:", err) + } + grpcServer := server.NewServer(*cfg) + grpcServer.Run() } diff --git a/services/app-ratescron/.env.example b/services/app-ratescron/.env.example new file mode 100644 index 0000000..2ff4844 --- /dev/null +++ b/services/app-ratescron/.env.example @@ -0,0 +1,4 @@ +EXCHANGE_API_NAME=exchange-rate +EXCHANGE_API_KEY=5384cde6a9954790eaabc0d2 +DB_PORT=3001 +DB_HOST=app-db diff --git a/services/app-ratescron/Dockerfile b/services/app-ratescron/Dockerfile new file mode 100644 index 0000000..b1f7a88 --- /dev/null +++ b/services/app-ratescron/Dockerfile @@ -0,0 +1,39 @@ +# Initial stage: download modules +FROM ubuntu:latest as golang-builder + + +RUN apt-get update && apt-get install -y git cron golang +RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64; + + +FROM golang-builder AS app-builder +WORKDIR /app/ratescron + +# Copy go mod files +COPY go.mod go.sum \ + /app/ratescron/ + +RUN go mod download + +COPY . /app/ratescron + + +RUN go build -o /tmp/app-ratescron + +FROM app-builder AS prepare-bin + +COPY --from=app-builder /tmp/app-ratescron /usr/bin/ratescron-service + +RUN ls /etc/cron.d/ +# Create a cron job file +RUN echo "0 0 * * * /usr/bin/ratescron-service >> /var/log/cron.log 2>&1" > /etc/cron.d/rates-cron-job + +# Give execution rights to the cron job +RUN chmod 0644 /etc/cron.d/rates-cron-job + +# Apply the cron job +RUN crontab /etc/cron.d/rates-cron-job + +RUN touch /var/log/cron.log +# Run the command to start the cron service +CMD ["cron", "-f", "-L", "/var/log/cron.log"] \ No newline at end of file diff --git a/services/app-ratescron/README.md b/services/app-ratescron/README.md new file mode 100644 index 0000000..4a6fdbf --- /dev/null +++ b/services/app-ratescron/README.md @@ -0,0 +1,3 @@ +# Rates CRON + +A cron task for fetching Exchange rates \ No newline at end of file diff --git a/services/app-ratescron/config/config.go b/services/app-ratescron/config/config.go new file mode 100644 index 0000000..fa3cd04 --- /dev/null +++ b/services/app-ratescron/config/config.go @@ -0,0 +1,56 @@ +package config + +import ( + "fmt" + "os" + "strconv" +) + +type ExchangeApi struct { + Name string + APIKey string +} + +type DB struct { + Host string + Port int +} + +type Config struct { + API ExchangeApi + DB DB +} + +func LoadFromEnv() (*Config, error) { + var config Config + + if apiName, ok := os.LookupEnv("EXCHANGE_API_NAME"); ok { + config.API.Name = apiName + } else { + return nil, fmt.Errorf("missing EXCHANGE_API_NAME environment variable") + } + + if apiKey, ok := os.LookupEnv("EXCHANGE_API_KEY"); ok { + config.API.APIKey = apiKey + } else { + return nil, fmt.Errorf("missing EXCHANGE_API_KEY environment variable") + } + + if dbHost, ok := os.LookupEnv("DB_HOST"); ok { + config.DB.Host = dbHost + } else { + return nil, fmt.Errorf("missing DB_HOST environment variable") + } + + if dbPortStr, ok := os.LookupEnv("DB_PORT"); ok { + dbPort, err := strconv.Atoi(dbPortStr) + if err != nil { + return nil, fmt.Errorf("failed to parse DB_PORT: %w", err) + } + config.DB.Port = dbPort + } else { + return nil, fmt.Errorf("missing DB_PORT environment variable") + } + + return &config, nil +} diff --git a/services/app-ratescron/go.mod b/services/app-ratescron/go.mod new file mode 100644 index 0000000..0fccb2c --- /dev/null +++ b/services/app-ratescron/go.mod @@ -0,0 +1,19 @@ +module github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron + +go 1.20 + +require ( + github.com/AppsLab-KE/be-go-gen-grpc v0.0.13 + github.com/goccy/go-json v0.10.2 + github.com/sirupsen/logrus v1.9.2 + google.golang.org/grpc v1.54.0 +) + +require ( + github.com/golang/protobuf v1.5.2 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect + google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect + google.golang.org/protobuf v1.30.0 // indirect +) diff --git a/services/app-ratescron/go.sum b/services/app-ratescron/go.sum new file mode 100644 index 0000000..eaee366 --- /dev/null +++ b/services/app-ratescron/go.sum @@ -0,0 +1,38 @@ +github.com/AppsLab-KE/be-go-gen-grpc v0.0.13 h1:x3nsaE7AAiLI1xSKBtHkgzpjWY9sxSeVCD+0Vk9jW9g= +github.com/AppsLab-KE/be-go-gen-grpc v0.0.13/go.mod h1:F0zAKKv7O9FfplN/28CsWBRJu2ZArWNgIZbix6isDKY= +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/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= +github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/services/app-ratescron/internal/core/model/conversion-rate.go b/services/app-ratescron/internal/core/model/conversion-rate.go new file mode 100644 index 0000000..adb3f4f --- /dev/null +++ b/services/app-ratescron/internal/core/model/conversion-rate.go @@ -0,0 +1,8 @@ +package model + +type ConversionRate struct { + FromCurrency string + ToCurrency string + Rate float64 + DateUpdatedUnixUTC int64 +} diff --git a/services/app-ratescron/internal/core/ports/database.go b/services/app-ratescron/internal/core/ports/database.go new file mode 100644 index 0000000..7ad8396 --- /dev/null +++ b/services/app-ratescron/internal/core/ports/database.go @@ -0,0 +1,27 @@ +package ports + +import ( + "context" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "google.golang.org/grpc" +) + +type DBStorage interface { + HealthCheck(ctx context.Context, in *db.DefaultRequest, opts ...grpc.CallOption) (*db.HealthResponse, error) + // USERS + CreateUser(ctx context.Context, in *db.CreateUserReq, opts ...grpc.CallOption) (*db.CreateUserRes, error) + UpdateUser(ctx context.Context, in *db.UpdateUserReq, opts ...grpc.CallOption) (*db.UpdateUserRes, error) + GetPagedUsers(ctx context.Context, in *db.GetPagedUsersReq, opts ...grpc.CallOption) (*db.GetPagedUsersRes, error) + GetUserByField(ctx context.Context, in *db.GetByfieldReq, opts ...grpc.CallOption) (*db.GetByfieldRes, error) + CreateConversionRate(ctx context.Context, in *db.CreateConversionRateRequest, opts ...grpc.CallOption) (*db.CreateConversionRateResponse, error) + ReadConversionRate(ctx context.Context, in *db.ReadConversionRateRequest, opts ...grpc.CallOption) (*db.ReadConversionRateResponse, error) + UpdateConversionRate(ctx context.Context, in *db.UpdateConversionRateRequest, opts ...grpc.CallOption) (*db.UpdateConversionRateResponse, error) + DeleteConversionRate(ctx context.Context, in *db.DeleteConversionRateRequest, opts ...grpc.CallOption) (*db.DeleteConversionRateResponse, error) +} + +type DBRepository interface { + CreateConversionRate(ctx context.Context, in *db.CreateConversionRateRequest) (*db.CreateConversionRateResponse, error) + ReadConversionRate(ctx context.Context, in *db.ReadConversionRateRequest) (*db.ReadConversionRateResponse, error) + UpdateConversionRate(ctx context.Context, in *db.UpdateConversionRateRequest) (*db.UpdateConversionRateResponse, error) + DeleteConversionRate(ctx context.Context, in *db.DeleteConversionRateRequest) (*db.DeleteConversionRateResponse, error) +} diff --git a/services/app-ratescron/internal/core/ports/rates.go b/services/app-ratescron/internal/core/ports/rates.go new file mode 100644 index 0000000..1bd1af6 --- /dev/null +++ b/services/app-ratescron/internal/core/ports/rates.go @@ -0,0 +1,29 @@ +package ports + +import ( + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/model" +) + +type ConversionRateRepository interface { + // GetConversionRate returns the conversion rate for the given base currency + GetConversionRate(baseCurrency string) ([]model.ConversionRate, error) +} + +type ExchangeRateAPIStorage interface { + // GetRates returns the exchange rates for the given base currency + GetRates(baseCurrency string) ([]model.ConversionRate, error) +} + +type FixerAPIStorage interface { + // GetRates returns the exchange rates for the given base currency + GetRates(baseCurrency string) ([]model.ConversionRate, error) +} + +type RatesAPIRepository interface { + // GetRates returns the exchange rates for the given base currency + GetRates(baseCurrency string) ([]model.ConversionRate, error) +} + +type RatesUsecase interface { + FetchAndStoreRates() error +} diff --git a/services/app-ratescron/internal/core/repository/database.go b/services/app-ratescron/internal/core/repository/database.go new file mode 100644 index 0000000..8b0a661 --- /dev/null +++ b/services/app-ratescron/internal/core/repository/database.go @@ -0,0 +1,31 @@ +package repository + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/ports" + "github.com/AppsLab-KE/be-go-gen-grpc/db" +) + +type dbRepo struct { + storage ports.DBStorage +} + +func (d dbRepo) CreateConversionRate(ctx context.Context, in *db.CreateConversionRateRequest) (*db.CreateConversionRateResponse, error) { + return d.storage.CreateConversionRate(ctx, in) +} + +func (d dbRepo) ReadConversionRate(ctx context.Context, in *db.ReadConversionRateRequest) (*db.ReadConversionRateResponse, error) { + return d.storage.ReadConversionRate(ctx, in) +} + +func (d dbRepo) UpdateConversionRate(ctx context.Context, in *db.UpdateConversionRateRequest) (*db.UpdateConversionRateResponse, error) { + return d.storage.UpdateConversionRate(ctx, in) +} + +func (d dbRepo) DeleteConversionRate(ctx context.Context, in *db.DeleteConversionRateRequest) (*db.DeleteConversionRateResponse, error) { + return d.storage.DeleteConversionRate(ctx, in) +} + +func NewDBRepository(storage ports.DBStorage) ports.DBRepository { + return &dbRepo{storage: storage} +} diff --git a/services/app-ratescron/internal/core/repository/rates-api.go b/services/app-ratescron/internal/core/repository/rates-api.go new file mode 100644 index 0000000..8637d16 --- /dev/null +++ b/services/app-ratescron/internal/core/repository/rates-api.go @@ -0,0 +1,33 @@ +package repository + +import ( + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/config" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/model" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/ports" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/storage" +) + +type store interface { + GetRates(baseCurrency string) ([]model.ConversionRate, error) +} + +type ratesApi struct { + storage store +} + +func (r ratesApi) GetRates(baseCurrency string) ([]model.ConversionRate, error) { + return r.storage.GetRates(baseCurrency) +} + +func NewRatesAPIRepository(exchangeRateAPI config.ExchangeApi) ports.RatesAPIRepository { + var requestedStore store + switch exchangeRateAPI.Name { + case "fixer": + requestedStore = storage.NewFixerAPIStorage(exchangeRateAPI.APIKey) + case "exchange-rate": + requestedStore = storage.NewExchangeRateStorage(exchangeRateAPI.APIKey) + } + return &ratesApi{ + storage: requestedStore, + } +} diff --git a/services/app-ratescron/internal/core/storage/database.go b/services/app-ratescron/internal/core/storage/database.go new file mode 100644 index 0000000..dc285e4 --- /dev/null +++ b/services/app-ratescron/internal/core/storage/database.go @@ -0,0 +1,70 @@ +package storage + +import ( + "context" + "errors" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/config" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/ports" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/platform" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + "google.golang.org/grpc" +) + +var ( + ErrDbDown = errors.New("database service down") +) + +type dbStorage struct { + dbClient db.DbServiceClient +} + +func (d dbStorage) CreateConversionRate(ctx context.Context, in *db.CreateConversionRateRequest, opts ...grpc.CallOption) (*db.CreateConversionRateResponse, error) { + return d.dbClient.CreateConversionRate(ctx, in, opts...) +} + +func (d dbStorage) ReadConversionRate(ctx context.Context, in *db.ReadConversionRateRequest, opts ...grpc.CallOption) (*db.ReadConversionRateResponse, error) { + return d.dbClient.ReadConversionRate(ctx, in, opts...) +} + +func (d dbStorage) UpdateConversionRate(ctx context.Context, in *db.UpdateConversionRateRequest, opts ...grpc.CallOption) (*db.UpdateConversionRateResponse, error) { + return d.dbClient.UpdateConversionRate(ctx, in, opts...) +} + +func (d dbStorage) DeleteConversionRate(ctx context.Context, in *db.DeleteConversionRateRequest, opts ...grpc.CallOption) (*db.DeleteConversionRateResponse, error) { + return d.dbClient.DeleteConversionRate(ctx, in, opts...) +} + +// Not implemented +func (d dbStorage) UpdateUser(ctx context.Context, req *db.UpdateUserReq, opts ...grpc.CallOption) (*db.UpdateUserRes, error) { + return nil, errors.New("not implemented") +} + +func (d dbStorage) HealthCheck(ctx context.Context, req *db.DefaultRequest, opts ...grpc.CallOption) (*db.HealthResponse, error) { + return nil, errors.New("not implemented") + +} + +func (d dbStorage) CreateUser(ctx context.Context, req *db.CreateUserReq, opts ...grpc.CallOption) (*db.CreateUserRes, error) { + return nil, errors.New("not implemented") + +} + +func (d dbStorage) GetUserByField(ctx context.Context, req *db.GetByfieldReq, opts ...grpc.CallOption) (*db.GetByfieldRes, error) { + return nil, errors.New("not implemented") + +} + +func (d dbStorage) GetPagedUsers(ctx context.Context, req *db.GetPagedUsersReq, opts ...grpc.CallOption) (*db.GetPagedUsersRes, error) { + return nil, errors.New("not implemented") + +} + +func NewDbStorage(serviceCfg config.DB) (ports.DBStorage, error) { + client, err := platform.NewDBServiceClient(serviceCfg) + if err != nil { + return nil, err + } + return &dbStorage{ + dbClient: client, + }, nil +} diff --git a/services/app-ratescron/internal/core/storage/exchange-rate-api.go b/services/app-ratescron/internal/core/storage/exchange-rate-api.go new file mode 100644 index 0000000..4fcc731 --- /dev/null +++ b/services/app-ratescron/internal/core/storage/exchange-rate-api.go @@ -0,0 +1,23 @@ +package storage + +import ( + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/model" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/ports" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/platform" +) + +type exchangeRateAPIStorage struct { + api platform.ExchangeRateAPI +} + +func (e exchangeRateAPIStorage) GetRates(baseCurrency string) ([]model.ConversionRate, error) { + apiResponse, err := e.api.GetRates(baseCurrency) + if err != nil { + return nil, err + } + return apiResponse.ExtractedRates(), nil +} + +func NewExchangeRateStorage(apiKey string) ports.ExchangeRateAPIStorage { + return &exchangeRateAPIStorage{api: platform.NewExchangeRateAPI(apiKey)} +} diff --git a/services/app-ratescron/internal/core/storage/fixer-api.go b/services/app-ratescron/internal/core/storage/fixer-api.go new file mode 100644 index 0000000..3a2d435 --- /dev/null +++ b/services/app-ratescron/internal/core/storage/fixer-api.go @@ -0,0 +1,19 @@ +package storage + +import ( + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/ports" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/dto" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/platform" +) + +type fixerAPIStorage struct { + api platform.ExchangeRateAPI +} + +func (e fixerAPIStorage) GetRates(baseCurrency string) (*dto.ExchangeRateApiResponse, error) { + return e.api.GetRates(baseCurrency) +} + +func NewFixerAPIStorage(apiKey string) ports.FixerAPIStorage { + return &exchangeRateAPIStorage{api: platform.NewFixerAPI(apiKey)} +} diff --git a/services/app-ratescron/internal/dto/exchange-rate-api.go b/services/app-ratescron/internal/dto/exchange-rate-api.go new file mode 100644 index 0000000..140f9e0 --- /dev/null +++ b/services/app-ratescron/internal/dto/exchange-rate-api.go @@ -0,0 +1,31 @@ +package dto + +import "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/model" + +// ExchangeRateApiResponse is the response from the exchange rate API +// https://www.exchangerate-api.com/ +type ExchangeRateApiResponse struct { + Result string `json:"result"` + Documentation string `json:"documentation"` + TermsOfUse string `json:"terms_of_use"` + TimeLastUpdateUnix int64 `json:"time_last_update_unix"` + TimeLastUpdateUTC string `json:"time_last_update_utc"` + TimeNextUpdateUnix int64 `json:"time_next_update_unix"` + TimeNextUpdateUTC string `json:"time_next_update_utc"` + BaseCode string `json:"base_code"` + ConversionRates map[string]float64 `json:"conversion_rates"` +} + +// ExtractedRates returns the conversion rates from the API response +func (r *ExchangeRateApiResponse) ExtractedRates() []model.ConversionRate { + rates := make([]model.ConversionRate, 0) + for k, v := range r.ConversionRates { + rates = append(rates, model.ConversionRate{ + FromCurrency: r.BaseCode, + ToCurrency: k, + DateUpdatedUnixUTC: r.TimeLastUpdateUnix, + Rate: v, + }) + } + return rates +} diff --git a/services/app-ratescron/internal/dto/fixer-api.go b/services/app-ratescron/internal/dto/fixer-api.go new file mode 100644 index 0000000..4c1b276 --- /dev/null +++ b/services/app-ratescron/internal/dto/fixer-api.go @@ -0,0 +1,24 @@ +package dto + +import "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/model" + +type FixerAPIResponse struct { + Success bool `json:"success"` + Timestamp int64 `json:"timestamp"` + Base string `json:"base"` + Date string `json:"date"` + Rates map[string]float64 `json:"rates"` +} + +func (r *FixerAPIResponse) ExtractedRates() []model.ConversionRate { + rates := make([]model.ConversionRate, 0) + for k, v := range r.Rates { + rates = append(rates, model.ConversionRate{ + FromCurrency: r.Base, + ToCurrency: k, + DateUpdatedUnixUTC: r.Timestamp, + Rate: v, + }) + } + return rates +} diff --git a/services/app-ratescron/internal/platform/app-db.go b/services/app-ratescron/internal/platform/app-db.go new file mode 100644 index 0000000..83b4dab --- /dev/null +++ b/services/app-ratescron/internal/platform/app-db.go @@ -0,0 +1,25 @@ +package platform + +import ( + "fmt" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/config" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/ports" + "github.com/AppsLab-KE/be-go-gen-grpc/db" + log "github.com/sirupsen/logrus" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +var _ ports.DBStorage = (db.DbServiceClient)(nil) + +func NewDBServiceClient(config config.DB) (db.DbServiceClient, error) { + uri := fmt.Sprintf("%s:%d", config.Host, config.Port) + conn, err := grpc.Dial(uri, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, err + } + + log.Info("connected to app-db") + client := db.NewDbServiceClient(conn) + return client, nil +} diff --git a/services/app-ratescron/internal/platform/exchange-rate-api.go b/services/app-ratescron/internal/platform/exchange-rate-api.go new file mode 100644 index 0000000..0457c6e --- /dev/null +++ b/services/app-ratescron/internal/platform/exchange-rate-api.go @@ -0,0 +1,45 @@ +package platform + +import ( + "encoding/json" + "fmt" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/dto" + "net/http" +) + +const ( + ExchangeRateAPIURL = "https://v6.exchangerate-api.com/v6/%s/latest/%s" +) + +// ExchangeRateAPI is the interface for the exchange rate API +type ExchangeRateAPI interface { + GetRates(baseCurrency string) (*dto.ExchangeRateApiResponse, error) +} + +// NewExchangeRateAPI returns a new instance of the exchange rate ExchangeRateAPI interface +func NewExchangeRateAPI(apiKey string) ExchangeRateAPI { + return &ExchangeRateAPIImpl{ + APIKey: apiKey, + } +} + +// ExchangeRateAPIImpl is the implementation of the ExchangeRateAPI interface +type ExchangeRateAPIImpl struct { + APIKey string +} + +// GetRates returns the exchange rates for the given base currency +func (e *ExchangeRateAPIImpl) GetRates(baseCurrency string) (*dto.ExchangeRateApiResponse, error) { + url := fmt.Sprintf(ExchangeRateAPIURL, e.APIKey, baseCurrency) + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + var apiResponse dto.ExchangeRateApiResponse + err = json.NewDecoder(resp.Body).Decode(&apiResponse) + if err != nil { + return nil, err + } + return &apiResponse, nil +} diff --git a/services/app-ratescron/internal/platform/fixer-api.go b/services/app-ratescron/internal/platform/fixer-api.go new file mode 100644 index 0000000..91b45bc --- /dev/null +++ b/services/app-ratescron/internal/platform/fixer-api.go @@ -0,0 +1,45 @@ +package platform + +import ( + "fmt" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/dto" + "github.com/goccy/go-json" + "net/http" +) + +const ( + FixerAPIURL = "http://data.fixer.io/api/latest?access_key=%s&base=%s" +) + +// FixerAPI is the interface for fixer API +type FixerAPI interface { + GetRates(baseCurrency string) (*dto.FixerAPIResponse, error) +} + +// NewFixerAPI returns a new instance of the fixer API +func NewFixerAPI(apiKey string) ExchangeRateAPI { + return &ExchangeRateAPIImpl{ + APIKey: apiKey, + } +} + +// FixerAPIImpl is the implementation of the fixer API +type FixerAPIImpl struct { + APIKey string +} + +// GetRates returns the exchange rates for the given base currency +func (e *FixerAPIImpl) GetRates(baseCurrency string) (*dto.FixerAPIResponse, error) { + url := fmt.Sprintf(FixerAPIURL, e.APIKey, baseCurrency) + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + var apiResponse dto.FixerAPIResponse + err = json.NewDecoder(resp.Body).Decode(&apiResponse) + if err != nil { + return nil, err + } + return &apiResponse, nil +} diff --git a/services/app-ratescron/internal/usecase/fetch-rates.go b/services/app-ratescron/internal/usecase/fetch-rates.go new file mode 100644 index 0000000..e0fe73a --- /dev/null +++ b/services/app-ratescron/internal/usecase/fetch-rates.go @@ -0,0 +1,45 @@ +package usecase + +import ( + "context" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/ports" + "github.com/AppsLab-KE/be-go-gen-grpc/db" +) + +type usecase struct { + dbRepo ports.DBRepository + ratesRepo ports.RatesAPIRepository +} + +func NewUsecase(dbRepo ports.DBRepository, ratesRepo ports.RatesAPIRepository) ports.RatesUsecase { + return &usecase{ + dbRepo: dbRepo, + ratesRepo: ratesRepo, + } +} + +func (u usecase) FetchAndStoreRates() error { + rates, err := u.ratesRepo.GetRates("USD") + if err != nil { + return err + } + + var ratesRequest db.CreateConversionRateRequest + ratesRequest.ConversionRate = make([]*db.ConversionRate, len(rates)) + + for i, rate := range rates { + ratesRequest.ConversionRate[i] = &db.ConversionRate{ + FromCurrency: rate.FromCurrency, + ToCurrency: rate.ToCurrency, + Rate: rate.Rate, + DateUpdatedUnixUtc: rate.DateUpdatedUnixUTC, + } + } + + _, err = u.dbRepo.CreateConversionRate(context.Background(), &ratesRequest) + if err != nil { + return err + } + + return nil +} diff --git a/services/app-ratescron/main.go b/services/app-ratescron/main.go new file mode 100644 index 0000000..c7f4976 --- /dev/null +++ b/services/app-ratescron/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/config" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/repository" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/core/storage" + "github.com/AppsLab-KE/backend-everyshilling/services/app-ratescron/internal/usecase" + log "github.com/sirupsen/logrus" + "os" +) + +func main() { + cfg, err := config.LoadFromEnv() + + if err != nil { + log.Fatal(err) + os.Exit(1) + } + + dbStorage, err := storage.NewDbStorage(cfg.DB) + if err != nil { + log.Fatal(err) + os.Exit(1) + } + + ratesRepository := repository.NewRatesAPIRepository(cfg.API) + dbRepository := repository.NewDBRepository(dbStorage) + + uc := usecase.NewUsecase(dbRepository, ratesRepository) + + err = uc.FetchAndStoreRates() + if err != nil { + log.Fatal(err) + os.Exit(1) + } +} From d6171c7238e4c779b42fb4c34971f327159b1b45 Mon Sep 17 00:00:00 2001 From: MikeMwita Date: Mon, 3 Jul 2023 09:28:41 -0400 Subject: [PATCH 3/3] Updated changes --- sdk/go-proto-gen | 1 - .../internal/core/storage/exchange.go | 40 +++++++++---------- .../internal/core/usecase/exchange.go | 40 +++++++++---------- 3 files changed, 40 insertions(+), 41 deletions(-) delete mode 160000 sdk/go-proto-gen diff --git a/sdk/go-proto-gen b/sdk/go-proto-gen deleted file mode 160000 index b5df800..0000000 --- a/sdk/go-proto-gen +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b5df8001ee94280b9f2b9311f69935c1f3441586 diff --git a/services/app-auth/internal/core/storage/exchange.go b/services/app-auth/internal/core/storage/exchange.go index 2931e82..b62c334 100644 --- a/services/app-auth/internal/core/storage/exchange.go +++ b/services/app-auth/internal/core/storage/exchange.go @@ -8,11 +8,11 @@ import ( "google.golang.org/grpc" ) -type ExchangeStorageImpl struct { +type exchangeStorageImpl struct { exchangeClient exchange.ExchangeServiceClient } -func (e ExchangeStorageImpl) CreateConversionRate(ctx context.Context, in *exchange.CreateConversionRateRequest, opts ...grpc.CallOption) (*exchange.CreateConversionRateResponse, error) { +func (e exchangeStorageImpl) CreateConversionRate(ctx context.Context, in *exchange.CreateConversionRateRequest, opts ...grpc.CallOption) (*exchange.CreateConversionRateResponse, error) { response, err := e.exchangeClient.CreateConversionRate(ctx, in, opts...) if err != nil { return nil, err @@ -20,7 +20,7 @@ func (e ExchangeStorageImpl) CreateConversionRate(ctx context.Context, in *excha return response, nil } -func (e ExchangeStorageImpl) ReadConversionRate(ctx context.Context, in *exchange.ReadConversionRateRequest, opts ...grpc.CallOption) (*exchange.ReadConversionRateResponse, error) { +func (e exchangeStorageImpl) ReadConversionRate(ctx context.Context, in *exchange.ReadConversionRateRequest, opts ...grpc.CallOption) (*exchange.ReadConversionRateResponse, error) { response, err := e.exchangeClient.ReadConversionRate(ctx, in, opts...) if err != nil { return nil, err @@ -28,7 +28,7 @@ func (e ExchangeStorageImpl) ReadConversionRate(ctx context.Context, in *exchang return response, nil } -func (e ExchangeStorageImpl) UpdateConversionRate(ctx context.Context, in *exchange.UpdateConversionRateRequest, opts ...grpc.CallOption) (*exchange.UpdateConversionRateResponse, error) { +func (e exchangeStorageImpl) UpdateConversionRate(ctx context.Context, in *exchange.UpdateConversionRateRequest, opts ...grpc.CallOption) (*exchange.UpdateConversionRateResponse, error) { response, err := e.exchangeClient.UpdateConversionRate(ctx, in, opts...) if err != nil { return nil, err @@ -36,7 +36,7 @@ func (e ExchangeStorageImpl) UpdateConversionRate(ctx context.Context, in *excha return response, nil } -func (e ExchangeStorageImpl) DeleteConversionRate(ctx context.Context, in *exchange.DeleteConversionRateRequest, opts ...grpc.CallOption) (*exchange.DeleteConversionRateResponse, error) { +func (e exchangeStorageImpl) DeleteConversionRate(ctx context.Context, in *exchange.DeleteConversionRateRequest, opts ...grpc.CallOption) (*exchange.DeleteConversionRateResponse, error) { response, err := e.exchangeClient.DeleteConversionRate(ctx, in, opts...) if err != nil { return nil, err @@ -44,7 +44,7 @@ func (e ExchangeStorageImpl) DeleteConversionRate(ctx context.Context, in *excha return response, nil } -func (e ExchangeStorageImpl) CreateAccount(ctx context.Context, in *exchange.CreateAccountRequest, opts ...grpc.CallOption) (*exchange.CreateAccountResponse, error) { +func (e exchangeStorageImpl) CreateAccount(ctx context.Context, in *exchange.CreateAccountRequest, opts ...grpc.CallOption) (*exchange.CreateAccountResponse, error) { response, err := e.exchangeClient.CreateAccount(ctx, in, opts...) if err != nil { return nil, err @@ -52,7 +52,7 @@ func (e ExchangeStorageImpl) CreateAccount(ctx context.Context, in *exchange.Cre return response, nil } -func (e ExchangeStorageImpl) DeleteAccount(ctx context.Context, in *exchange.DeleteAccountRequest, opts ...grpc.CallOption) (*exchange.DeleteAccountResponse, error) { +func (e exchangeStorageImpl) DeleteAccount(ctx context.Context, in *exchange.DeleteAccountRequest, opts ...grpc.CallOption) (*exchange.DeleteAccountResponse, error) { response, err := e.exchangeClient.DeleteAccount(ctx, in, opts...) if err != nil { return nil, err @@ -60,7 +60,7 @@ func (e ExchangeStorageImpl) DeleteAccount(ctx context.Context, in *exchange.Del return response, nil } -func (e ExchangeStorageImpl) UpdateAccount(ctx context.Context, in *exchange.UpdateAccountRequest, opts ...grpc.CallOption) (*exchange.UpdateAccountResponse, error) { +func (e exchangeStorageImpl) UpdateAccount(ctx context.Context, in *exchange.UpdateAccountRequest, opts ...grpc.CallOption) (*exchange.UpdateAccountResponse, error) { response, err := e.exchangeClient.UpdateAccount(ctx, in, opts...) if err != nil { return nil, err @@ -68,7 +68,7 @@ func (e ExchangeStorageImpl) UpdateAccount(ctx context.Context, in *exchange.Upd return response, nil } -func (e ExchangeStorageImpl) SearchAccount(ctx context.Context, in *exchange.SearchAccountRequest, opts ...grpc.CallOption) (*exchange.SearchAccountResponse, error) { +func (e exchangeStorageImpl) SearchAccount(ctx context.Context, in *exchange.SearchAccountRequest, opts ...grpc.CallOption) (*exchange.SearchAccountResponse, error) { response, err := e.exchangeClient.SearchAccount(ctx, in, opts...) if err != nil { return nil, err @@ -76,7 +76,7 @@ func (e ExchangeStorageImpl) SearchAccount(ctx context.Context, in *exchange.Sea return response, nil } -func (e ExchangeStorageImpl) CreateTransaction(ctx context.Context, in *exchange.CreateTransactionRequest, opts ...grpc.CallOption) (*exchange.CreateTransactionResponse, error) { +func (e exchangeStorageImpl) CreateTransaction(ctx context.Context, in *exchange.CreateTransactionRequest, opts ...grpc.CallOption) (*exchange.CreateTransactionResponse, error) { response, err := e.exchangeClient.CreateTransaction(ctx, in, opts...) if err != nil { return nil, err @@ -84,7 +84,7 @@ func (e ExchangeStorageImpl) CreateTransaction(ctx context.Context, in *exchange return response, nil } -func (e ExchangeStorageImpl) DeleteTransaction(ctx context.Context, in *exchange.DeleteTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { +func (e exchangeStorageImpl) DeleteTransaction(ctx context.Context, in *exchange.DeleteTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { response, err := e.exchangeClient.DeleteTransaction(ctx, in, opts...) if err != nil { return nil, err @@ -92,7 +92,7 @@ func (e ExchangeStorageImpl) DeleteTransaction(ctx context.Context, in *exchange return response, nil } -func (e ExchangeStorageImpl) UpdateTransaction(ctx context.Context, in *exchange.UpdateTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { +func (e exchangeStorageImpl) UpdateTransaction(ctx context.Context, in *exchange.UpdateTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { response, err := e.exchangeClient.UpdateTransaction(ctx, in, opts...) if err != nil { return nil, err @@ -100,7 +100,7 @@ func (e ExchangeStorageImpl) UpdateTransaction(ctx context.Context, in *exchange return response, nil } -func (e ExchangeStorageImpl) GetTransaction(ctx context.Context, in *exchange.GetTransactionRequest, opts ...grpc.CallOption) (*exchange.Transaction, error) { +func (e exchangeStorageImpl) GetTransaction(ctx context.Context, in *exchange.GetTransactionRequest, opts ...grpc.CallOption) (*exchange.Transaction, error) { response, err := e.exchangeClient.GetTransaction(ctx, in, opts...) if err != nil { return nil, err @@ -108,7 +108,7 @@ func (e ExchangeStorageImpl) GetTransaction(ctx context.Context, in *exchange.Ge return response, nil } -func (e ExchangeStorageImpl) GetTransactionByAccount(ctx context.Context, in *exchange.GetTransactionByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTransactionByAccountResponse, error) { +func (e exchangeStorageImpl) GetTransactionByAccount(ctx context.Context, in *exchange.GetTransactionByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTransactionByAccountResponse, error) { response, err := e.exchangeClient.GetTransactionByAccount(ctx, in, opts...) if err != nil { return nil, err @@ -116,7 +116,7 @@ func (e ExchangeStorageImpl) GetTransactionByAccount(ctx context.Context, in *ex return response, nil } -func (e ExchangeStorageImpl) CreateTrade(ctx context.Context, in *exchange.CreateTradeRequest, opts ...grpc.CallOption) (*exchange.CreateTradeResponse, error) { +func (e exchangeStorageImpl) CreateTrade(ctx context.Context, in *exchange.CreateTradeRequest, opts ...grpc.CallOption) (*exchange.CreateTradeResponse, error) { response, err := e.exchangeClient.CreateTrade(ctx, in, opts...) if err != nil { return nil, err @@ -124,7 +124,7 @@ func (e ExchangeStorageImpl) CreateTrade(ctx context.Context, in *exchange.Creat return response, nil } -func (e ExchangeStorageImpl) DeleteTrade(ctx context.Context, in *exchange.DeleteTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { +func (e exchangeStorageImpl) DeleteTrade(ctx context.Context, in *exchange.DeleteTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { response, err := e.exchangeClient.DeleteTrade(ctx, in, opts...) if err != nil { return nil, err @@ -132,7 +132,7 @@ func (e ExchangeStorageImpl) DeleteTrade(ctx context.Context, in *exchange.Delet return response, nil } -func (e ExchangeStorageImpl) UpdateTrade(ctx context.Context, in *exchange.UpdateTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { +func (e exchangeStorageImpl) UpdateTrade(ctx context.Context, in *exchange.UpdateTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { response, err := e.exchangeClient.UpdateTrade(ctx, in, opts...) if err != nil { return nil, err @@ -140,7 +140,7 @@ func (e ExchangeStorageImpl) UpdateTrade(ctx context.Context, in *exchange.Updat return response, nil } -func (e ExchangeStorageImpl) GetTrade(ctx context.Context, in *exchange.GetTradeRequest, opts ...grpc.CallOption) (*exchange.Trade, error) { +func (e exchangeStorageImpl) GetTrade(ctx context.Context, in *exchange.GetTradeRequest, opts ...grpc.CallOption) (*exchange.Trade, error) { response, err := e.exchangeClient.GetTrade(ctx, in, opts...) if err != nil { return nil, err @@ -148,7 +148,7 @@ func (e ExchangeStorageImpl) GetTrade(ctx context.Context, in *exchange.GetTrade return response, nil } -func (e ExchangeStorageImpl) GetTradeByAccount(ctx context.Context, in *exchange.GetTradeByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTradeByAccountResponse, error) { +func (e exchangeStorageImpl) GetTradeByAccount(ctx context.Context, in *exchange.GetTradeByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTradeByAccountResponse, error) { response, err := e.exchangeClient.GetTradeByAccount(ctx, in, opts...) if err != nil { return nil, err @@ -157,7 +157,7 @@ func (e ExchangeStorageImpl) GetTradeByAccount(ctx context.Context, in *exchange } func NewExchangeStorageImpl(exchangeClient exchange.ExchangeServiceClient) adapters.ExchangeStorage { - return ExchangeStorageImpl{ + return &exchangeStorageImpl{ exchangeClient: exchangeClient, } } diff --git a/services/app-auth/internal/core/usecase/exchange.go b/services/app-auth/internal/core/usecase/exchange.go index d20a35a..cc15054 100644 --- a/services/app-auth/internal/core/usecase/exchange.go +++ b/services/app-auth/internal/core/usecase/exchange.go @@ -8,11 +8,11 @@ import ( "google.golang.org/grpc" ) -type ExchangeUseCase struct { +type exchangeUseCaseImpl struct { exchangeService adapters.ExchangeService } -func (e ExchangeUseCase) CreateConversionRate(ctx context.Context, in *exchange.CreateConversionRateRequest, opts ...grpc.CallOption) (*exchange.CreateConversionRateResponse, error) { +func (e exchangeUseCaseImpl) CreateConversionRate(ctx context.Context, in *exchange.CreateConversionRateRequest, opts ...grpc.CallOption) (*exchange.CreateConversionRateResponse, error) { // Call the exchange service method to create the conversion rate response, err := e.exchangeService.CreateConversionRate(ctx, in, opts...) if err != nil { @@ -21,7 +21,7 @@ func (e ExchangeUseCase) CreateConversionRate(ctx context.Context, in *exchange. return response, nil } -func (e ExchangeUseCase) ReadConversionRate(ctx context.Context, in *exchange.ReadConversionRateRequest, opts ...grpc.CallOption) (*exchange.ReadConversionRateResponse, error) { +func (e exchangeUseCaseImpl) ReadConversionRate(ctx context.Context, in *exchange.ReadConversionRateRequest, opts ...grpc.CallOption) (*exchange.ReadConversionRateResponse, error) { // Call the exchange service method to read the conversion rate response, err := e.exchangeService.ReadConversionRate(ctx, in, opts...) if err != nil { @@ -31,7 +31,7 @@ func (e ExchangeUseCase) ReadConversionRate(ctx context.Context, in *exchange.Re } -func (e ExchangeUseCase) UpdateConversionRate(ctx context.Context, in *exchange.UpdateConversionRateRequest, opts ...grpc.CallOption) (*exchange.UpdateConversionRateResponse, error) { +func (e exchangeUseCaseImpl) UpdateConversionRate(ctx context.Context, in *exchange.UpdateConversionRateRequest, opts ...grpc.CallOption) (*exchange.UpdateConversionRateResponse, error) { response, err := e.exchangeService.UpdateConversionRate(ctx, in, opts...) if err != nil { return nil, err @@ -39,7 +39,7 @@ func (e ExchangeUseCase) UpdateConversionRate(ctx context.Context, in *exchange. return response, nil } -func (e ExchangeUseCase) DeleteConversionRate(ctx context.Context, in *exchange.DeleteConversionRateRequest, opts ...grpc.CallOption) (*exchange.DeleteConversionRateResponse, error) { +func (e exchangeUseCaseImpl) DeleteConversionRate(ctx context.Context, in *exchange.DeleteConversionRateRequest, opts ...grpc.CallOption) (*exchange.DeleteConversionRateResponse, error) { response, err := e.exchangeService.DeleteConversionRate(ctx, in, opts...) if err != nil { return nil, err @@ -48,7 +48,7 @@ func (e ExchangeUseCase) DeleteConversionRate(ctx context.Context, in *exchange. } -func (e ExchangeUseCase) CreateAccount(ctx context.Context, in *exchange.CreateAccountRequest, opts ...grpc.CallOption) (*exchange.CreateAccountResponse, error) { +func (e exchangeUseCaseImpl) CreateAccount(ctx context.Context, in *exchange.CreateAccountRequest, opts ...grpc.CallOption) (*exchange.CreateAccountResponse, error) { response, err := e.exchangeService.CreateAccount(ctx, in, opts...) if err != nil { return nil, err @@ -56,7 +56,7 @@ func (e ExchangeUseCase) CreateAccount(ctx context.Context, in *exchange.CreateA return response, nil } -func (e ExchangeUseCase) DeleteAccount(ctx context.Context, in *exchange.DeleteAccountRequest, opts ...grpc.CallOption) (*exchange.DeleteAccountResponse, error) { +func (e exchangeUseCaseImpl) DeleteAccount(ctx context.Context, in *exchange.DeleteAccountRequest, opts ...grpc.CallOption) (*exchange.DeleteAccountResponse, error) { response, err := e.exchangeService.DeleteAccount(ctx, in, opts...) if err != nil { return nil, err @@ -64,7 +64,7 @@ func (e ExchangeUseCase) DeleteAccount(ctx context.Context, in *exchange.DeleteA return response, nil } -func (e ExchangeUseCase) UpdateAccount(ctx context.Context, in *exchange.UpdateAccountRequest, opts ...grpc.CallOption) (*exchange.UpdateAccountResponse, error) { +func (e exchangeUseCaseImpl) UpdateAccount(ctx context.Context, in *exchange.UpdateAccountRequest, opts ...grpc.CallOption) (*exchange.UpdateAccountResponse, error) { response, err := e.exchangeService.UpdateAccount(ctx, in, opts...) if err != nil { return nil, err @@ -72,7 +72,7 @@ func (e ExchangeUseCase) UpdateAccount(ctx context.Context, in *exchange.UpdateA return response, nil } -func (e ExchangeUseCase) SearchAccount(ctx context.Context, in *exchange.SearchAccountRequest, opts ...grpc.CallOption) (*exchange.SearchAccountResponse, error) { +func (e exchangeUseCaseImpl) SearchAccount(ctx context.Context, in *exchange.SearchAccountRequest, opts ...grpc.CallOption) (*exchange.SearchAccountResponse, error) { response, err := e.exchangeService.SearchAccount(ctx, in, opts...) if err != nil { return nil, err @@ -80,7 +80,7 @@ func (e ExchangeUseCase) SearchAccount(ctx context.Context, in *exchange.SearchA return response, nil } -func (e ExchangeUseCase) CreateTransaction(ctx context.Context, in *exchange.CreateTransactionRequest, opts ...grpc.CallOption) (*exchange.CreateTransactionResponse, error) { +func (e exchangeUseCaseImpl) CreateTransaction(ctx context.Context, in *exchange.CreateTransactionRequest, opts ...grpc.CallOption) (*exchange.CreateTransactionResponse, error) { response, err := e.exchangeService.CreateTransaction(ctx, in, opts...) if err != nil { return nil, err @@ -88,7 +88,7 @@ func (e ExchangeUseCase) CreateTransaction(ctx context.Context, in *exchange.Cre return response, nil } -func (e ExchangeUseCase) DeleteTransaction(ctx context.Context, in *exchange.DeleteTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { +func (e exchangeUseCaseImpl) DeleteTransaction(ctx context.Context, in *exchange.DeleteTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { response, err := e.exchangeService.DeleteTransaction(ctx, in, opts...) if err != nil { return nil, err @@ -96,7 +96,7 @@ func (e ExchangeUseCase) DeleteTransaction(ctx context.Context, in *exchange.Del return response, nil } -func (e ExchangeUseCase) UpdateTransaction(ctx context.Context, in *exchange.UpdateTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { +func (e exchangeUseCaseImpl) UpdateTransaction(ctx context.Context, in *exchange.UpdateTransactionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { response, err := e.exchangeService.UpdateTransaction(ctx, in, opts...) if err != nil { return nil, err @@ -104,7 +104,7 @@ func (e ExchangeUseCase) UpdateTransaction(ctx context.Context, in *exchange.Upd return response, nil } -func (e ExchangeUseCase) GetTransaction(ctx context.Context, in *exchange.GetTransactionRequest, opts ...grpc.CallOption) (*exchange.Transaction, error) { +func (e exchangeUseCaseImpl) GetTransaction(ctx context.Context, in *exchange.GetTransactionRequest, opts ...grpc.CallOption) (*exchange.Transaction, error) { response, err := e.exchangeService.GetTransaction(ctx, in, opts...) if err != nil { return nil, err @@ -112,7 +112,7 @@ func (e ExchangeUseCase) GetTransaction(ctx context.Context, in *exchange.GetTra return response, nil } -func (e ExchangeUseCase) GetTransactionByAccount(ctx context.Context, in *exchange.GetTransactionByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTransactionByAccountResponse, error) { +func (e exchangeUseCaseImpl) GetTransactionByAccount(ctx context.Context, in *exchange.GetTransactionByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTransactionByAccountResponse, error) { response, err := e.exchangeService.GetTransactionByAccount(ctx, in, opts...) if err != nil { return nil, err @@ -120,7 +120,7 @@ func (e ExchangeUseCase) GetTransactionByAccount(ctx context.Context, in *exchan return response, nil } -func (e ExchangeUseCase) CreateTrade(ctx context.Context, in *exchange.CreateTradeRequest, opts ...grpc.CallOption) (*exchange.CreateTradeResponse, error) { +func (e exchangeUseCaseImpl) CreateTrade(ctx context.Context, in *exchange.CreateTradeRequest, opts ...grpc.CallOption) (*exchange.CreateTradeResponse, error) { response, err := e.exchangeService.CreateTrade(ctx, in, opts...) if err != nil { return nil, err @@ -128,7 +128,7 @@ func (e ExchangeUseCase) CreateTrade(ctx context.Context, in *exchange.CreateTra return response, nil } -func (e ExchangeUseCase) DeleteTrade(ctx context.Context, in *exchange.DeleteTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { +func (e exchangeUseCaseImpl) DeleteTrade(ctx context.Context, in *exchange.DeleteTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { response, err := e.exchangeService.DeleteTrade(ctx, in, opts...) if err != nil { return nil, err @@ -136,7 +136,7 @@ func (e ExchangeUseCase) DeleteTrade(ctx context.Context, in *exchange.DeleteTra return response, nil } -func (e ExchangeUseCase) UpdateTrade(ctx context.Context, in *exchange.UpdateTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { +func (e exchangeUseCaseImpl) UpdateTrade(ctx context.Context, in *exchange.UpdateTradeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { response, err := e.exchangeService.UpdateTrade(ctx, in, opts...) if err != nil { return nil, err @@ -144,7 +144,7 @@ func (e ExchangeUseCase) UpdateTrade(ctx context.Context, in *exchange.UpdateTra return response, nil } -func (e ExchangeUseCase) GetTrade(ctx context.Context, in *exchange.GetTradeRequest, opts ...grpc.CallOption) (*exchange.Trade, error) { +func (e exchangeUseCaseImpl) GetTrade(ctx context.Context, in *exchange.GetTradeRequest, opts ...grpc.CallOption) (*exchange.Trade, error) { response, err := e.exchangeService.GetTrade(ctx, in, opts...) if err != nil { return nil, err @@ -152,7 +152,7 @@ func (e ExchangeUseCase) GetTrade(ctx context.Context, in *exchange.GetTradeRequ return response, nil } -func (e ExchangeUseCase) GetTradeByAccount(ctx context.Context, in *exchange.GetTradeByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTradeByAccountResponse, error) { +func (e exchangeUseCaseImpl) GetTradeByAccount(ctx context.Context, in *exchange.GetTradeByAccountRequest, opts ...grpc.CallOption) (*exchange.GetTradeByAccountResponse, error) { response, err := e.exchangeService.GetTradeByAccount(ctx, in, opts...) if err != nil { return nil, err @@ -161,7 +161,7 @@ func (e ExchangeUseCase) GetTradeByAccount(ctx context.Context, in *exchange.Get } func NewExchangeUseCase(exchangeService adapters.ExchangeService) adapters.ExchangeService { - return &ExchangeUseCase{ + return &exchangeUseCaseImpl{ exchangeService: exchangeService, } }